mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
294 lines
10 KiB
C++
294 lines
10 KiB
C++
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmGeneratorRule.h"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/memory>
|
|
#include <cm/optional>
|
|
#include <cm/string_view>
|
|
#include <cmext/algorithm>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmCMakePath.h"
|
|
#include "cmCustomCommand.h"
|
|
#include "cmCustomCommandLines.h"
|
|
#include "cmDiagnostics.h"
|
|
#include "cmFileSet.h"
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmList.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmSourceFile.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
|
|
namespace {
|
|
std::vector<cm::string_view> ReservedPatterns{ "RULE"_s,
|
|
"TARGET"_s,
|
|
"FILE_SET"_s,
|
|
"SOURCE_DIR"_s,
|
|
"BINARY_DIR"_s,
|
|
"CURRENT_SOURCE_DIR"_s,
|
|
"CURRENT_BINARY_DIR"_s,
|
|
"SOURCE"_s,
|
|
"INPUT_DIR"_s,
|
|
"FILE_NAME"_s,
|
|
"BASE_NAME"_s,
|
|
"INCLUDE_DIRECTORIES"_s,
|
|
"COMPILE_OPTIONS"_s,
|
|
"COMPILE_DEFINITIONS"_s };
|
|
}
|
|
|
|
std::string cmGeneratorRule::RulePlaceholderExpander::ExpandVariable(
|
|
std::string const& variable)
|
|
{
|
|
if (cm::contains(this->Values, variable)) {
|
|
return this->Values[variable];
|
|
}
|
|
// If there is no variable defined, mark unresolved variable by '{' and '}'
|
|
return cmStrCat('{', variable, '}');
|
|
}
|
|
|
|
cmGeneratorRule::RulePlaceholderExpander::VariableMap const
|
|
cmGeneratorRule::DefaultProperties{
|
|
{ "INCLUDE_DIRECTORIES",
|
|
"$<LIST:FILTER,$<FILE_SET_PROPERTY:<FILE_SET>,TARGET:<TARGET>,INCLUDE_"
|
|
"DIRECTORIES>;$<SOURCE_PROPERTY:<SOURCE>,TARGET_DIRECTORY:<TARGET>,"
|
|
"INCLUDE_DIRECTORIES>;$<RULE_PROPERTY:<RULE>,INCLUDE_DIRECTORIES>,"
|
|
"EXCLUDE,^$>" },
|
|
{ "COMPILE_OPTIONS",
|
|
"$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_OPTIONS>;$<SOURCE_"
|
|
"PROPERTY:<SOURCE>,"
|
|
"TARGET_DIRECTORY:<TARGET>,COMPILE_OPTIONS>;$<FILE_SET_PROPERTY:<FILE_"
|
|
"SET>,TARGET:<TARGET>,COMPILE_OPTIONS>,EXCLUDE,^$>" },
|
|
{ "COMPILE_DEFINITIONS",
|
|
"$<LIST:FILTER,$<RULE_PROPERTY:<RULE>,COMPILE_DEFINITIONS>;$<SOURCE_"
|
|
"PROPERTY:<"
|
|
"SOURCE>,"
|
|
"TARGET_DIRECTORY:<TARGET>,COMPILE_DEFINITIONS>;$<FILE_SET_PROPERTY:<"
|
|
"FILE_SET>,TARGET:<TARGET>,COMPILE_DEFINITIONS>,EXCLUDE,^$>" }
|
|
};
|
|
|
|
cmGeneratorRule::cmGeneratorRule(cmRule const* rule, cmTarget const* target,
|
|
cmFileSet const* fileSet,
|
|
cmFileSet const* outputFileSet,
|
|
cmSourceFile const* source,
|
|
cmRule::PatternSet const& patterns)
|
|
: Rule(rule)
|
|
, Target(target)
|
|
, FileSet(fileSet)
|
|
, OutputFileSet(outputFileSet)
|
|
, Source(source)
|
|
, Makefile(*fileSet->GetMakefile())
|
|
{
|
|
this->Name = cmStrCat(rule->GetName(), '_',
|
|
std::hash<std::string>{}(cmStrCat(
|
|
rule->GetName(), '-', target->GetName(), '-',
|
|
fileSet->GetName(), '-', source->GetFullPath())));
|
|
|
|
auto& values = this->RuleExpander.Values;
|
|
|
|
cmCMakePath file = cmCMakePath{ this->Source->GetFullPath() }.Normal();
|
|
cmCMakePath sourceDir = file.IsAbsolute()
|
|
? file.GetParentPath()
|
|
: this->GetMakefile().GetCurrentSourceDirectory();
|
|
|
|
/* clang-format off */
|
|
values.insert({ "RULE", this->GetName() });
|
|
values.insert({ "TARGET", this->Target->GetName() });
|
|
values.insert({ "FILE_SET", this->FileSet->GetName() });
|
|
values.insert({ "SOURCE_DIR", this->GetMakefile().GetHomeDirectory() });
|
|
values.insert({ "BINARY_DIR", this->GetMakefile().GetHomeOutputDirectory() });
|
|
values.insert({ "CURRENT_SOURCE_DIR", this->GetMakefile().GetCurrentSourceDirectory() });
|
|
values.insert({ "CURRENT_BINARY_DIR", this->GetMakefile().GetCurrentBinaryDirectory() });
|
|
values.insert({ "SOURCE", file.GenericString() });
|
|
values.insert({ "INPUT_DIR", file.GetParentPath().GenericString() });
|
|
values.insert({ "FILE_NAME", file.GetFileName().GenericString() });
|
|
values.insert({ "BASE_NAME", file.GetFileName().RemoveWideExtension().GenericString() });
|
|
/* clang-format on */
|
|
|
|
// instantiate default properties pattern
|
|
for (auto const& item : DefaultProperties) {
|
|
values.insert(
|
|
{ item.first, this->RuleExpander.ExpandVariables(item.second) });
|
|
}
|
|
|
|
this->UpdateRuleExpander(patterns);
|
|
}
|
|
|
|
void cmGeneratorRule::UpdateRuleExpander(cmRule::PatternSet const& patterns)
|
|
{
|
|
if (patterns.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto& values = this->RuleExpander.Values;
|
|
|
|
for (cmRule::Pattern const& pattern : patterns) {
|
|
if (cm::contains(ReservedPatterns, pattern.Name)) {
|
|
// overwriting a reserved pattern is not allowed
|
|
continue;
|
|
}
|
|
|
|
std::string data{ pattern.Value };
|
|
values[pattern.Name] = this->RuleExpander.ExpandVariables(data);
|
|
}
|
|
}
|
|
|
|
cmValue cmGeneratorRule::GetProperty(std::string const& property) const
|
|
{
|
|
cmValue value = this->Properties.GetPropertyValue(property);
|
|
if (value) {
|
|
return value;
|
|
}
|
|
|
|
if (property == "OUTPUT_FILE_SET"_s) {
|
|
this->Properties.SetProperty(
|
|
property,
|
|
cmList{ this->OutputFileSet->GetName(), this->OutputFileSet->GetType() }
|
|
.to_string());
|
|
return this->Properties.GetPropertyValue(property);
|
|
}
|
|
|
|
// property not yet instantiated, retrieve it from cmRule
|
|
value = this->Rule->GetProperty(property);
|
|
if (value) {
|
|
std::string expandedValue{ *value };
|
|
this->Properties.SetProperty(
|
|
property, this->RuleExpander.ExpandVariables(expandedValue));
|
|
return this->Properties.GetPropertyValue(property);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
std::unique_ptr<cmCustomCommand> cmGeneratorRule::CreateCustomCommand() const
|
|
{
|
|
auto expandVariables = [this](std::string const& item) -> std::string {
|
|
return this->RuleExpander.ExpandVariables(item);
|
|
};
|
|
|
|
auto expandVector =
|
|
[&expandVariables](
|
|
std::vector<std::string> const& data) -> std::vector<std::string> {
|
|
std::vector<std::string> result;
|
|
result.reserve(data.size());
|
|
std::transform(data.begin(), data.end(), std::back_inserter(result),
|
|
expandVariables);
|
|
return result;
|
|
};
|
|
|
|
auto expandPaths =
|
|
[&expandVariables](
|
|
std::string const& binaryDirectory,
|
|
std::vector<std::string> const& data) -> std::vector<std::string> {
|
|
std::vector<std::string> result;
|
|
result.reserve(data.size());
|
|
std::transform(
|
|
data.begin(), data.end(), std::back_inserter(result),
|
|
[&expandVariables,
|
|
&binaryDirectory](std::string const& item) -> std::string {
|
|
std::string path = expandVariables(item);
|
|
if (!cmSystemTools::FileIsFullPath(path) &&
|
|
!cmGeneratorExpression::StartsWithGeneratorExpression(path)) {
|
|
path = cmStrCat(binaryDirectory, '/', path);
|
|
}
|
|
cmSystemTools::ConvertToUnixSlashes(path);
|
|
if (cmSystemTools::FileIsFullPath(path)) {
|
|
path = cmSystemTools::CollapseFullPath(path);
|
|
}
|
|
return path;
|
|
});
|
|
return result;
|
|
};
|
|
|
|
auto cc = cm::make_unique<cmCustomCommand>();
|
|
|
|
cmCustomCommandLines commandLines;
|
|
for (cmRule::CustomCommand const& line : this->Rule->GetCommands()) {
|
|
cmCustomCommandLine command;
|
|
command.reserve(line.size());
|
|
std::transform(line.begin(), line.end(), std::back_inserter(command),
|
|
expandVariables);
|
|
commandLines.push_back(command);
|
|
}
|
|
cc->SetCommandLines(std::move(commandLines));
|
|
|
|
cc->SetOutputs(expandPaths(this->GetMakefile().GetCurrentBinaryDirectory(),
|
|
this->Rule->GetOutputs()));
|
|
|
|
if (!this->Rule->GetByproducts().empty()) {
|
|
cc->SetByproducts(expandVector(this->Rule->GetByproducts()));
|
|
}
|
|
|
|
if (!this->Rule->GetDepfile().empty()) {
|
|
cc->SetDepfile(expandVariables(this->Rule->GetDepfile()));
|
|
}
|
|
std::vector<std::string> depends{ 1, this->Source->GetFullPath() };
|
|
if (!this->Rule->GetDepends().empty()) {
|
|
depends.insert(depends.end(), this->Rule->GetDepends().begin(),
|
|
this->Rule->GetDepends().end());
|
|
}
|
|
cc->SetDepends(expandVector(depends));
|
|
if (cmValue deps_explicit =
|
|
this->Rule->GetProperty("DEPENDS_EXPLICIT_ONLY")) {
|
|
cc->SetDependsExplicitOnly(deps_explicit.IsOn());
|
|
} else {
|
|
cc->SetDependsExplicitOnly(this->GetMakefile().IsOn(
|
|
"CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY"));
|
|
}
|
|
|
|
if (cmValue wd = this->Rule->GetProperty("WORKING_DIRECTORY")) {
|
|
cc->SetWorkingDirectory(expandVariables(*wd));
|
|
}
|
|
|
|
if (this->Rule->GetProperty("USES_TERMINAL").IsOn() &&
|
|
!this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) {
|
|
this->GetMakefile().IssueDiagnostic(
|
|
cmDiagnostics::CMD_AUTHOR,
|
|
cmStrCat("RULE \"", this->GetName(), "\": JOB_POOL \"",
|
|
expandVariables(this->Rule->GetProperty("JOB_POOL_COMPILE")),
|
|
"\" is shadowed by USES_TERMINAL."));
|
|
}
|
|
if (!this->Rule->GetProperty("JOB_POOL_COMPILE")->empty()) {
|
|
cc->SetJobPool(
|
|
expandVariables(*this->Rule->GetProperty("JOB_POOL_COMPILE")));
|
|
} else if (this->Rule->GetProperty("USES_TERMINAL").IsOn()) {
|
|
cc->SetUsesTerminal(true);
|
|
}
|
|
|
|
cc->SetJobserverAware(this->Rule->GetProperty("JOB_SERVER_AWARE").IsOn());
|
|
|
|
cc->SetEscapeOldStyle(!this->Rule->GetProperty("VERBATIM").IsOn());
|
|
cc->SetCommandExpandLists(
|
|
this->Rule->GetProperty("COMMAND_EXPAND_LISTS").IsOn());
|
|
|
|
if (cmValue comment = this->Rule->GetProperty("COMMENT")) {
|
|
cc->SetComment(expandVariables(*comment));
|
|
}
|
|
|
|
return cc;
|
|
}
|
|
|
|
std::unique_ptr<cmCustomCommand> cmGeneratorRule::Generate(
|
|
cmFileSet* outFileSet) const
|
|
{
|
|
auto cc = this->CreateCustomCommand();
|
|
|
|
// populate file set with outputs from custom command
|
|
for (auto const& output : cc->GetOutputs()) {
|
|
outFileSet->AddFileEntry(BT<std::string>{ output });
|
|
// get the directory of the generated file
|
|
outFileSet->AddDirectoryEntry(
|
|
BT<std::string>{ cmStrCat("$<PATH:GET_PARENT_PATH,", output, '>') });
|
|
}
|
|
|
|
return cc;
|
|
}
|