From 558531e3b53b1de46a58a89bd9d3919ef0407fa9 Mon Sep 17 00:00:00 2001 From: Michael Simacek Date: Mon, 5 Oct 2026 14:22:37 +0200 Subject: [PATCH] Implement native sys.getwindowsversion --- .../src/tests/test_sys.py | 135 ++++++++++++++++++ .../oracle/graal/python/PythonLanguage.java | 26 ++++ .../builtins/modules/SysModuleBuiltins.java | 56 ++------ .../python/runtime/EmulatedPosixSupport.java | 33 +++++ .../python/runtime/LoggingPosixSupport.java | 12 ++ .../python/runtime/NativePosixSupport.java | 25 ++++ .../python/runtime/PosixSupportLibrary.java | 8 ++ .../python/runtime/PreInitPosixSupport.java | 7 + graalpython/python-libposix/src/posix.c | 61 ++++++++ mx.graalpython/suite.py | 2 +- 10 files changed, 323 insertions(+), 42 deletions(-) create mode 100644 graalpython/com.oracle.graal.python.test/src/tests/test_sys.py diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sys.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sys.py new file mode 100644 index 0000000000..31f9e974aa --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sys.py @@ -0,0 +1,135 @@ +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import subprocess +import sys +import unittest + + +@unittest.skipUnless(sys.platform == "win32", "Windows only") +class WindowsVersionTests(unittest.TestCase): + def test_getwindowsversion_matches_os(self): + if sys.implementation.name == "graalpy" and __graalpython__.posix_module_backend() == "java": + self.skipTest("The Java backend uses the Java OS version property") + import ctypes + from ctypes import wintypes + + class VersionInfo(ctypes.Structure): + _fields_ = [ + ("size", wintypes.DWORD), + ("major", wintypes.DWORD), + ("minor", wintypes.DWORD), + ("build", wintypes.DWORD), + ("platform", wintypes.DWORD), + ("service_pack", wintypes.WCHAR * 128), + ("service_pack_major", wintypes.WORD), + ("service_pack_minor", wintypes.WORD), + ("suite_mask", wintypes.WORD), + ("product_type", wintypes.BYTE), + ("reserved", wintypes.BYTE), + ] + + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + get_version = kernel32.GetVersionExW + get_version.argtypes = [ctypes.POINTER(VersionInfo)] + get_version.restype = wintypes.BOOL + native_version = VersionInfo() + native_version.size = ctypes.sizeof(native_version) + self.assertTrue(get_version(ctypes.byref(native_version))) + + version = sys.getwindowsversion() + expected = (native_version.major, native_version.minor, native_version.build) + self.assertEqual(version[:3], expected) + self.assertEqual(version.platform, native_version.platform) + self.assertEqual(version.service_pack, native_version.service_pack) + self.assertEqual(version.service_pack_major, native_version.service_pack_major) + self.assertEqual(version.service_pack_minor, native_version.service_pack_minor) + self.assertEqual(version.suite_mask, native_version.suite_mask) + self.assertEqual(version.product_type, native_version.product_type) + self.assertGreater(version.build, 0) + + class FixedFileInfo(ctypes.Structure): + _fields_ = [(name, wintypes.DWORD) for name in ( + "signature", "struc_version", "file_version_ms", "file_version_ls", + "product_version_ms", "product_version_ls", "file_flags_mask", "file_flags", + "file_os", "file_type", "file_subtype", "file_date_ms", "file_date_ls", + )] + + get_module_handle = kernel32.GetModuleHandleW + get_module_handle.argtypes = [wintypes.LPCWSTR] + get_module_handle.restype = wintypes.HMODULE + get_module_filename = kernel32.GetModuleFileNameW + get_module_filename.argtypes = [wintypes.HMODULE, wintypes.LPWSTR, wintypes.DWORD] + get_module_filename.restype = wintypes.DWORD + path = ctypes.create_unicode_buffer(260) + self.assertTrue(get_module_filename(get_module_handle("kernel32.dll"), path, len(path))) + version_dll = ctypes.WinDLL("version", use_last_error=True) + get_size = version_dll.GetFileVersionInfoSizeW + get_size.argtypes = [wintypes.LPCWSTR, ctypes.POINTER(wintypes.DWORD)] + get_size.restype = wintypes.DWORD + size = get_size(path.value, None) + self.assertGreater(size, 0) + block = ctypes.create_string_buffer(size) + get_info = version_dll.GetFileVersionInfoW + get_info.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, wintypes.LPVOID] + get_info.restype = wintypes.BOOL + self.assertTrue(get_info(path.value, 0, size, block)) + query_value = version_dll.VerQueryValueW + query_value.argtypes = [wintypes.LPCVOID, wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_void_p), + ctypes.POINTER(wintypes.UINT)] + query_value.restype = wintypes.BOOL + value = ctypes.c_void_p() + length = wintypes.UINT() + self.assertTrue(query_value(block, "", ctypes.byref(value), ctypes.byref(length))) + info = ctypes.cast(value, ctypes.POINTER(FixedFileInfo)).contents + self.assertEqual(version.platform_version, ( + info.product_version_ms >> 16, info.product_version_ms & 0xffff, info.product_version_ls >> 16, + )) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy backend selection") + def test_getwindowsversion_java_backend(self): + for java_version, expected in [("10.0", (10, 0, 0)), ("10.0.12345", (10, 0, 12345))]: + with self.subTest(java_version=java_version): + result = subprocess.run( + [sys.executable, "--experimental-options", "--python.PosixModuleBackend=java", + "--vm.Dos.version=" + java_version, "-c", + "import sys; print(sys.getwindowsversion()[:3]); print(sys.getwindowsversion().platform_version)"], + capture_output=True, text=True, check=True, + ) + self.assertEqual(result.stdout.splitlines(), [str(expected), str(expected)]) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index e48ba41e93..25f73178cd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -82,6 +82,7 @@ import com.oracle.graal.python.compiler.ParserCallbacksImpl; import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler; import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler.BytecodeDSLCompilerResult; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallDispatchers; @@ -102,6 +103,9 @@ import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; +import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.PythonImageBuildOptions; @@ -140,6 +144,7 @@ import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.EncapsulatingNodeReference; import com.oracle.truffle.api.nodes.ExecutableNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -1063,6 +1068,27 @@ public CaseMap.Title getCachedICUTitleCaser() { return cachedICUTitleCaser; } + @CompilationFinal private WindowsVersion windowsVersion; + + public WindowsVersion getWindowsVersion(Node node) { + if (windowsVersion == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + assert !ImageInfo.inImageBuildtimeCode(); + try { + windowsVersion = PosixSupportLibrary.getUncached().getWindowsVersion(PythonContext.get(node).getPosixSupport()); + } catch (PosixException e) { + EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); + Node previousNode = encapsulating.set(node); + try { + throw PConstructAndRaiseNode.getUncached().raiseOSErrorFromPosixException(null, e); + } finally { + encapsulating.set(previousNode); + } + } + } + return windowsVersion; + } + @Override protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { if (singleThreaded) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index c3263bd19b..6d1622df5b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -255,6 +255,7 @@ import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonContext.CApiState; import com.oracle.graal.python.runtime.PythonOptions; @@ -2432,50 +2433,23 @@ protected ArgumentClinicProvider getArgumentClinic() { @Builtin(name = "getwindowsversion", minNumOfPositionalArgs = 0, os = PLATFORM_WIN32) @GenerateNodeFactory - abstract static class Getwindowsversion extends PythonBuiltinNode { - static int[] CACHED_VERSION_INFO = null; - static int PLATFORM = 2; + abstract static class GetWindowsVersion extends PythonBuiltinNode { + @Specialization(guards = "isSingleContext()") + static PTuple getVersionCached( + @Bind PythonLanguage language, + @Cached(value = "getVersion($node, language)", neverDefault = true) PTuple version) { + return version; + } - @Specialization - PTuple getVersion( + @Specialization(replaces = "getVersionCached") + static PTuple getVersion( + @Bind Node inliningTarget, @Bind PythonLanguage language) { - if (CACHED_VERSION_INFO == null) { - cacheVersion(); - } + WindowsVersion versionInfo = language.getWindowsVersion(inliningTarget); return PFactory.createStructSeq(language, WINDOWS_VER_DESC, - CACHED_VERSION_INFO[0], CACHED_VERSION_INFO[1], CACHED_VERSION_INFO[2], - PLATFORM, T_EMPTY_STRING, 0, 0, 0, 1, - PFactory.createTuple(language, CACHED_VERSION_INFO)); - } - - @TruffleBoundary - static void cacheVersion() { - String[] winvers = System.getProperty("os.version", "10.0.20000").split("\\."); - int major = 0; - int minor = 0; - int build = 0; - if (winvers.length > 0) { - try { - major = Integer.parseInt(winvers[0]); - } catch (NumberFormatException e) { - // use default - } - } - if (winvers.length > 1) { - try { - minor = Integer.parseInt(winvers[1]); - } catch (NumberFormatException e) { - // use default - } - } - if (winvers.length > 2) { - try { - build = Integer.parseInt(winvers[2]); - } catch (NumberFormatException e) { - // use default - } - } - CACHED_VERSION_INFO = new int[]{major, minor, build}; + versionInfo.major(), versionInfo.minor(), versionInfo.build(), + versionInfo.platform(), versionInfo.servicePack(), versionInfo.servicePackMajor(), versionInfo.servicePackMinor(), versionInfo.suiteMask(), versionInfo.productType(), + PFactory.createTuple(language, new int[]{versionInfo.platformMajor(), versionInfo.platformMinor(), versionInfo.platformBuild()})); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java index a77c43c72e..92241c2cf8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java @@ -250,6 +250,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.graal.python.runtime.exception.PythonExitException; import com.oracle.graal.python.util.FileDeleteShutdownHook; import com.oracle.graal.python.util.IPAddressUtil; @@ -1515,6 +1516,38 @@ public Object[] uname( }; } + @ExportMessage + @TruffleBoundary + @SuppressWarnings("static-method") + public WindowsVersion getWindowsVersion() { + String[] winvers = System.getProperty("os.version", "10.0.20000").split("\\."); + int major = 0; + int minor = 0; + int build = 0; + if (winvers.length > 0) { + try { + major = Integer.parseInt(winvers[0]); + } catch (NumberFormatException e) { + // use default + } + } + if (winvers.length > 1) { + try { + minor = Integer.parseInt(winvers[1]); + } catch (NumberFormatException e) { + // use default + } + } + if (winvers.length > 2) { + try { + build = Integer.parseInt(winvers[2]); + } catch (NumberFormatException e) { + // use default + } + } + return new WindowsVersion(major, minor, build, 2, T_EMPTY_STRING, 0, 0, 0, 1, major, minor, build); + } + @TruffleBoundary private static String getOsVersion() { return System.getProperty("os.version", ""); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java index c17800ebcc..f6407c5abc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java @@ -65,6 +65,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; @@ -485,6 +486,17 @@ final Object[] uname( } } + @ExportMessage + final WindowsVersion getWindowsVersion( + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("getWindowsVersion", ""); + try { + return logExit("getWindowsVersion", "%s", lib.getWindowsVersion(delegate)); + } catch (PosixException e) { + throw logException("getWindowsVersion", e); + } + } + @ExportMessage final void unlinkat(int dirFd, Object pathname, boolean rmdir, @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java index 0606acc0ee..0653be98fe 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java @@ -123,6 +123,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; import com.oracle.graal.python.runtime.nativeaccess.NativeLibraryLoadException; @@ -290,6 +291,9 @@ abstract static class PosixNativeFunctionInvoker { @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, POINTER, POINTER, POINTER, SINT32}) abstract int call_uname(long sysname, long nodename, long release, long version, long machine, int size); + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_get_windows_version(long out, long servicePack); + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) abstract int call_unlinkat(int dirFd, long pathname, int rmdir); @@ -1176,6 +1180,27 @@ public Object[] uname( } } + @ExportMessage + @TruffleBoundary + public WindowsVersion getWindowsVersion() throws PosixException { + long out = NativeMemory.mallocIntArray(11); + // OSVERSIONINFOEXW.szCSDVersion contains 128 UTF-16 code units. + long servicePack = NativeMemory.mallocByteArray(256); + try { + if (posixNativeFunctionInvoker.call_get_windows_version(out, servicePack) != 0) { + throw getErrnoAndThrowPosixException(); + } + int[] info = NativeMemory.readIntArrayElements(out, 0, 11); + byte[] buffer = new byte[256]; + NativeMemory.readByteArrayElements(servicePack, 0, buffer, 0, buffer.length); + TruffleString servicePackString = TruffleString.fromByteArrayUncached(buffer, 0, findWideZero(buffer), UTF_16LE, false).switchEncodingUncached(TS_ENCODING); + return new WindowsVersion(info[0], info[1], info[2], info[3], servicePackString, info[4], info[5], info[6], info[7], info[8], info[9], info[10]); + } finally { + NativeMemory.free(servicePack); + NativeMemory.free(out); + } + } + @ExportMessage public void unlinkat(int dirFd, Object pathname, boolean rmdir) throws PosixException { long pathnamePtr = pathToNativeCString(pathname); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java index 99dd66312a..1d4ddb47be 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java @@ -158,6 +158,14 @@ public abstract class PosixSupportLibrary extends Library { public abstract Object[] uname(Object receiver) throws PosixException; + public record WindowsVersion(int major, int minor, int build, int platform, TruffleString servicePack, + int servicePackMajor, int servicePackMinor, int suiteMask, int productType, + int platformMajor, int platformMinor, int platformBuild) { + } + + /** Returns the Windows version and product information. */ + public abstract WindowsVersion getWindowsVersion(Object receiver) throws PosixException; + public abstract void unlinkat(Object receiver, int dirFd, Object pathname, boolean rmdir) throws PosixException; public abstract void linkat(Object receiver, int oldFdDir, Object oldPath, int newFdDir, Object newPath, int flags) throws PosixException; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java index 1c7f61128a..eff964a8bd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java @@ -63,6 +63,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.WindowsVersion; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.library.CachedLibrary; @@ -404,6 +405,12 @@ final Object[] uname(@CachedLibrary("this.nativePosixSupport") PosixSupportLibra return nativeLib.uname(nativePosixSupport); } + @ExportMessage + final WindowsVersion getWindowsVersion(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + return nativeLib.getWindowsVersion(nativePosixSupport); + } + @ExportMessage final void unlinkat(int dirFd, Object pathname, boolean rmdir, @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { diff --git a/graalpython/python-libposix/src/posix.c b/graalpython/python-libposix/src/posix.c index a371d83e2f..df8d2bb45b 100644 --- a/graalpython/python-libposix/src/posix.c +++ b/graalpython/python-libposix/src/posix.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -1459,6 +1460,60 @@ GP_EXPORT int32_t call_fstatvfs(int32_t fd, int64_t *out) { } GP_EXPORT int32_t call_uname(char *sysname, char *nodename, char *release, char *version, char *machine, int32_t size) { return unsupported(); } +static void get_windows_version_from_kernel32(int32_t *out) { + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + wchar_t kernel32_path[MAX_PATH]; + if (kernel32 == NULL || !GetModuleFileNameW(kernel32, kernel32_path, MAX_PATH)) { + return; + } + DWORD verblock_size = GetFileVersionInfoSizeW(kernel32_path, NULL); + if (verblock_size == 0) { + return; + } + void *verblock = malloc(verblock_size); + if (verblock == NULL) { + return; + } + VS_FIXEDFILEINFO *ffi; + UINT ffi_len; + if (GetFileVersionInfoW(kernel32_path, 0, verblock_size, verblock) && + VerQueryValueW(verblock, L"", (void **) &ffi, &ffi_len)) { + out[0] = (int32_t) HIWORD(ffi->dwProductVersionMS); + out[1] = (int32_t) LOWORD(ffi->dwProductVersionMS); + out[2] = (int32_t) HIWORD(ffi->dwProductVersionLS); + } + free(verblock); +} + +// Like CPython, pass the deprecated GetVersionExW result through to the caller. +#pragma warning(push) +#pragma warning(disable:4996) +GP_EXPORT int32_t call_get_windows_version(int32_t *out, wchar_t *service_pack) { + OSVERSIONINFOEXW version = {0}; + version.dwOSVersionInfoSize = sizeof(version); + if (!GetVersionExW((OSVERSIONINFOW *) &version)) { + set_win_errno(GetLastError()); + return -1; + } + out[0] = (int32_t) version.dwMajorVersion; + out[1] = (int32_t) version.dwMinorVersion; + out[2] = (int32_t) version.dwBuildNumber; + out[3] = (int32_t) version.dwPlatformId; + out[4] = (int32_t) version.wServicePackMajor; + out[5] = (int32_t) version.wServicePackMinor; + out[6] = (int32_t) version.wSuiteMask; + out[7] = (int32_t) version.wProductType; + memcpy(service_pack, version.szCSDVersion, sizeof(version.szCSDVersion)); + // Compatibility mode can affect GetVersionExW. Read the diagnostic version + // from kernel32.dll, falling back to the main version if the lookup fails. + out[8] = out[0]; + out[9] = out[1]; + out[10] = out[2]; + get_windows_version_from_kernel32(out + 8); + return 0; +} +#pragma warning(pop) + GP_EXPORT int32_t call_unlinkat(int32_t dirFd, const wchar_t *pathname, int32_t rmdir) { if (!is_default_dir_fd(dirFd)) { return unsupported(); @@ -2675,6 +2730,12 @@ int32_t call_uname(char *sysname, char *nodename, char *release, char *version, return result; } +int32_t call_get_windows_version(int32_t *out, void *service_pack) { + errno = ENOSYS; + capture_errno(); + return -1; +} + int32_t call_unlinkat(int32_t dirFd, const char *pathname, int32_t rmdir) { CAPTURE_ERRNO_AND_RETURN(-1, unlinkat(dirFd, pathname, rmdir ? AT_REMOVEDIR : 0)); } diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 5cfd9c0866..790d94147b 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -811,7 +811,7 @@ "os_arch": { "windows": { "": { - "ldlibs": ["Ws2_32.lib"], + "ldlibs": ["Ws2_32.lib", "Version.lib"], "multitarget": { "libc": ["default"], },