فهرست منبع

file: `STRINGS` + `REGEX` store match results

Signed-off-by: Cristian Le <[email protected]>
Cristian Le 1 سال پیش
والد
کامیت
fa00928bcd

+ 5 - 0
Help/command/file.rst

@@ -113,6 +113,11 @@ Reading
       Consider only strings that match the given regular expression,
       as described under :ref:`string(REGEX) <Regex Specification>`.
 
+      .. versionchanged:: 3.29
+        Capture groups from the last match in the file are stored in
+        :variable:`CMAKE_MATCH_<n>`, similar to
+        :command:`string(REGEX MATCHALL)`.  See policy :policy:`CMP0159`.
+
     ``ENCODING <encoding-type>``
       .. versionadded:: 3.1
 

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.29
 .. toctree::
    :maxdepth: 1
 
+   CMP0159: file(STRINGS) with REGEX updates CMAKE_MATCH_<n>. </policy/CMP0159>
    CMP0158: add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when cross-compiling. </policy/CMP0158>
    CMP0157: Swift compilation mode is selected by an abstraction. </policy/CMP0157>
    CMP0156: De-duplicate libraries on link lines based on linker capabilities. </policy/CMP0156>

+ 24 - 0
Help/policy/CMP0159.rst

@@ -0,0 +1,24 @@
+CMP0159
+-------
+
+.. versionadded:: 3.29
+
+:command:`file(STRINGS)` with ``REGEX`` updates :variable:`CMAKE_MATCH_<n>`.
+
+In CMake 3.28 and below the :command:`file(STRINGS)` command's ``REGEX``
+option does not affect :variable:`CMAKE_MATCH_<n>` variables.  CMake 3.29
+and above prefer to update the :variable:`CMAKE_MATCH_<n>` variables using
+captures from the last match in the file, similar to the
+:command:`string(REGEX MATCHALL)` command.  This policy provides
+compatibility for projects that have not been updated to expect the behavior.
+
+The ``OLD`` behavior for this policy is for :command:`file(STRINGS)` with
+``REGEX`` to not store capture groups in :variable:`CMAKE_MATCH_<n>`
+variables.  The ``NEW`` behavior is to store the capture groups.
+
+This policy was introduced in CMake version 3.29. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt

+ 37 - 0
Source/cmFileCommand.cxx

@@ -315,6 +315,7 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
   unsigned int limit_count = 0;
   cmsys::RegularExpression regex;
   bool have_regex = false;
+  bool store_regex = true;
   bool newline_consume = false;
   bool hex_conversion_enabled = true;
   enum
@@ -399,6 +400,26 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
         return false;
       }
       have_regex = true;
+      switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0159)) {
+        case cmPolicies::REQUIRED_IF_USED:
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::NEW:
+          // store_regex = true
+          break;
+        case cmPolicies::WARN:
+          if (status.GetMakefile().PolicyOptionalWarningEnabled(
+                "CMAKE_POLICY_WARNING_CMP0159")) {
+            status.GetMakefile().IssueMessage(
+              MessageType::AUTHOR_WARNING,
+              cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0159), '\n',
+                       "For compatibility, CMake is leaving CMAKE_MATCH_<n> "
+                       "unchanged."));
+          }
+          CM_FALLTHROUGH;
+        case cmPolicies::OLD:
+          store_regex = false;
+          break;
+      }
       arg_mode = arg_none;
     } else if (arg_mode == arg_encoding) {
       if (args[i] == "UTF-8") {
@@ -539,6 +560,10 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
       // string matches the requirements.  The length may now be as
       // low as zero since blank lines are allowed.
       if (s.length() >= minlen && (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -555,6 +580,10 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
       // be at least one no matter what the user specified.
       if (s.length() >= minlen && !s.empty() &&
           (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -572,6 +601,10 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
     if (maxlen > 0 && s.size() == maxlen) {
       // Terminate a string if the maximum length is reached.
       if (s.length() >= minlen && (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -588,6 +621,10 @@ bool HandleStringsCommand(std::vector<std::string> const& args,
   // matches the requirements.
   if ((!limit_count || strings.size() < limit_count) && !s.empty() &&
       s.length() >= minlen && (!have_regex || regex.find(s))) {
+    if (store_regex) {
+      status.GetMakefile().ClearMatches();
+      status.GetMakefile().StoreMatches(regex);
+    }
     output_size += static_cast<int>(s.size()) + 1;
     if (limit_output < 0 || output_size < limit_output) {
       strings.push_back(s);

+ 4 - 1
Source/cmPolicies.h

@@ -484,7 +484,10 @@ class cmMakefile;
   SELECT(POLICY, CMP0158,                                                     \
          "add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when "         \
          "cross-compiling.",                                                  \
-         3, 29, 0, cmPolicies::WARN)
+         3, 29, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0159,                                                     \
+         "file(STRINGS) with REGEX updates CMAKE_MATCH_<n>.", 3, 29, 0,       \
+         cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -538,6 +538,7 @@ foreach(var
 endforeach()
 add_RunCMake_test(file-DOWNLOAD)
 add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
+add_RunCMake_test(file-STRINGS)
 add_RunCMake_test(find_file -DMINGW=${MINGW})
 add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DMSVC=${MSVC})
 add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS})

+ 1 - 0
Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake

@@ -1,2 +1,3 @@
 cmake_policy(VERSION 3.24)
+cmake_policy(SET CMP0159 NEW)
 enable_language(C)

+ 12 - 0
Tests/RunCMake/file-STRINGS/CMP0159-Common.cmake

@@ -0,0 +1,12 @@
+function (output_results msg)
+  message(STATUS "results from: ${msg}")
+  message(STATUS "CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--")
+  message(STATUS "CMAKE_MATCH_1: -->${CMAKE_MATCH_1}<--")
+  message(STATUS "CMAKE_MATCH_2: -->${CMAKE_MATCH_2}<--")
+  message(STATUS "CMAKE_MATCH_COUNT: -->${CMAKE_MATCH_COUNT}<--")
+endfunction ()
+
+# Populate `CMAKE_MATCH_<n>` with some initial value
+string(REGEX MATCH "(.*):" _ "Initial-value:")
+file(STRINGS CMP0159.txt _ REGEX "(.*): (.*)")
+output_results(CMP0159)

+ 5 - 0
Tests/RunCMake/file-STRINGS/CMP0159-NEW-stdout.txt

@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->real-value: 1<--
+-- CMAKE_MATCH_1: -->real-value<--
+-- CMAKE_MATCH_2: -->1<--
+-- CMAKE_MATCH_COUNT: -->2<--

+ 4 - 0
Tests/RunCMake/file-STRINGS/CMP0159-NEW.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0159 NEW)
+
+include(CMP0159-Common.cmake)

+ 5 - 0
Tests/RunCMake/file-STRINGS/CMP0159-OLD-stdout.txt

@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->Initial-value:<--
+-- CMAKE_MATCH_1: -->Initial-value<--
+-- CMAKE_MATCH_2: --><--
+-- CMAKE_MATCH_COUNT: -->1<--

+ 4 - 0
Tests/RunCMake/file-STRINGS/CMP0159-OLD.cmake

@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0159 OLD)
+
+include(CMP0159-Common.cmake)

+ 10 - 0
Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt

@@ -0,0 +1,10 @@
+CMake Warning \(dev\) at CMP0159-Common.cmake:[0-9]+ \(file\):
+  Policy CMP0159 is not set: file\(STRINGS\) with REGEX updates
+  CMAKE_MATCH_<n>\.  Run "cmake --help-policy CMP0159" for policy details\.
+  Use the cmake_policy command to set the policy and suppress this warning\.
+
+  For compatibility, CMake is leaving CMAKE_MATCH_<n> unchanged\.
+Call Stack \(most recent call first\):
+  CMP0159-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 5 - 0
Tests/RunCMake/file-STRINGS/CMP0159-WARN-stdout.txt

@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->Initial-value:<--
+-- CMAKE_MATCH_1: -->Initial-value<--
+-- CMAKE_MATCH_2: --><--
+-- CMAKE_MATCH_COUNT: -->1<--

+ 4 - 0
Tests/RunCMake/file-STRINGS/CMP0159-WARN.cmake

@@ -0,0 +1,4 @@
+
+set(CMAKE_POLICY_WARNING_CMP0159 TRUE)
+
+include(CMP0159-Common.cmake)

+ 3 - 0
Tests/RunCMake/file-STRINGS/CMP0159.txt

@@ -0,0 +1,3 @@
+overwritten-value: -1
+real-value: 1
+ignored = -2

+ 3 - 0
Tests/RunCMake/file-STRINGS/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.13)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 5 - 0
Tests/RunCMake/file-STRINGS/RunCMakeTest.cmake

@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0159-WARN)
+run_cmake(CMP0159-OLD)
+run_cmake(CMP0159-NEW)