From f1aada8cc6419c3c9934fa880c8f6e5c59e23385 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 13 Mar 2023 13:08:19 +0200 Subject: Windows --- libbutl/host-os-release.cxx | 85 ++++++++++++++++++++++++++++++++++++++++ libbutl/host-os-release.hxx | 13 ++++++ tests/host-os-release/driver.cxx | 2 +- tests/host-os-release/testscript | 17 +++++++- 4 files changed, 115 insertions(+), 2 deletions(-) diff --git a/libbutl/host-os-release.cxx b/libbutl/host-os-release.cxx index fff05f7..f13f62c 100644 --- a/libbutl/host-os-release.cxx +++ b/libbutl/host-os-release.cxx @@ -8,11 +8,16 @@ #include #include +#include #include #include #include // file_exists() #include // parse_quoted() +#ifdef _WIN32 +# include +#endif + using namespace std; namespace butl @@ -201,6 +206,83 @@ namespace butl } } + static os_release + host_os_release_windows () + { +#ifdef _WIN32 + // The straightforward way to get the version would be the GetVersionEx() + // Win32 function. However, if the application is built with a certain + // assembly manifest, this function will return the version the + // application was built for rather than what's actually running. + // + // The other plausible options are to call the `ver` program and parse it + // output (of questionable regularity) or to call RtlGetVersion(). The + // latter combined with GetProcAddress() seems to be a widely-used + // approach, so we are going with that (seeing that we employ a similar + // technique in quite a few places). + // + HMODULE nh (GetModuleHandle ("ntdll.dll")); + if (nh == nullptr) + throw runtime_error ("unable to get handle to ntdll.dll"); + + using RtlGetVersion = LONG /*NTSTATUS*/ (WINAPI*)(PRTL_OSVERSIONINFOW); + + RtlGetVersion gv ( + function_cast ( + GetProcAddress (nh, "RtlGetVersion"))); + + // RtlGetVersion() is available from Windows 2000 which is way before + // anything we might possibly care about (e.g., XP or 7). + // + if (gv == nullptr) + throw runtime_error ("unable to get address of RtlGetVersion()"); + + RTL_OSVERSIONINFOW vi; + vi.dwOSVersionInfoSize = sizeof (vi); + gv (&vi); // Always succeeds, according to documentation. + + // Ok, the real mess starts here. Here is how the commonly known Windows + // versions correspond to the major/minor/build numbers and how we will + // map them (note that there are also Server versions in the mix; see the + // OSVERSIONINFOEXW struct documentation for the complete picture): + // + // major minor build mapped + // Windows 11 10 0 >=22000 11 + // Windows 10 10 0 <22000 10 + // Windows 8.1 6 3 8.1 + // Windows 8 6 2 8 + // Windows 7 6 1 7 + // Windows Vista 6 0 6 + // Windows XP Pro/64-bit 5 2 5.2 + // Windows XP 5 1 5.1 + // Windows 2000 5 0 5 + // + // Based on this it's probably not wise to try to map any future versions + // automatically. + // + string v; + if (vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0) + { + v = vi.dwBuildNumber >= 22000 ? "11" : "10"; + } + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 3) v = "8.1"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 2) v = "8"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 1) v = "7"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 0) v = "6"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 2) v = "5.2"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 1) v = "5.1"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 0) v = "5"; + else throw ("unknown windows version " + + std::to_string (vi.dwMajorVersion) + '.' + + std::to_string (vi.dwMinorVersion) + '.' + + std::to_string (vi.dwBuildNumber)); + + return os_release {"windows", {}, move (v), "", "Windows", "", ""}; +#else + throw runtime_error ("unexpected host operating system"); +#endif + } + optional host_os_release (const target_triplet& h) { @@ -213,6 +295,9 @@ namespace butl if (c == "macos") return host_os_release_macos (); + if (c == "windows") + return host_os_release_windows (); + if (c == "bsd") { // @@ TODO: ideally we would want to run uname and obtain the actual diff --git a/libbutl/host-os-release.hxx b/libbutl/host-os-release.hxx index 9fd24a2..058afdc 100644 --- a/libbutl/host-os-release.hxx +++ b/libbutl/host-os-release.hxx @@ -38,6 +38,19 @@ namespace butl // Note that for Mac OS, the version is the Mac OS version (as printed by // sw_vers) rather than Darwin version (as printed by uname). // + // For Windows we currently do not distinguish the Server edition and the + // version mapping is as follows: + // + // Windows 11 11 + // Windows 10 10 + // Windows 8.1 8.1 + // Windows 8 8 + // Windows 7 7 + // Windows Vista 6 + // Windows XP Pro/64-bit 5.2 + // Windows XP 5.1 + // Windows 2000 5 + // // Note that version_id may be empty, for example, on Debian testing: // // {"debian", {}, "", "", diff --git a/tests/host-os-release/driver.cxx b/tests/host-os-release/driver.cxx index 3b40634..249cbff 100644 --- a/tests/host-os-release/driver.cxx +++ b/tests/host-os-release/driver.cxx @@ -1,4 +1,4 @@ -// file : host-os-release/driver.cxx -*- C++ -*- +// file : tests/host-os-release/driver.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include diff --git a/tests/host-os-release/testscript b/tests/host-os-release/testscript index c340da3..a18aa74 100644 --- a/tests/host-os-release/testscript +++ b/tests/host-os-release/testscript @@ -1,4 +1,4 @@ -# file : bpkg/host-os-release.test.testscript +# file : tests/host-os-release/testscript # license : MIT; see accompanying LICENSE file : linux @@ -206,3 +206,18 @@ if ($build.host.system == 'freebsd') EOO } + +: windows +: +if ($build.host.system == 'windows') +{ + $* $build.host >>~/EOO/ + windows + + /[0-9]+(\.[0-9]+)?/ + + Windows + + + EOO +} -- cgit v1.1