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