blob: f52ac99700078de31b542d7fed6e284efca3bfe9 [file] [log] [blame] [edit]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmFastbuildTargetGenerator.h"
#include <algorithm>
#include <cstddef>
#include <unordered_map>
#include <unordered_set>
#include <cm/memory>
#include <cm/optional>
#include "cmCryptoHash.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandGenerator.h"
#include "cmCustomCommandLines.h"
#include "cmFastbuildNormalTargetGenerator.h"
#include "cmFastbuildUtilityTargetGenerator.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalCommonGenerator.h"
#include "cmGlobalFastbuildGenerator.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalCommonGenerator.h"
#include "cmLocalFastbuildGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmOSXBundleGenerator.h"
#include "cmOutputConverter.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
#define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG"
constexpr auto FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT =
"CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT";
constexpr auto FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC =
"CMAKE_FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC";
cmFastbuildTargetGenerator* cmFastbuildTargetGenerator::New(
cmGeneratorTarget* target, std::string config)
{
switch (target->GetType()) {
case cmStateEnums::EXECUTABLE:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::MODULE_LIBRARY:
case cmStateEnums::OBJECT_LIBRARY:
return new cmFastbuildNormalTargetGenerator(target, std::move(config));
case cmStateEnums::UTILITY:
case cmStateEnums::GLOBAL_TARGET:
case cmStateEnums::INTERFACE_LIBRARY:
return new cmFastbuildUtilityTargetGenerator(target, std::move(config));
default:
return nullptr;
}
}
cmFastbuildTargetGenerator::cmFastbuildTargetGenerator(
cmGeneratorTarget* target, std::string configParam)
: cmCommonTargetGenerator(target)
, LocalGenerator(
static_cast<cmLocalFastbuildGenerator*>(target->GetLocalGenerator()))
, TargetDirectDependencies(
this->GlobalCommonGenerator->GetTargetDirectDepends(GeneratorTarget))
, Config(std::move(configParam))
{
this->MacOSXContentGenerator =
cm::make_unique<MacOSXContentGeneratorType>(this, Config);
}
void cmFastbuildTargetGenerator::LogMessage(std::string const& m) const
{
this->GetGlobalGenerator()->LogMessage(m);
}
std::string cmFastbuildTargetGenerator::GetUtilityAliasFromBuildStep(
FastbuildBuildStep step) const
{
if (step == FastbuildBuildStep::PRE_BUILD) {
return GetTargetName() + FASTBUILD_PRE_BUILD_ALIAS_POSTFIX;
}
if (step == FastbuildBuildStep::PRE_LINK) {
return GetTargetName() + FASTBUILD_PRE_LINK_ALIAS_POSTFIX;
}
if (step == FastbuildBuildStep::POST_BUILD) {
return GetTargetName() + FASTBUILD_POST_BUILD_ALIAS_POSTFIX;
}
return GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
}
void cmFastbuildTargetGenerator::MacOSXContentGeneratorType::operator()(
cmSourceFile const& source, char const* pkgloc,
std::string const& configName)
{
// Skip OS X content when not building a Framework or Bundle.
if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
return;
}
// Get the input file location.
std::string input = source.GetFullPath();
input = this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(input);
// Get the output file location.
std::string output =
this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(
pkgloc, configName);
output += "/";
output += cmSystemTools::GetFilenameName(input);
output =
this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(output);
FastbuildCopyNode node;
node.Name = "Copy_" + output;
node.Source = std::move(input);
if (cmSystemTools::FileIsDirectory(node.Source)) {
node.CopyDir = true;
}
node.Dest = std::move(output);
// Just in case if "from" is generated by some custom command.
// Tested in "BundleTest" test.
node.PreBuildDependencies =
this->Generator->GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
this->Generator->CopyNodes.emplace_back(std::move(node));
}
std::string cmFastbuildTargetGenerator::GetCustomCommandTargetName(
cmCustomCommand const& cc, FastbuildBuildStep step) const
{
std::string const extra = this->Makefile->GetCurrentBinaryDirectory();
std::string targetName = "cc";
std::string extras = extra;
// Compute hash based on commands & args & output.
for (cmCustomCommandLine const& commandLine : cc.GetCommandLines()) {
extras += cmJoin(commandLine, "");
}
for (std::string const& output : cc.GetOutputs()) {
extras += output;
}
extras += std::to_string(static_cast<int>(step));
cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
targetName += "-" + hash.HashString(extras).substr(0, 14);
return targetName;
}
void cmFastbuildTargetGenerator::WriteScriptProlog(cmsys::ofstream& file) const
{
#ifdef _WIN32
file << "@echo off\n";
#else
file << "set -e\n\n";
#endif
}
void cmFastbuildTargetGenerator::WriteScriptEpilog(cmsys::ofstream& file) const
{
(void)file;
#ifdef _WIN32
file << "goto :EOF\n\n"
":ABORT\n"
"set ERROR_CODE=%ERRORLEVEL%\n"
"echo Batch file failed at line %FAIL_LINE% "
"with errorcode %ERRORLEVEL%\n"
"exit /b %ERROR_CODE%";
#endif
}
std::string cmFastbuildTargetGenerator::GetScriptWorkingDir(
cmCustomCommandGenerator const& ccg) const
{
std::string workingDirectory = ccg.GetWorkingDirectory();
if (workingDirectory.empty()) {
return this->LocalCommonGenerator->GetCurrentBinaryDirectory();
}
return workingDirectory;
}
std::string cmFastbuildTargetGenerator::GetScriptFilename(
std::string const& utilityTargetName) const
{
std::string scriptFileName = Makefile->GetCurrentBinaryDirectory();
scriptFileName += "/CMakeFiles/";
scriptFileName += utilityTargetName;
scriptFileName += FASTBUILD_SCRIPT_FILE_EXTENSION;
return scriptFileName;
}
void cmFastbuildTargetGenerator::AddCommentPrinting(
std::vector<std::string>& cmdLines,
cmCustomCommandGenerator const& ccg) const
{
std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat(
cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
auto const comment = ccg.GetComment();
if (comment) {
// Comment printing should be first. Tested in
// RunCMake.ExternalProject:EnvVars-build test.
cmdLines.insert(
cmdLines.begin(),
cmakeCommand.append(" -E echo ")
.append(LocalGenerator->EscapeForShell(cmGeneratorExpression::Evaluate(
*comment, this->LocalGenerator, Config))));
}
}
std::string cmFastbuildTargetGenerator::GetCdCommand(
cmCustomCommandGenerator const& ccg) const
{
return cmStrCat(FASTBUILD_SCRIPT_CD,
this->LocalGenerator->ConvertToOutputFormat(
GetScriptWorkingDir(ccg), cmOutputConverter::SHELL));
}
void cmFastbuildTargetGenerator::WriteCmdsToFile(
cmsys::ofstream& file, std::vector<std::string> const& cmds) const
{
#ifdef _WIN32
int line = 1;
for (auto cmd : cmds) {
// On Windows batch, '%' is a special character that needs to be
// doubled to be escaped
cmSystemTools::ReplaceString(cmd, "%", "%%");
file << cmd << " || (set FAIL_LINE=" << ++line << "& goto :ABORT)" << '\n';
#else
for (auto const& cmd : cmds) {
file << cmd << '\n';
#endif
}
}
void cmFastbuildTargetGenerator::AddOutput(cmCustomCommandGenerator const& ccg,
FastbuildExecNode& exec)
{
std::string dummyOutput = cmSystemTools::JoinPath(
{ LocalCommonGenerator->GetMakefile()->GetHomeOutputDirectory(),
"/_fbuild_dummy" });
this->GetGlobalGenerator()->AllFoldersToClean.insert(dummyOutput);
dummyOutput.append("/").append(exec.Name).append(
FASTBUILD_DUMMY_OUTPUT_EXTENSION);
std::vector<std::string> const& outputs = ccg.GetOutputs();
std::vector<std::string> const& byproducts = ccg.GetByproducts();
exec.OutputsAlias.Name = exec.Name + FASTBUILD_OUTPUTS_ALIAS_POSTFIX;
// If CC doesn't have any output - we should always run it.
// Tested in "RunCMake.CMakePresetsBuild" test.
bool hasAnyNonSymbolicOutput = false;
bool const trackByproducts =
this->Makefile->IsDefinitionSet(FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT);
auto const isSymbolic = [this](std::string const& file) {
cmSourceFile* sf = this->Makefile->GetSource(file);
if (sf && sf->GetPropertyAsBool("SYMBOLIC")) {
LogMessage("Skipping symbolic file: " + file);
return true;
}
return false;
};
for (std::string const& output : outputs) {
// Tested in "RunCMake.BuildDepends".
if (isSymbolic(output)) {
continue;
}
hasAnyNonSymbolicOutput = true;
std::string const outputPath = this->ConvertToFastbuildPath(output);
LogMessage("CC's output: " + outputPath);
exec.OutputsAlias.PreBuildDependencies.emplace(outputPath);
// Ensure output path exists. For some reason, "CMake -E touch" fails with
// "cmake -E touch: failed to update "...
cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outputPath));
this->GetGlobalGenerator()->AddFileToClean(outputPath);
}
exec.ByproductsAlias.Name = exec.Name + FASTBUILD_BYPRODUCTS_ALIAS_POSTFIX;
for (std::string const& byproduct : byproducts) {
if (trackByproducts) {
hasAnyNonSymbolicOutput = true;
}
std::string const byproductPath = this->ConvertToFastbuildPath(byproduct);
exec.ByproductsAlias.PreBuildDependencies.emplace(byproductPath);
this->GetGlobalGenerator()->AddFileToClean(byproductPath);
}
auto const addDummyOutput = [&] {
// So that the dummy file is always created.
exec.ExecUseStdOutAsOutput = true;
exec.ExecOutput = this->ConvertToFastbuildPath(dummyOutput);
for (auto const& output : exec.OutputsAlias.PreBuildDependencies) {
OutputsToReplace[output.Name] = exec.ExecOutput;
LogMessage("Adding replace from " + output.Name + " to " +
exec.ExecOutput);
}
};
// We don't have any output that is expected to appear on disk -> run always.
// Tested in "RunCMake.ExternalProject":BUILD_ALWAYS
if (!hasAnyNonSymbolicOutput) {
exec.ExecAlways = true;
addDummyOutput();
return;
}
if (!exec.OutputsAlias.PreBuildDependencies.empty()) {
exec.ExecOutput = this->ConvertToFastbuildPath(
exec.OutputsAlias.PreBuildDependencies.begin()->Name);
} else {
exec.ExecOutput = this->ConvertToFastbuildPath(
exec.ByproductsAlias.PreBuildDependencies.begin()->Name);
}
// Optionally add the "deps-check" Exec if we have more than 1 OUTPUT, but
// allow user to opt out.
if (exec.OutputsAlias.PreBuildDependencies.size() > 1 &&
!this->Makefile->IsDefinitionSet(
FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC)) {
exec.NeedsDepsCheckExec = true;
}
}
void cmFastbuildTargetGenerator::AddExecArguments(
FastbuildExecNode& exec, std::string const& scriptFilename) const
{
exec.ExecArguments = FASTBUILD_SCRIPT_FILE_ARG;
exec.ExecArguments +=
cmGlobalFastbuildGenerator::QuoteIfHasSpaces(scriptFilename);
exec.ScriptFile = scriptFilename;
exec.ExecExecutable =
cmGlobalFastbuildGenerator::GetExternalShellExecutable();
}
void cmFastbuildTargetGenerator::GetDepends(
cmCustomCommandGenerator const& ccg, std::string const& currentCCName,
std::vector<std::string>& fileLevelDeps,
std::set<FastbuildTargetDep>& targetDep) const
{
for (auto dep : ccg.GetDepends()) {
LogMessage("Dep: " + dep);
auto orig = dep;
if (this->LocalCommonGenerator->GetRealDependency(dep, Config, dep)) {
LogMessage("Real dep: " + dep);
if (!dep.empty()) {
LogMessage("Custom command real dep: " + dep);
for (auto const& item : cmList{ cmGeneratorExpression::Evaluate(
this->ConvertToFastbuildPath(dep), this->LocalGenerator,
Config) }) {
fileLevelDeps.emplace_back(item);
}
}
}
dep = this->ConvertToFastbuildPath(dep);
LogMessage("Real dep converted: " + dep);
auto const targetInfo = this->LocalGenerator->GetSourcesWithOutput(dep);
if (targetInfo.Target) {
LogMessage("dep: " + dep + ", target: " + targetInfo.Target->GetName());
auto const& target = targetInfo.Target;
auto const processCCs = [this, &currentCCName, &targetDep,
dep](std::vector<cmCustomCommand> const& ccs,
FastbuildBuildStep step) {
for (auto const& cc : ccs) {
for (auto const& output : cc.GetOutputs()) {
LogMessage("dep: " + dep + ", post output: " +
this->ConvertToFastbuildPath(output));
if (this->ConvertToFastbuildPath(output) == dep) {
auto ccName = this->GetCustomCommandTargetName(cc, step);
if (ccName != currentCCName) {
LogMessage("Additional CC dep from target: " + ccName);
targetDep.emplace(std::move(ccName));
}
}
}
for (auto const& byproduct : cc.GetByproducts()) {
LogMessage("dep: " + dep + ", post byproduct: " +
this->ConvertToFastbuildPath(byproduct));
if (this->ConvertToFastbuildPath(byproduct) == dep) {
auto ccName = this->GetCustomCommandTargetName(cc, step);
if (ccName != currentCCName) {
LogMessage("Additional CC dep from target: " + ccName);
targetDep.emplace(std::move(ccName));
}
}
}
}
};
processCCs(target->GetPreBuildCommands(), FastbuildBuildStep::PRE_BUILD);
processCCs(target->GetPreLinkCommands(), FastbuildBuildStep::PRE_LINK);
processCCs(target->GetPostBuildCommands(),
FastbuildBuildStep::POST_BUILD);
continue;
}
if (!targetInfo.Source) {
LogMessage("dep: " + dep + ", no source, byproduct: " +
std::to_string(targetInfo.SourceIsByproduct));
// Tested in "OutDir" test.
if (!cmSystemTools::FileIsFullPath(orig)) {
targetDep.emplace(std::move(orig));
}
continue;
}
if (!targetInfo.Source->GetCustomCommand()) {
LogMessage("dep: " + dep + ", no GetCustomCommand");
continue;
}
if (targetInfo.Source && targetInfo.Source->GetCustomCommand()) {
auto ccName = this->GetCustomCommandTargetName(
*targetInfo.Source->GetCustomCommand(), FastbuildBuildStep::REST);
if (ccName != currentCCName) {
LogMessage("Additional CC dep: " + ccName);
targetDep.emplace(std::move(ccName));
}
}
}
}
void cmFastbuildTargetGenerator::ReplaceProblematicMakeVars(
std::string& command) const
{
// TODO: fix problematic global targets. For now, search and replace the
// makefile vars.
cmSystemTools::ReplaceString(
command, "$(CMAKE_SOURCE_DIR)",
this->LocalGenerator->ConvertToOutputFormat(
this->LocalGenerator->GetSourceDirectory(), cmOutputConverter::SHELL));
cmSystemTools::ReplaceString(
command, "$(CMAKE_BINARY_DIR)",
this->LocalGenerator->ConvertToOutputFormat(
this->LocalGenerator->GetBinaryDirectory(), cmOutputConverter::SHELL));
cmSystemTools::ReplaceString(command, "$(ARGS)", "");
}
FastbuildExecNode cmFastbuildTargetGenerator::GetAppleTextStubCommand() const
{
FastbuildExecNode res;
if (!this->GeneratorTarget->IsApple() ||
!this->GeneratorTarget->HasImportLibrary(Config)) {
return res;
}
auto const names = DetectOutput();
std::string const outpathImp =
this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
Config, cmStateEnums::ImportLibraryArtifact));
std::string const binPath =
this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
Config, cmStateEnums::RuntimeBinaryArtifact));
cmSystemTools::MakeDirectory(outpathImp);
std::string rule = this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_CREATE_TEXT_STUBS");
LogMessage("CMAKE_CREATE_TEXT_STUBS:" + rule);
auto rulePlaceholderExpander =
this->GetLocalGenerator()->CreateRulePlaceholderExpander();
cmRulePlaceholderExpander::RuleVariables vars;
res.ExecOutput = cmStrCat(outpathImp, '/', names.ImportReal);
res.ExecInput = { cmStrCat(binPath, '/', names.SharedObject) };
vars.Target = res.ExecInput[0].c_str();
rulePlaceholderExpander->SetTargetImpLib(res.ExecOutput);
rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), rule,
vars);
LogMessage("CMAKE_CREATE_TEXT_STUBS expanded:" + rule);
std::string executable;
std::string args;
if (!cmSystemTools::SplitProgramFromArgs(rule, executable, args)) {
cmSystemTools::Error("Failed to split program from args: " + rule);
return res;
}
res.Name = "create_" + names.ImportOutput + "_text_stub";
res.ExecExecutable = std::move(executable);
res.ExecArguments = std::move(args);
res.ExecWorkingDir = this->LocalCommonGenerator->GetCurrentBinaryDirectory();
// Wait for the build.
res.PreBuildDependencies.emplace(this->GetTargetName());
return res;
}
FastbuildExecNode cmFastbuildTargetGenerator::GetDepsCheckExec(
FastbuildExecNode const& depender)
{
FastbuildExecNode exec;
exec.Name = depender.Name + "-check-depends";
exec.ExecAlways = true;
exec.ExecUseStdOutAsOutput = true;
exec.ExecOutput = depender.ExecOutput + ".deps-checker";
exec.ExecExecutable = cmSystemTools::GetCMakeCommand();
exec.ExecArguments += "-E cmake_fastbuild_check_depends ";
exec.ExecArguments += depender.ExecOutput + " ";
char const* sep = "";
for (auto const& dep : depender.OutputsAlias.PreBuildDependencies) {
exec.ExecArguments += sep;
exec.ExecArguments += dep.Name;
sep = " ";
}
for (auto const& dep : depender.ByproductsAlias.PreBuildDependencies) {
exec.ExecArguments += sep;
exec.ExecArguments += dep.Name;
sep = " ";
}
return exec;
}
FastbuildExecNodes cmFastbuildTargetGenerator::GenerateCommands(
FastbuildBuildStep buildStep)
{
FastbuildExecNodes execs;
execs.Alias.Name = GetUtilityAliasFromBuildStep(buildStep);
std::vector<cmCustomCommand> commands;
if (buildStep == FastbuildBuildStep::PRE_BUILD) {
commands = GeneratorTarget->GetPreBuildCommands();
LogMessage("STEP: PRE_BUILD");
} else if (buildStep == FastbuildBuildStep::PRE_LINK) {
commands = GeneratorTarget->GetPreLinkCommands();
LogMessage("STEP: PRE_LINK");
} else if (buildStep == FastbuildBuildStep::POST_BUILD) {
commands = GeneratorTarget->GetPostBuildCommands();
LogMessage("STEP: POST_BUILD");
} else {
LogMessage("STEP: ALL CUSTOM COMMANDS");
std::vector<cmSourceFile const*> customCommands;
GeneratorTarget->GetCustomCommands(customCommands, Config);
for (cmSourceFile const* source : customCommands) {
cmCustomCommand const* cmd = source->GetCustomCommand();
if (!cmd->GetCommandLines().empty()) {
commands.emplace_back(*cmd);
}
}
}
LogMessage(cmStrCat("Number of custom commands: ", commands.size()));
for (cmCustomCommand const& customCommand : commands) {
cmCustomCommandGenerator ccg(customCommand, Config, LocalCommonGenerator);
std::string launcher = this->MakeCustomLauncher(ccg);
std::string const execName =
GetCustomCommandTargetName(customCommand, buildStep);
std::vector<std::string> cmdLines;
if (ccg.GetNumberOfCommands() > 0) {
cmdLines.push_back(GetCdCommand(ccg));
}
// Since we are not using FASTBuild Exec nodes natively, we need to
// have shell specific escape.
this->LocalGenerator->GetState()->SetFastbuildMake(false);
// To avoid replacing $ with $$ in the command line.
this->LocalGenerator->SetLinkScriptShell(true);
for (unsigned j = 0; j != ccg.GetNumberOfCommands(); ++j) {
std::string const command = ccg.GetCommand(j);
// Tested in "CustomCommand" ("empty_command") test.
if (!command.empty()) {
cmdLines.emplace_back(launcher +
this->LocalGenerator->ConvertToOutputFormat(
command, cmOutputConverter::SHELL));
std::string& cmd = cmdLines.back();
ccg.AppendArguments(j, cmd);
ReplaceProblematicMakeVars(cmd);
LogMessage("cmCustomCommandLine: " + cmd);
}
}
if (cmdLines.empty()) {
return {};
}
this->LocalGenerator->GetState()->SetFastbuildMake(true);
FastbuildExecNode execNode;
execNode.Name = execName;
// Add depncencies to "ExecInput" so that FASTBuild will re-run the Exec
// when needed, but also add to "PreBuildDependencies" for correct sorting.
// Tested in "ObjectLibrary / complexOneConfig" tests.
GetDepends(ccg, execName, execNode.ExecInput,
execNode.PreBuildDependencies);
for (auto const& util : ccg.GetUtilities()) {
auto const& utilTargetName = util.Value.first;
LogMessage("Util: " + utilTargetName +
", cross: " + std::to_string(util.Value.second));
auto* const target = this->Makefile->FindTargetToUse(utilTargetName);
if (target && target->IsImported()) {
std::string importedLoc =
this->ConvertToFastbuildPath(target->ImportedGetFullPath(
Config, cmStateEnums::ArtifactType::RuntimeBinaryArtifact));
if (importedLoc.empty()) {
importedLoc =
this->ConvertToFastbuildPath(target->ImportedGetFullPath(
Config, cmStateEnums::ArtifactType::ImportLibraryArtifact));
}
LogMessage("adding file level dep on imported target: " + importedLoc);
execNode.ExecInput.emplace_back(std::move(importedLoc));
continue;
}
// This CC uses some executable produced by another target. Add explicit
// dep. Tested in "CustomCommand" test.
if (util.Value.second) {
if (utilTargetName != customCommand.GetTarget()) {
LogMessage("Adding util dep: " + utilTargetName);
execNode.PreBuildDependencies.emplace(utilTargetName);
}
}
}
execs.Alias.PreBuildDependencies.emplace(execNode.Name);
LogMessage(cmStrCat("cmdLines size ", cmdLines.size()));
if (!cmdLines.empty()) {
std::string const scriptFileName = GetScriptFilename(execName);
cmsys::ofstream scriptFile(scriptFileName.c_str());
AddOutput(ccg, execNode);
AddExecArguments(execNode, scriptFileName);
AddCommentPrinting(cmdLines, ccg);
WriteScriptProlog(scriptFile);
WriteCmdsToFile(scriptFile, cmdLines);
WriteScriptEpilog(scriptFile);
}
if (buildStep == FastbuildBuildStep::POST_BUILD) {
// Execute POST_BUILD in order in which they are declared.
// Tested in "complex" test.
for (auto& exec : execs.Nodes) {
execNode.PreBuildDependencies.emplace(exec.Name);
}
}
for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
LogMessage("Adding replace from " + out.Name + " to " + execName);
OutputToExecName[out.Name] = execName;
}
execs.Nodes.emplace_back(std::move(execNode));
}
for (auto& exec : execs.Nodes) {
for (auto& inputFile : exec.ExecInput) {
auto const iter = OutputsToReplace.find(inputFile);
if (iter != OutputsToReplace.end()) {
LogMessage("Replacing input: " + inputFile + " with " + iter->second);
inputFile = iter->second;
}
auto const depIter = std::find_if(
exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
[this](FastbuildTargetDep const& dep) {
return !OutputToExecName[dep.Name].empty();
});
if (depIter != exec.PreBuildDependencies.end()) {
LogMessage("Replacing dep " + depIter->Name + " with " +
OutputToExecName[depIter->Name]);
exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
exec.PreBuildDependencies.erase(depIter);
}
}
if (exec.NeedsDepsCheckExec) {
auto depsCheckExec = GetDepsCheckExec(exec);
LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
exec.PreBuildDependencies.emplace(depsCheckExec.Name);
this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
}
}
return execs;
}
std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
cmCustomCommandGenerator const& ccg)
{
// Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
if (!cmNonempty(property_value)) {
return std::string();
}
// Expand rule variables referenced in the given launcher command.
cmRulePlaceholderExpander::RuleVariables vars;
std::string output;
std::vector<std::string> const& outputs = ccg.GetOutputs();
for (size_t i = 0; i < outputs.size(); ++i) {
output =
cmStrCat(output,
this->LocalGenerator->ConvertToOutputFormat(
ccg.GetWorkingDirectory().empty()
? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
: outputs[i],
cmOutputConverter::SHELL));
if (i != outputs.size() - 1) {
output = cmStrCat(output, ',');
}
}
vars.Output = output.c_str();
vars.Role = ccg.GetCC().GetRole().c_str();
vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
vars.Config = ccg.GetOutputConfig().c_str();
auto rulePlaceholderExpander =
this->LocalGenerator->CreateRulePlaceholderExpander();
std::string launcher = *property_value;
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
vars);
if (!launcher.empty()) {
launcher += " ";
}
LogMessage("CC Launcher: " + launcher);
return launcher;
}
std::string cmFastbuildTargetGenerator::GetTargetName() const
{
if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
}
return this->GeneratorTarget->GetName();
}
cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
{
if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
return GeneratorTarget->GetExecutableNames(Config);
}
return GeneratorTarget->GetLibraryNames(Config);
}
void cmFastbuildTargetGenerator::AddObjectDependencies(
FastbuildTarget& fastbuildTarget,
std::vector<std::string>& allObjectDepends) const
{
auto const FindObjListWhichOutputs = [&fastbuildTarget](
std::string const& output) {
for (FastbuildObjectListNode const& objList :
fastbuildTarget.ObjectListNodes) {
if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
return objList.Name;
}
}
return std::string{};
};
for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
for (auto const& objDep : objList.ObjectDepends) {
// Check if there is another object list which outputs (OBJECT_OUTPUTS)
// something that this object list needs (OBJECT_DEPENDS).
auto anotherObjList = FindObjListWhichOutputs(objDep);
if (!anotherObjList.empty()) {
LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
allObjectDepends.emplace_back(anotherObjList);
objList.PreBuildDependencies.emplace(std::move(anotherObjList));
} else {
LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
allObjectDepends.emplace_back(objDep);
objList.PreBuildDependencies.emplace(objDep);
}
}
}
cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
}
void cmFastbuildTargetGenerator::AddLinkerNodeDependnecies(
FastbuildTarget& fastbuildTarget)
{
for (auto& linkerNode : fastbuildTarget.LinkerNode) {
if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
linkerNode.PreBuildDependencies.emplace(
fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
}
}
}
std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
std::string const& directory, cmSourceFile const& source,
std::string const& /*config*/) const
{
std::string objectDir =
this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
std::string const& objectName =
this->GeneratorTarget->GetObjectName(&source);
std::string path =
cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
LogMessage("ClangTidy replacements file: " + path);
return path;
}
void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
std::string const& language,
std::string const&)
{
std::vector<std::string> includes;
this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
language, Config);
// Add include directory flags.
std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
includes, this->GeneratorTarget, language, Config, false);
this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
}
std::string cmFastbuildTargetGenerator::GetName()
{
return GeneratorTarget->GetName();
}
std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
std::string const& path) const
{
return GetGlobalGenerator()->ConvertToFastbuildPath(path);
}
cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
const
{
return this->LocalGenerator->GetGlobalFastbuildGenerator();
}
void cmFastbuildTargetGenerator::AdditionalCleanFiles()
{
if (cmValue prop_value =
this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
auto* lg = this->LocalGenerator;
cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
this->GeneratorTarget));
std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
auto* gg = lg->GetGlobalFastbuildGenerator();
for (auto const& cleanFile : cleanFiles) {
// Support relative paths
gg->AddFileToClean(gg->ConvertToFastbuildPath(
cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
}
}
}