Files
CMake/Source/cmCMakeHostSystemInformationCommand.cxx

413 lines
11 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCMakeHostSystemInformationCommand.h"
#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/SystemInformation.hxx"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#ifdef _WIN32
# include "cmAlgorithms.h"
# include "cmGlobalGenerator.h"
# include "cmGlobalVisualStudioVersionedGenerator.h"
# include "cmVSSetupHelper.h"
# define HAVE_VS_SETUP_HELPER
#endif
namespace {
std::string const DELIM[2] = { {}, ";" };
// BEGIN Private functions
std::string ValueToString(std::size_t const value)
{
return std::to_string(value);
}
std::string ValueToString(const char* 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 == "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 == "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 {};
}
#ifdef __linux__
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;
}
if (state == IGNORE_REST) {
break;
}
prev = ch;
}
if (!(key.empty() || value.empty())) {
return std::make_pair(key, value);
}
return {};
}
std::map<std::string, std::string> GetOSReleaseVariables(
cmExecutionStatus& status)
{
const auto& sysroot =
status.GetMakefile().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;
}
}
return data;
}
cm::optional<std::string> GetValue(cmExecutionStatus& status,
std::string const& key,
std::string const& variable)
{
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{};
}
#endif
#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
// 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];
auto value = GetValue(info, key);
if (!value) {
#ifdef HAVE_VS_SETUP_HELPER
value = GetValue(status, key);
if (!value) {
status.SetError("does not recognize <key> " + key);
return false;
}
#elif defined(__linux__)
value = GetValue(status, key, variable);
if (!value) {
status.SetError("does not recognize <key> " + key);
return false;
}
#else
status.SetError("does not recognize <key> " + key);
return false;
#endif
}
result_list += value.value();
}
status.GetMakefile().AddDefinition(variable, result_list);
return true;
}