| /* 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); |
| } |
| } |