1
0
Эх сурвалжийг харах

cmake_host_system_information: Can run fallback scripts

Alex Turbov 4 жил өмнө
parent
commit
efe139d1b8

+ 67 - 0
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

+ 41 - 0
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)

+ 38 - 0
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)

+ 90 - 2
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;
 }
 

+ 1 - 0
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")

+ 21 - 0
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
+  )

+ 6 - 0
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`

+ 5 - 0
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()

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release

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

+ 4 - 0
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`

+ 5 - 0
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()

+ 1 - 0
Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version

@@ -0,0 +1 @@
+6.0.10

+ 5 - 0
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()

+ 5 - 0
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\)

+ 6 - 0
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`

+ 12 - 0
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()