| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmFastbuildNormalTargetGenerator.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cstddef> | |
| #include <functional> | |
| #include <iterator> | |
| #include <map> | |
| #include <string> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| #include "cmsys/FStream.hxx" | |
| #include "cmCommonTargetGenerator.h" | |
| #include "cmCryptoHash.h" | |
| #include "cmFastbuildLinkLineComputer.h" | |
| #include "cmFastbuildTargetGenerator.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorFileSets.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalCommonGenerator.h" | |
| #include "cmGlobalFastbuildGenerator.h" | |
| #include "cmInstrumentation.h" | |
| #include "cmInstrumentationQuery.h" | |
| #include "cmLinkLineComputer.h" | |
| #include "cmLinkLineDeviceComputer.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalCommonGenerator.h" | |
| #include "cmLocalFastbuildGenerator.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmOSXBundleGenerator.h" | |
| #include "cmObjectLocation.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmSourceFile.h" | |
| #include "cmState.h" | |
| #include "cmStateDirectory.h" | |
| #include "cmStateSnapshot.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetDepend.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| namespace { | |
| std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); | |
| std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); | |
| std::string const COMPILE_FLAGS("COMPILE_FLAGS"); | |
| std::string const CMAKE_LANGUAGE("CMAKE"); | |
| std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); | |
| std::string const CMAKE_UNITY_BUILD("CMAKE_UNITY_BUILD"); | |
| std::string const CMAKE_UNITY_BUILD_BATCH_SIZE("CMAKE_UNITY_BUILD_BATCH_SIZE"); | |
| std::string const UNITY_BUILD("UNITY_BUILD"); | |
| std::string const UNITY_BUILD_BATCH_SIZE("UNITY_BUILD_BATCH_SIZE"); | |
| std::string const SKIP_UNITY_BUILD_INCLUSION("SKIP_UNITY_BUILD_INCLUSION"); | |
| std::string const UNITY_GROUP("UNITY_GROUP"); | |
| #ifdef _WIN32 | |
| char const kPATH_SLASH = '\\'; | |
| #else | |
| char const kPATH_SLASH = '/'; | |
| #endif | |
| bool IsExcludedFromUnity(cmGeneratorTarget const* target, | |
| cmGeneratorFileSet const* fileSet, | |
| cmSourceFile const& srcFile) | |
| { | |
| if (fileSet && | |
| (!cm::FileSetMetadata::GetAttributes(fileSet->GetType()) | |
| .contains(cm::FileSetMetadata::FileSetAttributes::UnityBuild) || | |
| fileSet->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn() || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "COMPILE_OPTIONS" | |
| : "INTERFACE_COMPILE_OPTIONS") || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "COMPILE_DEFINITIONS" | |
| : "INTERFACE_COMPILE_DEFINITIONS") || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "INCLUDE_DIRECTORIES" | |
| : "INTERFACE_INCLUDE_DIRECTORIES"))) { | |
| return true; | |
| } | |
| return srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION) || | |
| srcFile.GetProperty(COMPILE_OPTIONS) || | |
| srcFile.GetProperty(COMPILE_DEFINITIONS) || | |
| srcFile.GetProperty(COMPILE_FLAGS) || | |
| srcFile.GetProperty(INCLUDE_DIRECTORIES); | |
| } | |
| } // anonymous namespace | |
| cmFastbuildNormalTargetGenerator::cmFastbuildNormalTargetGenerator( | |
| cmGeneratorTarget* gt, std::string configParam) | |
| : cmFastbuildTargetGenerator(gt, std::move(configParam)) | |
| , RulePlaceholderExpander( | |
| this->LocalCommonGenerator->CreateRulePlaceholderExpander()) | |
| , ObjectOutDir(this->GetGlobalGenerator()->ConvertToFastbuildPath( | |
| this->GeneratorTarget->GetObjectDirectory(Config))) | |
| , Languages(GetLanguages()) | |
| , CompileObjectCmakeRules(GetCompileObjectCommand()) | |
| , CudaCompileMode(this->GetCudaCompileMode()) | |
| { | |
| LogMessage(cmStrCat("objectOutDir: ", ObjectOutDir)); | |
| this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(gt); | |
| this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); | |
| // Quotes to account for potential spaces. | |
| RulePlaceholderExpander->SetTargetImpLib( | |
| "\"" FASTBUILD_DOLLAR_TAG "TargetOutputImplib" FASTBUILD_DOLLAR_TAG "\""); | |
| for (auto const& lang : Languages) { | |
| TargetIncludesByLanguage[lang] = this->GetIncludes(lang, Config); | |
| LogMessage(cmStrCat("targetIncludes for lang ", lang, " = ", | |
| TargetIncludesByLanguage[lang])); | |
| for (auto const& arch : this->GetArches()) { | |
| auto& flags = CompileFlagsByLangAndArch[std::make_pair(lang, arch)]; | |
| this->LocalCommonGenerator->GetTargetCompileFlags( | |
| this->GeneratorTarget, Config, lang, flags, arch); | |
| LogMessage( | |
| cmStrCat("Lang: ", lang, ", arch: ", arch, ", flags: ", flags)); | |
| } | |
| } | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::DetectCompilerFlags( | |
| cmSourceFile const& srcFile, std::string const& arch) | |
| { | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(&srcFile, Config); | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->GetLocalGenerator(), Config, this->GeneratorTarget, language); | |
| auto const* fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( | |
| this->Config, &srcFile); | |
| std::vector<std::string> sourceIncludesVec; | |
| if (fileSet) { | |
| auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetIncludeDirectories(this->Config, language) | |
| : fileSet->GetInterfaceIncludeDirectories(this->Config, language); | |
| if (!fsIncludes.empty()) { | |
| this->LocalGenerator->AppendIncludeDirectories( | |
| sourceIncludesVec, cm::remove_BT(fsIncludes), srcFile); | |
| } | |
| } | |
| if (cmValue cincludes = srcFile.GetProperty(INCLUDE_DIRECTORIES)) { | |
| this->LocalGenerator->AppendIncludeDirectories( | |
| sourceIncludesVec, | |
| genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES), srcFile); | |
| } | |
| std::string sourceIncludesStr = this->LocalGenerator->GetIncludeFlags( | |
| sourceIncludesVec, this->GeneratorTarget, language, Config, false); | |
| LogMessage(cmStrCat("sourceIncludes = ", sourceIncludesStr)); | |
| std::string compileFlags = | |
| CompileFlagsByLangAndArch[std::make_pair(language, arch)]; | |
| this->GeneratorTarget->AddExplicitLanguageFlags(compileFlags, srcFile, | |
| Config); | |
| if (cmValue const cflags = srcFile.GetProperty(COMPILE_FLAGS)) { | |
| this->LocalGenerator->AppendFlags( | |
| compileFlags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); | |
| } | |
| if (cmValue const coptions = srcFile.GetProperty(COMPILE_OPTIONS)) { | |
| this->LocalGenerator->AppendCompileOptions( | |
| compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); | |
| } | |
| // Add flags from file set properties. | |
| if (fileSet) { | |
| auto options = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileOptions(this->Config, language) | |
| : fileSet->GetInterfaceCompileOptions(this->Config, language); | |
| if (!options.empty()) { | |
| this->LocalGenerator->AppendCompileOptions(compileFlags, | |
| cm::remove_BT(options)); | |
| } | |
| } | |
| // Source includes take precedence over target includes. | |
| this->LocalGenerator->AppendFlags(compileFlags, sourceIncludesStr); | |
| this->LocalGenerator->AppendFlags(compileFlags, | |
| TargetIncludesByLanguage[language]); | |
| if (language == "Fortran") { | |
| this->AppendFortranFormatFlags(compileFlags, srcFile); | |
| this->AppendFortranPreprocessFlags(compileFlags, srcFile); | |
| } | |
| LogMessage(cmStrCat("compileFlags = ", compileFlags)); | |
| return compileFlags; | |
| } | |
| void cmFastbuildNormalTargetGenerator::SplitLinkerFromArgs( | |
| std::string const& command, std::string& outLinkerExecutable, | |
| std::string& outLinkerArgs) const | |
| { | |
| #ifdef _WIN32 | |
| std::vector<std::string> args; | |
| std::string tmp; | |
| cmSystemTools::SplitProgramFromArgs(command, tmp, outLinkerArgs); | |
| // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when | |
| // appending "STATIC_LIBRARY_FLAGS_DEBUG"... | |
| cmSystemTools::ReplaceString(outLinkerArgs, "\"\"", ""); | |
| cmSystemTools::ParseWindowsCommandLine(command.c_str(), args); | |
| outLinkerExecutable = std::move(args[0]); | |
| #else | |
| cmSystemTools::SplitProgramFromArgs(command, outLinkerExecutable, | |
| outLinkerArgs); | |
| #endif | |
| } | |
| void cmFastbuildNormalTargetGenerator::GetLinkerExecutableAndArgs( | |
| std::string const& command, std::string& outLinkerExecutable, | |
| std::string& outLinkerArgs) | |
| { | |
| if (command.empty()) { | |
| return; | |
| } | |
| LogMessage("Link Command: " + command); | |
| auto const launcher = cmCommonTargetGenerator::GetLinkerLauncher(Config); | |
| // Tested in "RunCMake.LinkerLauncher" test. | |
| if (!launcher.empty() && | |
| this->GeneratorTarget->GetType() != cm::TargetType::STATIC_LIBRARY) { | |
| outLinkerExecutable = launcher.front(); | |
| outLinkerArgs.clear(); | |
| for (std::size_t i = 1; i < launcher.size(); ++i) { | |
| outLinkerArgs = cmStrCat( | |
| std::move(outLinkerArgs), | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces(launcher[i]), ' '); | |
| } | |
| outLinkerArgs += command; | |
| } else { | |
| SplitLinkerFromArgs(command, outLinkerExecutable, outLinkerArgs); | |
| } | |
| LogMessage("Linker Exe: " + outLinkerExecutable); | |
| LogMessage("Linker args: " + outLinkerArgs); | |
| } | |
| bool cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand( | |
| std::string& command, std::string const& arch, | |
| cmGeneratorTarget::Names const& targetNames) | |
| { | |
| std::string const linkLanguage = | |
| this->GeneratorTarget->GetLinkerLanguage(Config); | |
| if (linkLanguage.empty()) { | |
| cmSystemTools::Error("CMake can not determine linker language for " | |
| "target: " + | |
| this->GeneratorTarget->GetName()); | |
| return false; | |
| } | |
| LogMessage("linkLanguage: " + linkLanguage); | |
| std::string linkLibs; | |
| std::string targetFlags; | |
| std::string linkFlags; | |
| std::string frameworkPath; | |
| // Tested in "RunCMake.StandardLinkDirectories" test. | |
| std::string linkPath; | |
| std::unique_ptr<cmLinkLineComputer> const linkLineComputer = | |
| this->GetGlobalGenerator()->CreateLinkLineComputer( | |
| this->LocalGenerator, | |
| this->GetLocalGenerator()->GetStateSnapshot().GetDirectory()); | |
| this->LocalCommonGenerator->GetTargetFlags( | |
| linkLineComputer.get(), Config, linkLibs, targetFlags, linkFlags, | |
| frameworkPath, linkPath, this->GeneratorTarget); | |
| // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when | |
| // appending "STATIC_LIBRARY_FLAGS_DEBUG"... | |
| cmSystemTools::ReplaceString(linkFlags, "\"\"", ""); | |
| LogMessage("linkLibs: " + linkLibs); | |
| LogMessage("targetFlags: " + targetFlags); | |
| LogMessage("linkFlags: " + linkFlags); | |
| LogMessage("frameworkPath: " + frameworkPath); | |
| LogMessage("linkPath: " + linkPath); | |
| LogMessage("MANIFESTS: " + this->GetManifests(Config)); | |
| cmComputeLinkInformation* linkInfo = | |
| this->GeneratorTarget->GetLinkInformation(Config); | |
| if (!linkInfo) { | |
| return false; | |
| } | |
| // Tested in "RunCMake.RuntimePath" test. | |
| std::string const rpath = linkLineComputer->ComputeRPath(*linkInfo); | |
| LogMessage("RPath: " + rpath); | |
| if (!linkFlags.empty()) { | |
| linkFlags += " "; | |
| } | |
| linkFlags += cmJoin({ rpath, frameworkPath, linkPath }, " "); | |
| cm::TargetType const targetType = this->GeneratorTarget->GetType(); | |
| std::string languageCompileFlags; | |
| if (targetType == cm::TargetType::SHARED_LIBRARY || | |
| targetType == cm::TargetType::MODULE_LIBRARY) { | |
| this->LocalCommonGenerator->AddLanguageFlagsForLinking( | |
| languageCompileFlags, this->GeneratorTarget, linkLanguage, Config); | |
| // Add OS X version flags, if any. | |
| this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true); | |
| this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false); | |
| } | |
| // Add Arch flags to link flags for binaries | |
| if (targetType == cm::TargetType::SHARED_LIBRARY || | |
| targetType == cm::TargetType::MODULE_LIBRARY || | |
| targetType == cm::TargetType::EXECUTABLE) { | |
| this->LocalCommonGenerator->AddArchitectureFlags( | |
| linkFlags, this->GeneratorTarget, linkLanguage, Config, arch); | |
| this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags( | |
| linkFlags, this->GetGeneratorTarget(), linkLanguage); | |
| } | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| vars.CMTargetName = this->GeneratorTarget->GetName().c_str(); | |
| vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str(); | |
| vars.Config = Config.c_str(); | |
| vars.Language = linkLanguage.c_str(); | |
| std::string const manifests = this->GetManifests(Config); | |
| vars.Manifests = manifests.c_str(); | |
| std::string const stdLibString = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES")); | |
| LogMessage(cmStrCat("Target type: ", | |
| static_cast<int>(this->GeneratorTarget->GetType()))); | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::EXECUTABLE || | |
| this->GeneratorTarget->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::MODULE_LIBRARY) { | |
| vars.Objects = FASTBUILD_1_0_INPUT_PLACEHOLDER; | |
| vars.LinkLibraries = stdLibString.c_str(); | |
| } else { | |
| vars.Objects = FASTBUILD_1_INPUT_PLACEHOLDER; | |
| } | |
| vars.ObjectDir = FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG; | |
| vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER; | |
| std::string install_dir; | |
| std::string target_so_name; | |
| if (this->GeneratorTarget->HasSOName(Config)) { | |
| vars.SONameFlag = this->Makefile->GetSONameFlag( | |
| this->GeneratorTarget->GetLinkerLanguage(Config)); | |
| target_so_name = | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces(targetNames.SharedObject); | |
| vars.TargetSOName = target_so_name.c_str(); | |
| // Tested in "RunCMake.RuntimePath / RunCMake.INSTALL_NAME_DIR" | |
| // tests. | |
| install_dir = this->LocalGenerator->ConvertToOutputFormat( | |
| this->GeneratorTarget->GetInstallNameDirForBuildTree(Config), | |
| cmOutputConverter::SHELL); | |
| vars.TargetInstallNameDir = install_dir.c_str(); | |
| } else { | |
| vars.TargetSOName = ""; | |
| } | |
| vars.TargetPDB = FASTBUILD_DOLLAR_TAG "LinkerPDB" FASTBUILD_DOLLAR_TAG; | |
| // Setup the target version. | |
| std::string targetVersionMajor; | |
| std::string targetVersionMinor; | |
| { | |
| int major; | |
| int minor; | |
| this->GeneratorTarget->GetTargetVersion(major, minor); | |
| targetVersionMajor = std::to_string(major); | |
| targetVersionMinor = std::to_string(minor); | |
| } | |
| vars.TargetVersionMajor = targetVersionMajor.c_str(); | |
| vars.TargetVersionMinor = targetVersionMinor.c_str(); | |
| vars.Defines = | |
| FASTBUILD_DOLLAR_TAG "CompileDefineFlags" FASTBUILD_DOLLAR_TAG; | |
| vars.Flags = targetFlags.c_str(); | |
| vars.LinkFlags = linkFlags.c_str(); | |
| vars.LanguageCompileFlags = languageCompileFlags.c_str(); | |
| std::string const linker = this->GeneratorTarget->GetLinkerTool(Config); | |
| vars.Linker = linker.c_str(); | |
| std::string const targetSupportPath = this->ConvertToFastbuildPath( | |
| this->GetGeneratorTarget()->GetCMFSupportDirectory()); | |
| vars.TargetSupportDir = targetSupportPath.c_str(); | |
| LogMessage("linkFlags: " + linkFlags); | |
| LogMessage("linker: " + linker); | |
| std::string linkRule = GetLinkCommand(); | |
| ApplyLinkRuleLauncher(linkRule); | |
| RulePlaceholderExpander->ExpandRuleVariables( | |
| dynamic_cast<cmLocalFastbuildGenerator*>(this->LocalCommonGenerator), | |
| linkRule, vars); | |
| command = std::move(linkRule); | |
| LogMessage(cmStrCat("Expanded link command: ", command)); | |
| return true; | |
| } | |
| void cmFastbuildNormalTargetGenerator::ApplyLinkRuleLauncher( | |
| std::string& command) | |
| { | |
| std::string const val = this->GetLocalGenerator()->GetRuleLauncher( | |
| this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", Config); | |
| if (cmNonempty(val)) { | |
| LogMessage("RULE_LAUNCH_LINK: " + val); | |
| command = cmStrCat(val, ' ', std::move(command)); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::ApplyLWYUToLinkerCommand( | |
| FastbuildLinkerNode& linkerNode) | |
| { | |
| cmValue const lwyuCheck = | |
| this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK"); | |
| if (this->UseLWYU && lwyuCheck) { | |
| LogMessage("UseLWYU=true"); | |
| std::string args = " -E __run_co_compile --lwyu="; | |
| args += this->GetLocalGenerator()->EscapeForShell(*lwyuCheck); | |
| args = cmStrCat( | |
| std::move(args), " --source=", | |
| this->ConvertToFastbuildPath(this->GetGeneratorTarget()->GetFullPath( | |
| Config, cmStateEnums::RuntimeBinaryArtifact, | |
| /*realname=*/true))); | |
| LogMessage("LWUY args: " + args); | |
| linkerNode.LinkerStampExe = cmSystemTools::GetCMakeCommand(); | |
| linkerNode.LinkerStampExeArgs = std::move(args); | |
| } | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::ComputeDefines( | |
| cmSourceFile const& srcFile) | |
| { | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(&srcFile, Config); | |
| std::set<std::string> defines; | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->GetLocalGenerator(), Config, this->GeneratorTarget, language); | |
| if (auto compile_defs = srcFile.GetProperty(COMPILE_DEFINITIONS)) { | |
| this->GetLocalGenerator()->AppendDefines( | |
| defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS)); | |
| } | |
| std::string defPropName = "COMPILE_DEFINITIONS_"; | |
| defPropName += cmSystemTools::UpperCase(Config); | |
| if (auto config_compile_defs = srcFile.GetProperty(defPropName)) { | |
| this->GetLocalGenerator()->AppendDefines( | |
| defines, | |
| genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS)); | |
| } | |
| if (auto const* fileSet = | |
| this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( | |
| this->Config, &srcFile)) { | |
| auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileDefinitions(this->Config, language) | |
| : fileSet->GetInterfaceCompileDefinitions(this->Config, language); | |
| if (!fsDefines.empty()) { | |
| this->LocalGenerator->AppendDefines(defines, fsDefines); | |
| } | |
| } | |
| std::string definesString = this->GetDefines(language, Config); | |
| LogMessage(cmStrCat("TARGET DEFINES = ", definesString)); | |
| this->GetLocalGenerator()->JoinDefines(defines, definesString, language); | |
| LogMessage(cmStrCat("DEFINES = ", definesString)); | |
| return definesString; | |
| } | |
| void cmFastbuildNormalTargetGenerator::ComputePCH( | |
| cmSourceFile const& srcFile, FastbuildObjectListNode& node, | |
| std::set<std::string>& createdPCH) | |
| { | |
| if (srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS")) { | |
| return; | |
| } | |
| cmGeneratorFileSet const* const fileSet = | |
| this->GeneratorTarget->GetFileSetForSource(Config, &srcFile); | |
| if (fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) { | |
| return; | |
| } | |
| // We have already computed PCH for this node. | |
| if (!node.PCHOptions.empty() || !node.PCHInputFile.empty() || | |
| !node.PCHOutputFile.empty()) { | |
| return; | |
| } | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(&srcFile, Config); | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->GetLocalGenerator(), Config, this->GeneratorTarget, language); | |
| //.cxx | |
| std::string const pchSource = | |
| this->GeneratorTarget->GetPchSource(Config, language); | |
| //.hxx | |
| std::string const pchHeader = | |
| this->GeneratorTarget->GetPchHeader(Config, language); | |
| //.pch | |
| std::string const pchFile = | |
| this->GeneratorTarget->GetPchFile(Config, language); | |
| if (pchHeader.empty() || pchFile.empty()) { | |
| return; | |
| } | |
| // In "RunCMake.GenEx-TARGET_PROPERTY" test we call set | |
| // CMAKE_PCH_EXTENSION="", so pchHeader becomes same as pchFile... | |
| if (pchHeader == pchFile) { | |
| LogMessage("pchHeader == pchFile > skipping"); | |
| LogMessage("pchHeader: " + pchHeader); | |
| LogMessage("pchFile: " + pchFile); | |
| return; | |
| } | |
| node.PCHOutputFile = | |
| this->GetGlobalGenerator()->ConvertToFastbuildPath(pchFile); | |
| // Tell the ObjectList how to use PCH. | |
| std::string const pchUseOption = | |
| this->GeneratorTarget->GetPchUseCompileOptions(Config, language); | |
| LogMessage(cmStrCat("pchUseOption: ", pchUseOption)); | |
| std::string origCompileOptions = node.CompilerOptions; | |
| for (auto const& opt : | |
| cmList{ genexInterpreter.Evaluate(pchUseOption, COMPILE_OPTIONS) }) { | |
| node.CompilerOptions += " "; | |
| node.CompilerOptions += opt; | |
| } | |
| if (!createdPCH.emplace(node.PCHOutputFile).second) { | |
| LogMessage(node.PCHOutputFile + " is already created by this target"); | |
| return; | |
| } | |
| // Short circuit if the PCH has already been created by another target. | |
| if (!this->GeneratorTarget->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM") | |
| .empty()) { | |
| LogMessage(cmStrCat("PCH: ", node.PCHOutputFile, | |
| " already created by another target")); | |
| return; | |
| } | |
| node.PCHInputFile = | |
| this->GetGlobalGenerator()->ConvertToFastbuildPath(pchSource); | |
| std::string const pchCreateOptions = | |
| this->GeneratorTarget->GetPchCreateCompileOptions(Config, language); | |
| LogMessage(cmStrCat("pchCreateOptions: ", pchCreateOptions)); | |
| char const* sep = ""; | |
| for (auto const& opt : cmList{ | |
| genexInterpreter.Evaluate(pchCreateOptions, COMPILE_OPTIONS) }) { | |
| node.PCHOptions += sep; | |
| node.PCHOptions += opt; | |
| sep = " "; | |
| } | |
| // Reuse compiler options for PCH options. | |
| node.PCHOptions += origCompileOptions; | |
| if (this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_COMPILER_ID")) == "MSVC") { | |
| cmSystemTools::ReplaceString(node.PCHOptions, | |
| FASTBUILD_2_INPUT_PLACEHOLDER, | |
| FASTBUILD_3_INPUT_PLACEHOLDER); | |
| } | |
| LogMessage("PCH Source: " + pchSource); | |
| LogMessage("node.PCHInputFile: " + node.PCHInputFile); | |
| LogMessage("node.PCHOutputFile: " + node.PCHOutputFile); | |
| LogMessage("node.PCHOptions: " + node.PCHOptions); | |
| LogMessage("node.CompilerOptions: " + node.CompilerOptions); | |
| } | |
| void cmFastbuildNormalTargetGenerator::EnsureDirectoryExists( | |
| std::string const& path) const | |
| { | |
| if (cmSystemTools::FileIsFullPath(path.c_str())) { | |
| cmSystemTools::MakeDirectory(path.c_str()); | |
| } else { | |
| auto* gg = this->GetGlobalGenerator(); | |
| std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory(); | |
| // Also ensures there is a trailing slash. | |
| fullPath += path; | |
| cmSystemTools::MakeDirectory(fullPath); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::EnsureParentDirectoryExists( | |
| std::string const& path) const | |
| { | |
| this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path)); | |
| } | |
| std::vector<std::string> | |
| cmFastbuildNormalTargetGenerator::GetManifestsAsFastbuildPath() const | |
| { | |
| std::vector<cmSourceFile const*> manifest_srcs; | |
| this->GeneratorTarget->GetManifests(manifest_srcs, Config); | |
| std::vector<std::string> manifests; | |
| manifests.reserve(manifest_srcs.size()); | |
| for (auto& manifest_src : manifest_srcs) { | |
| std::string str = this->ConvertToFastbuildPath( | |
| cmSystemTools::ConvertToOutputPath(manifest_src->GetFullPath())); | |
| LogMessage("Manifest: " + str); | |
| manifests.emplace_back(std::move(str)); | |
| } | |
| return manifests; | |
| } | |
| void cmFastbuildNormalTargetGenerator::GenerateModuleDefinitionInfo( | |
| FastbuildTarget& target) const | |
| { | |
| cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | |
| GeneratorTarget->GetModuleDefinitionInfo(Config); | |
| if (mdi && mdi->DefFileGenerated) { | |
| FastbuildExecNode execNode; | |
| execNode.Name = target.Name + "-def-files"; | |
| execNode.ExecExecutable = cmSystemTools::GetCMakeCommand(); | |
| execNode.ExecArguments = | |
| cmStrCat("-E __create_def ", FASTBUILD_2_INPUT_PLACEHOLDER, ' ', | |
| FASTBUILD_1_INPUT_PLACEHOLDER); | |
| std::string const obj_list_file = mdi->DefFile + ".objs"; | |
| auto const nm_executable = GetMakefile()->GetDefinition("CMAKE_NM"); | |
| if (!cmIsOff(nm_executable)) { | |
| execNode.ExecArguments += " --nm="; | |
| execNode.ExecArguments += ConvertToFastbuildPath(*nm_executable); | |
| } | |
| execNode.ExecOutput = ConvertToFastbuildPath(mdi->DefFile); | |
| execNode.ExecInput.push_back(ConvertToFastbuildPath(obj_list_file)); | |
| // RunCMake.AutoExportDll | |
| for (auto const& objList : target.ObjectListNodes) { | |
| execNode.PreBuildDependencies.emplace(objList.Name); | |
| } | |
| // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests. | |
| for (auto& linkerNode : target.LinkerNode) { | |
| linkerNode.Libraries2.emplace_back(execNode.Name); | |
| } | |
| target.PreLinkExecNodes.Nodes.emplace_back(std::move(execNode)); | |
| // create a list of obj files for the -E __create_def to read | |
| cmGeneratedFileStream fout(obj_list_file); | |
| // Since we generate this file once during configuration, we should not | |
| // remove it when "clean" is built. | |
| // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests. | |
| this->GetGlobalGenerator()->AllFilesToKeep.insert(obj_list_file); | |
| if (mdi->WindowsExportAllSymbols) { | |
| std::vector<cmSourceFile const*> objectSources; | |
| GeneratorTarget->GetObjectSources(objectSources, Config); | |
| std::map<cmSourceFile const*, cmObjectLocations> mapping; | |
| for (cmSourceFile const* it : objectSources) { | |
| mapping[it]; | |
| } | |
| GeneratorTarget->LocalGenerator->ComputeObjectFilenames(mapping, Config, | |
| GeneratorTarget); | |
| std::vector<std::string> objs; | |
| for (cmSourceFile const* it : objectSources) { | |
| auto const& v = mapping[it]; | |
| LogMessage("Obj source : " + v.LongLoc.GetPath()); | |
| std::string objFile = this->ConvertToFastbuildPath( | |
| GeneratorTarget->GetObjectDirectory(Config) + v.LongLoc.GetPath()); | |
| objFile = cmSystemTools::ConvertToOutputPath(objFile); | |
| LogMessage("objFile path: " + objFile); | |
| objs.push_back(objFile); | |
| } | |
| std::vector<cmSourceFile const*> externalObjectSources; | |
| GeneratorTarget->GetExternalObjects(externalObjectSources, Config); | |
| for (cmSourceFile const* it : externalObjectSources) { | |
| objs.push_back(cmSystemTools::ConvertToOutputPath( | |
| this->ConvertToFastbuildPath(it->GetFullPath()))); | |
| } | |
| for (std::string const& objFile : objs) { | |
| if (cmHasLiteralSuffix(objFile, ".obj")) { | |
| fout << objFile << "\n"; | |
| } | |
| } | |
| } | |
| for (cmSourceFile const* src : mdi->Sources) { | |
| fout << src->GetFullPath() << "\n"; | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddPrebuildDeps( | |
| FastbuildTarget& target) const | |
| { | |
| // All ObjectLists should wait for PRE_BUILD. | |
| for (FastbuildObjectListNode& node : target.ObjectListNodes) { | |
| if (!target.PreBuildExecNodes.Name.empty()) { | |
| node.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name); | |
| } | |
| if (!target.ExecNodes.Name.empty()) { | |
| node.PreBuildDependencies.emplace(target.ExecNodes.Name); | |
| } | |
| } | |
| for (auto& linkerNode : target.LinkerNode) { | |
| // Wait for 'PRE_BUILD' custom commands. | |
| if (!target.PreBuildExecNodes.Name.empty()) { | |
| linkerNode.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name); | |
| } | |
| // Wait for regular custom commands. | |
| if (!target.ExecNodes.Name.empty()) { | |
| linkerNode.PreBuildDependencies.emplace(target.ExecNodes.Name); | |
| } | |
| // All targets that we depend on must be prebuilt. | |
| if (!target.DependenciesAlias.PreBuildDependencies.empty()) { | |
| linkerNode.PreBuildDependencies.emplace(target.DependenciesAlias.Name); | |
| } | |
| } | |
| } | |
| std::set<std::string> cmFastbuildNormalTargetGenerator::GetLanguages() | |
| { | |
| std::set<std::string> result; | |
| this->GetGeneratorTarget()->GetLanguages(result, Config); | |
| for (std::string const& lang : result) { | |
| this->GetGlobalGenerator()->AddCompiler(lang, this->GetMakefile()); | |
| } | |
| LogMessage("Languages: " + cmJoin(result, ", ")); | |
| return result; | |
| } | |
| std::unordered_map<std::string, std::string> | |
| cmFastbuildNormalTargetGenerator::GetCompileObjectCommand() const | |
| { | |
| std::unordered_map<std::string, std::string> result; | |
| result.reserve(Languages.size()); | |
| for (std::string const& lang : Languages) { | |
| std::vector<std::string> commands; | |
| std::string cmakeVar; | |
| cmakeVar = "CMAKE_"; | |
| cmakeVar += lang; | |
| cmakeVar += "_COMPILE_OBJECT"; | |
| std::string cmakeValue = | |
| LocalCommonGenerator->GetMakefile()->GetSafeDefinition(cmakeVar); | |
| LogMessage(cmakeVar.append(" = ").append(cmakeValue)); | |
| result[lang] = std::move(cmakeValue); | |
| } | |
| return result; | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::GetCudaCompileMode() const | |
| { | |
| if (Languages.find("CUDA") == Languages.end()) { | |
| return {}; | |
| } | |
| // TODO: unify it with makefile / ninja generators. | |
| std::string cudaCompileMode; | |
| if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) { | |
| std::string const& rdcFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), rdcFlag, ' '); | |
| } | |
| static std::array<cm::string_view, 4> const compileModes{ | |
| { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } | |
| }; | |
| bool useNormalCompileMode = true; | |
| for (cm::string_view mode : compileModes) { | |
| auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); | |
| auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); | |
| if (this->GeneratorTarget->GetPropertyAsBool(propName)) { | |
| std::string const& flag = this->Makefile->GetRequiredDefinition(defName); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), flag); | |
| useNormalCompileMode = false; | |
| break; | |
| } | |
| } | |
| if (useNormalCompileMode) { | |
| std::string const& wholeFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), wholeFlag); | |
| } | |
| return cudaCompileMode; | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::GetLinkCommand() const | |
| { | |
| std::string const& linkLanguage = GeneratorTarget->GetLinkerLanguage(Config); | |
| std::string linkCmdVar = | |
| GeneratorTarget->GetCreateRuleVariable(linkLanguage, Config); | |
| std::string res = this->Makefile->GetSafeDefinition(linkCmdVar); | |
| if (res.empty() && | |
| this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| linkCmdVar = linkCmdVar = | |
| cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_CREATE"); | |
| res = this->Makefile->GetSafeDefinition(linkCmdVar); | |
| } | |
| LogMessage("Link rule: " + cmStrCat(linkCmdVar, " = ", res)); | |
| return res; | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddCompilerLaunchersForLanguages() | |
| { | |
| // General rule for all languages. | |
| std::string const launchCompile = this->GetLocalGenerator()->GetRuleLauncher( | |
| this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", Config); | |
| // See if we need to use a compiler launcher like ccache or distcc | |
| for (std::string const& language : Languages) { | |
| std::vector<std::string> expanded = | |
| cmCommonTargetGenerator::GetCompilerLauncher(language, Config); | |
| LogMessage("compilerLauncher: " + cmJoin(expanded, ";")); | |
| // FIXME(#27402): Empty arguments are not supported here. | |
| expanded.erase(std::remove(expanded.begin(), expanded.end(), ""), | |
| expanded.end()); | |
| if (!expanded.empty()) { | |
| std::string const exe = expanded[0]; | |
| expanded.erase(expanded.begin()); | |
| this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe, | |
| language, cmJoin(expanded, " ")); | |
| } else if (!launchCompile.empty()) { | |
| std::string exe; | |
| std::string args; | |
| cmSystemTools::SplitProgramFromArgs(launchCompile, exe, args); | |
| this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe, | |
| language, args); | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddLinkerLauncher() | |
| { | |
| std::vector<std::string> args = | |
| cmCommonTargetGenerator::GetLinkerLauncher(Config); | |
| if (!args.empty()) { | |
| std::string const exe = std::move(args[0]); | |
| args.erase(args.begin()); | |
| this->GetGlobalGenerator()->AddLauncher( | |
| FASTBUILD_LINKER_LAUNCHER_PREFIX, exe, | |
| this->GeneratorTarget->GetLinkerLanguage(Config), cmJoin(args, " ")); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddCMakeLauncher() | |
| { | |
| // Add CMake launcher (might be used for static analysis). | |
| this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, | |
| cmSystemTools::GetCMakeCommand(), | |
| CMAKE_LANGUAGE, ""); | |
| } | |
| void cmFastbuildNormalTargetGenerator::ComputePaths( | |
| FastbuildTarget& target) const | |
| { | |
| std::string const objPath = GetGeneratorTarget()->GetSupportDirectory(); | |
| EnsureDirectoryExists(objPath); | |
| target.Variables["TargetOutDir"] = | |
| cmSystemTools::ConvertToOutputPath(this->ConvertToFastbuildPath(objPath)); | |
| if (GeneratorTarget->GetType() <= cm::TargetType::MODULE_LIBRARY) { | |
| std::string const pdbDir = GeneratorTarget->GetPDBDirectory(Config); | |
| LogMessage("GetPDBDirectory: " + pdbDir); | |
| EnsureDirectoryExists(pdbDir); | |
| std::string const linkerPDB = | |
| cmStrCat(pdbDir, '/', this->GeneratorTarget->GetPDBName(Config)); | |
| if (!linkerPDB.empty()) { | |
| target.Variables["LinkerPDB"] = cmSystemTools::ConvertToOutputPath( | |
| this->ConvertToFastbuildPath(linkerPDB)); | |
| } | |
| } | |
| std::string const compilerPDB = this->ComputeTargetCompilePDB(this->Config); | |
| if (!compilerPDB.empty()) { | |
| LogMessage("ComputeTargetCompilePDB: " + compilerPDB); | |
| std::string compilerPDBArg = cmSystemTools::ConvertToOutputPath( | |
| this->ConvertToFastbuildPath(compilerPDB)); | |
| if (cmHasSuffix(compilerPDB, '/')) { | |
| // The compiler will choose the .pdb file name. | |
| this->EnsureDirectoryExists(compilerPDB); | |
| // ConvertToFastbuildPath dropped the trailing slash. Add it back. | |
| // We do this after ConvertToOutputPath so that we can use a forward | |
| // slash in the case that the argument is quoted. | |
| if (cmHasSuffix(compilerPDBArg, '"')) { | |
| // A quoted trailing backslash requires escaping, e.g., `/Fd"dir\\"`, | |
| // but fbuild does not parse such arguments correctly as of 1.15. | |
| // Always use a forward slash. | |
| compilerPDBArg.insert(compilerPDBArg.size() - 1, 1, '/'); | |
| } else { | |
| // An unquoted trailing slash or backslash is fine. | |
| compilerPDBArg.push_back(kPATH_SLASH); | |
| } | |
| } else { | |
| // We have an explicit .pdb path with file name. | |
| this->EnsureParentDirectoryExists(compilerPDB); | |
| } | |
| target.Variables["CompilerPDB"] = std::move(compilerPDBArg); | |
| } | |
| std::string const impLibFullPath = | |
| GeneratorTarget->GetFullPath(Config, cmStateEnums::ImportLibraryArtifact); | |
| std::string impLibFile = ConvertToFastbuildPath(impLibFullPath); | |
| cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(impLibFullPath)); | |
| if (!impLibFile.empty()) { | |
| cmSystemTools::ConvertToOutputSlashes(impLibFile); | |
| target.Variables["TargetOutputImplib"] = std::move(impLibFile); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::Generate() | |
| { | |
| this->GeneratorTarget->CheckCxxModuleStatus(Config); | |
| FastbuildTarget fastbuildTarget; | |
| auto const addUtilDepToTarget = [&fastbuildTarget](std::string depName) { | |
| FastbuildTargetDep dep{ depName }; | |
| dep.Type = FastbuildTargetDepType::UTIL; | |
| fastbuildTarget.PreBuildDependencies.emplace(std::move(dep)); | |
| }; | |
| fastbuildTarget.Name = GetTargetName(); | |
| fastbuildTarget.BaseName = this->GeneratorTarget->GetName(); | |
| LogMessage("<-------------->"); | |
| LogMessage("Generate target: " + fastbuildTarget.Name); | |
| LogMessage("Config: " + Config); | |
| LogMessage("Deps: "); | |
| for (cmTargetDepend const& dep : TargetDirectDependencies) { | |
| auto const tname = dep->GetName(); | |
| LogMessage(tname); | |
| FastbuildTargetDep targetDep{ tname }; | |
| if (dep->GetType() == cm::TargetType::OBJECT_LIBRARY) { | |
| targetDep.Type = FastbuildTargetDepType::ORDER_ONLY; | |
| } | |
| fastbuildTarget.PreBuildDependencies.emplace(std::move(targetDep)); | |
| } | |
| ComputePaths(fastbuildTarget); | |
| AddCompilerLaunchersForLanguages(); | |
| AddLinkerLauncher(); | |
| AddCMakeLauncher(); | |
| for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_BUILD).Nodes) { | |
| fastbuildTarget.PreBuildExecNodes.PreBuildDependencies.emplace(cc.Name); | |
| addUtilDepToTarget(cc.Name); | |
| this->GetGlobalGenerator()->AddTarget(std::move(cc)); | |
| } | |
| for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_LINK).Nodes) { | |
| cc.PreBuildDependencies.emplace(fastbuildTarget.Name + | |
| FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX); | |
| fastbuildTarget.PreLinkExecNodes.Nodes.emplace_back(std::move(cc)); | |
| } | |
| for (auto& cc : GenerateCommands(FastbuildBuildStep::REST).Nodes) { | |
| fastbuildTarget.ExecNodes.PreBuildDependencies.emplace(cc.Name); | |
| this->GetGlobalGenerator()->AddTarget(std::move(cc)); | |
| } | |
| for (auto& cc : GenerateCommands(FastbuildBuildStep::POST_BUILD).Nodes) { | |
| fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( | |
| cc.Name); | |
| fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(cc)); | |
| } | |
| GenerateObjects(fastbuildTarget); | |
| std::vector<std::string> objectDepends; | |
| AddObjectDependencies(fastbuildTarget, objectDepends); | |
| GenerateDeviceLink(fastbuildTarget); | |
| GenerateLink(fastbuildTarget, objectDepends); | |
| if (fastbuildTarget.LinkerNode.size() > 1) { | |
| if (!this->GeneratorTarget->IsApple()) { | |
| cmSystemTools::Error( | |
| "Can't handle more than 1 arch on non-Apple target"); | |
| return; | |
| } | |
| AddLipoCommand(fastbuildTarget); | |
| } | |
| fastbuildTarget.CopyNodes = std::move(this->CopyNodes); | |
| // Generate symlink commands if real output name differs from "expected". | |
| for (auto& symlink : GetSymlinkExecs()) { | |
| fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( | |
| symlink.Name); | |
| fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(symlink)); | |
| } | |
| { | |
| auto appleTextStubCommand = GetAppleTextStubCommand(); | |
| if (!appleTextStubCommand.Name.empty()) { | |
| fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( | |
| appleTextStubCommand.Name); | |
| fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back( | |
| std::move(appleTextStubCommand)); | |
| } | |
| } | |
| AddPrebuildDeps(fastbuildTarget); | |
| fastbuildTarget.IsGlobal = | |
| GeneratorTarget->GetType() == cm::TargetType::GLOBAL_TARGET; | |
| fastbuildTarget.ExcludeFromAll = | |
| this->GetGlobalGenerator()->IsExcluded(GeneratorTarget); | |
| if (GeneratorTarget->GetPropertyAsBool("DONT_DISTRIBUTE")) { | |
| fastbuildTarget.AllowDistribution = false; | |
| } | |
| GenerateModuleDefinitionInfo(fastbuildTarget); | |
| // Needs to be called after we've added all PRE-LINK steps (like creation of | |
| // .def files on Windows). | |
| AddLinkerNodeDependencies(fastbuildTarget); | |
| // Must be called after "GenerateObjects", since it also adds Prebuild deps | |
| // to it. | |
| // Also after "GenerateModuleDefinitionInfo", since uses PreLinkExecNodes. | |
| fastbuildTarget.GenerateAliases(); | |
| if (!fastbuildTarget.ExecNodes.PreBuildDependencies.empty()) { | |
| fastbuildTarget.DependenciesAlias.PreBuildDependencies.emplace( | |
| fastbuildTarget.ExecNodes.Name); | |
| } | |
| fastbuildTarget.Hidden = false; | |
| fastbuildTarget.BasePath = this->GetMakefile()->GetCurrentSourceDirectory(); | |
| this->GetGlobalGenerator()->AddIDEProject(fastbuildTarget, Config); | |
| AddStampExeIfApplicable(fastbuildTarget); | |
| // size 1 means that it's not a multi-arch lib (which can only be the case on | |
| // Darwin). | |
| if (fastbuildTarget.LinkerNode.size() == 1 && | |
| fastbuildTarget.LinkerNode[0].Type == | |
| FastbuildLinkerNode::STATIC_LIBRARY && | |
| !fastbuildTarget.PostBuildExecNodes.Nodes.empty()) { | |
| ProcessPostBuildForStaticLib(fastbuildTarget); | |
| } | |
| AdditionalCleanFiles(); | |
| if (!fastbuildTarget.DependenciesAlias.PreBuildDependencies.empty()) { | |
| for (FastbuildObjectListNode& objListNode : | |
| fastbuildTarget.ObjectListNodes) { | |
| objListNode.PreBuildDependencies.emplace( | |
| fastbuildTarget.DependenciesAlias.Name); | |
| } | |
| for (auto& linkerNode : fastbuildTarget.LinkerNode) { | |
| linkerNode.PreBuildDependencies.emplace( | |
| fastbuildTarget.DependenciesAlias.Name); | |
| } | |
| } | |
| this->GetGlobalGenerator()->AddTarget(std::move(fastbuildTarget)); | |
| } | |
| void cmFastbuildNormalTargetGenerator::ProcessManifests( | |
| FastbuildLinkerNode& linkerNode) const | |
| { | |
| if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) { | |
| return; | |
| } | |
| std::vector<std::string> const manifests = | |
| this->GetManifestsAsFastbuildPath(); | |
| // Manifests should always be in .Libraries2, so we re-link when needed. | |
| // Tested in RunCMake.BuildDepends | |
| linkerNode.Libraries2.reserve(linkerNode.Libraries2.size() + | |
| manifests.size()); | |
| for (auto const& manifest : manifests) { | |
| linkerNode.Libraries2.emplace_back(manifest); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddStampExeIfApplicable( | |
| FastbuildTarget& fastbuildTarget) const | |
| { | |
| LogMessage("AddStampExeIfApplicable(...)"); | |
| if (fastbuildTarget.LinkerNode.empty() || | |
| (fastbuildTarget.LinkerNode[0].Type != FastbuildLinkerNode::EXECUTABLE && | |
| fastbuildTarget.LinkerNode[0].Type != | |
| FastbuildLinkerNode::SHARED_LIBRARY)) { | |
| return; | |
| } | |
| // File which executes all POST_BUILD steps. | |
| // We use it in .LinkerStampExeArgs in order to run POST_BUILD steps after | |
| // the compilation. | |
| if (!fastbuildTarget.PostBuildExecNodes.Nodes.empty()) { | |
| std::string const AllPostBuildExecsScriptFile = | |
| cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/CMakeFiles/", | |
| fastbuildTarget.Name, | |
| "-all-postbuild-commands" FASTBUILD_SCRIPT_FILE_EXTENSION); | |
| CollapseAllExecsIntoOneScriptfile( | |
| AllPostBuildExecsScriptFile, fastbuildTarget.PostBuildExecNodes.Nodes); | |
| auto& linkerNode = fastbuildTarget.LinkerNode.back(); | |
| // On macOS, a target may have multiple linker nodes (e.g., for different | |
| // architectures). In that case, add the POST_BUILD step to only one node | |
| // to avoid running lipo multiple times. | |
| linkerNode.LinkerStampExe = | |
| cmGlobalFastbuildGenerator::GetExternalShellExecutable(); | |
| linkerNode.LinkerStampExeArgs = FASTBUILD_SCRIPT_FILE_ARG; | |
| linkerNode.LinkerStampExeArgs += | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces( | |
| AllPostBuildExecsScriptFile); | |
| } else { | |
| LogMessage("No POST_BUILD steps for target: " + fastbuildTarget.Name); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::ProcessPostBuildForStaticLib( | |
| FastbuildTarget& fastbuildTarget) const | |
| { | |
| // "Library" nodes do not have "LinkerStampExe" property, so we need to be | |
| // clever here: create an alias that will refer to the binary as well as to | |
| // all post-build steps. Also, make sure that post-build steps depend on the | |
| // binary itself. | |
| LogMessage("ProcessPostBuildForStaticLib(...)"); | |
| FastbuildAliasNode alias; | |
| alias.Name = std::move(fastbuildTarget.LinkerNode[0].Name); | |
| for (FastbuildExecNode& postBuildExec : | |
| fastbuildTarget.PostBuildExecNodes.Nodes) { | |
| postBuildExec.PreBuildDependencies.emplace( | |
| fastbuildTarget.LinkerNode[0].LinkerOutput); | |
| alias.PreBuildDependencies.emplace(postBuildExec.Name); | |
| } | |
| fastbuildTarget.AliasNodes.emplace_back(std::move(alias)); | |
| } | |
| void cmFastbuildNormalTargetGenerator::CollapseAllExecsIntoOneScriptfile( | |
| std::string const& scriptFileName, | |
| std::vector<FastbuildExecNode> const& execs) const | |
| { | |
| cmsys::ofstream scriptFile(scriptFileName.c_str()); | |
| if (!scriptFile.is_open()) { | |
| cmSystemTools::Error("Failed to open: " + scriptFileName); | |
| return; | |
| } | |
| LogMessage("Writing collapsed Execs to " + scriptFileName); | |
| auto const shell = cmGlobalFastbuildGenerator::GetExternalShellExecutable(); | |
| char const* suffix = "\n"; | |
| #if !defined(_WIN32) && !defined(CMAKE_BOOTSTRAP) | |
| cmInstrumentation* instrumentation = | |
| this->GetLocalGenerator()->GetCMakeInstance()->GetInstrumentation(); | |
| if (instrumentation->HasOption( | |
| cmInstrumentationQuery::Option::ProcessMetrics)) { | |
| // Prevent shell exec optimization from passing accumulated child resource | |
| // usage to the final instrumentation wrapper. | |
| suffix = " && :\n"; | |
| } | |
| #endif | |
| for (auto const& exec : execs) { | |
| if (exec.ScriptFile.empty()) { | |
| scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable) | |
| << " " << exec.ExecArguments << suffix; | |
| } else { | |
| #if defined(_WIN32) | |
| scriptFile << "call " | |
| << cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile) | |
| << suffix; | |
| #else | |
| scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " " | |
| << cmSystemTools::ConvertToOutputPath(exec.ScriptFile) | |
| << suffix; | |
| #endif | |
| } | |
| } | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions( | |
| cmSourceFile const& srcFile) | |
| { | |
| cmGeneratorFileSet const* fileSet = | |
| this->GeneratorTarget->GetFileSetForSource(Config, &srcFile); | |
| cmValue const fsSkipCodeCheckVal = | |
| fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; | |
| cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING"); | |
| bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() | |
| ? fsSkipCodeCheckVal.IsOn() | |
| : (srcSkipCodeCheckVal.IsSet() | |
| ? srcSkipCodeCheckVal.IsOn() | |
| : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); | |
| if (skipCodeCheck) { | |
| return {}; | |
| } | |
| std::vector<std::string> compilerLauncher; | |
| std::string staticCheckRule = this->GenerateCodeCheckRules( | |
| srcFile, compilerLauncher, "", Config, nullptr); | |
| LogMessage(cmStrCat("CodeCheck: ", staticCheckRule)); | |
| return staticCheckRule; | |
| } | |
| void cmFastbuildNormalTargetGenerator::ComputeCompilerAndOptions( | |
| std::string const& compilerOptions, std::string const& staticCheckOptions, | |
| std::string const& language, FastbuildObjectListNode& outObjectList) | |
| { | |
| auto& compilers = this->GetGlobalGenerator()->Compilers; | |
| auto const compilerIter = | |
| compilers.find(FASTBUILD_COMPILER_PREFIX + language); | |
| auto const launcherIter = | |
| compilers.find(FASTBUILD_LAUNCHER_PREFIX + language); | |
| if (!staticCheckOptions.empty()) { | |
| // If we want to run static checks - use CMake as a launcher. | |
| // Tested in "RunCMake.ClangTidy", "RunCMake.IncludeWhatYouUse", | |
| // "RunCMake.Cpplint", "RunCMake.Cppcheck", "RunCMake.MultiLint" tests. | |
| outObjectList.Compiler = "." FASTBUILD_LAUNCHER_PREFIX + CMAKE_LANGUAGE; | |
| outObjectList.CompilerOptions = staticCheckOptions; | |
| // Add compile command which will be passed to the static analyzer via | |
| // dash-dash. | |
| if (compilerIter != compilers.end()) { | |
| // Wrap in quotes to account for potential spaces in the path. | |
| outObjectList.CompilerOptions += | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces( | |
| compilerIter->second.Executable); | |
| outObjectList.CompilerOptions += compilerOptions; | |
| } | |
| } else if (launcherIter != compilers.end()) { | |
| // Tested in "RunCMake.CompilerLauncher" test. | |
| outObjectList.Compiler = "." + launcherIter->first; | |
| outObjectList.CompilerOptions = launcherIter->second.Args; | |
| auto vars = cmFastbuildNormalTargetGenerator::ComputeRuleVariables(); | |
| vars.Language = language.c_str(); | |
| std::string const targetSupportPath = this->ConvertToFastbuildPath( | |
| this->GetGeneratorTarget()->GetCMFSupportDirectory()); | |
| vars.TargetSupportDir = targetSupportPath.c_str(); | |
| RulePlaceholderExpander->ExpandRuleVariables( | |
| LocalCommonGenerator, outObjectList.CompilerOptions, vars); | |
| // Add compiler executable explicitly to the compile options. | |
| if (compilerIter != compilers.end()) { | |
| outObjectList.CompilerOptions += " "; | |
| // Wrap in quotes to account for potential spaces in the path. | |
| outObjectList.CompilerOptions += | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces( | |
| compilerIter->second.Executable); | |
| outObjectList.CompilerOptions += compilerOptions; | |
| } | |
| } else if (compilerIter != compilers.end()) { | |
| outObjectList.Compiler = "." + compilerIter->first; | |
| outObjectList.CompilerOptions = compilerOptions; | |
| } | |
| LogMessage(cmStrCat(".Compiler = ", outObjectList.Compiler)); | |
| LogMessage(cmStrCat(".CompilerOptions = ", outObjectList.CompilerOptions)); | |
| } | |
| cmRulePlaceholderExpander::RuleVariables | |
| cmFastbuildNormalTargetGenerator::ComputeRuleVariables() const | |
| { | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars; | |
| compileObjectVars.CMTargetName = GeneratorTarget->GetName().c_str(); | |
| compileObjectVars.CMTargetType = | |
| cmState::GetTargetTypeName(GeneratorTarget->GetType()).c_str(); | |
| compileObjectVars.Source = FASTBUILD_1_INPUT_PLACEHOLDER; | |
| compileObjectVars.Object = FASTBUILD_2_INPUT_PLACEHOLDER; | |
| compileObjectVars.ObjectDir = | |
| FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG; | |
| compileObjectVars.ObjectFileDir = ""; | |
| compileObjectVars.Flags = ""; | |
| compileObjectVars.Includes = ""; | |
| compileObjectVars.Defines = ""; | |
| compileObjectVars.Includes = ""; | |
| compileObjectVars.TargetCompilePDB = | |
| FASTBUILD_DOLLAR_TAG "CompilerPDB" FASTBUILD_DOLLAR_TAG; | |
| compileObjectVars.Config = Config.c_str(); | |
| return compileObjectVars; | |
| } | |
| std::vector<std::string> cmFastbuildNormalTargetGenerator::GetSourceProperty( | |
| cmSourceFile const& srcFile, std::string const& prop) const | |
| { | |
| std::vector<std::string> res; | |
| if (cmValue val = srcFile.GetProperty(prop)) { | |
| cmExpandList(*val, res); | |
| return GetGlobalGenerator()->ConvertToFastbuildPath(res); | |
| } | |
| return res; | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendExtraResources( | |
| std::set<std::string>& deps) const | |
| { | |
| // Generate Fastbuild's "Copy" commands to copy resources. | |
| auto const generateCopyCommands = | |
| [this](std::vector<cmSourceFile const*>& frameworkDeps) { | |
| this->OSXBundleGenerator->GenerateMacOSXContentStatements( | |
| frameworkDeps, this->MacOSXContentGenerator.get(), Config); | |
| }; | |
| std::vector<cmSourceFile const*> headerSources; | |
| this->GeneratorTarget->GetHeaderSources(headerSources, Config); | |
| generateCopyCommands(headerSources); | |
| std::vector<cmSourceFile const*> extraSources; | |
| this->GeneratorTarget->GetExtraSources(extraSources, Config); | |
| generateCopyCommands(extraSources); | |
| std::vector<cmSourceFile const*> externalObjects; | |
| this->GeneratorTarget->GetExternalObjects(externalObjects, Config); | |
| generateCopyCommands(externalObjects); | |
| for (FastbuildCopyNode const& node : this->CopyNodes) { | |
| LogMessage("Adding resource: " + node.Name); | |
| deps.emplace(node.Name); | |
| } | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::GetCompileOptions( | |
| cmSourceFile const& srcFile, std::string const& arch) | |
| { | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(&srcFile, Config); | |
| cmRulePlaceholderExpander::RuleVariables compileObjectVars = | |
| ComputeRuleVariables(); | |
| std::string const compilerFlags = DetectCompilerFlags(srcFile, arch); | |
| std::string const compilerDefines = ComputeDefines(srcFile); | |
| compileObjectVars.Flags = compilerFlags.c_str(); | |
| compileObjectVars.Defines = compilerDefines.c_str(); | |
| compileObjectVars.Language = language.c_str(); | |
| if (language == "CUDA") { | |
| compileObjectVars.CudaCompileMode = this->CudaCompileMode.c_str(); | |
| } | |
| std::string rule = CompileObjectCmakeRules.at(language); | |
| RulePlaceholderExpander->ExpandRuleVariables(LocalCommonGenerator, rule, | |
| compileObjectVars); | |
| std::string compilerExecutable; | |
| // Remove the compiler from .CompilerOptions, since it would be set as | |
| // .Compiler in Fastbuild. | |
| // See https://www.fastbuild.org/docs/functions/objectlist.html for a | |
| // reference. | |
| std::string options; | |
| if (!cmSystemTools::SplitProgramFromArgs(rule, compilerExecutable, | |
| options)) { | |
| cmSystemTools::Error(cmStrCat("Failed to split compiler options: ", rule)); | |
| } | |
| LogMessage("Expanded compile options = " + options); | |
| LogMessage("Compiler executable = " + compilerExecutable); | |
| return options; | |
| } | |
| std::vector<std::string> cmFastbuildNormalTargetGenerator::GetArches() const | |
| { | |
| auto arches = this->GetGeneratorTarget()->GetAppleArchs(Config, {}); | |
| // Don't add any arch-specific logic if arch is only one. | |
| if (arches.empty() || arches.size() == 1) { | |
| arches.clear(); | |
| arches.emplace_back(); | |
| } | |
| return arches; | |
| } | |
| void cmFastbuildNormalTargetGenerator::GetDeviceLinkLinkerAndArgs( | |
| std::string const& language, std::string& linker, std::string& args) const | |
| { | |
| this->GeneratorTarget->GetLinkInformation(this->Config); | |
| cmGeneratorTarget::DeviceLinkSetter deviceLink(*this->GeneratorTarget); | |
| std::string linkCmd = this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_DEVICE_LINK_LIBRARY")); | |
| auto vars = ComputeRuleVariables(); | |
| vars.Language = language.c_str(); | |
| // Libraries2 tracks archive changes; the computed link line already contains | |
| // these archives in driver-required order, so expand only local objects | |
| // here. | |
| vars.Objects = FASTBUILD_1_0_INPUT_PLACEHOLDER; | |
| vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER; | |
| std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( | |
| new cmFastbuildLinkLineDeviceComputer( | |
| this->LocalGenerator, | |
| this->LocalGenerator->GetStateSnapshot().GetDirectory(), | |
| this->GetGlobalGenerator(), language)); | |
| std::string linkLibs; | |
| std::string targetFlags; | |
| std::string linkFlags; | |
| std::string frameworkPath; | |
| std::string linkPath; | |
| // GetTargetFlags also collects host link flags and libraries, which are not | |
| // part of the device link. | |
| std::string dummyLinkLibs; | |
| std::string dummyLinkFlags; | |
| this->LocalCommonGenerator->GetDeviceLinkFlags( | |
| *linkLineComputer, Config, linkLibs, linkFlags, frameworkPath, linkPath, | |
| this->GeneratorTarget); | |
| this->LocalCommonGenerator->GetTargetFlags( | |
| linkLineComputer.get(), Config, dummyLinkLibs, targetFlags, dummyLinkFlags, | |
| frameworkPath, linkPath, this->GeneratorTarget); | |
| vars.LanguageCompileFlags = ""; | |
| vars.LinkFlags = linkFlags.c_str(); | |
| vars.LinkLibraries = linkLibs.c_str(); | |
| vars.LanguageCompileFlags = targetFlags.c_str(); | |
| this->RulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | |
| linkCmd, vars); | |
| SplitLinkerFromArgs(linkCmd, linker, args); | |
| } | |
| void cmFastbuildNormalTargetGenerator::GenerateDeviceLink( | |
| FastbuildTarget& target) const | |
| { | |
| auto const arches = this->GetArches(); | |
| std::string const language = deviceLinkLanguage( | |
| *this->GeneratorTarget, *this->GetLocalGenerator(), Config); | |
| if (language.empty()) { | |
| return; | |
| } | |
| LogMessage("GenerateDeviceLink(...)"); | |
| for (auto const& arch : arches) { | |
| std::string linker; | |
| std::string args; | |
| GetDeviceLinkLinkerAndArgs(language, linker, args); | |
| FastbuildLinkerNode deviceLinkNode; | |
| deviceLinkNode.Name = cmStrCat(target.Name, "_device_link"); | |
| deviceLinkNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; | |
| deviceLinkNode.Linker = std::move(linker); | |
| deviceLinkNode.LinkerOptions = std::move(args); | |
| // Output | |
| deviceLinkNode.LinkerOutput = this->ConvertToFastbuildPath(cmStrCat( | |
| FASTBUILD_DOLLAR_TAG "TargetOutDi" | |
| "r" FASTBUILD_DOLLAR_TAG "/cmake_device_link", | |
| (args.empty() ? "" : "_" + arch), | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION")))); | |
| // Input | |
| for (auto const& objList : target.ObjectListNodes) { | |
| deviceLinkNode.LibrarianAdditionalInputs.push_back(objList.Name); | |
| } | |
| deviceLinkNode.PreBuildDependencies.emplace( | |
| target.Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX); | |
| if (auto const* linkInfo = | |
| this->GeneratorTarget->GetLinkInformation(this->Config)) { | |
| for (auto const& dependency : linkInfo->GetDepends()) { | |
| // ObjectList owns generated object nodes. Adding their paths here | |
| // would create File nodes before FASTBuild discovers the objects. | |
| if (cmHasLiteralSuffix(dependency, ".o") || | |
| cmHasLiteralSuffix(dependency, ".obj")) { | |
| continue; | |
| } | |
| deviceLinkNode.Libraries2.push_back( | |
| this->ConvertToFastbuildPath(dependency)); | |
| } | |
| } | |
| this->GetGlobalGenerator()->AddFileToClean( | |
| cmStrCat(this->GeneratorTarget->GetObjectDirectory(this->Config), | |
| "cmake_device_link", (args.empty() ? "" : "_" + arch), | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION")))); | |
| target.DeviceLinkNodes.emplace_back(std::move(deviceLinkNode)); | |
| } | |
| LogMessage("GenerateDeviceLink end"); | |
| } | |
| void cmFastbuildNormalTargetGenerator::GenerateObjects(FastbuildTarget& target) | |
| { | |
| this->GetGlobalGenerator()->AllFoldersToClean.insert(ObjectOutDir); | |
| std::map<std::string, FastbuildObjectListNode> nodesPermutations; | |
| cmCryptoHash hash(cmCryptoHash::AlgoSHA256); | |
| std::vector<cmSourceFile const*> objectSources; | |
| GeneratorTarget->GetObjectSources(objectSources, Config); | |
| std::set<std::string> createdPCH; | |
| // Directory level. | |
| bool useUnity = | |
| GeneratorTarget->GetLocalGenerator()->GetMakefile()->IsDefinitionSet( | |
| CMAKE_UNITY_BUILD); | |
| // Check if explicitly disabled for this target. | |
| auto const targetProp = GeneratorTarget->GetProperty(UNITY_BUILD); | |
| if (targetProp.IsSet() && targetProp.IsOff()) { | |
| useUnity = false; | |
| } | |
| // List of sources excluded from the unity build if enabled. | |
| std::set<std::string> excludedFromUnity; | |
| // Mapping from unity group (if any) to sources belonging to that group. | |
| std::map<std::string, std::vector<std::string>> sourcesWithGroups; | |
| for (cmSourceFile const* source : objectSources) { | |
| cmSourceFile const& srcFile = *source; | |
| std::string const pathToFile = srcFile.GetFullPath(); | |
| std::string const language = | |
| this->GeneratorTarget->GetSourceFileLanguage(&srcFile, Config); | |
| cmGeneratorFileSet const* const fileSet = | |
| GeneratorTarget->GetFileSetForSource(Config, source); | |
| bool fileUsesUnity = useUnity; | |
| if (useUnity) { | |
| // Check if the source should be added to "UnityInputExcludedFiles". | |
| if (IsExcludedFromUnity(GeneratorTarget, fileSet, srcFile) || | |
| language != srcFile.GetLanguage()) { | |
| fileUsesUnity = false; | |
| excludedFromUnity.emplace(pathToFile); | |
| } | |
| std::string const perFileUnityGroup = | |
| srcFile.GetSafeProperty(UNITY_GROUP); | |
| if (!perFileUnityGroup.empty()) { | |
| sourcesWithGroups[perFileUnityGroup].emplace_back(pathToFile); | |
| } | |
| } | |
| this->GetGlobalGenerator()->AddFileToClean(cmStrCat( | |
| ObjectOutDir, '/', this->GeneratorTarget->GetObjectName(source))); | |
| // Do not generate separate node for PCH source file. | |
| if (this->GeneratorTarget->GetPchSource(Config, language) == pathToFile) { | |
| continue; | |
| } | |
| LogMessage( | |
| cmStrCat("Source file: ", this->ConvertToFastbuildPath(pathToFile))); | |
| LogMessage("Language: " + language); | |
| std::string const staticCheckOptions = ComputeCodeCheckOptions(srcFile); | |
| auto const isDisabled = [this](char const* prop) { | |
| auto const propValue = this->GeneratorTarget->GetProperty(prop); | |
| return propValue && propValue.IsOff(); | |
| }; | |
| bool const disableCaching = isDisabled("FASTBUILD_CACHING"); | |
| bool const disableDistribution = isDisabled("FASTBUILD_DISTRIBUTION"); | |
| for (auto const& arch : this->GetArches()) { | |
| std::string const compileOptions = GetCompileOptions(srcFile, arch); | |
| std::string objOutDirWithPossibleSubdir = ObjectOutDir; | |
| // If object should be placed in some subdir in the output | |
| // path. Tested in "SourceGroups" test. | |
| // Not necessary for files in unity buckets because they are | |
| // built into a single unity object file. Executing this logic | |
| // for unity bucketed files prevents buckets from containing | |
| // source files in different subdirectories. | |
| if (!fileUsesUnity) { | |
| auto const subdir = cmSystemTools::GetFilenamePath( | |
| this->GeneratorTarget->GetObjectName(source)); | |
| if (!subdir.empty()) { | |
| objOutDirWithPossibleSubdir += "/"; | |
| objOutDirWithPossibleSubdir += subdir; | |
| } | |
| } | |
| std::string const objectListHash = hash.HashString(cmStrCat( | |
| compileOptions, staticCheckOptions, objOutDirWithPossibleSubdir, | |
| // If file does not need PCH - it must be in another ObjectList. | |
| (fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"), | |
| language)); | |
| LogMessage("ObjectList Hash: " + objectListHash); | |
| FastbuildObjectListNode& objectListNode = | |
| nodesPermutations[objectListHash]; | |
| // Absolute path needed in "RunCMake.SymlinkTrees" test. | |
| objectListNode.CompilerInputFiles.push_back(pathToFile); | |
| std::vector<std::string> const outputs = | |
| GetSourceProperty(srcFile, "OBJECT_OUTPUTS"); | |
| objectListNode.ObjectOutputs.insert(outputs.begin(), outputs.end()); | |
| std::vector<std::string> const depends = | |
| GetSourceProperty(srcFile, "OBJECT_DEPENDS"); | |
| objectListNode.ObjectDepends.insert(depends.begin(), depends.end()); | |
| // We have already computed properties that are computed below. | |
| // (.CompilerOptions, .PCH*, etc.). Short circuit this iteration. | |
| if (!objectListNode.CompilerOptions.empty()) { | |
| continue; | |
| } | |
| if (disableCaching) { | |
| objectListNode.AllowCaching = false; | |
| } | |
| if (disableDistribution) { | |
| objectListNode.AllowDistribution = false; | |
| } | |
| objectListNode.CompilerOutputPath = objOutDirWithPossibleSubdir; | |
| LogMessage(cmStrCat("Output path: ", objectListNode.CompilerOutputPath)); | |
| ComputeCompilerAndOptions(compileOptions, staticCheckOptions, language, | |
| objectListNode); | |
| ComputePCH(*source, objectListNode, createdPCH); | |
| objectListNode.Name = cmStrCat(this->GetName(), '_', language, "_Objs"); | |
| // TODO: Ask cmake the output objects and group by extension instead | |
| // of doing this | |
| if (language == "RC") { | |
| objectListNode.CompilerOutputExtension = ".res"; | |
| } else { | |
| if (!arch.empty()) { | |
| objectListNode.CompilerOutputExtension = cmStrCat('.', arch); | |
| objectListNode.arch = arch; | |
| } | |
| char const* customExt = | |
| this->GeneratorTarget->GetCustomObjectExtension(); | |
| objectListNode.CompilerOutputExtension += | |
| this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION")); | |
| // Tested in "CudaOnly.ExportPTX" test. | |
| if (customExt) { | |
| objectListNode.CompilerOutputExtension += customExt; | |
| } | |
| } | |
| } | |
| } | |
| int groupNameCount = 0; | |
| for (auto& val : nodesPermutations) { | |
| auto& objectListNode = val.second; | |
| objectListNode.Name = cmStrCat(objectListNode.Name, '_', ++groupNameCount); | |
| LogMessage(cmStrCat("ObjectList name: ", objectListNode.Name)); | |
| } | |
| std::vector<FastbuildObjectListNode>& objects = target.ObjectListNodes; | |
| objects.reserve(nodesPermutations.size()); | |
| for (auto& val : nodesPermutations) { | |
| auto& node = val.second; | |
| if (!node.PCHInputFile.empty()) { | |
| // Node that produces PCH should be the first one, since other nodes | |
| // might reuse this PCH. | |
| // Note: we might have several such nodes for different languages. | |
| objects.insert(objects.begin(), std::move(node)); | |
| } else { | |
| objects.emplace_back(std::move(node)); | |
| } | |
| } | |
| if (useUnity) { | |
| target.UnityNodes = | |
| GenerateUnity(objects, excludedFromUnity, sourcesWithGroups); | |
| } | |
| } | |
| FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity( | |
| std::set<std::string> const& excludedFiles, std::vector<std::string>& files, | |
| int unitySize) const | |
| { | |
| FastbuildUnityNode result; | |
| for (auto iter = files.begin(); iter != files.end();) { | |
| std::string pathToFile = std::move(*iter); | |
| iter = files.erase(iter); | |
| // This source must be excluded from the generated unity file. | |
| if (excludedFiles.find(pathToFile) != excludedFiles.end()) { | |
| result.UnityInputFiles.emplace_back(pathToFile); | |
| result.UnityInputExcludedFiles.emplace_back(std::move(pathToFile)); | |
| } else { | |
| result.UnityInputFiles.emplace_back(std::move(pathToFile)); | |
| } | |
| if (int(result.UnityInputFiles.size() - | |
| result.UnityInputExcludedFiles.size()) == unitySize) { | |
| break; | |
| } | |
| } | |
| return result; | |
| } | |
| int cmFastbuildNormalTargetGenerator::GetUnityBatchSize() const | |
| { | |
| int unitySize = 8; | |
| try { | |
| auto const perTargetSize = | |
| GeneratorTarget->GetSafeProperty(UNITY_BUILD_BATCH_SIZE); | |
| if (!perTargetSize.empty()) { | |
| unitySize = std::stoi(perTargetSize); | |
| } | |
| // Per-directory level. | |
| else { | |
| unitySize = std::stoi( | |
| GeneratorTarget->GetLocalGenerator()->GetMakefile()->GetDefinition( | |
| CMAKE_UNITY_BUILD_BATCH_SIZE)); | |
| } | |
| } catch (...) { | |
| return unitySize; | |
| } | |
| return unitySize; | |
| } | |
| std::vector<FastbuildUnityNode> | |
| cmFastbuildNormalTargetGenerator::GenerateUnity( | |
| std::vector<FastbuildObjectListNode>& objects, | |
| std::set<std::string> const& excludedSources, | |
| std::map<std::string, std::vector<std::string>> const& sourcesWithGroups) | |
| { | |
| int const unitySize = GetUnityBatchSize(); | |
| // Unity of size less than 2 doesn't make sense. | |
| if (unitySize < 2) { | |
| return {}; | |
| } | |
| int unityNumber = 0; | |
| int unityGroupNumber = 0; | |
| std::vector<FastbuildUnityNode> result; | |
| for (FastbuildObjectListNode& obj : objects) { | |
| // Don't use unity for only 1 file. | |
| if (obj.CompilerInputFiles.size() < 2) { | |
| continue; | |
| } | |
| std::string const ext = | |
| cmSystemTools::GetFilenameExtension(obj.CompilerInputFiles[0]); | |
| // Process groups. | |
| auto groupedNode = GenerateGroupedUnityNode( | |
| obj.CompilerInputFiles, sourcesWithGroups, unityGroupNumber); | |
| // We have at least 2 sources in the group. | |
| if (groupedNode.UnityInputFiles.size() > 1) { | |
| groupedNode.UnityOutputPath = obj.CompilerOutputPath; | |
| obj.CompilerInputUnity.emplace_back(groupedNode.Name); | |
| groupedNode.UnityOutputPattern = cmStrCat(groupedNode.Name, ext); | |
| result.emplace_back(std::move(groupedNode)); | |
| } | |
| // General unity batching of the remaining (non-grouped) sources. | |
| while (!obj.CompilerInputFiles.empty()) { | |
| FastbuildUnityNode node = | |
| GetOneUnity(excludedSources, obj.CompilerInputFiles, unitySize); | |
| node.Name = cmStrCat(this->GetName(), "_Unity_", ++unityNumber); | |
| node.UnityOutputPath = obj.CompilerOutputPath; | |
| node.UnityOutputPattern = cmStrCat(node.Name, ext); | |
| // Unity group of size 1 doesn't make sense - just exclude the source. | |
| if (groupedNode.UnityInputFiles.size() == 1) { | |
| node.UnityInputExcludedFiles.emplace_back( | |
| groupedNode.UnityInputFiles[0]); | |
| node.UnityInputFiles.emplace_back( | |
| std::move(groupedNode.UnityInputFiles[0])); | |
| // Clear so we don't enter here on the next iteration. | |
| groupedNode.UnityInputFiles.clear(); | |
| } | |
| // We've got only 1 file left. No need to create a Unity node for it, | |
| // just return it back to the ObjectList and exit. | |
| if (node.UnityInputFiles.size() == 1) { | |
| obj.CompilerInputFiles.emplace_back( | |
| std::move(node.UnityInputFiles[0])); | |
| break; | |
| } | |
| obj.CompilerInputUnity.emplace_back(node.Name); | |
| result.emplace_back(std::move(node)); | |
| } | |
| } | |
| return result; | |
| } | |
| FastbuildUnityNode cmFastbuildNormalTargetGenerator::GenerateGroupedUnityNode( | |
| std::vector<std::string>& inputFiles, | |
| std::map<std::string, std::vector<std::string>> const& sourcesWithGroups, | |
| int& groupId) | |
| { | |
| std::vector<FastbuildUnityNode> result; | |
| for (auto const& item : sourcesWithGroups) { | |
| auto const& group = item.first; | |
| auto const& sources = item.second; | |
| FastbuildUnityNode node; | |
| // Check if any of the sources belong to this group. | |
| for (auto const& source : sources) { | |
| auto const iter = | |
| std::find(inputFiles.begin(), inputFiles.end(), source); | |
| if (iter == inputFiles.end()) { | |
| continue; | |
| } | |
| node.Name = | |
| cmStrCat(this->GetName(), "_Unity_Group_", group, '_', ++groupId); | |
| node.UnityInputFiles.emplace_back(source); | |
| // Remove from the general batching. | |
| inputFiles.erase( | |
| std::remove(inputFiles.begin(), inputFiles.end(), source), | |
| inputFiles.end()); | |
| } | |
| if (!node.UnityInputFiles.empty()) { | |
| // The unity group belongs to the ObjectLists that we're processing. | |
| // We've grouped all the sources we could from the current ObjectList. | |
| return node; | |
| } | |
| } | |
| return {}; | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::ResolveIfAlias( | |
| std::string const& targetName) const | |
| { | |
| LogMessage("targetName: " + targetName); | |
| std::map<std::string, std::string> const aliases = | |
| this->Makefile->GetAliasTargets(); | |
| auto const iter = aliases.find(targetName); | |
| if (iter != aliases.end()) { | |
| LogMessage("Non alias name: " + iter->second); | |
| return iter->second; | |
| } | |
| return targetName; | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendExternalObject( | |
| FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedDeps) const | |
| { | |
| // Different aspects of this logic exercised in "ObjectLibrary" and | |
| // "ExportImport" test. When making changes here - verify that both of those | |
| // tests are still passing. | |
| LogMessage("AppendExternalObject(...)"); | |
| std::vector<cmSourceFile const*> extObjects; | |
| this->GeneratorTarget->GetExternalObjects(extObjects, Config); | |
| for (cmSourceFile const* src : extObjects) { | |
| std::string const pathToObj = | |
| this->ConvertToFastbuildPath(src->GetFullPath()); | |
| LogMessage("EXT OBJ: " + pathToObj); | |
| std::string const objLibName = ResolveIfAlias(src->GetObjectLibrary()); | |
| LogMessage("GetObjectLibrary: " + objLibName); | |
| // Tested in "ExternalOBJ" test. | |
| cmTarget const* target = | |
| this->GlobalCommonGenerator->FindTarget(objLibName); | |
| if (objLibName.empty()) { | |
| linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj); | |
| } | |
| // We know how to generate this target and haven't added this dependency | |
| // yet. | |
| else if (target) { | |
| if (!linkedDeps.emplace(objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX) | |
| .second) { | |
| LogMessage(cmStrCat("Object Target: ", objLibName, | |
| FASTBUILD_OBJECTS_ALIAS_POSTFIX | |
| " already linked")); | |
| continue; | |
| } | |
| linkerNode.LibrarianAdditionalInputs.emplace_back( | |
| objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX); | |
| } else if (linkedDeps.emplace(pathToObj).second) { | |
| LogMessage("Adding obj dep : " + pathToObj); | |
| linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj); | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendExeToLink( | |
| FastbuildLinkerNode& linkerNode, | |
| cmComputeLinkInformation::Item const& item) const | |
| { | |
| std::string const decorated = | |
| item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value)) | |
| .Value; | |
| LogMessage("Linking to executable : " + decorated); | |
| // Tested in "InterfaceLinkLibrariesDirect" and "Plugin" test. | |
| linkerNode.LinkerOptions += | |
| (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(decorated)); | |
| } | |
| std::string cmFastbuildNormalTargetGenerator::GetImportedLoc( | |
| cmComputeLinkInformation::Item const& item) const | |
| { | |
| // Link to import library when possible. | |
| // Tested in "StagingPrefix" test on Windows/MSVC. | |
| cmStateEnums::ArtifactType const artifact = | |
| item.Target->HasImportLibrary(Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| std::string importedLoc = this->ConvertToFastbuildPath( | |
| item.Target->GetFullPath(Config, artifact, true)); | |
| LogMessage("ImportedGetLocation: " + importedLoc); | |
| return importedLoc; | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendTargetDep( | |
| FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects, | |
| cmComputeLinkInformation::Item const& item) const | |
| { | |
| LogMessage("AppendTargetDep(...)"); | |
| cm::TargetType const depType = item.Target->GetType(); | |
| LogMessage("Link dep type: " + std::to_string(static_cast<int>(depType))); | |
| LogMessage("Target name: " + item.Target->GetName()); | |
| auto const resolvedTargetName = ResolveIfAlias(item.Target->GetName()); | |
| LogMessage("Resolved: " + resolvedTargetName); | |
| if (depType == cm::TargetType::INTERFACE_LIBRARY) { | |
| return; | |
| } | |
| std::string const feature = item.GetFeatureName(); | |
| if (item.Target->IsImported()) { | |
| if (feature == "FRAMEWORK") { | |
| // Use just framework's name. The exact path where to look for the | |
| // framework will be provided from "frameworkPath" in | |
| // "cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand(...)". | |
| // Tested in "RunCMake.Framework - ImportedFrameworkConsumption". | |
| std::string const decorated = | |
| item.GetFormattedItem(item.Value.Value).Value; | |
| LogMessage( | |
| cmStrCat("Adding framework dep <", decorated, "> to command line")); | |
| linkerNode.LinkerOptions += (" " + decorated); | |
| return; | |
| } | |
| if (depType == cm::TargetType::UNKNOWN_LIBRARY) { | |
| LogMessage("Unknown library -- adding to LibrarianAdditionalInputs or " | |
| "Libraries2"); | |
| if (UsingCommandLine) { | |
| AppendCommandLineDep(linkerNode, item); | |
| } else { | |
| AppendLinkDep(linkerNode, GetImportedLoc(item)); | |
| } | |
| return; | |
| } | |
| // Tested in "ExportImport" test. | |
| if (depType == cm::TargetType::EXECUTABLE) { | |
| AppendExeToLink(linkerNode, item); | |
| return; | |
| } | |
| // Skip exported objects. | |
| // Tested in "ExportImport" test. | |
| if (depType == cm::TargetType::OBJECT_LIBRARY) { | |
| LogMessage(cmStrCat("target : ", item.Target->GetName(), | |
| " already linked... Skipping")); | |
| return; | |
| } | |
| if (UsingCommandLine || feature != "DEFAULT") { | |
| AppendCommandLineDep(linkerNode, item); | |
| return; | |
| } | |
| // Tested in "ExportImport" test. | |
| cmList const list{ GetImportedLoc(item) }; | |
| for (std::string const& linkDep : list) { | |
| AppendLinkDep(linkerNode, linkDep); | |
| } | |
| } else { | |
| if (depType == cm::TargetType::SHARED_LIBRARY && | |
| this->GeneratorTarget->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED")) { | |
| // It moves the dep outside of FASTBuild control, so the binary won't | |
| // be re-built if the shared lib has changed. | |
| // Tested in "BuildDepends" test. | |
| LogMessage( | |
| cmStrCat("LINK_DEPENDS_NO_SHARED is set on the target, adding dep", | |
| item.Value.Value, " as is")); | |
| linkerNode.LinkerOptions += | |
| (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(item.Value.Value)); | |
| return; | |
| } | |
| // Just add path to binary artifact to command line (except for OBJECT | |
| // libraries which we will link directly). | |
| if (UsingCommandLine && depType != cm::TargetType::OBJECT_LIBRARY) { | |
| // Take transitively linked objects into account, | |
| // so we don't link them again. | |
| AppendTransitivelyLinkedObjects(*item.Target, linkedObjects); | |
| AppendCommandLineDep(linkerNode, item); | |
| return; | |
| } | |
| // This dep has a special way of linking to it (e.g. | |
| // "CMAKE_LINK_LIBRARY_USING_<FEATURE>"). | |
| bool const isFeature = !feature.empty() && feature != "DEFAULT"; | |
| if (isFeature) { | |
| std::string const decorated = | |
| item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value)) | |
| .Value; | |
| LogMessage("Prepending with feature: " + decorated); | |
| linkerNode.LinkerOptions += (" " + decorated); | |
| } | |
| std::string dep = resolvedTargetName + | |
| (depType == cm::TargetType::OBJECT_LIBRARY | |
| ? FASTBUILD_OBJECTS_ALIAS_POSTFIX | |
| : FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX); | |
| if (!linkerNode.Arch.empty()) { | |
| dep = cmStrCat(std::move(dep), '-', linkerNode.Arch); | |
| } | |
| // If we have a special way of linking the dep, we can't have it in | |
| // ".Libraries" (since there might be multiple such deps, but | |
| // FASTBuild expands ".Libraries" as a continuous array, so we can't | |
| // inject any properties in between). Tested in | |
| // "RunCMake.target_link_libraries-LINK_LIBRARY" test. | |
| if (isFeature) { | |
| LogMessage(cmStrCat("AppendTargetDep: ", dep, " as prebuild")); | |
| linkerNode.PreBuildDependencies.emplace(dep); | |
| return; | |
| } | |
| if (depType != cm::TargetType::OBJECT_LIBRARY || | |
| linkedObjects.emplace(dep).second) { | |
| AppendLinkDep(linkerNode, dep); | |
| } | |
| AppendTransitivelyLinkedObjects(*item.Target, linkedObjects); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendPrebuildDeps( | |
| FastbuildLinkerNode& linkerNode, | |
| cmComputeLinkInformation::Item const& item) const | |
| { | |
| if (!item.Target->IsImported()) { | |
| return; | |
| } | |
| // In "RunCMake.FileAPI" imported object library "imported_object_lib" is | |
| // added w/o import location... | |
| if (item.Target->GetType() == cm::TargetType::OBJECT_LIBRARY) { | |
| return; | |
| } | |
| cmList const list{ GetImportedLoc(item) }; | |
| for (std::string const& linkDep : list) { | |
| // In case we know how to generate this file (needed for proper | |
| // sorting by deps). Tested in "RunCMake.target_link_libraries-ALIAS" | |
| // test. | |
| auto fastbuildTarget = | |
| this->GetGlobalGenerator()->GetTargetByOutputName(linkDep); | |
| std::string fastbuildTargetName; | |
| if (fastbuildTarget) { | |
| fastbuildTargetName = std::move(fastbuildTarget->Name); | |
| } | |
| if (!fastbuildTargetName.empty()) { | |
| LogMessage("Adding dep to " + fastbuildTargetName); | |
| linkerNode.PreBuildDependencies.insert(std::move(fastbuildTargetName)); | |
| } else { | |
| if (!cmIsNOTFOUND(linkDep)) { | |
| LogMessage(cmStrCat("Adding dep ", linkDep, " for sorting")); | |
| linkerNode.PreBuildDependencies.insert(linkDep); | |
| } | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendTransitivelyLinkedObjects( | |
| cmGeneratorTarget const& target, std::set<std::string>& linkedObjects) const | |
| { | |
| std::vector<std::string> objs; | |
| // Consider that all those object are now linked as well. | |
| // Tested in "ExportImport" test. | |
| target.GetTargetObjectNames(Config, objs); | |
| for (std::string const& obj : objs) { | |
| std::string const pathToObj = this->ConvertToFastbuildPath( | |
| cmStrCat(target.GetObjectDirectory(Config), '/', obj)); | |
| linkedObjects.insert(pathToObj); | |
| } | |
| // Object libs should not be propagated transitively. It's especially | |
| // important for LinkObjRHSObject2 test where the absence of the propagation | |
| // is tested. | |
| for (auto const& linkedTarget : | |
| target.Target->GetLinkImplementationEntries()) { | |
| auto objAlias = linkedTarget.Value + FASTBUILD_OBJECTS_ALIAS_POSTFIX; | |
| LogMessage("Object target is linked transitively " + objAlias); | |
| linkedObjects.emplace(std::move(objAlias)); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendCommandLineDep( | |
| FastbuildLinkerNode& linkerNode, | |
| cmComputeLinkInformation::Item const& item) const | |
| { | |
| LogMessage("AppendCommandLineDep(...)"); | |
| // Tested in: | |
| // "LinkDirectory" (TargetType::EXECUTABLE), | |
| // "ObjC.simple-build-test" (TargetType::SHARED_LIBRARY), | |
| // "XCTest" (TargetType::MODULE_LIBRARY) tests. | |
| std::string formatted; | |
| if (item.Target && item.Target->IsImported()) { | |
| formatted = item.GetFormattedItem(GetImportedLoc(item)).Value; | |
| } else { | |
| formatted = item.GetFormattedItem(item.Value.Value).Value; | |
| } | |
| formatted = this->ConvertToFastbuildPath(formatted); | |
| LogMessage( | |
| cmStrCat("Unknown link dep: ", formatted, ", adding to command line")); | |
| // Only add real artifacts to .Libraries2, otherwise Fastbuild will always | |
| // consider the target out-of-date (since its input doesn't exist). | |
| if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes && | |
| item.GetFeatureName() == "DEFAULT") { | |
| linkerNode.LinkerOptions += | |
| (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(formatted)); | |
| AppendToLibraries2IfApplicable(linkerNode, std::move(formatted)); | |
| } else { | |
| // It's some link option, not a path. | |
| linkerNode.LinkerOptions += (" " + formatted); | |
| if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) { | |
| AppendToLibraries2IfApplicable( | |
| linkerNode, this->ConvertToFastbuildPath(item.Value.Value)); | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendToLibraries2IfApplicable( | |
| FastbuildLinkerNode& linkerNode, std::string dep) const | |
| { | |
| // Strings like "-framework Cocoa" in .Libraries2 node will always make the | |
| // target out-of-date (since it never exists). | |
| if (this->GeneratorTarget->IsApple() && | |
| cmSystemTools::StringStartsWith(dep, "-framework")) { | |
| LogMessage(cmStrCat("Not adding framework: ", dep, " to .Libraries2")); | |
| return; | |
| } | |
| auto const target = this->GetGlobalGenerator()->GetTargetByOutputName(dep); | |
| // Fastbuild doesn't support executables in .Libraries2, though we can use | |
| // Executables via "-bundle_loader" on Apple. | |
| if (this->GeneratorTarget->IsApple() && target && | |
| !target->LinkerNode.empty() && | |
| target->LinkerNode[0].Type == FastbuildLinkerNode::EXECUTABLE) { | |
| LogMessage(cmStrCat("Not adding DLL/Executable(", linkerNode.Name, | |
| " to .Libraries2")); | |
| return; | |
| } | |
| // Adding to .Libraries2 for tracking. | |
| LogMessage(cmStrCat("Adding ", dep, " .Libraries2")); | |
| linkerNode.Libraries2.emplace_back(std::move(dep)); | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendLINK_DEPENDS( | |
| FastbuildLinkerNode& linkerNode) const | |
| { | |
| // LINK_DEPENDS and such. | |
| // Tested in "BuildDepends" test. | |
| for (std::string const& lang : Languages) { | |
| for (BT<std::string> const& dep : | |
| this->GeneratorTarget->GetLinkDepends(Config, lang)) { | |
| // We can't add "LINK_DEPENDS" to .PreBuildDependencies, since FASTBuild | |
| // only forces such targets to be built and doesn't force re-linking if | |
| // they've changed. | |
| linkerNode.Libraries2.emplace_back( | |
| this->ConvertToFastbuildPath(dep.Value)); | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendLinkDep( | |
| FastbuildLinkerNode& linkerNode, std::string dep) const | |
| { | |
| LogMessage(cmStrCat("AppendLinkDep: ", dep, | |
| " to .LibrarianAdditionalInputs/.Libraries")); | |
| linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep)); | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendDirectObjectLibs( | |
| FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects) | |
| { | |
| auto const srcs = this->GeneratorTarget->GetSourceFiles(Config); | |
| for (auto const& entry : srcs) { | |
| auto const objLib = entry.Value->GetObjectLibrary(); | |
| auto const objPath = entry.Value->GetFullPath(); | |
| LogMessage("Source obj entry: " + objPath); | |
| if (!objLib.empty()) { | |
| auto* const objTarget = | |
| this->LocalGenerator->FindGeneratorTargetToUse(objLib); | |
| if (objTarget) { | |
| LogMessage("Imported: " + std::to_string(objTarget->IsImported())); | |
| std::string fastbuildTarget; | |
| // If target is imported - we don't have it in our build file, so can't | |
| // refer to it by name. Use file path to the object then. | |
| // Tested in "ExportImport" test. | |
| if (objTarget->IsImported()) { | |
| fastbuildTarget = entry.Value->GetFullPath(); | |
| } else { | |
| // Mark all target objects as linked. | |
| linkedObjects.emplace(this->ConvertToFastbuildPath(objPath)); | |
| fastbuildTarget = | |
| objTarget->GetName() + FASTBUILD_OBJECTS_ALIAS_POSTFIX; | |
| } | |
| if (linkedObjects.emplace(fastbuildTarget).second) { | |
| LogMessage("Adding object target: " + fastbuildTarget); | |
| linkerNode.LibrarianAdditionalInputs.emplace_back( | |
| std::move(fastbuildTarget)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AppendLinkDeps( | |
| std::set<FastbuildTargetDep>& preBuildDeps, FastbuildLinkerNode& linkerNode, | |
| FastbuildLinkerNode& deviceLinkNode) | |
| { | |
| std::set<std::string> linkedObjects; | |
| cmComputeLinkInformation const* linkInfo = | |
| this->GeneratorTarget->GetLinkInformation(Config); | |
| if (!linkInfo) { | |
| return; | |
| } | |
| UsingCommandLine = false; | |
| AppendLINK_DEPENDS(linkerNode); | |
| // Object libs that are linked directly to target (e.g. | |
| // add_executable(test_exe archiveObjs) | |
| AppendDirectObjectLibs(linkerNode, linkedObjects); | |
| AppendExternalObject(linkerNode, linkedObjects); | |
| if (!deviceLinkNode.Name.empty()) { | |
| std::set<std::string> deviceObjects; | |
| AppendDirectObjectLibs(deviceLinkNode, deviceObjects); | |
| AppendExternalObject(deviceLinkNode, deviceObjects); | |
| } | |
| std::size_t numberOfDirectlyLinkedObjects = | |
| linkerNode.LibrarianAdditionalInputs.size(); | |
| // target_link_libraries. | |
| cmComputeLinkInformation::ItemVector const items = linkInfo->GetItems(); | |
| LogMessage(cmStrCat("Link items size: ", items.size())); | |
| for (cmComputeLinkInformation::Item const& item : items) { | |
| std::string const feature = item.GetFeatureName(); | |
| LogMessage("GetFeatureName: " + feature); | |
| std::string const formatted = | |
| item.GetFormattedItem(item.Value.Value).Value; | |
| if (!feature.empty()) { | |
| LogMessage("GetFormattedItem: " + | |
| item.GetFormattedItem(item.Value.Value).Value); | |
| } | |
| // We're linked to `$<TARGET_OBJECTS>`. | |
| // Static libs transitively propagate such deps, see: | |
| // https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries-via-target-objects | |
| if (item.ObjectSource && | |
| linkerNode.Type != FastbuildLinkerNode::STATIC_LIBRARY) { | |
| // Tested in "ObjectLibrary" test. | |
| std::string const libName = item.ObjectSource->GetObjectLibrary(); | |
| std::string dep = libName + FASTBUILD_OBJECTS_ALIAS_POSTFIX; | |
| if (linkedObjects.emplace(dep).second) { | |
| FastbuildTargetDep targetDep{ libName }; | |
| targetDep.Type = FastbuildTargetDepType::ORDER_ONLY; | |
| preBuildDeps.emplace(std::move(targetDep)); | |
| cmTarget const* importedTarget = | |
| this->LocalGenerator->GetMakefile()->FindImportedTarget(libName); | |
| // Add direct path to the object for imported target | |
| // since such targets are not defined in fbuild.bff file. | |
| if (importedTarget) { | |
| LogMessage( | |
| cmStrCat("Adding ", formatted, " to LibrarianAdditionalInputs")); | |
| linkerNode.LibrarianAdditionalInputs.emplace_back(formatted); | |
| } else { | |
| LogMessage( | |
| cmStrCat("Adding ", dep, " to LibrarianAdditionalInputs")); | |
| linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep)); | |
| } | |
| } | |
| } else if (linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY) { | |
| LogMessage(cmStrCat("Skipping linking to STATIC_LIBRARY (", | |
| linkerNode.Name, ')')); | |
| continue; | |
| } | |
| // We're linked to exact target. | |
| else if (item.Target) { | |
| AppendTargetDep(linkerNode, linkedObjects, item); | |
| AppendPrebuildDeps(linkerNode, item); | |
| } else { | |
| AppendCommandLineDep(linkerNode, item); | |
| UsingCommandLine = true; | |
| } | |
| } | |
| if (!deviceLinkNode.Name.empty()) { | |
| // The device-link stub needs to go AFTER direct object dependencies, but | |
| // BEFORE all other dependencies. Needed for the correct left-to-right | |
| // symbols resolution on Linux. | |
| linkerNode.LibrarianAdditionalInputs.insert( | |
| linkerNode.LibrarianAdditionalInputs.begin() + | |
| numberOfDirectlyLinkedObjects, | |
| deviceLinkNode.Name); | |
| } | |
| } | |
| void cmFastbuildNormalTargetGenerator::AddLipoCommand(FastbuildTarget& target) | |
| { | |
| static auto const lipo = cmSystemTools::FindProgram("lipo"); | |
| LogMessage("found lipo at " + lipo); | |
| FastbuildExecNode exec; | |
| exec.ExecExecutable = lipo; | |
| exec.ExecOutput = target.RealOutput; | |
| if (exec.ExecOutput != target.Name) { | |
| exec.Name = target.Name; | |
| } | |
| for (auto const& ArchSpecificTarget : target.LinkerNode) { | |
| exec.ExecInput.emplace_back(ArchSpecificTarget.LinkerOutput); | |
| } | |
| exec.ExecArguments = | |
| cmStrCat(std::move(exec.ExecArguments), "-create -output ", | |
| target.RealOutput, ' ', cmJoin(exec.ExecInput, " ")); | |
| target.PostBuildExecNodes.Alias.PreBuildDependencies.emplace( | |
| exec.ExecOutput); | |
| target.PostBuildExecNodes.Nodes.emplace_back(std::move(exec)); | |
| } | |
| void cmFastbuildNormalTargetGenerator::GenerateLink( | |
| FastbuildTarget& target, std::vector<std::string> const& objectDepends) | |
| { | |
| std::string const targetName = this->GetTargetName(); | |
| cmGeneratorTarget::Names const targetNames = DetectOutput(); | |
| LogMessage("targetNames.Real: " + targetNames.Real); | |
| LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput); | |
| LogMessage("targetNames.SharedObject: " + targetNames.SharedObject); | |
| LogMessage("targetNames.Base: " + targetNames.Base); | |
| std::vector<std::string> allNodes; | |
| auto const arches = this->GetArches(); | |
| for (std::size_t i = 0; i < arches.size(); ++i) { | |
| auto const& arch = arches[i]; | |
| FastbuildLinkerNode linkerNode; | |
| ProcessManifests(linkerNode); | |
| // Objects built by the current target. | |
| for (auto const& objectList : target.ObjectListNodes) { | |
| if (objectList.arch.empty() || objectList.arch == arch) { | |
| linkerNode.LibrarianAdditionalInputs.push_back(objectList.Name); | |
| } | |
| } | |
| // Detection of the link command as follows: | |
| auto const type = this->GeneratorTarget->GetType(); | |
| switch (type) { | |
| case cm::TargetType::EXECUTABLE: { | |
| LogMessage("Generating EXECUTABLE"); | |
| linkerNode.Type = FastbuildLinkerNode::EXECUTABLE; | |
| break; | |
| } | |
| case cm::TargetType::MODULE_LIBRARY: { | |
| LogMessage("Generating MODULE_LIBRARY"); | |
| linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; | |
| break; | |
| } | |
| case cm::TargetType::SHARED_LIBRARY: { | |
| LogMessage("Generating SHARED_LIBRARY"); | |
| linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; | |
| break; | |
| } | |
| case cm::TargetType::STATIC_LIBRARY: { | |
| LogMessage("Generating STATIC_LIBRARY"); | |
| linkerNode.Type = FastbuildLinkerNode::STATIC_LIBRARY; | |
| break; | |
| } | |
| case cm::TargetType::OBJECT_LIBRARY: { | |
| LogMessage("Generating OBJECT_LIBRARY"); | |
| return; | |
| } | |
| default: { | |
| LogMessage("Skipping GenerateLink"); | |
| return; | |
| } | |
| } | |
| std::string const targetOutput = | |
| ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config)); | |
| std::string targetOutputReal = ConvertToFastbuildPath( | |
| GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact, | |
| /*realname=*/true)); | |
| LogMessage("targetOutput: " + targetOutput); | |
| LogMessage("targetOutputReal: " + targetOutputReal); | |
| std::string const output = | |
| cmSystemTools::GetFilenameName(targetNames.Output); | |
| std::string const outputReal = | |
| cmSystemTools::GetFilenameName(targetNames.Real); | |
| // Generate "Copy" nodes for copying Framework / Bundle resources. | |
| AppendExtraResources(linkerNode.PreBuildDependencies); | |
| if (type == cm::TargetType::EXECUTABLE || | |
| type == cm::TargetType::SHARED_LIBRARY) { | |
| // Tested in "RunCMake.BuildDepends" test (we need to rebuild when | |
| // manifest changes). | |
| std::copy(objectDepends.begin(), objectDepends.end(), | |
| std::back_inserter(linkerNode.Libraries2)); | |
| } | |
| if (GeneratorTarget->IsAppBundleOnApple()) { | |
| // Create the app bundle | |
| std::string outpath = GeneratorTarget->GetDirectory(Config); | |
| this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath, | |
| Config); | |
| targetOutputReal = cmStrCat(outpath, '/', outputReal); | |
| targetOutputReal = this->ConvertToFastbuildPath(targetOutputReal); | |
| } else if (GeneratorTarget->IsFrameworkOnApple()) { | |
| // Create the library framework. | |
| this->OSXBundleGenerator->CreateFramework( | |
| targetNames.Output, GeneratorTarget->GetDirectory(Config), Config); | |
| } else if (GeneratorTarget->IsCFBundleOnApple()) { | |
| // Create the core foundation bundle. | |
| this->OSXBundleGenerator->CreateCFBundle( | |
| targetNames.Output, GeneratorTarget->GetDirectory(Config), Config); | |
| } | |
| std::string linkCmd; | |
| if (!DetectBaseLinkerCommand(linkCmd, arch, targetNames)) { | |
| LogMessage("No linker command detected"); | |
| return; | |
| } | |
| std::string executable; | |
| std::string linkerOptions; | |
| std::string linkerType = "auto"; | |
| GetLinkerExecutableAndArgs(linkCmd, executable, linkerOptions); | |
| linkerNode.Compiler = ".Compiler_dummy"; | |
| linkerNode.CompilerOptions = " "; | |
| linkerNode.Name = targetName; | |
| linkerNode.LinkerOutput = targetOutputReal; | |
| this->GetGlobalGenerator()->AddFileToClean(linkerNode.LinkerOutput); | |
| target.RealOutput = targetOutputReal; | |
| if (!arch.empty()) { | |
| linkerNode.Name = cmStrCat(std::move(linkerNode.Name), '-', arch); | |
| linkerNode.LinkerOutput = | |
| cmStrCat(std::move(linkerNode.LinkerOutput), '.', arch); | |
| linkerNode.Arch = arch; | |
| } | |
| linkerNode.Linker = executable; | |
| linkerNode.LinkerType = linkerType; | |
| linkerNode.LinkerOptions += linkerOptions; | |
| // Check if we have CUDA device link stub for this target. | |
| FastbuildLinkerNode dummyDeviceLinkNode; | |
| AppendLinkDeps(target.PreBuildDependencies, linkerNode, | |
| target.DeviceLinkNodes.size() > i | |
| ? target.DeviceLinkNodes[i] | |
| : dummyDeviceLinkNode); | |
| ApplyLWYUToLinkerCommand(linkerNode); | |
| // On macOS, only the last LinkerNode performs lipo in POST_BUILD. | |
| // Make it depend on all previous nodes to ensure correct execution order. | |
| if (i == arches.size() - 1) { | |
| for (auto& prevNode : allNodes) { | |
| linkerNode.PreBuildDependencies.emplace(std::move(prevNode)); | |
| } | |
| } else { | |
| allNodes.emplace_back(linkerNode.Name); | |
| } | |
| if (!target.ObjectListNodes.empty()) { | |
| // Just reuse any of compiler options mainly for the correct IDE project | |
| // generation. | |
| linkerNode.CompilerOptions = target.ObjectListNodes[0].CompilerOptions; | |
| } | |
| target.LinkerNode.emplace_back(std::move(linkerNode)); | |
| } | |
| } | |
| std::vector<FastbuildExecNode> | |
| cmFastbuildNormalTargetGenerator::GetSymlinkExecs() const | |
| { | |
| std::vector<FastbuildExecNode> res; | |
| cmGeneratorTarget::Names const targetNames = DetectOutput(); | |
| LogMessage("targetNames.Real: " + targetNames.Real); | |
| LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput); | |
| LogMessage("targetNames.SharedObject: " + targetNames.SharedObject); | |
| LogMessage("targetNames.Base: " + targetNames.Base); | |
| std::string const targetOutput = | |
| ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config)); | |
| std::string const targetOutputReal = ConvertToFastbuildPath( | |
| GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact, | |
| /*realname=*/true)); | |
| LogMessage("targetOutput: " + targetOutput); | |
| LogMessage("targetOutputReal: " + targetOutputReal); | |
| if (targetOutput != targetOutputReal && | |
| !GeneratorTarget->IsFrameworkOnApple()) { | |
| auto const generateSymlinkCommand = [&](std::string const& from, | |
| std::string const& to) { | |
| if (from.empty() || to.empty() || from == to) { | |
| return; | |
| } | |
| LogMessage(cmStrCat("Symlinking ", from, " -> ", to)); | |
| FastbuildExecNode postBuildExecNode; | |
| postBuildExecNode.Name = "cmake_symlink_" + to; | |
| postBuildExecNode.ExecOutput = | |
| cmJoin({ GeneratorTarget->GetDirectory(Config), to }, "/"); | |
| postBuildExecNode.ExecExecutable = cmSystemTools::GetCMakeCommand(); | |
| postBuildExecNode.ExecArguments = cmStrCat( | |
| "-E cmake_symlink_executable ", | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces(from), ' ', | |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces( | |
| this->ConvertToFastbuildPath(postBuildExecNode.ExecOutput))); | |
| res.emplace_back(std::move(postBuildExecNode)); | |
| }; | |
| // name -> soname -> realname | |
| std::string const& outputLinkTarget = | |
| (targetNames.SharedObject.empty() || | |
| targetNames.SharedObject == targetNames.Output) | |
| ? targetNames.Real | |
| : targetNames.SharedObject; | |
| generateSymlinkCommand(outputLinkTarget, targetNames.Output); | |
| generateSymlinkCommand(targetNames.Real, targetNames.SharedObject); | |
| generateSymlinkCommand(targetNames.ImportReal, targetNames.ImportOutput); | |
| } | |
| return res; | |
| } |