| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmNinjaUtilityTargetGenerator.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <iterator> | |
| #include <memory> | |
| #include <set> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/optional> | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandGenerator.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalNinjaGenerator.h" | |
| #include "cmLocalNinjaGenerator.h" | |
| #include "cmNinjaTypes.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmSourceFile.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator( | |
| cmGeneratorTarget* target) | |
| : cmNinjaTargetGenerator(target) | |
| { | |
| } | |
| cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() = default; | |
| void cmNinjaUtilityTargetGenerator::Generate(std::string const& 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() == | |
| cm::TargetType::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); | |
| std::string commandDesc; | |
| cmGeneratorExpression ge(*this->GetLocalGenerator()->GetCMakeInstance()); | |
| 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); | |
| if (ci.GetComment()) { | |
| if (!commandDesc.empty()) { | |
| commandDesc += "; "; | |
| } | |
| auto cge = ge.Parse(ci.GetComment().value()); | |
| commandDesc += cge->Evaluate(this->GetLocalGenerator(), config); | |
| } | |
| 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. | |
| std::vector<std::string> const& ccOutputs = ccg.GetOutputs(); | |
| std::vector<std::string> const& 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() != cm::TargetType::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() != cm::TargetType::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 if (!commandDesc.empty()) { | |
| desc = commandDesc; | |
| } 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() != cm::TargetType::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() != cm::TargetType::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() != cm::TargetType::GLOBAL_TARGET) { | |
| gg->AddTargetAlias(this->GetTargetName(), genTarget, config); | |
| } | |
| } |