From 82be694c7a1a878650563cd2c72b60f314d5dc85 Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 8 Jun 2017 11:54:03 -0400 Subject: 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 --- Help/command/file.rst | 5 ++ Help/manual/cmake-policies.7.rst | 8 +++ Help/policy/CMP0070.rst | 25 +++++++++ Help/release/dev/file-generate-relative-paths.rst | 7 +++ Source/cmGeneratorExpressionEvaluationFile.cxx | 62 +++++++++++++++++++++- Source/cmGeneratorExpressionEvaluationFile.h | 13 ++++- Source/cmMakefile.cxx | 3 +- Source/cmPolicies.h | 3 ++ .../RunCMake/File_Generate/CMP0070-NEW-check.cmake | 13 +++++ Tests/RunCMake/File_Generate/CMP0070-NEW.cmake | 2 + .../RunCMake/File_Generate/CMP0070-OLD-check.cmake | 13 +++++ Tests/RunCMake/File_Generate/CMP0070-OLD.cmake | 3 ++ .../File_Generate/CMP0070-WARN-check.cmake | 13 +++++ .../RunCMake/File_Generate/CMP0070-WARN-stderr.txt | 27 ++++++++++ Tests/RunCMake/File_Generate/CMP0070-WARN.cmake | 2 + Tests/RunCMake/File_Generate/RunCMakeTest.cmake | 4 ++ .../RunCMake/File_Generate/relative-input-NEW.txt | 1 + 17 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 Help/policy/CMP0070.rst create mode 100644 Help/release/dev/file-generate-relative-paths.rst create mode 100644 Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake create mode 100644 Tests/RunCMake/File_Generate/CMP0070-NEW.cmake create mode 100644 Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake create mode 100644 Tests/RunCMake/File_Generate/CMP0070-OLD.cmake create mode 100644 Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake create mode 100644 Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt create mode 100644 Tests/RunCMake/File_Generate/CMP0070-WARN.cmake create mode 100644 Tests/RunCMake/File_Generate/relative-input-NEW.txt diff --git a/Help/command/file.rst b/Help/command/file.rst index b2e4eea..7afb715 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -291,6 +291,8 @@ from the input content to produce the output content. The options are: ``INPUT `` 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 `` 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 ```` 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)``. diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 7b85817..eb9af27 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/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. + Policies Introduced by CMake 3.9 ================================ diff --git a/Help/policy/CMP0070.rst b/Help/policy/CMP0070.rst new file mode 100644 index 0000000..0fb3617 --- /dev/null +++ b/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 diff --git a/Help/release/dev/file-generate-relative-paths.rst b/Help/release/dev/file-generate-relative-paths.rst new file mode 100644 index 0000000..fdeb9e0e --- /dev/null +++ b/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`. diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx index c21e4f6..5003e8d 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.cxx +++ b/Source/cmGeneratorExpressionEvaluationFile.cxx @@ -20,11 +20,13 @@ cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile( const std::string& input, CM_AUTO_PTR outputFileExpr, - CM_AUTO_PTR condition, bool inputIsContent) + CM_AUTO_PTR 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::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; +} diff --git a/Source/cmGeneratorExpressionEvaluationFile.h b/Source/cmGeneratorExpressionEvaluationFile.h index 9872746..ecf919d 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.h +++ b/Source/cmGeneratorExpressionEvaluationFile.h @@ -10,6 +10,7 @@ #include #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 outputFileExpr, - CM_AUTO_PTR condition, bool inputIsContent); + CM_AUTO_PTR condition, bool inputIsContent, + cmPolicies::PolicyStatus policyStatusCMP0070); void Generate(cmLocalGenerator* lg); @@ -35,12 +37,21 @@ private: cmCompiledGeneratorExpression* inputExpression, std::map& 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 OutputFileExpr; const CM_AUTO_PTR Condition; std::vector Files; const bool InputIsContent; + cmPolicies::PolicyStatus PolicyStatusCMP0070; }; #endif diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 802a427..2a93966 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -592,7 +592,8 @@ void cmMakefile::AddEvaluationFile( CM_AUTO_PTR condition, bool inputIsContent) { this->EvaluationFiles.push_back(new cmGeneratorExpressionEvaluationFile( - inputFile, outputName, condition, inputIsContent)); + inputFile, outputName, condition, inputIsContent, + this->GetPolicyStatus(cmPolicies::CMP0070))); } std::vector diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 69cbc18..0a0178c 100644 --- a/Source/cmPolicies.h +++ b/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) diff --git a/Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake b/Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake new file mode 100644 index 0000000..05ec26e --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/File_Generate/CMP0070-NEW.cmake b/Tests/RunCMake/File_Generate/CMP0070-NEW.cmake new file mode 100644 index 0000000..1a03822 --- /dev/null +++ b/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) diff --git a/Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake b/Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake new file mode 100644 index 0000000..a71d822 --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/File_Generate/CMP0070-OLD.cmake b/Tests/RunCMake/File_Generate/CMP0070-OLD.cmake new file mode 100644 index 0000000..0fb47cd --- /dev/null +++ b/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) diff --git a/Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake b/Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake new file mode 100644 index 0000000..1488df0 --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt b/Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt new file mode 100644 index 0000000..dbabaa9 --- /dev/null +++ b/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.)+$ diff --git a/Tests/RunCMake/File_Generate/CMP0070-WARN.cmake b/Tests/RunCMake/File_Generate/CMP0070-WARN.cmake new file mode 100644 index 0000000..ccb0452 --- /dev/null +++ b/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) diff --git a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake b/Tests/RunCMake/File_Generate/RunCMakeTest.cmake index 82e903d..b660463 100644 --- a/Tests/RunCMake/File_Generate/RunCMakeTest.cmake +++ b/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) diff --git a/Tests/RunCMake/File_Generate/relative-input-NEW.txt b/Tests/RunCMake/File_Generate/relative-input-NEW.txt new file mode 100644 index 0000000..7293e90 --- /dev/null +++ b/Tests/RunCMake/File_Generate/relative-input-NEW.txt @@ -0,0 +1 @@ +relative-input-NEW -- cgit v0.12