mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-24 07:08:38 -05:00
Ninja Multi-Config: Add support for cross-config custom commands
Co-Author: Brad King <brad.king@kitware.com>
This commit is contained in:
@@ -7,7 +7,9 @@
|
||||
#include <utility>
|
||||
|
||||
#include <cm/optional>
|
||||
#include <cm/string_view>
|
||||
#include <cmext/algorithm>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmCryptoHash.h"
|
||||
#include "cmCustomCommand.h"
|
||||
@@ -24,15 +26,95 @@
|
||||
#include "cmTransformDepfile.h"
|
||||
|
||||
namespace {
|
||||
std::string EvaluateSplitConfigGenex(
|
||||
cm::string_view input, cmGeneratorExpression const& ge, cmLocalGenerator* lg,
|
||||
bool useOutputConfig, std::string const& outputConfig,
|
||||
std::string const& commandConfig,
|
||||
std::set<BT<std::pair<std::string, bool>>>* utils = nullptr)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
while (!input.empty()) {
|
||||
// Copy non-genex content directly to the result.
|
||||
std::string::size_type pos = input.find("$<");
|
||||
result += input.substr(0, pos);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
input = input.substr(pos);
|
||||
|
||||
// Find the balanced end of this regex.
|
||||
size_t nestingLevel = 1;
|
||||
for (pos = 2; pos < input.size(); ++pos) {
|
||||
cm::string_view cur = input.substr(pos);
|
||||
if (cmHasLiteralPrefix(cur, "$<")) {
|
||||
++nestingLevel;
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
if (cmHasLiteralPrefix(cur, ">")) {
|
||||
--nestingLevel;
|
||||
if (nestingLevel == 0) {
|
||||
++pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split this genex from following input.
|
||||
cm::string_view genex = input.substr(0, pos);
|
||||
input = input.substr(pos);
|
||||
|
||||
// Convert an outer COMMAND_CONFIG or OUTPUT_CONFIG to the matching config.
|
||||
std::string const* config =
|
||||
useOutputConfig ? &outputConfig : &commandConfig;
|
||||
if (nestingLevel == 0) {
|
||||
static cm::string_view const COMMAND_CONFIG = "$<COMMAND_CONFIG:"_s;
|
||||
static cm::string_view const OUTPUT_CONFIG = "$<OUTPUT_CONFIG:"_s;
|
||||
if (cmHasPrefix(genex, COMMAND_CONFIG)) {
|
||||
genex.remove_prefix(COMMAND_CONFIG.size());
|
||||
genex.remove_suffix(1);
|
||||
useOutputConfig = false;
|
||||
config = &commandConfig;
|
||||
} else if (cmHasPrefix(genex, OUTPUT_CONFIG)) {
|
||||
genex.remove_prefix(OUTPUT_CONFIG.size());
|
||||
genex.remove_suffix(1);
|
||||
useOutputConfig = true;
|
||||
config = &outputConfig;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate this genex in the selected configuration.
|
||||
std::unique_ptr<cmCompiledGeneratorExpression> cge =
|
||||
ge.Parse(std::string(genex));
|
||||
result += cge->Evaluate(lg, *config);
|
||||
|
||||
// Record targets referenced by the genex.
|
||||
if (utils) {
|
||||
// FIXME: What is the proper condition for a cross-dependency?
|
||||
bool const cross = !useOutputConfig;
|
||||
for (cmGeneratorTarget* gt : cge->GetTargets()) {
|
||||
utils->emplace(BT<std::pair<std::string, bool>>(
|
||||
{ gt->GetName(), cross }, cge->GetBacktrace()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> EvaluateDepends(std::vector<std::string> const& paths,
|
||||
cmGeneratorExpression const& ge,
|
||||
cmLocalGenerator* lg,
|
||||
std::string const& config)
|
||||
std::string const& outputConfig,
|
||||
std::string const& commandConfig)
|
||||
{
|
||||
std::vector<std::string> depends;
|
||||
for (std::string const& p : paths) {
|
||||
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p);
|
||||
std::string const& ep = cge->Evaluate(lg, config);
|
||||
std::string const& ep =
|
||||
EvaluateSplitConfigGenex(p, ge, lg, /*useOutputConfig=*/true,
|
||||
/*outputConfig=*/outputConfig,
|
||||
/*commandConfig=*/commandConfig);
|
||||
cm::append(depends, cmExpandedList(ep));
|
||||
}
|
||||
for (std::string& p : depends) {
|
||||
@@ -59,12 +141,12 @@ std::vector<std::string> EvaluateOutputs(std::vector<std::string> const& paths,
|
||||
}
|
||||
}
|
||||
|
||||
cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
|
||||
std::string config,
|
||||
cmLocalGenerator* lg,
|
||||
bool transformDepfile)
|
||||
cmCustomCommandGenerator::cmCustomCommandGenerator(
|
||||
cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg,
|
||||
bool transformDepfile, cm::optional<std::string> crossConfig)
|
||||
: CC(&cc)
|
||||
, Config(std::move(config))
|
||||
, OutputConfig(crossConfig ? *crossConfig : config)
|
||||
, CommandConfig(std::move(config))
|
||||
, LG(lg)
|
||||
, OldStyle(cc.GetEscapeOldStyle())
|
||||
, MakeVars(cc.GetEscapeAllowMakeVars())
|
||||
@@ -75,18 +157,20 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
|
||||
const cmCustomCommandLines& cmdlines = this->CC->GetCommandLines();
|
||||
for (cmCustomCommandLine const& cmdline : cmdlines) {
|
||||
cmCustomCommandLine argv;
|
||||
// For the command itself, we default to the COMMAND_CONFIG.
|
||||
bool useOutputConfig = false;
|
||||
for (std::string const& clarg : cmdline) {
|
||||
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(clarg);
|
||||
std::string parsed_arg = cge->Evaluate(this->LG, this->Config);
|
||||
for (cmGeneratorTarget* gt : cge->GetTargets()) {
|
||||
this->Utilities.emplace(BT<std::pair<std::string, bool>>(
|
||||
{ gt->GetName(), true }, cge->GetBacktrace()));
|
||||
}
|
||||
std::string parsed_arg = EvaluateSplitConfigGenex(
|
||||
clarg, ge, this->LG, useOutputConfig, this->OutputConfig,
|
||||
this->CommandConfig, &this->Utilities);
|
||||
if (this->CC->GetCommandExpandLists()) {
|
||||
cm::append(argv, cmExpandedList(parsed_arg));
|
||||
} else {
|
||||
argv.push_back(std::move(parsed_arg));
|
||||
}
|
||||
|
||||
// For remaining arguments, we default to the OUTPUT_CONFIG.
|
||||
useOutputConfig = true;
|
||||
}
|
||||
|
||||
if (!argv.empty()) {
|
||||
@@ -94,8 +178,10 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
|
||||
// collect the target to add a target-level dependency on it.
|
||||
cmGeneratorTarget* gt = this->LG->FindGeneratorTargetToUse(argv.front());
|
||||
if (gt && gt->GetType() == cmStateEnums::EXECUTABLE) {
|
||||
// FIXME: What is the proper condition for a cross-dependency?
|
||||
bool const cross = true;
|
||||
this->Utilities.emplace(BT<std::pair<std::string, bool>>(
|
||||
{ gt->GetName(), true }, cc.GetBacktrace()));
|
||||
{ gt->GetName(), cross }, cc.GetBacktrace()));
|
||||
}
|
||||
} else {
|
||||
// Later code assumes at least one entry exists, but expanding
|
||||
@@ -137,16 +223,18 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
|
||||
this->CommandLines.push_back(std::move(argv));
|
||||
}
|
||||
|
||||
this->Outputs = EvaluateOutputs(cc.GetOutputs(), ge, this->LG, this->Config);
|
||||
this->Outputs =
|
||||
EvaluateOutputs(cc.GetOutputs(), ge, this->LG, this->OutputConfig);
|
||||
this->Byproducts =
|
||||
EvaluateOutputs(cc.GetByproducts(), ge, this->LG, this->Config);
|
||||
this->Depends = EvaluateDepends(cc.GetDepends(), ge, this->LG, this->Config);
|
||||
EvaluateOutputs(cc.GetByproducts(), ge, this->LG, this->OutputConfig);
|
||||
this->Depends = EvaluateDepends(cc.GetDepends(), ge, this->LG,
|
||||
this->OutputConfig, this->CommandConfig);
|
||||
|
||||
const std::string& workingdirectory = this->CC->GetWorkingDirectory();
|
||||
if (!workingdirectory.empty()) {
|
||||
std::unique_ptr<cmCompiledGeneratorExpression> cge =
|
||||
ge.Parse(workingdirectory);
|
||||
this->WorkingDirectory = cge->Evaluate(this->LG, this->Config);
|
||||
this->WorkingDirectory =
|
||||
EvaluateSplitConfigGenex(workingdirectory, ge, this->LG, true,
|
||||
this->OutputConfig, this->CommandConfig);
|
||||
// Convert working directory to a full path.
|
||||
if (!this->WorkingDirectory.empty()) {
|
||||
std::string const& build_dir = this->LG->GetCurrentBinaryDirectory();
|
||||
@@ -203,7 +291,7 @@ const char* cmCustomCommandGenerator::GetArgv0Location(unsigned int c) const
|
||||
(target->IsImported() ||
|
||||
target->GetProperty("CROSSCOMPILING_EMULATOR") ||
|
||||
!this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING"))) {
|
||||
return target->GetLocation(this->Config).c_str();
|
||||
return target->GetLocation(this->CommandConfig).c_str();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user