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
This commit is contained in:
parent
69050f4d16
commit
82be694c7a
@ -291,6 +291,8 @@ from the input content to produce the output content. The options are:
|
|||||||
|
|
||||||
``INPUT <input-file>``
|
``INPUT <input-file>``
|
||||||
Use the content from a given file as 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 <output-file>``
|
``OUTPUT <output-file>``
|
||||||
Specify the output file name to generate. Use generator expressions
|
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
|
name. Multiple configurations may generate the same output file only
|
||||||
if the generated content is identical. Otherwise, the ``<output-file>``
|
if the generated content is identical. Otherwise, the ``<output-file>``
|
||||||
must evaluate to an unique name for each configuration.
|
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
|
Exactly one ``CONTENT`` or ``INPUT`` option must be given. A specific
|
||||||
``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
|
``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
|
||||||
|
@ -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
|
to determine whether to report an error on use of deprecated macros or
|
||||||
functions.
|
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
|
Policies Introduced by CMake 3.9
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|
25
Help/policy/CMP0070.rst
Normal file
25
Help/policy/CMP0070.rst
Normal file
@ -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
Help/release/dev/file-generate-relative-paths.rst
Normal file
7
Help/release/dev/file-generate-relative-paths.rst
Normal file
@ -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`.
|
@ -20,11 +20,13 @@
|
|||||||
cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
|
cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
|
||||||
const std::string& input,
|
const std::string& input,
|
||||||
CM_AUTO_PTR<cmCompiledGeneratorExpression> outputFileExpr,
|
CM_AUTO_PTR<cmCompiledGeneratorExpression> outputFileExpr,
|
||||||
CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent)
|
CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent,
|
||||||
|
cmPolicies::PolicyStatus policyStatusCMP0070)
|
||||||
: Input(input)
|
: Input(input)
|
||||||
, OutputFileExpr(outputFileExpr)
|
, OutputFileExpr(outputFileExpr)
|
||||||
, Condition(condition)
|
, Condition(condition)
|
||||||
, InputIsContent(inputIsContent)
|
, InputIsContent(inputIsContent)
|
||||||
|
, PolicyStatusCMP0070(policyStatusCMP0070)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +60,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(
|
|||||||
|
|
||||||
if (cmSystemTools::FileIsFullPath(outputFileName)) {
|
if (cmSystemTools::FileIsFullPath(outputFileName)) {
|
||||||
outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
|
outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
|
||||||
|
} else {
|
||||||
|
outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string>::iterator it =
|
std::map<std::string, std::string>::iterator it =
|
||||||
@ -118,6 +122,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
|
|||||||
std::string inputFileName = this->Input;
|
std::string inputFileName = this->Input;
|
||||||
if (cmSystemTools::FileIsFullPath(inputFileName)) {
|
if (cmSystemTools::FileIsFullPath(inputFileName)) {
|
||||||
inputFileName = cmSystemTools::CollapseFullPath(inputFileName);
|
inputFileName = cmSystemTools::CollapseFullPath(inputFileName);
|
||||||
|
} else {
|
||||||
|
inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg);
|
||||||
}
|
}
|
||||||
lg->GetMakefile()->AddCMakeDependFile(inputFileName);
|
lg->GetMakefile()->AddCMakeDependFile(inputFileName);
|
||||||
cmSystemTools::GetPermissions(inputFileName.c_str(), perm);
|
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;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "cmGeneratorExpression.h"
|
#include "cmGeneratorExpression.h"
|
||||||
|
#include "cmPolicies.h"
|
||||||
#include "cm_auto_ptr.hxx"
|
#include "cm_auto_ptr.hxx"
|
||||||
#include "cm_sys_stat.h"
|
#include "cm_sys_stat.h"
|
||||||
|
|
||||||
@ -21,7 +22,8 @@ public:
|
|||||||
cmGeneratorExpressionEvaluationFile(
|
cmGeneratorExpressionEvaluationFile(
|
||||||
const std::string& input,
|
const std::string& input,
|
||||||
CM_AUTO_PTR<cmCompiledGeneratorExpression> outputFileExpr,
|
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);
|
void Generate(cmLocalGenerator* lg);
|
||||||
|
|
||||||
@ -35,12 +37,21 @@ private:
|
|||||||
cmCompiledGeneratorExpression* inputExpression,
|
cmCompiledGeneratorExpression* inputExpression,
|
||||||
std::map<std::string, std::string>& outputFiles, mode_t perm);
|
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:
|
private:
|
||||||
const std::string Input;
|
const std::string Input;
|
||||||
const CM_AUTO_PTR<cmCompiledGeneratorExpression> OutputFileExpr;
|
const CM_AUTO_PTR<cmCompiledGeneratorExpression> OutputFileExpr;
|
||||||
const CM_AUTO_PTR<cmCompiledGeneratorExpression> Condition;
|
const CM_AUTO_PTR<cmCompiledGeneratorExpression> Condition;
|
||||||
std::vector<std::string> Files;
|
std::vector<std::string> Files;
|
||||||
const bool InputIsContent;
|
const bool InputIsContent;
|
||||||
|
cmPolicies::PolicyStatus PolicyStatusCMP0070;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -592,7 +592,8 @@ void cmMakefile::AddEvaluationFile(
|
|||||||
CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent)
|
CM_AUTO_PTR<cmCompiledGeneratorExpression> condition, bool inputIsContent)
|
||||||
{
|
{
|
||||||
this->EvaluationFiles.push_back(new cmGeneratorExpressionEvaluationFile(
|
this->EvaluationFiles.push_back(new cmGeneratorExpressionEvaluationFile(
|
||||||
inputFile, outputName, condition, inputIsContent));
|
inputFile, outputName, condition, inputIsContent,
|
||||||
|
this->GetPolicyStatus(cmPolicies::CMP0070)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<cmGeneratorExpressionEvaluationFile*>
|
std::vector<cmGeneratorExpressionEvaluationFile*>
|
||||||
|
@ -206,6 +206,9 @@ class cmMakefile;
|
|||||||
cmPolicies::WARN) \
|
cmPolicies::WARN) \
|
||||||
SELECT(POLICY, CMP0069, \
|
SELECT(POLICY, CMP0069, \
|
||||||
"INTERPROCEDURAL_OPTIMIZATION is enforced when enabled.", 3, 9, 0, \
|
"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)
|
cmPolicies::WARN)
|
||||||
|
|
||||||
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
|
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
|
||||||
|
13
Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake
Normal file
13
Tests/RunCMake/File_Generate/CMP0070-NEW-check.cmake
Normal file
@ -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
Tests/RunCMake/File_Generate/CMP0070-NEW.cmake
Normal file
2
Tests/RunCMake/File_Generate/CMP0070-NEW.cmake
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cmake_policy(SET CMP0070 NEW)
|
||||||
|
file(GENERATE OUTPUT relative-output-NEW.txt INPUT relative-input-NEW.txt)
|
13
Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake
Normal file
13
Tests/RunCMake/File_Generate/CMP0070-OLD-check.cmake
Normal file
@ -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
Tests/RunCMake/File_Generate/CMP0070-OLD.cmake
Normal file
3
Tests/RunCMake/File_Generate/CMP0070-OLD.cmake
Normal file
@ -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
Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake
Normal file
13
Tests/RunCMake/File_Generate/CMP0070-WARN-check.cmake
Normal file
@ -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
Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt
Normal file
27
Tests/RunCMake/File_Generate/CMP0070-WARN-stderr.txt
Normal file
@ -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
Tests/RunCMake/File_Generate/CMP0070-WARN.cmake
Normal file
2
Tests/RunCMake/File_Generate/CMP0070-WARN.cmake
Normal file
@ -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)
|
@ -1,5 +1,9 @@
|
|||||||
include(RunCMake)
|
include(RunCMake)
|
||||||
|
|
||||||
|
run_cmake(CMP0070-NEW)
|
||||||
|
run_cmake(CMP0070-OLD)
|
||||||
|
run_cmake(CMP0070-WARN)
|
||||||
|
|
||||||
run_cmake(CommandConflict)
|
run_cmake(CommandConflict)
|
||||||
if("${RunCMake_GENERATOR}" MATCHES "Visual Studio|Xcode")
|
if("${RunCMake_GENERATOR}" MATCHES "Visual Studio|Xcode")
|
||||||
run_cmake(OutputConflict)
|
run_cmake(OutputConflict)
|
||||||
|
1
Tests/RunCMake/File_Generate/relative-input-NEW.txt
Normal file
1
Tests/RunCMake/File_Generate/relative-input-NEW.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
relative-input-NEW
|
Loading…
Reference in New Issue
Block a user