| /* 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 <cm/string_view> | |
| #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 "cmTargetTypes.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 cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::OBJECT_LIBRARY: | |
| return new cmFastbuildNormalTargetGenerator(target, std::move(config)); | |
| case cm::TargetType::UTILITY: | |
| case cm::TargetType::GLOBAL_TARGET: | |
| case cm::TargetType::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(cmStrCat("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 = | |
| cmStrCat(FASTBUILD_SCRIPT_FILE_ARG, | |
| 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, ccg.GetCC().GetCMP0212Status())) { | |
| 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( | |
| cmStrCat("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(cmStrCat("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(cmStrCat("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(cmStrCat("dep: ", dep, ", no source, byproduct: ", | |
| targetInfo.SourceIsByproduct)); | |
| // Tested in "OutDir" test. | |
| if (!cmSystemTools::FileIsFullPath(orig)) { | |
| targetDep.emplace(std::move(orig)); | |
| } | |
| continue; | |
| } | |
| if (!targetInfo.Source->GetCustomCommand()) { | |
| LogMessage(cmStrCat("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 = cmStrCat("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; | |
| } | |
| namespace { | |
| std::vector<std::vector<cm::string_view>> GroupDeps( | |
| FastbuildExecNode const& depender, size_t commandLineUsedLength) | |
| { | |
| std::vector<cm::string_view> depsCheckExecNames; | |
| depsCheckExecNames.reserve( | |
| depender.OutputsAlias.PreBuildDependencies.size() + | |
| depender.ByproductsAlias.PreBuildDependencies.size()); | |
| for (FastbuildTargetDep const& dep : | |
| depender.OutputsAlias.PreBuildDependencies) { | |
| depsCheckExecNames.emplace_back(dep.Name); | |
| } | |
| for (FastbuildTargetDep const& dep : | |
| depender.ByproductsAlias.PreBuildDependencies) { | |
| depsCheckExecNames.emplace_back(dep.Name); | |
| } | |
| size_t commandLineLimit = cmSystemTools::CalculateCommandLineLengthLimit(); | |
| std::vector<std::vector<cm::string_view>> depsCheckExecNamesGrouped; | |
| if (commandLineLimit == 0) { | |
| depsCheckExecNamesGrouped.emplace_back(std::move(depsCheckExecNames)); | |
| return depsCheckExecNamesGrouped; | |
| } | |
| commandLineLimit -= commandLineUsedLength; | |
| std::vector<cm::string_view> currentGroup; | |
| currentGroup.reserve(depsCheckExecNames.size()); | |
| size_t currentGroupSizeMayUse = commandLineLimit; | |
| for (size_t i = 0; i < depsCheckExecNames.size(); ++i) { | |
| cm::string_view depsCheckExecName = depsCheckExecNames[i]; | |
| if (currentGroupSizeMayUse > depsCheckExecName.size()) { | |
| currentGroup.emplace_back(depsCheckExecName); | |
| // +1 for the space between arguments | |
| currentGroupSizeMayUse -= depsCheckExecName.size() + 1; | |
| continue; | |
| } | |
| // Current dependency cannot be placed in the current group | |
| if (currentGroup.empty()) { | |
| cmSystemTools::Error(cmStrCat("Failed to group dependencies: command " | |
| "line limit exceeded. Filename too long: ", | |
| depsCheckExecName, | |
| ". Size limit: ", commandLineLimit)); | |
| return {}; | |
| } | |
| depsCheckExecNamesGrouped.emplace_back(std::move(currentGroup)); | |
| currentGroup.clear(); | |
| currentGroup.reserve(depsCheckExecNames.size() - i); | |
| currentGroupSizeMayUse = commandLineLimit; | |
| // Go back to the previous dependency to check if it can be placed in the | |
| // current group. | |
| --i; | |
| } | |
| if (!currentGroup.empty()) { | |
| depsCheckExecNamesGrouped.emplace_back(std::move(currentGroup)); | |
| } | |
| return depsCheckExecNamesGrouped; | |
| } | |
| } | |
| std::vector<FastbuildExecNode> cmFastbuildTargetGenerator::GetDepsCheckExecs( | |
| FastbuildExecNode const& depender) | |
| { | |
| std::string const& executable = cmSystemTools::GetCMakeCommand(); | |
| std::string execArgumentsBase = | |
| cmStrCat("-E cmake_fastbuild_check_depends ", depender.ExecOutput, ' '); | |
| std::vector<std::vector<cm::string_view>> depsCheckExecNamesGrouped = | |
| GroupDeps(depender, executable.size() + execArgumentsBase.size()); | |
| std::vector<FastbuildExecNode> res; | |
| res.reserve(depsCheckExecNamesGrouped.size()); | |
| for (size_t i = 0; i < depsCheckExecNamesGrouped.size(); ++i) { | |
| FastbuildExecNode exec; | |
| exec.Name = cmStrCat(depender.Name, "-check-depends-", i); | |
| exec.ExecAlways = true; | |
| exec.ExecUseStdOutAsOutput = true; | |
| exec.ExecOutput = cmStrCat(depender.ExecOutput, '.', i, ".deps-checker"); | |
| exec.ExecExecutable = executable; | |
| exec.ExecArguments = | |
| cmJoinStrings(depsCheckExecNamesGrouped[i], " ", execArgumentsBase); | |
| res.emplace_back(std::move(exec)); | |
| } | |
| return res; | |
| } | |
| 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 dependencies 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); | |
| 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(cmStrCat("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( | |
| cmStrCat("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(cmStrCat("Replacing dep ", depIter->Name, " with ", | |
| OutputToExecName[depIter->Name])); | |
| exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]); | |
| exec.PreBuildDependencies.erase(depIter); | |
| } | |
| } | |
| if (exec.NeedsDepsCheckExec) { | |
| std::vector<FastbuildExecNode> depsCheckExecs = GetDepsCheckExecs(exec); | |
| std::vector<cm::string_view> depsCheckExecNames; | |
| depsCheckExecNames.reserve(depsCheckExecs.size()); | |
| for (FastbuildExecNode const& depsCheckExec : depsCheckExecs) { | |
| depsCheckExecNames.emplace_back(depsCheckExec.Name); | |
| } | |
| LogMessage( | |
| cmJoinStrings(depsCheckExecNames, ", ", "Adding deps check Exec: ")); | |
| for (FastbuildExecNode& depsCheckExec : depsCheckExecs) { | |
| 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(); | |
| std::string filePathWithOutput = ccg.StoreContentToFile(output); | |
| vars.FilePathWithOutput = filePathWithOutput.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() == cm::TargetType::GLOBAL_TARGET) { | |
| return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget); | |
| } | |
| return this->GeneratorTarget->GetName(); | |
| } | |
| cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const | |
| { | |
| if (GeneratorTarget->GetType() == cm::TargetType::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::AddLinkerNodeDependencies( | |
| 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))); | |
| } | |
| } | |
| } |