ソースを参照

file(GENERATE): Add policy CMP0070 to define relative path behavior

Previously `file(GENERATE)` did not define any behavior for relative
paths given to the `OUTPUT` or `INPUT` arguments.  Define behavior
consistent with CMake conventions and add a policy to provide
compatibility for projects that relied on the old accidental behavior.

Fixes: #16786
Brad King 8 年 前
コミット
82be694c7a

+ 5 - 0
Help/command/file.rst

@@ -291,6 +291,8 @@ from the input content to produce the output content.  The options are:
 
 ``INPUT <input-file>``
   Use the content from a given file as input.
+  A relative path is treated with respect to the value of
+  :variable:`CMAKE_CURRENT_SOURCE_DIR`.  See policy :policy:`CMP0070`.
 
 ``OUTPUT <output-file>``
   Specify the output file name to generate.  Use generator expressions
@@ -298,6 +300,9 @@ from the input content to produce the output content.  The options are:
   name.  Multiple configurations may generate the same output file only
   if the generated content is identical.  Otherwise, the ``<output-file>``
   must evaluate to an unique name for each configuration.
+  A relative path (after evaluating generator expressions) is treated
+  with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
+  See policy :policy:`CMP0070`.
 
 Exactly one ``CONTENT`` or ``INPUT`` option must be given.  A specific
 ``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.

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

@@ -51,6 +51,14 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.10
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0070: Define file(GENERATE) behavior for relative paths. </policy/CMP0070>
+
 Policies Introduced by CMake 3.9
 ================================
 

+ 25 - 0
Help/policy/CMP0070.rst

@@ -0,0 +1,25 @@
+CMP0070
+-------
+
+Define :command:`file(GENERATE)` behavior for relative paths.
+
+CMake 3.10 and newer define that relative paths given to ``INPUT`` and
+``OUTPUT`` arguments of ``file(GENERATE)`` are interpreted relative to the
+current source and binary directories, respectively.  CMake 3.9 and lower did
+not define any behavior for relative paths but did not diagnose them either
+and accidentally treated them relative to the process working directory.
+Policy ``CMP0070`` provides compatibility with projects that used the old
+undefined behavior.
+
+This policy affects behavior of relative paths given to ``file(GENERATE)``.
+The ``OLD`` behavior for this policy is to treat the paths relative to the
+working directory of CMake.  The ``NEW`` behavior for this policy is to
+interpret relative paths with respect to the current source or binary
+directory of the caller.
+
+This policy was introduced in CMake version 3.10.  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

+ 7 - 0
Help/release/dev/file-generate-relative-paths.rst

@@ -0,0 +1,7 @@
+file-generate-relative-paths
+----------------------------
+
+* The :command:`file(GENERATE)` command now interprets relative paths
+  given to its ``OUTPUT`` and ``INPUT`` arguments with respect to the
+  caller's current binary and source directories, respectively.
+  See policy :policy:`CMP0070`.

+ 61 - 1
Source/cmGeneratorExpressionEvaluationFile.cxx

@@ -20,11 +20,13 @@
 cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
   const std::string& input,
   CM_AUTO_PTR<cmCompiledGeneratorExpression> outputFileExpr,
-  CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent)
+  CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent,
+  cmPolicies::PolicyStatus policyStatusCMP0070)
   : Input(input)
   , OutputFileExpr(outputFileExpr)
   , Condition(condition)
   , InputIsContent(inputIsContent)
+  , PolicyStatusCMP0070(policyStatusCMP0070)
 {
 }
 
@@ -58,6 +60,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(
 
   if (cmSystemTools::FileIsFullPath(outputFileName)) {
     outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
+  } else {
+    outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg);
   }
 
   std::map<std::string, std::string>::iterator it =
@@ -118,6 +122,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
     std::string inputFileName = this->Input;
     if (cmSystemTools::FileIsFullPath(inputFileName)) {
       inputFileName = cmSystemTools::CollapseFullPath(inputFileName);
+    } else {
+      inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg);
     }
     lg->GetMakefile()->AddCMakeDependFile(inputFileName);
     cmSystemTools::GetPermissions(inputFileName.c_str(), perm);
@@ -167,3 +173,57 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
     }
   }
 }
+
+std::string cmGeneratorExpressionEvaluationFile::FixRelativePath(
+  std::string const& relativePath, PathRole role, cmLocalGenerator* lg)
+{
+  std::string resultPath;
+  switch (this->PolicyStatusCMP0070) {
+    case cmPolicies::WARN: {
+      std::string arg;
+      switch (role) {
+        case PathForInput:
+          arg = "INPUT";
+          break;
+        case PathForOutput:
+          arg = "OUTPUT";
+          break;
+      }
+      std::ostringstream w;
+      /* clang-format off */
+      w <<
+        cmPolicies::GetPolicyWarning(cmPolicies::CMP0070) << "\n"
+        "file(GENERATE) given relative " << arg << " path:\n"
+        "  " << relativePath << "\n"
+        "This is not defined behavior unless CMP0070 is set to NEW.  "
+        "For compatibility with older versions of CMake, the previous "
+        "undefined behavior will be used."
+        ;
+      /* clang-format on */
+      lg->IssueMessage(cmake::AUTHOR_WARNING, w.str());
+    }
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD:
+      // OLD behavior is to use the relative path unchanged,
+      // which ends up being used relative to the working dir.
+      resultPath = relativePath;
+      break;
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::NEW:
+      // NEW behavior is to interpret the relative path with respect
+      // to the current source or binary directory.
+      switch (role) {
+        case PathForInput:
+          resultPath = cmSystemTools::CollapseFullPath(
+            relativePath, lg->GetCurrentSourceDirectory());
+          break;
+        case PathForOutput:
+          resultPath = cmSystemTools::CollapseFullPath(
+            relativePath, lg->GetCurrentBinaryDirectory());
+          break;
+      }
+      break;
+  }
+  return resultPath;
+}

+ 12 - 1
Source/cmGeneratorExpressionEvaluationFile.h

@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "cmGeneratorExpression.h"
+#include "cmPolicies.h"
 #include "cm_auto_ptr.hxx"
 #include "cm_sys_stat.h"
 
@@ -21,7 +22,8 @@ public:
   cmGeneratorExpressionEvaluationFile(
     const std::string& input,
     CM_AUTO_PTR<cmCompiledGeneratorExpression> outputFileExpr,
-    CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent);
+    CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent,
+    cmPolicies::PolicyStatus policyStatusCMP0070);
 
   void Generate(cmLocalGenerator* lg);
 
@@ -35,12 +37,21 @@ private:
                 cmCompiledGeneratorExpression* inputExpression,
                 std::map<std::string, std::string>& outputFiles, mode_t perm);
 
+  enum PathRole
+  {
+    PathForInput,
+    PathForOutput
+  };
+  std::string FixRelativePath(std::string const& filePath, PathRole role,
+                              cmLocalGenerator* lg);
+
 private:
   const std::string Input;
   const CM_AUTO_PTR<cmCompiledGeneratorExpression> OutputFileExpr;
   const CM_AUTO_PTR<cmCompiledGeneratorExpression> Condition;
   std::vector<std::string> Files;
   const bool InputIsContent;
+  cmPolicies::PolicyStatus PolicyStatusCMP0070;
 };
 
 #endif

+ 2 - 1
Source/cmMakefile.cxx

@@ -592,7 +592,8 @@ void cmMakefile::AddEvaluationFile(
   CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent)
 {
   this->EvaluationFiles.push_back(new cmGeneratorExpressionEvaluationFile(
-    inputFile, outputName, condition, inputIsContent));
+    inputFile, outputName, condition, inputIsContent,
+    this->GetPolicyStatus(cmPolicies::CMP0070)));
 }
 
 std::vector<cmGeneratorExpressionEvaluationFile*>

+ 3 - 0
Source/cmPolicies.h

@@ -206,6 +206,9 @@ class cmMakefile;
          cmPolicies::WARN)                                                    \
   SELECT(POLICY, CMP0069,                                                     \
          "INTERPROCEDURAL_OPTIMIZATION is enforced when enabled.", 3, 9, 0,   \
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0070,                                                     \
+         "Define file(GENERATE) behavior for relative paths.", 3, 10, 0,      \
          cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)

+ 13 - 0
Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake

@@ -0,0 +1,13 @@
+foreach(f
+    "${RunCMake_TEST_SOURCE_DIR}/relative-input-NEW.txt"
+    "${RunCMake_TEST_BINARY_DIR}/relative-output-NEW.txt"
+    )
+  if(EXISTS "${f}")
+    file(READ "${f}" content)
+    if(NOT content MATCHES "^relative-input-NEW[\r\n]*$")
+      string(APPEND RunCMake_TEST_FAILED "File\n  ${f}\ndoes not have expected content.\n")
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "Missing\n  ${f}\n")
+  endif()
+endforeach()

+ 2 - 0
Tests/RunCMake/File_Generate/CMP0070-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0070 NEW)
+file(GENERATE OUTPUT relative-output-NEW.txt INPUT relative-input-NEW.txt)

+ 13 - 0
Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake

@@ -0,0 +1,13 @@
+foreach(f
+    "${RunCMake_TEST_BINARY_DIR}/relative-input-OLD.txt"
+    "${RunCMake_TEST_BINARY_DIR}/relative-output-OLD.txt"
+    )
+  if(EXISTS "${f}")
+    file(READ "${f}" content)
+    if(NOT content MATCHES "^relative-input-OLD[\r\n]*$")
+      string(APPEND RunCMake_TEST_FAILED "File\n  ${f}\ndoes not have expected content.\n")
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "Missing\n  ${f}\n")
+  endif()
+endforeach()

+ 3 - 0
Tests/RunCMake/File_Generate/CMP0070-OLD.cmake

@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0070 OLD)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/relative-input-OLD.txt "relative-input-OLD\n")
+file(GENERATE OUTPUT relative-output-OLD.txt INPUT relative-input-OLD.txt)

+ 13 - 0
Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake

@@ -0,0 +1,13 @@
+foreach(f
+    "${RunCMake_TEST_BINARY_DIR}/relative-input-WARN.txt"
+    "${RunCMake_TEST_BINARY_DIR}/relative-output-WARN.txt"
+    )
+  if(EXISTS "${f}")
+    file(READ "${f}" content)
+    if(NOT content MATCHES "^relative-input-WARN[\r\n]*$")
+      string(APPEND RunCMake_TEST_FAILED "File\n  ${f}\ndoes not have expected content.\n")
+    endif()
+  else()
+    string(APPEND RunCMake_TEST_FAILED "Missing\n  ${f}\n")
+  endif()
+endforeach()

+ 27 - 0
Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt

@@ -0,0 +1,27 @@
+^CMake Warning \(dev\) in CMakeLists.txt:
+  Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative
+  paths.  Run "cmake --help-policy CMP0070" for policy details.  Use the
+  cmake_policy command to set the policy and suppress this warning.
+
+  file\(GENERATE\) given relative INPUT path:
+
+    relative-input-WARN.txt
+
+  This is not defined behavior unless CMP0070 is set to NEW.  For
+  compatibility with older versions of CMake, the previous undefined behavior
+  will be used.
+This warning is for project developers.  Use -Wno-dev to suppress it.(
++
+CMake Warning \(dev\) in CMakeLists.txt:
+  Policy CMP0070 is not set: Define file\(GENERATE\) behavior for relative
+  paths.  Run "cmake --help-policy CMP0070" for policy details.  Use the
+  cmake_policy command to set the policy and suppress this warning.
+
+  file\(GENERATE\) given relative OUTPUT path:
+
+    relative-output-WARN.txt
+
+  This is not defined behavior unless CMP0070 is set to NEW.  For
+  compatibility with older versions of CMake, the previous undefined behavior
+  will be used.
+This warning is for project developers.  Use -Wno-dev to suppress it.)+$

+ 2 - 0
Tests/RunCMake/File_Generate/CMP0070-WARN.cmake

@@ -0,0 +1,2 @@
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/relative-input-WARN.txt "relative-input-WARN\n")
+file(GENERATE OUTPUT relative-output-WARN.txt INPUT relative-input-WARN.txt)

+ 4 - 0
Tests/RunCMake/File_Generate/RunCMakeTest.cmake

@@ -1,5 +1,9 @@
 include(RunCMake)
 
+run_cmake(CMP0070-NEW)
+run_cmake(CMP0070-OLD)
+run_cmake(CMP0070-WARN)
+
 run_cmake(CommandConflict)
 if("${RunCMake_GENERATOR}" MATCHES "Visual Studio|Xcode")
   run_cmake(OutputConflict)

+ 1 - 0
Tests/RunCMake/File_Generate/relative-input-NEW.txt

@@ -0,0 +1 @@
+relative-input-NEW