| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmCMakeHostSystemInformationCommand.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <initializer_list> | |
| #include <map> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cm/type_traits> | |
| #include <cmext/string_view> | |
| #if !defined(_WIN32) | |
| # include <langinfo.h> | |
| #endif | |
| #include "cmsys/FStream.hxx" | |
| #include "cmsys/Glob.hxx" | |
| #include "cmsys/String.h" | |
| #include "cmsys/SystemInformation.hxx" | |
| #include "cmArgumentParser.h" | |
| #include "cmExecutionStatus.h" | |
| #include "cmList.h" | |
| #include "cmMakefile.h" | |
| #include "cmPolicies.h" | |
| #include "cmRange.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmValue.h" | |
| #include "cmWindowsRegistry.h" | |
| #ifdef _WIN32 | |
| # include "cmAlgorithms.h" | |
| # include "cmGlobalGenerator.h" | |
| # include "cmGlobalVisualStudio10Generator.h" | |
| # include "cmGlobalVisualStudioVersionedGenerator.h" | |
| # include "cmVSSetupHelper.h" | |
| #endif | |
| namespace { | |
| std::string const DELIM[2] = { {}, ";" }; | |
| // BEGIN Private functions | |
| template <typename T> | |
| cm::enable_if_t<std::is_arithmetic<T>::value, std::string> ValueToString( | |
| T const& value) | |
| { | |
| return std::to_string(value); | |
| } | |
| std::string ValueToString(char const* const value) | |
| { | |
| return value ? value : std::string{}; | |
| } | |
| std::string ValueToString(std::string const& value) | |
| { | |
| return value; | |
| } | |
| cm::optional<std::string> GetValue(cmsys::SystemInformation& info, | |
| std::string const& key) | |
| { | |
| if (key == "NUMBER_OF_LOGICAL_CORES"_s) { | |
| return ValueToString(info.GetNumberOfLogicalCPU()); | |
| } | |
| if (key == "NUMBER_OF_PHYSICAL_CORES"_s) { | |
| return ValueToString(info.GetNumberOfPhysicalCPU()); | |
| } | |
| if (key == "HOSTNAME"_s) { | |
| return ValueToString(info.GetHostname()); | |
| } | |
| if (key == "FQDN"_s) { | |
| return ValueToString(info.GetFullyQualifiedDomainName()); | |
| } | |
| if (key == "LOCALE_CHARSET"_s) { | |
| #if defined(_WIN32) | |
| // On Windows we always use UTF-8. | |
| return ValueToString("UTF-8"); | |
| #else | |
| // On non-Windows platforms we use the locale's encoding. | |
| if (char const* charset = nl_langinfo(CODESET)) { | |
| if (cmsysString_strcasecmp(charset, "utf-8") == 0 || | |
| cmsysString_strcasecmp(charset, "utf8") == 0) { | |
| charset = "UTF-8"; | |
| } | |
| return ValueToString(charset); | |
| } | |
| return ValueToString(""); | |
| #endif | |
| } | |
| if (key == "TOTAL_VIRTUAL_MEMORY"_s) { | |
| return ValueToString(info.GetTotalVirtualMemory()); | |
| } | |
| if (key == "AVAILABLE_VIRTUAL_MEMORY"_s) { | |
| return ValueToString(info.GetAvailableVirtualMemory()); | |
| } | |
| if (key == "TOTAL_PHYSICAL_MEMORY"_s) { | |
| return ValueToString(info.GetTotalPhysicalMemory()); | |
| } | |
| if (key == "AVAILABLE_PHYSICAL_MEMORY"_s) { | |
| return ValueToString(info.GetAvailablePhysicalMemory()); | |
| } | |
| if (key == "IS_64BIT"_s) { | |
| return ValueToString(info.Is64Bits()); | |
| } | |
| if (key == "HAS_FPU"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_FPU)); | |
| } | |
| if (key == "HAS_MMX"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX)); | |
| } | |
| if (key == "HAS_MMX_PLUS"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS)); | |
| } | |
| if (key == "HAS_SSE"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE)); | |
| } | |
| if (key == "HAS_SSE2"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2)); | |
| } | |
| if (key == "HAS_SSE_FP"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_SSE_FP)); | |
| } | |
| if (key == "HAS_SSE_MMX"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_SSE_MMX)); | |
| } | |
| if (key == "HAS_AMD_3DNOW"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW)); | |
| } | |
| if (key == "HAS_AMD_3DNOW_PLUS"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS)); | |
| } | |
| if (key == "HAS_IA64"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64)); | |
| } | |
| if (key == "HAS_SERIAL_NUMBER"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER)); | |
| } | |
| if (key == "HAS_APIC"_s) { | |
| return ValueToString( | |
| info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_APIC)); | |
| } | |
| if (key == "HAS_L1_CACHE"_s) { | |
| return ValueToString(info.DoesCPUSupportFeature( | |
| cmsys::SystemInformation::CPU_FEATURE_L1CACHE)); | |
| } | |
| if (key == "PROCESSOR_NAME"_s) { | |
| return ValueToString(info.GetExtendedProcessorName()); | |
| } | |
| if (key == "PROCESSOR_DESCRIPTION"_s) { | |
| return info.GetCPUDescription(); | |
| } | |
| if (key == "PROCESSOR_SERIAL_NUMBER"_s) { | |
| return ValueToString(info.GetProcessorSerialNumber()); | |
| } | |
| if (key == "OS_NAME"_s) { | |
| return ValueToString(info.GetOSName()); | |
| } | |
| if (key == "OS_RELEASE"_s) { | |
| return ValueToString(info.GetOSRelease()); | |
| } | |
| if (key == "OS_VERSION"_s) { | |
| return ValueToString(info.GetOSVersion()); | |
| } | |
| if (key == "OS_PLATFORM"_s) { | |
| return ValueToString(info.GetOSPlatform()); | |
| } | |
| if (key == "FAMILY_ID"_s) { | |
| return ValueToString(info.GetFamilyID()); | |
| } | |
| if (key == "MODEL_ID"_s) { | |
| return ValueToString(info.GetModelID()); | |
| } | |
| if (key == "MODEL_NAME"_s) { | |
| return ValueToString(info.GetModelName()); | |
| } | |
| if (key == "PROCESSOR_APIC_ID"_s) { | |
| int apicId = info.GetProcessorAPICID(); | |
| if (apicId) { | |
| return ValueToString(apicId); | |
| } | |
| return ""; | |
| } | |
| if (key == "PROCESSOR_CACHE_SIZE"_s) { | |
| int cacheSize = info.GetProcessorCacheSize(); | |
| if (cacheSize > 0) { | |
| return ValueToString(cacheSize); | |
| } | |
| return ""; | |
| } | |
| if (key == "PROCESSOR_CLOCK_FREQUENCY"_s) { | |
| return ValueToString(info.GetProcessorClockFrequency()); | |
| } | |
| if (key == "VENDOR_ID"_s) { | |
| return ValueToString(info.GetVendorID()); | |
| } | |
| if (key == "VENDOR_STRING"_s) { | |
| return ValueToString(info.GetVendorString()); | |
| } | |
| return {}; | |
| } | |
| cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine( | |
| std::string const& line) | |
| { | |
| std::string key; | |
| std::string value; | |
| char prev = 0; | |
| enum ParserState | |
| { | |
| PARSE_KEY_1ST, | |
| PARSE_KEY, | |
| FOUND_EQ, | |
| PARSE_SINGLE_QUOTE_VALUE, | |
| PARSE_DBL_QUOTE_VALUE, | |
| PARSE_VALUE, | |
| IGNORE_REST | |
| } state = PARSE_KEY_1ST; | |
| for (auto ch : line) { | |
| switch (state) { | |
| case PARSE_KEY_1ST: | |
| if (cmsysString_isalpha(ch) || ch == '_') { | |
| key += ch; | |
| state = PARSE_KEY; | |
| } else if (!cmsysString_isspace(ch)) { | |
| state = IGNORE_REST; | |
| } | |
| break; | |
| case PARSE_KEY: | |
| if (ch == '=') { | |
| state = FOUND_EQ; | |
| } else if (cmsysString_isalnum(ch) || ch == '_') { | |
| key += ch; | |
| } else { | |
| state = IGNORE_REST; | |
| } | |
| break; | |
| case FOUND_EQ: | |
| switch (ch) { | |
| case '\'': | |
| state = PARSE_SINGLE_QUOTE_VALUE; | |
| break; | |
| case '"': | |
| state = PARSE_DBL_QUOTE_VALUE; | |
| break; | |
| case '#': | |
| case '\\': | |
| state = IGNORE_REST; | |
| break; | |
| default: | |
| value += ch; | |
| state = PARSE_VALUE; | |
| } | |
| break; | |
| case PARSE_SINGLE_QUOTE_VALUE: | |
| if (ch == '\'') { | |
| if (prev != '\\') { | |
| state = IGNORE_REST; | |
| } else { | |
| assert(!value.empty()); | |
| value[value.size() - 1] = ch; | |
| } | |
| } else { | |
| value += ch; | |
| } | |
| break; | |
| case PARSE_DBL_QUOTE_VALUE: | |
| if (ch == '"') { | |
| if (prev != '\\') { | |
| state = IGNORE_REST; | |
| } else { | |
| assert(!value.empty()); | |
| value[value.size() - 1] = ch; | |
| } | |
| } else { | |
| value += ch; | |
| } | |
| break; | |
| case PARSE_VALUE: | |
| if (ch == '#' || cmsysString_isspace(ch)) { | |
| state = IGNORE_REST; | |
| } else { | |
| value += ch; | |
| } | |
| break; | |
| default: | |
| // Unexpected os-release parser state! | |
| state = IGNORE_REST; | |
| break; | |
| } | |
| if (state == IGNORE_REST) { | |
| break; | |
| } | |
| prev = ch; | |
| } | |
| if (!(key.empty() || value.empty())) { | |
| return std::make_pair(key, value); | |
| } | |
| return {}; | |
| } | |
| // Fallback os-release scripts read ${CMAKE_SYSROOT} directly, so point it at | |
| // the effective root while they run; restored on destruction even if one | |
| // fails. | |
| class SysrootOverride | |
| { | |
| public: | |
| SysrootOverride(cmMakefile& makefile, std::string const& root) | |
| : Makefile(makefile) | |
| { | |
| cmValue value = this->Makefile.GetDefinition("CMAKE_SYSROOT"); | |
| this->Existed = static_cast<bool>(value); | |
| if (this->Existed) { | |
| this->Saved = *value; | |
| } | |
| this->Makefile.AddDefinition("CMAKE_SYSROOT", root); | |
| } | |
| ~SysrootOverride() | |
| { | |
| if (this->Existed) { | |
| this->Makefile.AddDefinition("CMAKE_SYSROOT", this->Saved); | |
| } else { | |
| this->Makefile.RemoveDefinition("CMAKE_SYSROOT"); | |
| } | |
| } | |
| SysrootOverride(SysrootOverride const&) = delete; | |
| SysrootOverride& operator=(SysrootOverride const&) = delete; | |
| private: | |
| cmMakefile& Makefile; | |
| std::string Saved; | |
| bool Existed; | |
| }; | |
| std::map<std::string, std::string> GetOSReleaseVariables( | |
| cmExecutionStatus& status, std::string const& root) | |
| { | |
| auto& makefile = status.GetMakefile(); | |
| std::map<std::string, std::string> data; | |
| // Based on | |
| // https://www.freedesktop.org/software/systemd/man/latest/os-release.html | |
| for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) { | |
| auto const& filename = cmStrCat(root, name); | |
| if (cmSystemTools::FileExists(filename)) { | |
| cmsys::ifstream fin(filename.c_str()); | |
| for (std::string line; !std::getline(fin, line).fail();) { | |
| auto kv = ParseOSReleaseLine(line); | |
| if (kv.has_value()) { | |
| data.emplace(kv.value()); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| // Got smth? | |
| if (!data.empty()) { | |
| return data; | |
| } | |
| // Ugh, it could be some pre-os-release distro. | |
| // Lets try some fallback getters. | |
| // See also: | |
| // - http://linuxmafia.com/faq/Admin/release-files.html | |
| // 1. CMake provided | |
| cmsys::Glob gl; | |
| std::vector<std::string> scripts; | |
| auto const findExpr = cmStrCat(cmSystemTools::GetCMakeRoot(), | |
| "/Modules/Internal/OSRelease/*.cmake"); | |
| if (gl.FindFiles(findExpr)) { | |
| scripts = gl.GetFiles(); | |
| } | |
| // 2. User provided (append to the CMake provided) | |
| cmList::append( | |
| scripts, makefile.GetDefinition("CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS")); | |
| // Filter out files that are not in format `NNN-name.cmake` | |
| auto checkName = [](std::string const& filepath) -> bool { | |
| auto const& filename = cmSystemTools::GetFilenameName(filepath); | |
| // NOTE Minimum filename length expected: | |
| // NNN-<at-least-one-char-name>.cmake --> 11 | |
| return (filename.size() < 11) || !cmsysString_isdigit(filename[0]) || | |
| !cmsysString_isdigit(filename[1]) || !cmsysString_isdigit(filename[2]) || | |
| filename[3] != '-'; | |
| }; | |
| scripts.erase(std::remove_if(scripts.begin(), scripts.end(), checkName), | |
| scripts.end()); | |
| // Make sure scripts are running in desired order | |
| std::sort(scripts.begin(), scripts.end(), | |
| [](std::string const& lhs, std::string const& rhs) -> bool { | |
| long lhs_order; | |
| cmStrToLong(cmSystemTools::GetFilenameName(lhs).substr(0u, 3u), | |
| &lhs_order); | |
| long rhs_order; | |
| cmStrToLong(cmSystemTools::GetFilenameName(rhs).substr(0u, 3u), | |
| &rhs_order); | |
| return lhs_order < rhs_order; | |
| }); | |
| // Name of the variable to put the results | |
| std::string const result_variable{ "CMAKE_GET_OS_RELEASE_FALLBACK_RESULT" }; | |
| SysrootOverride const sysrootOverride(makefile, root); | |
| for (auto const& script : scripts) { | |
| // Unset the result variable | |
| makefile.RemoveDefinition(result_variable); | |
| // include FATAL_ERROR and ERROR in the return status | |
| if (!makefile.ReadListFile(script) || | |
| cmSystemTools::GetErrorOccurredFlag()) { | |
| // Ok, no worries... go try the next script. | |
| continue; | |
| } | |
| cmList variables{ makefile.GetDefinition(result_variable) }; | |
| if (variables.empty()) { | |
| // Heh, this script didn't found anything... go try the next one. | |
| continue; | |
| } | |
| for (auto const& variable : variables) { | |
| auto value = makefile.GetSafeDefinition(variable); | |
| makefile.RemoveDefinition(variable); | |
| if (!cmHasPrefix(variable, cmStrCat(result_variable, '_'))) { | |
| // Ignore unknown variable set by the script | |
| continue; | |
| } | |
| auto key = variable.substr(result_variable.size() + 1, | |
| variable.size() - result_variable.size() - 1); | |
| data.emplace(std::move(key), std::move(value)); | |
| } | |
| // Try 'till some script can get anything | |
| if (!data.empty()) { | |
| data.emplace("USED_FALLBACK_SCRIPT", script); | |
| break; | |
| } | |
| } | |
| makefile.RemoveDefinition(result_variable); | |
| return data; | |
| } | |
| cm::optional<std::string> GetDistribValue( | |
| cmExecutionStatus& status, std::string const& key, | |
| std::string const& variable, std::string const& root, | |
| cm::optional<std::map<std::string, std::string>>& os_release) | |
| { | |
| auto const prefix = "DISTRIB_"_s; | |
| if (!cmHasPrefix(key, prefix)) { | |
| return {}; | |
| } | |
| // Parse once per call, not in a static: a static would freeze the first | |
| // call's result and ignore a later change of root. | |
| if (!os_release.has_value()) { | |
| os_release = GetOSReleaseVariables(status, root); | |
| } | |
| auto& makefile = status.GetMakefile(); | |
| std::string const subkey = | |
| key.substr(prefix.size(), key.size() - prefix.size()); | |
| if (subkey == "INFO"_s) { | |
| std::string vars; | |
| for (auto const& kv : *os_release) { | |
| auto cmake_var_name = cmStrCat(variable, '_', kv.first); | |
| vars = cmStrCat(std::move(vars), DELIM[!vars.empty()], cmake_var_name); | |
| makefile.AddDefinition(cmake_var_name, kv.second); | |
| } | |
| return cm::optional<std::string>(std::move(vars)); | |
| } | |
| // Query individual variable | |
| auto const it = os_release->find(subkey); | |
| if (it != os_release->cend()) { | |
| return it->second; | |
| } | |
| // NOTE Empty string means requested variable not set | |
| return std::string{}; | |
| } | |
| #ifdef _WIN32 | |
| std::string FindMSYSTEM_PREFIX(std::vector<std::string> prefixes) | |
| { | |
| for (std::string const& prefix : prefixes) { | |
| std::string out; | |
| std::string err; | |
| int ret; | |
| // In a modern MSYSTEM environment we expect cygpath to be in PATH. | |
| std::vector<std::string> cygpath_cmd{ "cygpath", "-w", prefix }; | |
| if (cmSystemTools::RunSingleCommand(cygpath_cmd, &out, &err, &ret, nullptr, | |
| cmSystemTools::OUTPUT_NONE)) { | |
| if (ret == 0) { | |
| out = cmTrimWhitespace(out); | |
| cmSystemTools::ConvertToUnixSlashes(out); | |
| if (cmSystemTools::FileIsDirectory(out)) { | |
| return out; | |
| } | |
| } | |
| } else { | |
| // In a legacy MSYSTEM environment (MinGW/MSYS 1.0) there is no | |
| // cygpath but we expect 'sh' to be in PATH. | |
| std::vector<std::string> sh_cmd{ | |
| "sh", "-c", cmStrCat("cd \"", prefix, "\" && cmd //c cd") | |
| }; | |
| if (cmSystemTools::RunSingleCommand(sh_cmd, &out, &err, &ret, nullptr, | |
| cmSystemTools::OUTPUT_NONE)) { | |
| if (ret == 0) { | |
| out = cmTrimWhitespace(out); | |
| cmSystemTools::ConvertToUnixSlashes(out); | |
| if (cmSystemTools::FileIsDirectory(out)) { | |
| return out; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return {}; | |
| } | |
| std::string FallbackMSYSTEM_PREFIX(cm::string_view msystem) | |
| { | |
| // These layouts are used by distributions such as | |
| // * MSYS2: https://www.msys2.org/docs/environments/ | |
| // * MinGW/MSYS 1.0: http://mingw.osdn.io/ | |
| if (msystem == "MSYS"_s) { | |
| static std::string const msystem_msys = FindMSYSTEM_PREFIX({ "/usr" }); | |
| return msystem_msys; | |
| } | |
| if (msystem == "MINGW32"_s) { | |
| static std::string const msystem_mingw32 = | |
| FindMSYSTEM_PREFIX({ "/mingw32", "/mingw" }); | |
| return msystem_mingw32; | |
| } | |
| if (msystem == "MINGW64"_s) { | |
| static std::string const msystem_mingw64 = | |
| FindMSYSTEM_PREFIX({ "/mingw64" }); | |
| return msystem_mingw64; | |
| } | |
| if (msystem == "UCRT64"_s) { | |
| static std::string const msystem_ucrt64 = | |
| FindMSYSTEM_PREFIX({ "/ucrt64" }); | |
| return msystem_ucrt64; | |
| } | |
| if (msystem == "CLANG32"_s) { | |
| static std::string const msystem_clang32 = | |
| FindMSYSTEM_PREFIX({ "/clang32" }); | |
| return msystem_clang32; | |
| } | |
| if (msystem == "CLANG64"_s) { | |
| static std::string const msystem_clang64 = | |
| FindMSYSTEM_PREFIX({ "/clang64" }); | |
| return msystem_clang64; | |
| } | |
| if (msystem == "CLANGARM64"_s) { | |
| static std::string const msystem_clangarm64 = | |
| FindMSYSTEM_PREFIX({ "/clangarm64" }); | |
| return msystem_clangarm64; | |
| } | |
| return {}; | |
| } | |
| cm::optional<std::string> GetWindowsValue(cmExecutionStatus& status, | |
| std::string const& key) | |
| { | |
| auto* const gg = status.GetMakefile().GetGlobalGenerator(); | |
| for (auto vs : { 15, 16, 17, 18 }) { | |
| if (key == cmStrCat("VS_"_s, vs, "_DIR"_s)) { | |
| std::string value; | |
| // If generating for the VS nn IDE, use the same instance. | |
| if (cmHasPrefix(gg->GetName(), cmStrCat("Visual Studio "_s, vs, ' '))) { | |
| cmGlobalVisualStudioVersionedGenerator* vsNNgen = | |
| static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); | |
| if (vsNNgen->GetVSInstance(value)) { | |
| return value; | |
| } | |
| } | |
| // Otherwise, find a VS nn instance ourselves. | |
| cmVSSetupAPIHelper vsSetupAPIHelper(vs); | |
| if (vsSetupAPIHelper.GetVSInstanceInfo(value)) { | |
| cmSystemTools::ConvertToUnixSlashes(value); | |
| } | |
| return value; | |
| } | |
| } | |
| if (key == "VS_MSBUILD_COMMAND"_s && gg->IsVisualStudioAtLeast10()) { | |
| cmGlobalVisualStudio10Generator* vs10gen = | |
| static_cast<cmGlobalVisualStudio10Generator*>(gg); | |
| return vs10gen->FindMSBuildCommandEarly(&status.GetMakefile()); | |
| } | |
| if (key == "MSYSTEM_PREFIX") { | |
| // MSYSTEM_PREFIX is meaningful only under a MSYSTEM environment. | |
| cm::optional<std::string> ms = cmSystemTools::GetEnvVar("MSYSTEM"); | |
| if (!ms || ms->empty()) { | |
| return std::string(); | |
| } | |
| // Prefer the MSYSTEM_PREFIX environment variable. | |
| if (cm::optional<std::string> msp = | |
| cmSystemTools::GetEnvVar("MSYSTEM_PREFIX")) { | |
| cmSystemTools::ConvertToUnixSlashes(*msp); | |
| if (cmSystemTools::FileIsDirectory(*msp)) { | |
| return msp; | |
| } | |
| } | |
| // Fall back to known distribution layouts. | |
| return FallbackMSYSTEM_PREFIX(*ms); | |
| } | |
| return {}; | |
| } | |
| #endif | |
| cm::optional<std::string> GetValueChained() | |
| { | |
| return {}; | |
| } | |
| template <typename GetterFn, typename... Next> | |
| cm::optional<std::string> GetValueChained(GetterFn current, Next... chain) | |
| { | |
| auto value = current(); | |
| if (value.has_value()) { | |
| return value; | |
| } | |
| return GetValueChained(chain...); | |
| } | |
| template <typename Range> | |
| bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, | |
| std::string const& variable) | |
| { | |
| using View = cmWindowsRegistry::View; | |
| if (args.empty()) { | |
| status.SetError("missing <key> specification."); | |
| return false; | |
| } | |
| std::string const& key = *args.begin(); | |
| struct Arguments : public ArgumentParser::ParseResult | |
| { | |
| std::string ValueName; | |
| bool ValueNames = false; | |
| bool SubKeys = false; | |
| std::string View; | |
| std::string Separator; | |
| std::string ErrorVariable; | |
| }; | |
| cmArgumentParser<Arguments> parser; | |
| parser.Bind("VALUE"_s, &Arguments::ValueName) | |
| .Bind("VALUE_NAMES"_s, &Arguments::ValueNames) | |
| .Bind("SUBKEYS"_s, &Arguments::SubKeys) | |
| .Bind("VIEW"_s, &Arguments::View) | |
| .Bind("SEPARATOR"_s, &Arguments::Separator) | |
| .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); | |
| std::vector<std::string> invalidArgs; | |
| Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs); | |
| if (!invalidArgs.empty()) { | |
| status.SetError(cmStrCat("given invalid argument(s) \"", | |
| cmJoin(invalidArgs, ", "_s), "\".")); | |
| return false; | |
| } | |
| if (arguments.MaybeReportError(status.GetMakefile())) { | |
| return true; | |
| } | |
| if ((!arguments.ValueName.empty() && | |
| (arguments.ValueNames || arguments.SubKeys)) || | |
| (arguments.ValueName.empty() && arguments.ValueNames && | |
| arguments.SubKeys)) { | |
| status.SetError("given mutually exclusive sub-options VALUE, " | |
| "VALUE_NAMES or SUBKEYS."); | |
| return false; | |
| } | |
| if (!arguments.View.empty() && !cmWindowsRegistry::ToView(arguments.View)) { | |
| status.SetError( | |
| cmStrCat("given invalid value for VIEW: ", arguments.View, '.')); | |
| return false; | |
| } | |
| auto& makefile = status.GetMakefile(); | |
| makefile.AddDefinition(variable, ""_s); | |
| auto view = arguments.View.empty() | |
| ? View::Both | |
| : *cmWindowsRegistry::ToView(arguments.View); | |
| cmWindowsRegistry registry(makefile); | |
| if (arguments.ValueNames) { | |
| auto result = registry.GetValueNames(key, view); | |
| if (result) { | |
| makefile.AddDefinition(variable, cmList::to_string(*result)); | |
| } | |
| } else if (arguments.SubKeys) { | |
| auto result = registry.GetSubKeys(key, view); | |
| if (result) { | |
| makefile.AddDefinition(variable, cmList::to_string(*result)); | |
| } | |
| } else { | |
| auto result = | |
| registry.ReadValue(key, arguments.ValueName, view, arguments.Separator); | |
| if (result) { | |
| makefile.AddDefinition(variable, *result); | |
| } | |
| } | |
| // return error message if requested | |
| if (!arguments.ErrorVariable.empty()) { | |
| makefile.AddDefinition(arguments.ErrorVariable, registry.GetLastError()); | |
| } | |
| return true; | |
| } | |
| // END Private functions | |
| } // anonymous namespace | |
| // cmCMakeHostSystemInformation | |
| bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args, | |
| cmExecutionStatus& status) | |
| { | |
| std::size_t current_index = 0; | |
| if (args.size() < (current_index + 2) || args[current_index] != "RESULT"_s) { | |
| status.SetError("missing RESULT specification."); | |
| return false; | |
| } | |
| auto const& variable = args[current_index + 1]; | |
| current_index += 2; | |
| if (args.size() < (current_index + 2) || args[current_index] != "QUERY"_s) { | |
| status.SetError("missing QUERY specification"); | |
| return false; | |
| } | |
| if (args[current_index + 1] == "WINDOWS_REGISTRY"_s) { | |
| return QueryWindowsRegistry(cmMakeRange(args).advance(current_index + 2), | |
| status, variable); | |
| } | |
| // FROM_SYSROOT <bool> selects the CMAKE_SYSROOT (target) or host root for | |
| // DISTRIB_* keys. Handled after the registry signature above, since a | |
| // registry argument could itself be "FROM_SYSROOT". | |
| enum class SysrootMode | |
| { | |
| Default, | |
| Host, | |
| Target, | |
| }; | |
| auto mode = SysrootMode::Default; | |
| std::vector<std::string> keys; | |
| for (size_t i = current_index + 1; i < args.size(); ++i) { | |
| if (args[i] != "FROM_SYSROOT"_s) { | |
| keys.push_back(args[i]); | |
| continue; | |
| } | |
| if (mode != SysrootMode::Default) { | |
| status.SetError("FROM_SYSROOT may be given at most once."); | |
| return false; | |
| } | |
| if (i + 1 >= args.size()) { | |
| status.SetError("FROM_SYSROOT requires a boolean value."); | |
| return false; | |
| } | |
| std::string const& value = args[++i]; | |
| if (cmIsOn(value)) { | |
| mode = SysrootMode::Target; | |
| } else if (cmIsOff(value)) { | |
| mode = SysrootMode::Host; | |
| } else { | |
| status.SetError(cmStrCat("FROM_SYSROOT requires a boolean value, got \"", | |
| value, "\".")); | |
| return false; | |
| } | |
| } | |
| cmMakefile& makefile = status.GetMakefile(); | |
| bool anyDistrib = false; | |
| for (std::string const& key : keys) { | |
| if (cmHasPrefix(key, "DISTRIB_"_s)) { | |
| anyDistrib = true; | |
| break; | |
| } | |
| } | |
| std::string const& sysroot = makefile.GetSafeDefinition("CMAKE_SYSROOT"); | |
| bool selectHost = false; | |
| switch (mode) { | |
| case SysrootMode::Host: | |
| selectHost = true; | |
| break; | |
| case SysrootMode::Target: | |
| selectHost = false; | |
| break; | |
| case SysrootMode::Default: | |
| switch (makefile.GetPolicyStatus(cmPolicies::CMP0221)) { | |
| case cmPolicies::NEW: | |
| selectHost = true; | |
| break; | |
| case cmPolicies::WARN: | |
| // Warn only where OLD and NEW diverge: a DISTRIB_* query with a | |
| // real sysroot. Empty sysroot or non-DISTRIB keys stay silent. | |
| if (anyDistrib && !sysroot.empty()) { | |
| makefile.IssuePolicyWarning(cmPolicies::CMP0221); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| selectHost = false; | |
| break; | |
| } | |
| break; | |
| } | |
| std::string const effectiveRoot = selectHost ? std::string{} : sysroot; | |
| static cmsys::SystemInformation info; | |
| static auto initialized = false; | |
| if (!initialized) { | |
| info.RunCPUCheck(); | |
| info.RunOSCheck(); | |
| info.RunMemoryCheck(); | |
| initialized = true; | |
| } | |
| cm::optional<std::map<std::string, std::string>> os_release; | |
| std::string result_list; | |
| for (std::string const& key : keys) { | |
| result_list += DELIM[!result_list.empty()]; | |
| // clang-format off | |
| auto value = | |
| GetValueChained( | |
| [&]() { return GetValue(info, key); } | |
| , [&]() { | |
| return GetDistribValue(status, key, variable, effectiveRoot, | |
| os_release); | |
| } | |
| #ifdef _WIN32 | |
| , [&]() { return GetWindowsValue(status, key); } | |
| #endif | |
| ); | |
| // clang-format on | |
| if (!value) { | |
| status.SetError("does not recognize <key> " + key); | |
| return false; | |
| } | |
| result_list += value.value(); | |
| } | |
| makefile.AddDefinition(variable, result_list); | |
| return true; | |
| } |