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:
Brad King 2017-06-08 11:54:03 -04:00
parent 69050f4d16
commit 82be694c7a
17 changed files with 201 additions and 3 deletions

View File

@ -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)``.

View File

@ -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
Help/policy/CMP0070.rst Normal file
View 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

View 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`.

View File

@ -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;
}

View File

@ -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

View File

@ -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*>

View File

@ -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)

View 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()

View File

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

View 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()

View 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)

View 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()

View 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.)+$

View 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)

View File

@ -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)

View File

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