mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-05 23:30:25 +00:00
add_custom_command: Disallow exe-suffixed targets in DEPENDS
Two targets whose names differ only in a `.exe` suffix create a conflict when adding target- and file-level dependencies for a custom command. Add CMP0212 to provide compatibility with the old behavior. Issue: #27612
This commit is contained in:
@@ -188,6 +188,12 @@ Generating Files
|
||||
cause the custom command to re-run whenever the target is
|
||||
recompiled.
|
||||
|
||||
.. versionchanged:: 4.4
|
||||
|
||||
Previously an executable target could be specified using the name
|
||||
of the target with ``.exe`` appended. This is no longer allowed.
|
||||
See policy :policy:`CMP0212`.
|
||||
|
||||
2. If the argument is an absolute path, a file-level dependency
|
||||
is created on that path.
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ Policies Introduced by CMake 4.4
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
CMP0212: add_custom_command DEPENDS does not strip .exe suffixes. </policy/CMP0212>
|
||||
CMP0211: A file may belong to at most one file set in a target. </policy/CMP0211>
|
||||
|
||||
Policies Introduced by CMake 4.3
|
||||
|
||||
31
Help/policy/CMP0212.rst
Normal file
31
Help/policy/CMP0212.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
CMP0212
|
||||
-------
|
||||
|
||||
.. versionadded:: 4.4
|
||||
|
||||
:command:`add_custom_command` ``DEPENDS`` does not strip ``.exe`` suffixes.
|
||||
|
||||
When searching for dependencies specified with the ``DEPENDS`` option of
|
||||
:command:`add_custom_command`, CMake 4.3 and below applied a heuristic to
|
||||
remove ``.exe`` from the end of ``DEPENDS`` argument values, which created a
|
||||
target-level dependency on an executable with that stripped name if such a
|
||||
target exists. This allowed using the name of the target output file as an
|
||||
alternative spelling when naming dependencies. However, with the emergence of
|
||||
CMake's target model, specifying the output file in this way is no longer
|
||||
needed, and leads to conflicts in creating target- and file-level dependencies
|
||||
when targets exist with the ``.exe`` suffix in the name itself.
|
||||
|
||||
The ``OLD`` behavior of this policy strips the ``.exe`` suffix from arguments
|
||||
to ``DEPENDS`` when searching for target-level dependencies. The ``NEW``
|
||||
behavior does not strip the ``.exe`` suffix in its search. CMake will otherwise
|
||||
proceed normally with the other dependency search heuristics as specified in
|
||||
:command:`add_custom_command` ``DEPENDS``. Users are encouraged to specify the
|
||||
name of the executable target if a target-level dependency is desired, or use
|
||||
the :genex:`TARGET_FILE` generator expression if a file-level dependency
|
||||
is desired, rather than implying the target indirectly via its output file name.
|
||||
|
||||
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4
|
||||
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
|
||||
.. include:: include/STANDARD_ADVICE.rst
|
||||
|
||||
.. include:: include/DEPRECATED.rst
|
||||
6
Help/release/dev/add_custom_command-DEPENDS-exe.rst
Normal file
6
Help/release/dev/add_custom_command-DEPENDS-exe.rst
Normal file
@@ -0,0 +1,6 @@
|
||||
add_custom_command-DEPENDS-exe
|
||||
------------------------------
|
||||
|
||||
* Names of executables given to the ``DEPENDS`` argument of
|
||||
:command:`add_custom_command` no longer have ``.exe`` suffixes stripped to
|
||||
establish target-level dependencies. See policy :policy:`CMP0212`.
|
||||
@@ -356,7 +356,8 @@ void cmFastbuildTargetGenerator::GetDepends(
|
||||
for (auto dep : ccg.GetDepends()) {
|
||||
LogMessage("Dep: " + dep);
|
||||
auto orig = dep;
|
||||
if (this->LocalCommonGenerator->GetRealDependency(dep, Config, dep)) {
|
||||
if (this->LocalCommonGenerator->GetRealDependency(
|
||||
dep, Config, dep, ccg.GetCC().GetCMP0212Status())) {
|
||||
LogMessage("Real dep: " + dep);
|
||||
if (!dep.empty()) {
|
||||
LogMessage("Custom command real dep: " + dep);
|
||||
|
||||
@@ -2095,7 +2095,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
|
||||
realDepends.reserve(ccg.GetDepends().size());
|
||||
for (auto const& d : ccg.GetDepends()) {
|
||||
std::string dep;
|
||||
if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) {
|
||||
if (this->CurrentLocalGenerator->GetRealDependency(
|
||||
d, configName, dep, cc.GetCMP0212Status())) {
|
||||
realDepends.emplace_back(std::move(dep));
|
||||
}
|
||||
}
|
||||
@@ -2464,7 +2465,8 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile(
|
||||
realDepends.reserve(ccg.GetDepends().size());
|
||||
for (auto const& d : ccg.GetDepends()) {
|
||||
std::string dep;
|
||||
if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) {
|
||||
if (this->CurrentLocalGenerator->GetRealDependency(
|
||||
d, configName, dep, command.GetCMP0212Status())) {
|
||||
realDepends.emplace_back(std::move(dep));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2317,7 +2317,8 @@ cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse(
|
||||
|
||||
bool cmLocalGenerator::GetRealDependency(std::string const& inName,
|
||||
std::string const& config,
|
||||
std::string& dep)
|
||||
std::string& dep,
|
||||
cmPolicies::PolicyStatus cmp0212)
|
||||
{
|
||||
// Older CMake code may specify the dependency using the target
|
||||
// output file rather than the target name. Such code would have
|
||||
@@ -2334,8 +2335,8 @@ bool cmLocalGenerator::GetRealDependency(std::string const& inName,
|
||||
|
||||
// Look for a CMake target with the given name.
|
||||
cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name);
|
||||
if (!target && cmHasSuffix(name, ".exe"_s)) {
|
||||
// If it doesn't exist, try to strip the `.exe` suffix.
|
||||
if (!target && cmHasSuffix(name, ".exe"_s) && cmp0212 != cmPolicies::NEW) {
|
||||
// If it doesn't exist, try to strip the `.exe` suffix per CMP0212.
|
||||
std::string strippedName =
|
||||
cmSystemTools::GetFilenameWithoutLastExtension(name);
|
||||
if (cmGeneratorTarget* strippedTarget =
|
||||
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
* used for dependencies of custom commands.
|
||||
*/
|
||||
bool GetRealDependency(std::string const& name, std::string const& config,
|
||||
std::string& dep);
|
||||
std::string& dep, cmPolicies::PolicyStatus cmp0212);
|
||||
|
||||
/** Called from command-line hook to clear dependencies. */
|
||||
virtual void ClearDependencies(cmMakefile* /* mf */, bool /* verbose */) {}
|
||||
|
||||
@@ -414,7 +414,8 @@ void cmLocalNinjaGenerator::AppendCustomCommandDeps(
|
||||
{
|
||||
for (std::string const& i : ccg.GetDepends()) {
|
||||
std::string dep;
|
||||
if (this->GetRealDependency(i, config, dep)) {
|
||||
if (this->GetRealDependency(i, config, dep,
|
||||
ccg.GetCC().GetCMP0212Status())) {
|
||||
ninjaDeps.push_back(
|
||||
this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(dep));
|
||||
}
|
||||
|
||||
@@ -957,7 +957,8 @@ void cmLocalUnixMakefileGenerator3::AppendCustomDepend(
|
||||
for (std::string const& d : ccg.GetDepends()) {
|
||||
// Lookup the real name of the dependency in case it is a CMake target.
|
||||
std::string dep;
|
||||
if (this->GetRealDependency(d, this->GetConfigName(), dep)) {
|
||||
if (this->GetRealDependency(d, this->GetConfigName(), dep,
|
||||
ccg.GetCC().GetCMP0212Status())) {
|
||||
depends.push_back(std::move(dep));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1867,7 +1867,8 @@ void cmLocalVisualStudio7Generator::WriteCustomRule(
|
||||
for (std::string const& d : ccg.GetDepends()) {
|
||||
// Get the real name of the dependency in case it is a CMake target.
|
||||
std::string dep;
|
||||
if (this->GetRealDependency(d, config, dep)) {
|
||||
if (this->GetRealDependency(d, config, dep,
|
||||
command.GetCMP0212Status())) {
|
||||
fout << this->ConvertToXMLOutputPath(dep) << ";";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,6 +632,9 @@ class cmMakefile;
|
||||
3, 0, WARN) \
|
||||
SELECT(POLICY, CMP0211, \
|
||||
"A file may belong to at most one file set in a target.", 4, 4, 0, \
|
||||
WARN) \
|
||||
SELECT(POLICY, CMP0212, \
|
||||
"add_custom_command DEPENDS does not strip .exe suffixes.", 4, 4, 0, \
|
||||
WARN)
|
||||
|
||||
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
|
||||
@@ -692,7 +695,8 @@ class cmMakefile;
|
||||
|
||||
#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
|
||||
F(CMP0116) \
|
||||
F(CMP0147)
|
||||
F(CMP0147) \
|
||||
F(CMP0212)
|
||||
|
||||
/** \class cmPolicies
|
||||
* \brief Handles changes in CMake behavior and policies
|
||||
|
||||
@@ -1840,7 +1840,7 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule(
|
||||
|
||||
for (std::string const& d : ccg.GetDepends()) {
|
||||
std::string dep;
|
||||
if (lg->GetRealDependency(d, c, dep)) {
|
||||
if (lg->GetRealDependency(d, c, dep, command.GetCMP0212Status())) {
|
||||
if (!unique_inputs.insert(dep).second) {
|
||||
// already listed
|
||||
continue;
|
||||
|
||||
1
Tests/RunCMake/CMP0212/CMP0212-NEW-build-result.txt
Normal file
1
Tests/RunCMake/CMP0212/CMP0212-NEW-build-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
[^0]
|
||||
1
Tests/RunCMake/CMP0212/CMP0212-NEW-build-stdout.txt
Normal file
1
Tests/RunCMake/CMP0212/CMP0212-NEW-build-stdout.txt
Normal file
@@ -0,0 +1 @@
|
||||
.*
|
||||
2
Tests/RunCMake/CMP0212/CMP0212-common-exe.cmake
Normal file
2
Tests/RunCMake/CMP0212/CMP0212-common-exe.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
add_executable(foo main.c)
|
||||
set_target_properties(foo PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
4
Tests/RunCMake/CMP0212/CMakeLists.txt
Normal file
4
Tests/RunCMake/CMP0212/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 4.3)
|
||||
project(${RunCMake_TEST} C)
|
||||
include(CMP0212-common-exe.cmake)
|
||||
add_subdirectory(subdir)
|
||||
12
Tests/RunCMake/CMP0212/RunCMakeTest.cmake
Normal file
12
Tests/RunCMake/CMP0212/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
include(RunCMake)
|
||||
|
||||
function(run_cmake_case_cmp0212 case)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
|
||||
run_cmake(${case})
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
set(RunCMake_TEST_OUTPUT_MERGE 1)
|
||||
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --target ${ARGN})
|
||||
endfunction()
|
||||
|
||||
run_cmake_case_cmp0212(CMP0212-OLD tgt1)
|
||||
run_cmake_case_cmp0212(CMP0212-NEW tgt1 bar)
|
||||
4
Tests/RunCMake/CMP0212/main.c
Normal file
4
Tests/RunCMake/CMP0212/main.c
Normal file
@@ -0,0 +1,4 @@
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
11
Tests/RunCMake/CMP0212/subdir/CMP0212-NEW.cmake
Normal file
11
Tests/RunCMake/CMP0212/subdir/CMP0212-NEW.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_policy(SET CMP0212 NEW)
|
||||
include(CMP0212-common-custom.cmake)
|
||||
|
||||
# Define an executable with a different target name so that the above custom
|
||||
# command finds the file-level dependency instead of the target-level
|
||||
# dependency, and thus fails the build by compiling a bad C file.
|
||||
add_executable(bar bad.c)
|
||||
set_target_properties(bar PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "$<1:${CMAKE_CURRENT_BINARY_DIR}>"
|
||||
RUNTIME_OUTPUT_NAME foo
|
||||
)
|
||||
2
Tests/RunCMake/CMP0212/subdir/CMP0212-OLD.cmake
Normal file
2
Tests/RunCMake/CMP0212/subdir/CMP0212-OLD.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
cmake_policy(SET CMP0212 OLD)
|
||||
include(CMP0212-common-custom.cmake)
|
||||
@@ -0,0 +1,6 @@
|
||||
add_custom_command(
|
||||
OUTPUT cmd1
|
||||
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/cmd1"
|
||||
DEPENDS foo.exe
|
||||
)
|
||||
add_custom_target(tgt1 DEPENDS cmd1)
|
||||
1
Tests/RunCMake/CMP0212/subdir/CMakeLists.txt
Normal file
1
Tests/RunCMake/CMP0212/subdir/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
1
Tests/RunCMake/CMP0212/subdir/bad.c
Normal file
1
Tests/RunCMake/CMP0212/subdir/bad.c
Normal file
@@ -0,0 +1 @@
|
||||
#error "This is an invalid C file."
|
||||
@@ -180,6 +180,9 @@ add_RunCMake_test(CMP0170)
|
||||
add_RunCMake_test(CMP0171)
|
||||
add_RunCMake_test(CMP0173)
|
||||
add_RunCMake_test(CMP0187)
|
||||
if(WIN32)
|
||||
add_RunCMake_test(CMP0212)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
|
||||
|
||||
Reference in New Issue
Block a user