Преглед изворни кода

install: Teach CODE,SCRIPT modes to evaluate generator expressions

This also introduces CMP0087 which will keep the OLD behaviour of not
evaluating generator expressions

Fixes: #15785
Jon Chronopoulos пре 7 година
родитељ
комит
25cae1e85d

+ 5 - 0
Help/command/install.rst

@@ -547,6 +547,11 @@ example, the code
 
 will print a message during installation.
 
+``<file>`` or ``<code>`` may use "generator expressions" with the syntax
+``$<...>`` (in the case of ``<file>``, this refers to their use in the file
+name, not the file's contents).  See the
+:manual:`cmake-generator-expressions(7)` manual for available expressions.
+
 Installing Exports
 ^^^^^^^^^^^^^^^^^^
 

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

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.14
 .. toctree::
    :maxdepth: 1
 
+   CMP0087: install(SCRIPT | CODE) supports generator expressions. </policy/CMP0087>
    CMP0086: UseSWIG honors SWIG_MODULE_NAME via -module flag. </policy/CMP0086>
    CMP0085: IN_LIST generator expression handles empty list items. </policy/CMP0085>
    CMP0084: The FindQt module does not exist for find_package(). </policy/CMP0084>

+ 29 - 0
Help/policy/CMP0087.rst

@@ -0,0 +1,29 @@
+CMP0087
+-------
+
+:command:`install(CODE)` and :command:`install(SCRIPT)` support generator
+expressions.
+
+In CMake 3.13 and earlier, :command:`install(CODE)` and
+:command:`install(SCRIPT)` did not evaluate generator expressions.  CMake 3.14
+and later will evaluate generator expressions for :command:`install(CODE)` and
+:command:`install(SCRIPT)`.
+
+The ``OLD`` behavior of this policy is for :command:`install(CODE)` and
+:command:`install(SCRIPT)` to not evaluate generator expressions.  The ``NEW``
+behavior is to evaluate generator expressions for :command:`install(CODE)` and
+:command:`install(SCRIPT)`.
+
+Note that it is the value of this policy setting at the end of the directory
+scope that is important, not its setting at the time of the call to
+:command:`install(CODE)` or :command:`install(SCRIPT)`.  This has implications
+for calling these commands from places that have their own policy scope but not
+their own directory scope (e.g. from files brought in via :command:`include()`
+rather than :command:`add_subdirectory()`).
+
+This policy was introduced in CMake version 3.14.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt

+ 5 - 0
Help/release/dev/install-code-script-genex.rst

@@ -0,0 +1,5 @@
+install-code-script-genex
+-------------------------
+
+* The :command:`install(CODE)` and :command:`install(SCRIPT)` commands
+  learned to support generator expressions.  See policy :policy:`CMP0087`.

+ 60 - 10
Source/cmInstallScriptGenerator.cxx

@@ -2,11 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallScriptGenerator.h"
 
-#include "cmScriptGenerator.h"
-
 #include <ostream>
 #include <vector>
 
+#include "cmGeneratorExpression.h"
+#include "cmLocalGenerator.h"
+#include "cmPolicies.h"
+#include "cmScriptGenerator.h"
+#include "cmake.h"
+
 cmInstallScriptGenerator::cmInstallScriptGenerator(const char* script,
                                                    bool code,
                                                    const char* component,
@@ -15,25 +19,71 @@ cmInstallScriptGenerator::cmInstallScriptGenerator(const char* script,
                        MessageDefault, exclude_from_all)
   , Script(script)
   , Code(code)
+  , AllowGenex(false)
 {
+  // We need per-config actions if the script has generator expressions.
+  if (cmGeneratorExpression::Find(Script) != std::string::npos) {
+    this->ActionsPerConfig = true;
+  }
 }
 
 cmInstallScriptGenerator::~cmInstallScriptGenerator()
 {
 }
 
-void cmInstallScriptGenerator::GenerateScript(std::ostream& os)
+void cmInstallScriptGenerator::Compute(cmLocalGenerator* lg)
 {
-  Indent indent;
-  std::string component_test =
-    this->CreateComponentTest(this->Component.c_str(), this->ExcludeFromAll);
-  os << indent << "if(" << component_test << ")\n";
+  this->LocalGenerator = lg;
 
+  if (this->ActionsPerConfig) {
+    switch (this->LocalGenerator->GetPolicyStatus(cmPolicies::CMP0087)) {
+      case cmPolicies::WARN:
+        this->LocalGenerator->IssueMessage(
+          cmake::AUTHOR_WARNING,
+          cmPolicies::GetPolicyWarning(cmPolicies::CMP0087));
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        break;
+      case cmPolicies::NEW:
+      case cmPolicies::REQUIRED_ALWAYS:
+      case cmPolicies::REQUIRED_IF_USED:
+        this->AllowGenex = true;
+        break;
+    }
+  }
+}
+
+void cmInstallScriptGenerator::AddScriptInstallRule(std::ostream& os,
+                                                    Indent indent,
+                                                    std::string const& script)
+{
   if (this->Code) {
-    os << indent << this->Script << "\n";
+    os << indent << script << "\n";
   } else {
-    os << indent << "include(\"" << this->Script << "\")\n";
+    os << indent << "include(\"" << script << "\")\n";
   }
+}
 
-  os << indent << "endif()\n\n";
+void cmInstallScriptGenerator::GenerateScriptActions(std::ostream& os,
+                                                     Indent indent)
+{
+  if (this->AllowGenex && this->ActionsPerConfig) {
+    this->cmInstallGenerator::GenerateScriptActions(os, indent);
+  } else {
+    this->AddScriptInstallRule(os, indent, this->Script);
+  }
+}
+
+void cmInstallScriptGenerator::GenerateScriptForConfig(
+  std::ostream& os, const std::string& config, Indent indent)
+{
+  if (this->AllowGenex) {
+    cmGeneratorExpression ge;
+    std::unique_ptr<cmCompiledGeneratorExpression> cge =
+      ge.Parse(this->Script);
+    this->AddScriptInstallRule(os, indent,
+                               cge->Evaluate(this->LocalGenerator, config));
+  } else {
+    this->AddScriptInstallRule(os, indent, this->Script);
+  }
 }

+ 13 - 1
Source/cmInstallScriptGenerator.h

@@ -6,10 +6,13 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
 
 #include <iosfwd>
 #include <string>
 
+class cmLocalGenerator;
+
 /** \class cmInstallScriptGenerator
  * \brief Generate target installation rules.
  */
@@ -20,10 +23,19 @@ public:
                            const char* component, bool exclude_from_all);
   ~cmInstallScriptGenerator() override;
 
+  void Compute(cmLocalGenerator* lg) override;
+
 protected:
-  void GenerateScript(std::ostream& os) override;
+  void GenerateScriptActions(std::ostream& os, Indent indent) override;
+  void GenerateScriptForConfig(std::ostream& os, const std::string& config,
+                               Indent indent) override;
+  void AddScriptInstallRule(std::ostream& os, Indent indent,
+                            std::string const& script);
+
   std::string Script;
   bool Code;
+  cmLocalGenerator* LocalGenerator;
+  bool AllowGenex;
 };
 
 #endif

+ 5 - 1
Source/cmPolicies.h

@@ -254,7 +254,11 @@ class cmMakefile;
          0, cmPolicies::WARN)                                                 \
   SELECT(POLICY, CMP0086,                                                     \
          "UseSWIG honors SWIG_MODULE_NAME via -module flag.", 3, 14, 0,       \
-         cmPolicies::WARN)
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0087,                                                     \
+         "Install CODE|SCRIPT allow the use of generator "                    \
+         "expressions.",                                                      \
+         3, 14, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 7 - 0
Tests/RunCMake/install/CMP0087-NEW-check.cmake

@@ -0,0 +1,7 @@
+execute_process(COMMAND ${CMAKE_COMMAND} -P ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake
+  OUTPUT_VARIABLE out ERROR_VARIABLE err)
+if(NOT out MATCHES "-- Install configuration: .*-- codegenexlib")
+  string(REGEX REPLACE "\n" "\n  " out "  ${out}")
+  string(APPEND RunCMake_TEST_FAILED
+      "\"-- codegenexlib\" was not found:\n${out}")
+endif()

+ 3 - 0
Tests/RunCMake/install/CMP0087-NEW.cmake

@@ -0,0 +1,3 @@
+# Need a new directory scope, not just a new policy scope
+# to test this correctly
+add_subdirectory(CMP0087-NEW)

+ 7 - 0
Tests/RunCMake/install/CMP0087-NEW/CMakeLists.txt

@@ -0,0 +1,7 @@
+# Note that it is the policy settings at the end of the directory
+# scope that will be used when deciding whether or not generator
+# expressions should be evaluated in the installed code.
+cmake_policy(VERSION 3.13)
+cmake_policy(SET CMP0087 NEW)
+add_library( codegenexlib INTERFACE )
+install(CODE "message( STATUS \"$<TARGET_PROPERTY:codegenexlib,NAME>\")")

+ 8 - 0
Tests/RunCMake/install/CMP0087-OLD-check.cmake

@@ -0,0 +1,8 @@
+execute_process(COMMAND ${CMAKE_COMMAND} -P ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake
+  OUTPUT_VARIABLE out ERROR_VARIABLE err)
+
+if(NOT out MATCHES "-- Install configuration: .*-- \\$<TARGET_PROPERTY:codegenexlib,NAME>")
+  string(REGEX REPLACE "\n" "\n  " out "  ${out}")
+  string(APPEND RunCMake_TEST_FAILED
+      "\"-- $<TARGET_PROPERTY:codegenexlib,NAME>\" was not found:\n${out}")
+endif()

+ 3 - 0
Tests/RunCMake/install/CMP0087-OLD.cmake

@@ -0,0 +1,3 @@
+# Need a new directory scope, not just a new policy scope
+# to test this correctly
+add_subdirectory(CMP0087-OLD)

+ 6 - 0
Tests/RunCMake/install/CMP0087-OLD/CMakeLists.txt

@@ -0,0 +1,6 @@
+# Note that it is the policy settings at the end of the directory
+# scope that will be used when deciding whether or not generator
+# expressions should be evaluated in the installed code.
+cmake_policy(VERSION 3.13)
+cmake_policy(SET CMP0087 OLD)
+install(CODE "message( STATUS \"$<TARGET_PROPERTY:codegenexlib,NAME>\")")

+ 5 - 0
Tests/RunCMake/install/CMP0087-WARN-stderr.txt

@@ -0,0 +1,5 @@
+CMake Warning (dev) in CMakeLists.txt:
+  Policy CMP0087 is not set: Install CODE|SCRIPT allow the use of generator
+  expressions.  Run "cmake --help-policy CMP0087" for policy details.  Use
+  the cmake_policy command to set the policy and suppress this warning.
+This warning is for project developers.  Use -Wno-dev to suppress it.

+ 2 - 0
Tests/RunCMake/install/CMP0087-WARN.cmake

@@ -0,0 +1,2 @@
+add_library( codegenexlib INTERFACE )
+install(CODE "message( STATUS \"$<TARGET_PROPERTY:codegenexlib,NAME>\")")

+ 3 - 0
Tests/RunCMake/install/RunCMakeTest.cmake

@@ -63,6 +63,9 @@ run_cmake(EXPORT-OldIFace)
 run_cmake(CMP0062-OLD)
 run_cmake(CMP0062-NEW)
 run_cmake(CMP0062-WARN)
+run_cmake(CMP0087-OLD)
+run_cmake(CMP0087-NEW)
+run_cmake(CMP0087-WARN)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-all)
 run_cmake(TARGETS-NAMELINK_COMPONENT-bad-exc)
 run_cmake(FILES-DESTINATION-TYPE)