summaryrefslogtreecommitdiffstats
path: root/Source/cmLocalNinjaGenerator.cxx
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2020-12-09 21:39:15 (GMT)
committerBrad King <brad.king@kitware.com>2020-12-15 12:01:21 (GMT)
commitdcf9f4d2f71d7c608ce24b690d30d465df51dc71 (patch)
tree0868f3cd8176eb883063a77350864b70782b2dad /Source/cmLocalNinjaGenerator.cxx
parent15467f12f796205f97721f43ae7d9ee7cdc47466 (diff)
downloadCMake-dcf9f4d2f71d7c608ce24b690d30d465df51dc71.zip
CMake-dcf9f4d2f71d7c608ce24b690d30d465df51dc71.tar.gz
CMake-dcf9f4d2f71d7c608ce24b690d30d465df51dc71.tar.bz2
Ninja Multi-Config: Add support for cross-config custom commands
Co-Author: Brad King <brad.king@kitware.com>
Diffstat (limited to 'Source/cmLocalNinjaGenerator.cxx')
-rw-r--r--Source/cmLocalNinjaGenerator.cxx194
1 files changed, 152 insertions, 42 deletions
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index a36c479..b035f70 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -10,6 +10,8 @@
#include <sstream>
#include <utility>
+#include <cmext/string_view>
+
#include "cmsys/FStream.hxx"
#include "cmCryptoHash.h"
@@ -567,16 +569,45 @@ void cmLocalNinjaGenerator::AppendCustomCommandLines(
}
void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
- cmCustomCommand const* cc, const cmNinjaDeps& orderOnlyDeps,
- const std::string& config)
+ cmCustomCommand const* cc, const std::set<cmGeneratorTarget*>& targets,
+ const std::string& fileConfig)
{
cmGlobalNinjaGenerator* gg = this->GetGlobalNinjaGenerator();
- if (gg->SeenCustomCommand(cc, config)) {
+ if (gg->SeenCustomCommand(cc, fileConfig)) {
return;
}
- for (cmCustomCommandGenerator const& ccg :
- this->MakeCustomCommandGenerators(*cc, config)) {
+ auto ccgs = this->MakeCustomCommandGenerators(*cc, fileConfig);
+ for (cmCustomCommandGenerator const& ccg : ccgs) {
+ cmNinjaDeps orderOnlyDeps;
+
+ // A custom command may appear on multiple targets. However, some build
+ // systems exist where the target dependencies on some of the targets are
+ // overspecified, leading to a dependency cycle. If we assume all target
+ // dependencies are a superset of the true target dependencies for this
+ // custom command, we can take the set intersection of all target
+ // dependencies to obtain a correct dependency list.
+ //
+ // FIXME: This won't work in certain obscure scenarios involving indirect
+ // dependencies.
+ auto j = targets.begin();
+ assert(j != targets.end());
+ this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
+ *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
+ std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
+ ++j;
+
+ for (; j != targets.end(); ++j) {
+ std::vector<std::string> jDeps;
+ std::vector<std::string> depsIntersection;
+ this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
+ *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
+ std::sort(jDeps.begin(), jDeps.end());
+ std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(),
+ jDeps.begin(), jDeps.end(),
+ std::back_inserter(depsIntersection));
+ orderOnlyDeps = depsIntersection;
+ }
const std::vector<std::string>& outputs = ccg.GetOutputs();
const std::vector<std::string>& byproducts = ccg.GetByproducts();
@@ -603,7 +634,7 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
}
cmNinjaDeps ninjaDeps;
- this->AppendCustomCommandDeps(ccg, ninjaDeps, config);
+ this->AppendCustomCommandDeps(ccg, ninjaDeps, fileConfig);
std::vector<std::string> cmdLines;
this->AppendCustomCommandLines(ccg, cmdLines);
@@ -614,7 +645,7 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
build.Outputs = std::move(ninjaOutputs);
build.ExplicitDeps = std::move(ninjaDeps);
build.OrderOnlyDeps = orderOnlyDeps;
- gg->WriteBuild(this->GetImplFileStream(config), build);
+ gg->WriteBuild(this->GetImplFileStream(fileConfig), build);
} else {
std::string customStep = cmSystemTools::GetFilenameName(ninjaOutputs[0]);
// Hash full path to make unique.
@@ -652,16 +683,92 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
this->BuildCommandLine(cmdLines, customStep),
this->ConstructComment(ccg), "Custom command for " + ninjaOutputs[0],
depfile, cc->GetJobPool(), cc->GetUsesTerminal(),
- /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, config,
+ /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, fileConfig,
ninjaDeps, orderOnlyDeps);
}
}
}
+namespace {
+bool HasUniqueByproducts(cmLocalGenerator& lg,
+ std::vector<std::string> const& byproducts,
+ cmListFileBacktrace const& bt)
+{
+ std::vector<std::string> configs =
+ lg.GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+ cmGeneratorExpression ge(bt);
+ for (std::string const& p : byproducts) {
+ if (cmGeneratorExpression::Find(p) == std::string::npos) {
+ return false;
+ }
+ std::set<std::string> seen;
+ std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p);
+ for (std::string const& config : configs) {
+ for (std::string const& b :
+ lg.ExpandCustomCommandOutputPaths(*cge, config)) {
+ if (!seen.insert(b).second) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool HasUniqueOutputs(std::vector<cmCustomCommandGenerator> const& ccgs)
+{
+ std::set<std::string> allOutputs;
+ std::set<std::string> allByproducts;
+ for (cmCustomCommandGenerator const& ccg : ccgs) {
+ for (std::string const& output : ccg.GetOutputs()) {
+ if (!allOutputs.insert(output).second) {
+ return false;
+ }
+ }
+ for (std::string const& byproduct : ccg.GetByproducts()) {
+ if (!allByproducts.insert(byproduct).second) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+}
+
+std::string cmLocalNinjaGenerator::CreateUtilityOutput(
+ std::string const& targetName, std::vector<std::string> const& byproducts,
+ cmListFileBacktrace const& bt)
+{
+ // In Ninja Multi-Config, we can only produce cross-config utility
+ // commands if all byproducts are per-config.
+ if (!this->GetGlobalGenerator()->IsMultiConfig() ||
+ !HasUniqueByproducts(*this, byproducts, bt)) {
+ return this->cmLocalGenerator::CreateUtilityOutput(targetName, byproducts,
+ bt);
+ }
+
+ std::string const base = cmStrCat(this->GetCurrentBinaryDirectory(),
+ "/CMakeFiles/", targetName, '-');
+ // The output is not actually created so mark it symbolic.
+ for (std::string const& config :
+ this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) {
+ std::string const force = cmStrCat(base, config);
+ if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) {
+ sf->SetProperty("SYMBOLIC", "1");
+ } else {
+ cmSystemTools::Error("Could not get source file entry for " + force);
+ }
+ }
+ this->GetGlobalNinjaGenerator()->AddPerConfigUtilityTarget(targetName);
+ return cmStrCat(base, "$<CONFIG>"_s);
+}
+
std::vector<cmCustomCommandGenerator>
-cmLocalNinjaGenerator::MakeCustomCommandGenerators(cmCustomCommand const& cc,
- std::string const& config)
+cmLocalNinjaGenerator::MakeCustomCommandGenerators(
+ cmCustomCommand const& cc, std::string const& fileConfig)
{
+ cmGlobalNinjaGenerator const* gg = this->GetGlobalNinjaGenerator();
+
bool transformDepfile = false;
switch (this->GetPolicyStatus(cmPolicies::CMP0116)) {
case cmPolicies::OLD:
@@ -674,8 +781,40 @@ cmLocalNinjaGenerator::MakeCustomCommandGenerators(cmCustomCommand const& cc,
break;
}
+ // Start with the build graph's configuration.
std::vector<cmCustomCommandGenerator> ccgs;
- ccgs.emplace_back(cc, config, this, transformDepfile);
+ ccgs.emplace_back(cc, fileConfig, this, transformDepfile);
+
+ // Consider adding cross configurations.
+ if (!gg->EnableCrossConfigBuild()) {
+ return ccgs;
+ }
+
+ // Outputs and byproducts must be expressed using generator expressions.
+ for (std::string const& output : cc.GetOutputs()) {
+ if (cmGeneratorExpression::Find(output) == std::string::npos) {
+ return ccgs;
+ }
+ }
+ for (std::string const& byproduct : cc.GetByproducts()) {
+ if (cmGeneratorExpression::Find(byproduct) == std::string::npos) {
+ return ccgs;
+ }
+ }
+
+ // Tentatively add the other cross configurations.
+ for (std::string const& config : gg->GetCrossConfigs(fileConfig)) {
+ if (fileConfig != config) {
+ ccgs.emplace_back(cc, fileConfig, this, transformDepfile, config);
+ }
+ }
+
+ // If outputs and byproducts are not unique to each configuration,
+ // drop the cross configurations.
+ if (!HasUniqueOutputs(ccgs)) {
+ ccgs.erase(ccgs.begin() + 1, ccgs.end());
+ }
+
return ccgs;
}
@@ -692,42 +831,13 @@ void cmLocalNinjaGenerator::AddCustomCommandTarget(cmCustomCommand const* cc,
}
void cmLocalNinjaGenerator::WriteCustomCommandBuildStatements(
- const std::string& config)
+ const std::string& fileConfig)
{
for (cmCustomCommand const* customCommand : this->CustomCommands) {
auto i = this->CustomCommandTargets.find(customCommand);
assert(i != this->CustomCommandTargets.end());
- // A custom command may appear on multiple targets. However, some build
- // systems exist where the target dependencies on some of the targets are
- // overspecified, leading to a dependency cycle. If we assume all target
- // dependencies are a superset of the true target dependencies for this
- // custom command, we can take the set intersection of all target
- // dependencies to obtain a correct dependency list.
- //
- // FIXME: This won't work in certain obscure scenarios involving indirect
- // dependencies.
- auto j = i->second.begin();
- assert(j != i->second.end());
- std::vector<std::string> ccTargetDeps;
- this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
- *j, ccTargetDeps, config);
- std::sort(ccTargetDeps.begin(), ccTargetDeps.end());
- ++j;
-
- for (; j != i->second.end(); ++j) {
- std::vector<std::string> jDeps;
- std::vector<std::string> depsIntersection;
- this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(*j, jDeps,
- config);
- std::sort(jDeps.begin(), jDeps.end());
- std::set_intersection(ccTargetDeps.begin(), ccTargetDeps.end(),
- jDeps.begin(), jDeps.end(),
- std::back_inserter(depsIntersection));
- ccTargetDeps = depsIntersection;
- }
-
- this->WriteCustomCommandBuildStatement(i->first, ccTargetDeps, config);
+ this->WriteCustomCommandBuildStatement(i->first, i->second, fileConfig);
}
}