diff options
author | Brad King <brad.king@kitware.com> | 2021-08-24 14:04:03 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2021-08-24 14:04:15 (GMT) |
commit | cf90aa5595eac39029f7516e0bc5790fe398ea3d (patch) | |
tree | 1eeb86857a1a7adcf70618b21e773f90f062f846 /Source | |
parent | 91d5bf68ae2bee13bc29ce89732844047c687334 (diff) | |
parent | b9698f89df275a33b01cfc7b4f88a510ad9d8bbe (diff) | |
download | CMake-cf90aa5595eac39029f7516e0bc5790fe398ea3d.zip CMake-cf90aa5595eac39029f7516e0bc5790fe398ea3d.tar.gz CMake-cf90aa5595eac39029f7516e0bc5790fe398ea3d.tar.bz2 |
Merge topic 'host-distro-info'
b9698f89df cmake_host_system_information: Make it available for all systems
5469c71a82 Refactor: Simplify `GetValue()` function calls
6c92f80f2e cmake_host_system_information: Also set `USED_FALLBACK_SCRIPT`
efe139d1b8 cmake_host_system_information: Can run fallback scripts
1e65e4a6e5 cmake_host_system_information: Can read `/etc/os-release` file
e808cbb1dd Testing: Convert `cmake_host_system_information` tests into `RunCMake`
9e831284e5 Documentation: Use definition list instead of tables
346f3de005 Refactor: Deduplicate code for `VS_nn_DIR` keys processing
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !6410
Diffstat (limited to 'Source')
-rw-r--r-- | Source/cmCMakeHostSystemInformationCommand.cxx | 606 |
1 files changed, 451 insertions, 155 deletions
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 0550568..74071ff 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -2,214 +2,510 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakeHostSystemInformationCommand.h" -#include <cstddef> +#include <algorithm> +#include <cassert> +#include <cctype> +#include <initializer_list> +#include <map> +#include <string> +#include <type_traits> +#include <utility> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmsys/FStream.hxx" +#include "cmsys/Glob.hxx" #include "cmsys/SystemInformation.hxx" #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" -#if defined(_WIN32) +#ifdef _WIN32 # include "cmAlgorithms.h" # include "cmGlobalGenerator.h" # include "cmGlobalVisualStudioVersionedGenerator.h" -# include "cmSystemTools.h" # include "cmVSSetupHelper.h" # define HAVE_VS_SETUP_HELPER #endif namespace { -bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info, - std::string const& key, std::string& value); -std::string ValueToString(size_t value); -std::string ValueToString(const char* value); -std::string ValueToString(std::string const& value); -} +std::string const DELIM[2] = { {}, ";" }; -// cmCMakeHostSystemInformation -bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args, - cmExecutionStatus& status) +// BEGIN Private functions +std::string ValueToString(std::size_t const value) { - size_t current_index = 0; - - if (args.size() < (current_index + 2) || args[current_index] != "RESULT") { - status.SetError("missing RESULT specification."); - return false; - } - - std::string const& variable = args[current_index + 1]; - current_index += 2; - - if (args.size() < (current_index + 2) || args[current_index] != "QUERY") { - status.SetError("missing QUERY specification"); - return false; - } - - cmsys::SystemInformation info; - info.RunCPUCheck(); - info.RunOSCheck(); - info.RunMemoryCheck(); - - std::string result_list; - for (size_t i = current_index + 1; i < args.size(); ++i) { - std::string const& key = args[i]; - if (i != current_index + 1) { - result_list += ";"; - } - std::string value; - if (!GetValue(status, info, key, value)) { - return false; - } - result_list += value; - } - - status.GetMakefile().AddDefinition(variable, result_list); + return std::to_string(value); +} - return true; +std::string ValueToString(const char* const value) +{ + return value ? value : std::string{}; } -namespace { +std::string ValueToString(std::string const& value) +{ + return value; +} -bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info, - std::string const& key, std::string& value) +cm::optional<std::string> GetValue(cmsys::SystemInformation& info, + std::string const& key) { - if (key == "NUMBER_OF_LOGICAL_CORES") { - value = ValueToString(info.GetNumberOfLogicalCPU()); - } else if (key == "NUMBER_OF_PHYSICAL_CORES") { - value = ValueToString(info.GetNumberOfPhysicalCPU()); - } else if (key == "HOSTNAME") { - value = ValueToString(info.GetHostname()); - } else if (key == "FQDN") { - value = ValueToString(info.GetFullyQualifiedDomainName()); - } else if (key == "TOTAL_VIRTUAL_MEMORY") { - value = ValueToString(info.GetTotalVirtualMemory()); - } else if (key == "AVAILABLE_VIRTUAL_MEMORY") { - value = ValueToString(info.GetAvailableVirtualMemory()); - } else if (key == "TOTAL_PHYSICAL_MEMORY") { - value = ValueToString(info.GetTotalPhysicalMemory()); - } else if (key == "AVAILABLE_PHYSICAL_MEMORY") { - value = ValueToString(info.GetAvailablePhysicalMemory()); - } else if (key == "IS_64BIT") { - value = ValueToString(info.Is64Bits()); - } else if (key == "HAS_FPU") { - value = ValueToString( + 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 == "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)); - } else if (key == "HAS_MMX") { - value = ValueToString( + } + if (key == "HAS_MMX"_s) { + return ValueToString( info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX)); - } else if (key == "HAS_MMX_PLUS") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_MMX_PLUS"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS)); - } else if (key == "HAS_SSE") { - value = ValueToString( + } + if (key == "HAS_SSE"_s) { + return ValueToString( info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE)); - } else if (key == "HAS_SSE2") { - value = ValueToString( + } + if (key == "HAS_SSE2"_s) { + return ValueToString( info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2)); - } else if (key == "HAS_SSE_FP") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_SSE_FP"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_SSE_FP)); - } else if (key == "HAS_SSE_MMX") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_SSE_MMX"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_SSE_MMX)); - } else if (key == "HAS_AMD_3DNOW") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_AMD_3DNOW"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW)); - } else if (key == "HAS_AMD_3DNOW_PLUS") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_AMD_3DNOW_PLUS"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS)); - } else if (key == "HAS_IA64") { - value = ValueToString( + } + if (key == "HAS_IA64"_s) { + return ValueToString( info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64)); - } else if (key == "HAS_SERIAL_NUMBER") { - value = ValueToString(info.DoesCPUSupportFeature( + } + if (key == "HAS_SERIAL_NUMBER"_s) { + return ValueToString(info.DoesCPUSupportFeature( cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER)); - } else if (key == "PROCESSOR_NAME") { - value = ValueToString(info.GetExtendedProcessorName()); - } else if (key == "PROCESSOR_DESCRIPTION") { - value = info.GetCPUDescription(); - } else if (key == "PROCESSOR_SERIAL_NUMBER") { - value = ValueToString(info.GetProcessorSerialNumber()); - } else if (key == "OS_NAME") { - value = ValueToString(info.GetOSName()); - } else if (key == "OS_RELEASE") { - value = ValueToString(info.GetOSRelease()); - } else if (key == "OS_VERSION") { - value = ValueToString(info.GetOSVersion()); - } else if (key == "OS_PLATFORM") { - value = ValueToString(info.GetOSPlatform()); -#ifdef HAVE_VS_SETUP_HELPER - } else if (key == "VS_15_DIR") { - // If generating for the VS 15 IDE, use the same instance. - cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator(); - if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 15 ")) { - cmGlobalVisualStudioVersionedGenerator* vs15gen = - static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); - if (vs15gen->GetVSInstance(value)) { - return true; - } + } + 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()); + } + 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 (std::isalpha(ch) || ch == '_') { + key += ch; + state = PARSE_KEY; + } else if (!std::isspace(ch)) { + state = IGNORE_REST; + } + break; + + case PARSE_KEY: + if (ch == '=') { + state = FOUND_EQ; + } else if (std::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 == '#' || std::isspace(ch)) { + state = IGNORE_REST; + } else { + value += ch; + } + break; + + default: + // Unexpected os-release parser state! + state = IGNORE_REST; + break; } - // Otherwise, find a VS 15 instance ourselves. - cmVSSetupAPIHelper vsSetupAPIHelper(15); - if (vsSetupAPIHelper.GetVSInstanceInfo(value)) { - cmSystemTools::ConvertToUnixSlashes(value); + if (state == IGNORE_REST) { + break; } - } else if (key == "VS_16_DIR") { - // If generating for the VS 16 IDE, use the same instance. - cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator(); - if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 16 ")) { - cmGlobalVisualStudioVersionedGenerator* vs16gen = - static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); - if (vs16gen->GetVSInstance(value)) { - return true; + prev = ch; + } + if (!(key.empty() || value.empty())) { + return std::make_pair(key, value); + } + return {}; +} + +std::map<std::string, std::string> GetOSReleaseVariables( + cmExecutionStatus& status) +{ + auto& makefile = status.GetMakefile(); + const auto& sysroot = makefile.GetSafeDefinition("CMAKE_SYSROOT"); + + std::map<std::string, std::string> data; + // Based on + // https://www.freedesktop.org/software/systemd/man/os-release.html + for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) { + const auto& filename = cmStrCat(sysroot, 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 prvided) + makefile.GetDefExpandList("CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS", 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) || !std::isdigit(filename[0]) || + !std::isdigit(filename[1]) || !std::isdigit(filename[2]) || + filename[3] != '-'; + }; + scripts.erase(std::remove_if(scripts.begin(), scripts.end(), checkName), + scripts.end()); - // Otherwise, find a VS 16 instance ourselves. - cmVSSetupAPIHelper vsSetupAPIHelper(16); - if (vsSetupAPIHelper.GetVSInstanceInfo(value)) { - cmSystemTools::ConvertToUnixSlashes(value); + // 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 + auto const result_variable = "CMAKE_GET_OS_RELEASE_FALLBACK_RESULT"_s; + + for (auto const& script : scripts) { + // Unset the result variable + makefile.RemoveDefinition(result_variable.data()); + + // include FATAL_ERROR and ERROR in the return status + if (!makefile.ReadListFile(script) || + cmSystemTools::GetErrorOccuredFlag()) { + // Ok, no worries... go try the next script. + continue; + } + + std::vector<std::string> variables; + if (!makefile.GetDefExpandList(result_variable.data(), variables)) { + // Heh, this script didn't found anything... go try the next one. + continue; } - } else if (key == "VS_17_DIR") { - // If generating for the VS 17 IDE, use the same instance. - cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator(); - if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 17 ")) { - cmGlobalVisualStudioVersionedGenerator* vs17gen = - static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); - if (vs17gen->GetVSInstance(value)) { - return true; + + 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)); } - // Otherwise, find a VS 17 instance ourselves. - cmVSSetupAPIHelper vsSetupAPIHelper(17); - if (vsSetupAPIHelper.GetVSInstanceInfo(value)) { - cmSystemTools::ConvertToUnixSlashes(value); + // Try 'till some script can get anything + if (!data.empty()) { + data.emplace("USED_FALLBACK_SCRIPT", script); + break; } -#endif - } else { - std::string e = "does not recognize <key> " + key; - status.SetError(e); - return false; } - return true; + makefile.RemoveDefinition(result_variable.data()); + + return data; } -std::string ValueToString(size_t value) +cm::optional<std::string> GetValue(cmExecutionStatus& status, + std::string const& key, + std::string const& variable) { - return std::to_string(value); + const auto prefix = "DISTRIB_"_s; + if (!cmHasPrefix(key, prefix)) { + return {}; + } + + static const std::map<std::string, std::string> s_os_release = + GetOSReleaseVariables(status); + + auto& makefile = status.GetMakefile(); + + const std::string subkey = + key.substr(prefix.size(), key.size() - prefix.size()); + if (subkey == "INFO"_s) { + std::string vars; + for (const auto& kv : s_os_release) { + auto cmake_var_name = cmStrCat(variable, '_', kv.first); + 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 + const auto it = s_os_release.find(subkey); + if (it != s_os_release.cend()) { + return it->second; + } + + // NOTE Empty string means requested variable not set + return std::string{}; +} + +#ifdef HAVE_VS_SETUP_HELPER +cm::optional<std::string> GetValue(cmExecutionStatus& status, + std::string const& key) +{ + auto* const gg = status.GetMakefile().GetGlobalGenerator(); + for (auto vs : { 15, 16, 17 }) { + 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; + } + } + + return {}; } +#endif -std::string ValueToString(const char* value) +cm::optional<std::string> GetValueChained() { - std::string safe_string = value ? value : ""; - return safe_string; + return {}; } -std::string ValueToString(std::string const& value) +template <typename GetterFn, typename... Next> +cm::optional<std::string> GetValueChained(GetterFn current, Next... chain) { - return value; + auto value = current(); + if (value.has_value()) { + return value; + } + return GetValueChained(chain...); } +// 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; + } + + static cmsys::SystemInformation info; + static auto initialized = false; + if (!initialized) { + info.RunCPUCheck(); + info.RunOSCheck(); + info.RunMemoryCheck(); + initialized = true; + } + + std::string result_list; + for (auto i = current_index + 1; i < args.size(); ++i) { + result_list += DELIM[!result_list.empty()]; + + auto const& key = args[i]; + // clang-format off + auto value = + GetValueChained( + [&]() { return GetValue(info, key); } + , [&]() { return GetValue(status, key, variable); } +#ifdef HAVE_VS_SETUP_HELPER + , [&]() { return GetValue(status, key); } +#endif + ); + // clang-format on + if (!value) { + status.SetError("does not recognize <key> " + key); + return false; + } + result_list += value.value(); + } + + status.GetMakefile().AddDefinition(variable, result_list); + + return true; } |