mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00

- Include `config` in all `install` and `custom` snippets - Include `target` for `custom` snippets where applicable - Document and test inclusion of `language` in `link` snippets Issue: #27244
859 lines
30 KiB
C++
859 lines
30 KiB
C++
/* 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, ¤tCCName, &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)));
|
|
}
|
|
}
|
|
}
|