cmake_host_system_information: Can run fallback scripts

This commit is contained in:
Alex Turbov 2021-08-03 16:49:02 +03:00 committed by Brad King
parent 1e65e4a6e5
commit efe139d1b8
16 changed files with 308 additions and 2 deletions

View File

@ -176,8 +176,75 @@ system identification as described in the `man 5 os-release`_.
-- DISTRO_VERSION_CODENAME=`focal`
-- DISTRO_VERSION_ID=`20.04`
If :file:`/etc/os-release` file is not found, the command tries to gather OS
identification via fallback scripts. The fallback script can use `various
distribution-specific files`_ to collect OS identification data and map it
into `man 5 os-release`_ variables.
Fallback Interface Variables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
In addition to the scripts shipped with CMake, a user may append full
paths to his script(s) to the this list. The script filename has the
following format: ``NNN-<name>.cmake``, where ``NNN`` is three digits
used to apply collected scripts in a specific order.
.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname>
Variables collected by the user provided fallback script
ought to be assigned to CMake variables using this naming
convention. Example, the ``ID`` variable from the manual becomes
``CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID``.
.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
The fallback script ought to store names of all assigned
``CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname>`` variables in this list.
Example:
.. code-block:: cmake
# Try to detect some old distribution
# See also
# - http://linuxmafia.com/faq/Admin/release-files.html
#
if(NOT EXISTS "${CMAKE_SYSROOT}/etc/foobar-release")
return()
endif()
# Get the first string only
file(
STRINGS "${CMAKE_SYSROOT}/etc/foobar-release" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
LIMIT_COUNT 1
)
#
# Example:
#
# Foobar distribution release 1.2.3 (server)
#
if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "Foobar distribution release ([0-9\.]+) .*")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME Foobar)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT}")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID foobar)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_MATCH_1})
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_MATCH_1})
list(
APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
)
endif()
unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)
.. rubric:: Footnotes
.. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes.
.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html
.. _various distribution-specific files: http://linuxmafia.com/faq/Admin/release-files.html

View File

@ -0,0 +1,41 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
# Author: Alex Turbov
if(NOT EXISTS "${CMAKE_SYSROOT}/etc/centos-release")
return()
endif()
# Get the first string only
file(
STRINGS "${CMAKE_SYSROOT}/etc/centos-release" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
LIMIT_COUNT 1
)
#
# Example:
# CentOS release 6.10 (Final)
#
if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "CentOS release ([0-9\.]+) .*")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME CentOS)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT}")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID centos)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE rhel)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_MATCH_1})
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_MATCH_1})
list(
APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
)
endif()
unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)

View File

@ -0,0 +1,38 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
# Author: Alex Turbov
if(NOT EXISTS "${CMAKE_SYSROOT}/etc/debian_version")
return()
endif()
# Get the first string only
file(
STRINGS "${CMAKE_SYSROOT}/etc/debian_version" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
LIMIT_COUNT 1
)
#
# Example:
# 6.0.10 # Old debian
# wheezy/sid # Ubuntu
#
if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "[0-9]+(\.[0-9]+)*")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME Debian)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID debian)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT})
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT})
list(
APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
)
endif()
unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)

View File

@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCMakeHostSystemInformationCommand.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <initializer_list>
@ -15,6 +16,7 @@
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/SystemInformation.hxx"
#include "cmExecutionStatus.h"
@ -259,8 +261,8 @@ cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
std::map<std::string, std::string> GetOSReleaseVariables(
cmExecutionStatus& status)
{
const auto& sysroot =
status.GetMakefile().GetSafeDefinition("CMAKE_SYSROOT");
auto& makefile = status.GetMakefile();
const auto& sysroot = makefile.GetSafeDefinition("CMAKE_SYSROOT");
std::map<std::string, std::string> data;
// Based on
@ -278,6 +280,92 @@ std::map<std::string, std::string> GetOSReleaseVariables(
break;
}
}
// Got smth?
if (!data.empty()) {
return data;
}
// Ugh, it could be some pre-os-release distro.
// Lets try some fallback getters.
// 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());
// 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;
}
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));
}
if (!data.empty()) {
// Try 'till some script can get anything
break;
}
}
makefile.RemoveDefinition(result_variable.data());
return data;
}

View File

@ -0,0 +1 @@
message(WARNING "The warning text to match just to make sure the script get executed")

View File

@ -0,0 +1,21 @@
if(DEFINED CMAKE_GET_OS_RELEASE_FALLBACK_RESULT)
message(FATAL_ERROR "The `CMAKE_GET_OS_RELEASE_FALLBACK_RESULT` expected to be unset at this moment")
endif()
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME UnitTest)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "Just a Unit Test")
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID unittest)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE nothing)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION 0.0.1)
set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID 0.0.1)
list(
APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
IGNORED_VARIABLE_NAME_WHICH_IS_NOT_STARTED_WITH_EXPECTED_PREFIX
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
)

View File

@ -0,0 +1,6 @@
-- CENTOS6_ID=`centos`
-- CENTOS6_ID_LIKE=`rhel`
-- CENTOS6_NAME=`CentOS`
-- CENTOS6_PRETTY_NAME=`CentOS release 6\.10 \(Final\)`
-- CENTOS6_VERSION=`6\.10`
-- CENTOS6_VERSION_ID=`6\.10`

View File

@ -0,0 +1,5 @@
cmake_host_system_information(RESULT CENTOS6 QUERY DISTRIB_INFO)
foreach(VAR IN LISTS CENTOS6)
message(STATUS "${VAR}=`${${VAR}}`")
endforeach()

View File

@ -0,0 +1 @@
CentOS release 6.10 (Final)

View File

@ -0,0 +1,4 @@
-- DEBIAN6_ID=`debian`
-- DEBIAN6_NAME=`Debian`
-- DEBIAN6_VERSION=`6\.0\.10`
-- DEBIAN6_VERSION_ID=`6\.0\.10`

View File

@ -0,0 +1,5 @@
cmake_host_system_information(RESULT DEBIAN6 QUERY DISTRIB_INFO)
foreach(VAR IN LISTS DEBIAN6)
message(STATUS "${VAR}=`${${VAR}}`")
endforeach()

View File

@ -13,4 +13,9 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
run_cmake_with_options(Exherbo)
run_cmake_with_options(Ubuntu)
run_cmake_with_options(CentOS6)
run_cmake_with_options(Debian6)
run_cmake_with_options(UserFallbackScript)
endif()

View File

@ -0,0 +1,5 @@
CMake Warning at 000-FirstFallbackScript\.cmake:[0-9]+ \(message\):
The warning text to match just to make sure the script get executed
Call Stack \(most recent call first\):
UserFallbackScript\.cmake:[0-9]+ \(cmake_host_system_information\)
CMakeLists\.txt:[0-9]+ \(include\)

View File

@ -0,0 +1,6 @@
-- UFS_ID=`unittest`
-- UFS_ID_LIKE=`nothing`
-- UFS_NAME=`UnitTest`
-- UFS_PRETTY_NAME=`Just a Unit Test`
-- UFS_VERSION=`0\.0\.1`
-- UFS_VERSION_ID=`0\.0\.1`

View File

@ -0,0 +1,12 @@
list(
APPEND CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
${CMAKE_CURRENT_SOURCE_DIR}/000-FirstFallbackScript.cmake
${CMAKE_CURRENT_SOURCE_DIR}/Ignored-Script.cmake
${CMAKE_CURRENT_SOURCE_DIR}/999-LastFallbackScript.cmake
)
cmake_host_system_information(RESULT UFS QUERY DISTRIB_INFO)
foreach(VAR IN LISTS UFS)
message(STATUS "${VAR}=`${${VAR}}`")
endforeach()