blob: 35c8367fe717707d4db903fd9a04f0abc53c30e0 [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmNinjaUtilityTargetGenerator.h"
#include <algorithm>
#include <array>
#include <iterator>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalNinjaGenerator.h"
#include "cmLocalNinjaGenerator.h"
#include "cmNinjaTypes.h"
#include "cmOutputConverter.h"
#include "cmSourceFile.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator(
cmGeneratorTarget* target)
: cmNinjaTargetGenerator(target)
{
}
cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() = default;
void cmNinjaUtilityTargetGenerator::Generate(const std::string& config)
{
if (!this->GetGeneratorTarget()->Target->IsPerConfig()) {
this->WriteUtilBuildStatements(config, config);
return;
}
for (auto const& fileConfig : this->GetConfigNames()) {
if (!this->GetGlobalGenerator()
->GetCrossConfigs(fileConfig)
.count(config)) {
continue;
}
if (fileConfig != config &&
this->GetGeneratorTarget()->GetType() == cmStateEnums::GLOBAL_TARGET) {
continue;
}
this->WriteUtilBuildStatements(config, fileConfig);
}
}
void cmNinjaUtilityTargetGenerator::WriteUtilBuildStatements(
std::string const& config, std::string const& fileConfig)
{
cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
cmLocalNinjaGenerator* lg = this->GetLocalGenerator();
cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
std::string configDir;
if (genTarget->Target->IsPerConfig()) {
configDir = gg->ConfigDirectory(fileConfig);
}
std::string utilCommandName =
cmStrCat(lg->GetCurrentBinaryDirectory(), "/CMakeFiles", configDir, "/",
this->GetTargetName(), ".util");
utilCommandName = this->ConvertToNinjaPath(utilCommandName);
cmNinjaBuild phonyBuild("phony");
std::vector<std::string> commands;
cmNinjaDeps deps;
cmGlobalNinjaGenerator::CCOutputs util_outputs(gg);
util_outputs.ExplicitOuts.emplace_back(utilCommandName);
bool uses_terminal = false;
{
std::array<std::vector<cmCustomCommand> const*, 2> const cmdLists = {
{ &genTarget->GetPreBuildCommands(), &genTarget->GetPostBuildCommands() }
};
for (std::vector<cmCustomCommand> const* cmdList : cmdLists) {
for (cmCustomCommand const& ci : *cmdList) {
cmCustomCommandGenerator ccg(ci, fileConfig, lg);
lg->AppendCustomCommandDeps(ccg, deps, fileConfig);
lg->AppendCustomCommandLines(ccg, commands);
util_outputs.Add(ccg.GetByproducts());
if (ci.GetUsesTerminal()) {
uses_terminal = true;
}
}
}
}
{
std::vector<cmSourceFile*> sources;
genTarget->GetSourceFiles(sources, config);
for (cmSourceFile const* source : sources) {
if (cmCustomCommand const* cc = source->GetCustomCommand()) {
cmCustomCommandGenerator ccg(*cc, config, lg);
lg->AddCustomCommandTarget(cc, genTarget);
// Depend on all custom command outputs.
const std::vector<std::string>& ccOutputs = ccg.GetOutputs();
const std::vector<std::string>& ccByproducts = ccg.GetByproducts();
std::transform(ccOutputs.begin(), ccOutputs.end(),
std::back_inserter(deps), this->MapToNinjaPath());
std::transform(ccByproducts.begin(), ccByproducts.end(),
std::back_inserter(deps), this->MapToNinjaPath());
}
}
}
std::string outputConfig;
if (genTarget->Target->IsPerConfig()) {
outputConfig = config;
}
lg->AppendTargetOutputs(genTarget, phonyBuild.Outputs, outputConfig);
if (genTarget->Target->GetType() != cmStateEnums::GLOBAL_TARGET) {
lg->AppendTargetOutputs(genTarget, gg->GetByproductsForCleanTarget(),
config);
std::copy(util_outputs.ExplicitOuts.begin(),
util_outputs.ExplicitOuts.end(),
std::back_inserter(gg->GetByproductsForCleanTarget()));
}
lg->AppendTargetDepends(genTarget, deps, config, fileConfig,
DependOnTargetArtifact);
if (commands.empty()) {
phonyBuild.Comment = "Utility command for " + this->GetTargetName();
phonyBuild.ExplicitDeps = std::move(deps);
if (genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) {
gg->WriteBuild(this->GetImplFileStream(fileConfig), phonyBuild);
} else {
gg->WriteBuild(this->GetCommonFileStream(), phonyBuild);
}
} else {
std::string command = lg->BuildCommandLine(
commands, config, fileConfig, "utility", this->GeneratorTarget);
std::string desc;
cmValue echoStr = genTarget->GetProperty("EchoString");
if (echoStr) {
desc = *echoStr;
} else {
desc = "Running utility command for " + this->GetTargetName();
}
// TODO: fix problematic global targets. For now, search and replace the
// makefile vars.
cmSystemTools::ReplaceString(
command, "$(CMAKE_SOURCE_DIR)",
lg->ConvertToOutputFormat(lg->GetSourceDirectory(),
cmOutputConverter::SHELL));
cmSystemTools::ReplaceString(
command, "$(CMAKE_BINARY_DIR)",
lg->ConvertToOutputFormat(lg->GetBinaryDirectory(),
cmOutputConverter::SHELL));
cmSystemTools::ReplaceString(command, "$(ARGS)", "");
command = gg->ExpandCFGIntDir(command, config);
std::string ccConfig;
if (genTarget->Target->IsPerConfig() &&
genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) {
ccConfig = config;
}
if (config == fileConfig ||
gg->GetPerConfigUtilityTargets().count(genTarget->GetName())) {
gg->WriteCustomCommandBuild(
command, desc, "Utility command for " + this->GetTargetName(),
/*depfile*/ "", /*job_pool*/ "", uses_terminal,
/*restat*/ true, ccConfig, std::move(util_outputs), std::move(deps));
}
phonyBuild.ExplicitDeps.push_back(utilCommandName);
if (genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) {
gg->WriteBuild(this->GetImplFileStream(fileConfig), phonyBuild);
} else {
gg->WriteBuild(this->GetCommonFileStream(), phonyBuild);
}
}
// Find ADDITIONAL_CLEAN_FILES
this->AdditionalCleanFiles(config);
// Add an alias for the logical target name regardless of what directory
// contains it. Skip this for GLOBAL_TARGET because they are meant to
// be per-directory and have one at the top-level anyway.
if (genTarget->GetType() != cmStateEnums::GLOBAL_TARGET) {
gg->AddTargetAlias(this->GetTargetName(), genTarget, config);
}
}