/* 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 #include #include #include #include #include #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(target->GetLocalGenerator())) , TargetDirectDependencies( this->GlobalCommonGenerator->GetTargetDirectDepends(GeneratorTarget)) , Config(std::move(configParam)) { this->MacOSXContentGenerator = cm::make_unique(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(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& 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 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 const& outputs = ccg.GetOutputs(); std::vector 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& fileLevelDeps, std::set& 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 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 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 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 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 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& 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 dep: " + anotherObjList); allObjectDepends.emplace_back(anotherObjList); objList.PreBuildDependencies.emplace(std::move(anotherObjList)); } else { LogMessage("Adding 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 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))); } } }