diff options
16 files changed, 308 insertions, 2 deletions
diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst index ae24b03..66249ad 100644 --- a/Help/command/cmake_host_system_information.rst +++ b/Help/command/cmake_host_system_information.rst @@ -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 diff --git a/Modules/Internal/OSRelease/010-TryOldCentOS.cmake b/Modules/Internal/OSRelease/010-TryOldCentOS.cmake new file mode 100644 index 0000000..ff35897 --- /dev/null +++ b/Modules/Internal/OSRelease/010-TryOldCentOS.cmake @@ -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) diff --git a/Modules/Internal/OSRelease/020-TryDebianVersion.cmake b/Modules/Internal/OSRelease/020-TryDebianVersion.cmake new file mode 100644 index 0000000..8ebe19a --- /dev/null +++ b/Modules/Internal/OSRelease/020-TryDebianVersion.cmake @@ -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) diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index cdd6b4c..90240a1 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -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; } diff --git a/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake new file mode 100644 index 0000000..ad873eb --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake @@ -0,0 +1 @@ +message(WARNING "The warning text to match just to make sure the script get executed") diff --git a/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake new file mode 100644 index 0000000..08d8da8 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake @@ -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 + ) diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt b/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt new file mode 100644 index 0000000..803ee08 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt @@ -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` diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake b/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake new file mode 100644 index 0000000..3bc632b --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake @@ -0,0 +1,5 @@ +cmake_host_system_information(RESULT CENTOS6 QUERY DISTRIB_INFO) + +foreach(VAR IN LISTS CENTOS6) + message(STATUS "${VAR}=`${${VAR}}`") +endforeach() diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release b/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release new file mode 100644 index 0000000..294ccc9 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release @@ -0,0 +1 @@ +CentOS release 6.10 (Final) diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt b/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt new file mode 100644 index 0000000..60d6425 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt @@ -0,0 +1,4 @@ +-- DEBIAN6_ID=`debian` +-- DEBIAN6_NAME=`Debian` +-- DEBIAN6_VERSION=`6\.0\.10` +-- DEBIAN6_VERSION_ID=`6\.0\.10` diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6.cmake b/Tests/RunCMake/cmake_host_system_information/Debian6.cmake new file mode 100644 index 0000000..cbf83a9 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Debian6.cmake @@ -0,0 +1,5 @@ +cmake_host_system_information(RESULT DEBIAN6 QUERY DISTRIB_INFO) + +foreach(VAR IN LISTS DEBIAN6) + message(STATUS "${VAR}=`${${VAR}}`") +endforeach() diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version b/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version new file mode 100644 index 0000000..c7d48f0 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version @@ -0,0 +1 @@ +6.0.10 diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake index 6cbb7fd..7462f7a 100644 --- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake +++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake @@ -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() diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt new file mode 100644 index 0000000..78acea2 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt @@ -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\) diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt new file mode 100644 index 0000000..afcc000 --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt @@ -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` diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake new file mode 100644 index 0000000..660aa1c --- /dev/null +++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake @@ -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() |