Makefile: Add policy CMP0113 to avoid duplication of custom commands

Do not attach a custom command to a target if it is already attached to one of
the target's dependencies.  The command's output will be available by the time
the target needs it because the dependency containing the command will have
already been built.

This may break existing projects that do not properly mark non-created
outputs with the `SYMBOLIC` property.  Previously a chain of two custom
commands whose intermediate dependency is not created would put both
commands in a dependent project's Makefile even if the first command is
also in its dependency's Makefile.  The first command would run twice
but the build would work.  Now the second command needs an explicit
`SYMBOLIC` mark on its input to tell CMake that it is not expected to
exist.  To maintain compatibility with projects that left out the mark,
add a policy activating the behavior.
This commit is contained in:
Brad King
2020-09-04 09:51:15 -04:00
parent 844779bdc1
commit 45fedf0e17
20 changed files with 168 additions and 1 deletions
+10
View File
@@ -38,6 +38,7 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTargetDepend.h"
#include "cmVersion.h"
#include "cmake.h"
@@ -105,6 +106,15 @@ void cmLocalUnixMakefileGenerator3::Generate()
if (!gt->IsInBuildSystem()) {
continue;
}
auto& gtVisited = this->GetCommandsVisited(gt);
auto& deps = this->GlobalGenerator->GetTargetDirectDepends(gt);
for (auto& d : deps) {
// Take the union of visited source files of custom commands
auto depVisited = this->GetCommandsVisited(d);
gtVisited.insert(depVisited.begin(), depVisited.end());
}
std::unique_ptr<cmMakefileTargetGenerator> tg(
cmMakefileTargetGenerator::New(gt));
if (tg) {
+10
View File
@@ -19,6 +19,7 @@ class cmCustomCommandGenerator;
class cmGeneratorTarget;
class cmGlobalGenerator;
class cmMakefile;
class cmSourceFile;
/** \class cmLocalUnixMakefileGenerator3
* \brief Write a LocalUnix makefiles.
@@ -294,4 +295,13 @@ private:
bool ColorMakefile;
bool SkipPreprocessedSourceRules;
bool SkipAssemblySourceRules;
std::set<cmSourceFile const*>& GetCommandsVisited(
cmGeneratorTarget const* target)
{
return this->CommandsVisited[target];
};
std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>>
CommandsVisited;
};
+35
View File
@@ -26,10 +26,12 @@
#include "cmMakefileLibraryTargetGenerator.h"
#include "cmMakefileUtilityTargetGenerator.h"
#include "cmOutputConverter.h"
#include "cmPolicies.h"
#include "cmProperty.h"
#include "cmRange.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
#include "cmSourceFileLocationKind.h"
#include "cmState.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
@@ -51,6 +53,17 @@ cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target)
if (cmProp ruleStatus = cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
this->NoRuleMessages = cmIsOff(*ruleStatus);
}
switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) {
case cmPolicies::WARN:
case cmPolicies::OLD:
this->CMP0113New = false;
break;
case cmPolicies::NEW:
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
this->CMP0113New = true;
break;
}
MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this);
}
@@ -217,6 +230,12 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
this->GeneratorTarget->GetCustomCommands(customCommands,
this->GetConfigName());
for (cmSourceFile const* sf : customCommands) {
if (this->CMP0113New &&
!this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget)
.insert(sf)
.second) {
continue;
}
cmCustomCommandGenerator ccg(*sf->GetCustomCommand(),
this->GetConfigName(), this->LocalGenerator);
this->GenerateCustomRuleFile(ccg);
@@ -1337,6 +1356,22 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile(
bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
depends, commands);
// Symbolic inputs are not expected to exist, so add dummy rules.
if (this->CMP0113New && !depends.empty()) {
std::vector<std::string> no_depends;
std::vector<std::string> no_commands;
for (std::string const& dep : depends) {
if (cmSourceFile* dsf =
this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) {
if (dsf->GetPropertyAsBool("SYMBOLIC")) {
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
dep, no_depends, no_commands,
true);
}
}
}
}
// If the rule has changed make sure the output is rebuilt.
if (!symbolic) {
this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str());
+2
View File
@@ -196,6 +196,8 @@ protected:
unsigned long NumberOfProgressActions;
bool NoRuleMessages;
bool CMP0113New = false;
// the path to the directory the build file is in
std::string TargetBuildDirectory;
std::string TargetBuildDirectoryFull;
+6 -1
View File
@@ -333,6 +333,10 @@ class cmMakefile;
SELECT(POLICY, CMP0112, \
"Target file component generator expressions do not add target " \
"dependencies.", \
3, 19, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0113, \
"Makefile generators do not repeat custom commands from target " \
"dependencies.", \
3, 19, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
@@ -367,7 +371,8 @@ class cmMakefile;
F(CMP0104) \
F(CMP0105) \
F(CMP0108) \
F(CMP0112)
F(CMP0112) \
F(CMP0113)
/** \class cmPolicies
* \brief Handles changes in CMake behavior and policies