| /* 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 <cstddef> |
| #include <iterator> |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <utility> |
| |
| #include <cm/memory> |
| #include <cm/optional> |
| |
| #include "cmsys/FStream.hxx" |
| |
| #include "cmAlgorithms.h" |
| #include "cmCommonTargetGenerator.h" |
| #include "cmCryptoHash.h" |
| #include "cmFastbuildTargetGenerator.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGlobalCommonGenerator.h" |
| #include "cmGlobalFastbuildGenerator.h" |
| #include "cmLinkLineComputer.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 "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"); |
| |
| } // 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()) |
| { |
| |
| 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("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 = srcFile.GetLanguage(); |
| cmGeneratorExpressionInterpreter genexInterpreter( |
| this->GetLocalGenerator(), Config, this->GeneratorTarget, language); |
| |
| std::vector<std::string> sourceIncludesVec; |
| 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); |
| |
| 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)); |
| } |
| // 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::GetLinkerExecutableAndArgs( |
| std::string const& command, std::string& outLinkerExecutable, |
| std::string& outLinkerArgs) |
| { |
| if (command.empty()) { |
| return; |
| } |
| |
| LogMessage("Link Command: " + command); |
| |
| auto const& compilers = this->GetGlobalGenerator()->Compilers; |
| auto const linkerLauncherVarName = FASTBUILD_LINKER_LAUNCHER_PREFIX + |
| this->GeneratorTarget->GetLinkerLanguage(Config); |
| auto const iter = compilers.find(linkerLauncherVarName); |
| // Tested in "RunCMake.LinkerLauncher" test. |
| if (iter != compilers.end()) { |
| LogMessage("Linker launcher: " + iter->first); |
| outLinkerExecutable = iter->second.Executable; |
| outLinkerArgs = cmStrCat(iter->second.Args, " ", command); |
| } else { |
| #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 |
| } |
| 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 }, " "); |
| |
| cmStateEnums::TargetType const targetType = this->GeneratorTarget->GetType(); |
| // Add OS X version flags, if any. |
| if (targetType == cmStateEnums::SHARED_LIBRARY || |
| targetType == cmStateEnums::MODULE_LIBRARY) { |
| this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true); |
| this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false); |
| } |
| // Add Arch flags to link flags for binaries |
| if (targetType == cmStateEnums::SHARED_LIBRARY || |
| targetType == cmStateEnums::MODULE_LIBRARY || |
| targetType == cmStateEnums::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 = |
| cmJoin(this->GetManifestsAsFastbuildPath(), " "); |
| vars.Manifests = manifests.c_str(); |
| |
| std::string const stdLibString = this->Makefile->GetSafeDefinition( |
| cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES")); |
| |
| LogMessage(cmStrCat("Target type: ", this->GeneratorTarget->GetType())); |
| if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || |
| this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || |
| this->GeneratorTarget->GetType() == cmStateEnums::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; |
| { |
| std::ostringstream majorStream; |
| std::ostringstream minorStream; |
| int major; |
| int minor; |
| this->GeneratorTarget->GetTargetVersion(major, minor); |
| majorStream << major; |
| minorStream << minor; |
| targetVersionMajor = majorStream.str(); |
| targetVersionMinor = minorStream.str(); |
| } |
| 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 = ""; |
| 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, " ", 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( |
| " --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 = srcFile.GetLanguage(); |
| 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)); |
| } |
| |
| 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; |
| } |
| // We have already computed PCH for this node. |
| if (!node.PCHOptions.empty() || !node.PCHInputFile.empty() || |
| !node.PCHOutputFile.empty()) { |
| return; |
| } |
| std::string const language = srcFile.GetLanguage(); |
| 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("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); |
| } |
| } |
| |
| 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 (!nm_executable.IsEmpty()) { |
| 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; |
| if (lang == "CUDA") { |
| if (this->GeneratorTarget->GetPropertyAsBool( |
| "CUDA_SEPARABLE_COMPILATION")) { |
| cmakeVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"; |
| } else if (this->GeneratorTarget->GetPropertyAsBool( |
| "CUDA_PTX_COMPILATION")) { |
| cmakeVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION"; |
| } else { |
| cmakeVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"; |
| } |
| } else { |
| 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::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() == cmStateEnums::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::string const compilerLauncher = |
| cmCommonTargetGenerator::GetCompilerLauncher(language, Config); |
| LogMessage("compilerLauncher: " + compilerLauncher); |
| std::vector<std::string> expanded; |
| cmExpandList(compilerLauncher, expanded); |
| |
| 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::string const linkerLauncher = |
| cmCommonTargetGenerator::GetLinkerLauncher(Config); |
| std::vector<std::string> args; |
| #ifdef _WIN32 |
| cmSystemTools::ParseWindowsCommandLine(linkerLauncher.c_str(), args); |
| #else |
| cmSystemTools::ParseUnixCommandLine(linkerLauncher.c_str(), args); |
| #endif |
| 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() <= cmStateEnums::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)); |
| } |
| } |
| if (GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { |
| std::string const pdbDir = GeneratorTarget->GetCompilePDBDirectory(Config); |
| LogMessage("GetCompilePDBDirectory: " + pdbDir); |
| EnsureDirectoryExists(pdbDir); |
| std::string pdbName = this->GeneratorTarget->GetCompilePDBName(Config); |
| LogMessage("GetCompilePDBName: " + pdbDir); |
| // If we don't have Compiler's PDB, we must add a trailing slash to satisfy |
| // MSVC. |
| bool needTrailingSlash = false; |
| if (pdbName.empty()) { |
| needTrailingSlash = true; |
| } |
| std::string const compilerPDB = cmStrCat(pdbDir, '\\', pdbName); |
| if (!compilerPDB.empty()) { |
| target.Variables["CompilerPDB"] = cmSystemTools::ConvertToOutputPath( |
| this->ConvertToFastbuildPath(compilerPDB) + |
| (needTrailingSlash ? "\\ " : "")); |
| } |
| } |
| std::string impLibFile = ConvertToFastbuildPath( |
| GeneratorTarget->GetFullPath(Config, cmStateEnums::ImportLibraryArtifact)); |
| cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(impLibFile)); |
| if (!impLibFile.empty()) { |
| cmSystemTools::ConvertToOutputSlashes(impLibFile); |
| target.Variables["TargetOutputImplib"] = std::move(impLibFile); |
| } |
| } |
| |
| void cmFastbuildNormalTargetGenerator::Generate() |
| { |
| this->GeneratorTarget->CheckCxxModuleStatus(Config); |
| |
| FastbuildTarget fastbuildTarget; |
| 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() == cmStateEnums::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); |
| fastbuildTarget.PreBuildDependencies.emplace(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); |
| |
| 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() == cmStateEnums::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). |
| AddLinkerNodeDependnecies(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; |
| } |
| auto manifests = this->GetManifestsAsFastbuildPath(); |
| if (manifests.empty()) { |
| return; |
| } |
| // Manifests should always be in .Libraries2, so we re-link when needed. |
| // Tested in RunCMake.BuildDepends |
| for (auto const& manifest : manifests) { |
| linkerNode.Libraries2.emplace_back(manifest); |
| } |
| |
| if (this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID") != "MSVC") { |
| return; |
| } |
| |
| for (auto const& manifest : manifests) { |
| linkerNode.LinkerOptions = |
| cmStrCat("/MANIFESTINPUT:", manifest, ' ', linkerNode.LinkerOptions); |
| } |
| // /MANIFESTINPUT only works with /MANIFEST:EMBED |
| linkerNode.LinkerOptions = |
| cmStrCat("/MANIFEST:EMBED ", linkerNode.LinkerOptions); |
| } |
| |
| 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(); |
| for (auto const& exec : execs) { |
| if (exec.ScriptFile.empty()) { |
| scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable) |
| << " " << exec.ExecArguments << '\n'; |
| } else { |
| #if defined(_WIN32) |
| scriptFile << "call " |
| << cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile) |
| << '\n'; |
| #else |
| scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " " |
| << cmSystemTools::ConvertToOutputPath(exec.ScriptFile) |
| << '\n'; |
| #endif |
| } |
| } |
| } |
| |
| std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions( |
| cmSourceFile const& srcFile) |
| { |
| cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING"); |
| bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet() |
| ? srcSkipCodeCheckVal.IsOn() |
| : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"); |
| |
| if (skipCodeCheck) { |
| return {}; |
| } |
| 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 "TargetOutputDir" 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 = srcFile.GetLanguage(); |
| 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(); |
| |
| 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::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 isolated from the unity build if enabled. |
| std::set<std::string> isolatedFromUnity; |
| |
| // 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(); |
| if (useUnity) { |
| // Check if the source should be added to "UnityInputIsolatedFiles". |
| if (srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION)) { |
| isolatedFromUnity.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, srcFile.GetLanguage()) == |
| pathToFile) { |
| continue; |
| } |
| |
| std::string const language = srcFile.GetLanguage(); |
| 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. |
| 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. |
| srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"), |
| srcFile.GetLanguage())); |
| |
| 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; |
| } |
| objectListNode.CompilerOutputExtension += |
| this->GetMakefile()->GetSafeDefinition( |
| cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION")); |
| } |
| } |
| } |
| |
| 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; |
| objects.emplace_back(std::move(node)); |
| if (!objects.back().PCHInputFile.empty()) { |
| // Node that produces PCH should be the first one, since other nodes |
| // might reuse this PCH. |
| std::swap(*objects.begin(), objects.back()); |
| } |
| } |
| if (useUnity) { |
| target.UnityNodes = |
| GenerateUnity(objects, isolatedFromUnity, sourcesWithGroups); |
| } |
| } |
| |
| FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity( |
| std::set<std::string> const& isolatedFiles, 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 isolated |
| if (isolatedFiles.find(pathToFile) != isolatedFiles.end()) { |
| result.UnityInputFiles.emplace_back(pathToFile); |
| result.UnityInputIsolatedFiles.emplace_back(std::move(pathToFile)); |
| } else { |
| result.UnityInputFiles.emplace_back(std::move(pathToFile)); |
| } |
| if (int(result.UnityInputFiles.size() - |
| result.UnityInputIsolatedFiles.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& isolatedSources, |
| 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(isolatedSources, 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 isolate the source. |
| if (groupedNode.UnityInputFiles.size() == 1) { |
| node.UnityInputIsolatedFiles.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("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(...)"); |
| cmStateEnums::TargetType const depType = item.Target->GetType(); |
| LogMessage("Link dep type: " + std::to_string(depType)); |
| LogMessage("Target name: " + item.Target->GetName()); |
| auto const resolvedTargetName = ResolveIfAlias(item.Target->GetName()); |
| LogMessage("Resolved: " + resolvedTargetName); |
| if (depType == cmStateEnums::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("Adding framework dep <" + decorated + "> to command line"); |
| linkerNode.LinkerOptions += (" " + decorated); |
| return; |
| } |
| if (depType == cmStateEnums::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 == cmStateEnums::EXECUTABLE) { |
| AppendExeToLink(linkerNode, item); |
| return; |
| } |
| // Skip exported objects. |
| // Tested in "ExportImport" test. |
| if (depType == cmStateEnums::OBJECT_LIBRARY) { |
| LogMessage("target : " + item.Target->GetName() + |
| " already linked... Skipping"); |
| return; |
| } |
| // Tested in "ExportImport" test. |
| cmList const list{ GetImportedLoc(item) }; |
| for (std::string const& linkDep : list) { |
| AppendLinkDep(linkerNode, linkDep); |
| } |
| } else { |
| if (depType == cmStateEnums::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("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 != cmStateEnums::OBJECT_LIBRARY) { |
| 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 == cmStateEnums::OBJECT_LIBRARY |
| ? FASTBUILD_OBJECTS_ALIAS_POSTFIX |
| : FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX); |
| if (!linkerNode.Arch.empty()) { |
| dep += cmStrCat('-', 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("AppendTargetDep: " + dep + " as prebuild"); |
| linkerNode.PreBuildDependencies.emplace(dep); |
| return; |
| } |
| |
| if (depType != cmStateEnums::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() == cmStateEnums::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("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 = GetImportedLoc(item); |
| } else { |
| formatted = item.GetFormattedItem(item.Value.Value).Value; |
| } |
| formatted = this->ConvertToFastbuildPath(formatted); |
| |
| LogMessage("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); |
| } |
| } |
| |
| 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("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("Not adding DLL/Executable(" + linkerNode.Name + |
| " to .Libraries2"); |
| return; |
| } |
| |
| // Additing to .Libraries2 for tracking. |
| LogMessage("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("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) |
| { |
| 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); |
| // 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); |
| 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. |
| auto libName = item.ObjectSource->GetObjectLibrary(); |
| std::string dep = libName + FASTBUILD_OBJECTS_ALIAS_POSTFIX; |
| if (linkedObjects.emplace(dep).second) { |
| FastbuildTargetDep targetDep{ std::move(libName) }; |
| targetDep.Type = FastbuildTargetDepType::ORDER_ONLY; |
| preBuildDeps.emplace(std::move(targetDep)); |
| linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep)); |
| } |
| } else if (linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY) { |
| LogMessage("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; |
| } |
| } |
| AppendExternalObject(linkerNode, linkedObjects); |
| } |
| |
| 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 += |
| "-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 cmStateEnums::EXECUTABLE: { |
| LogMessage("Generating EXECUTABLE"); |
| linkerNode.Type = FastbuildLinkerNode::EXECUTABLE; |
| break; |
| } |
| case cmStateEnums::MODULE_LIBRARY: { |
| LogMessage("Generating MODULE_LIBRARY"); |
| linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; |
| break; |
| } |
| case cmStateEnums::SHARED_LIBRARY: { |
| LogMessage("Generating SHARED_LIBRARY"); |
| linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY; |
| break; |
| } |
| case cmStateEnums::STATIC_LIBRARY: { |
| LogMessage("Generating STATIC_LIBRARY"); |
| linkerNode.Type = FastbuildLinkerNode::STATIC_LIBRARY; |
| break; |
| } |
| case cmStateEnums::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 == cmStateEnums::EXECUTABLE || |
| type == cmStateEnums::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 = outpath; |
| targetOutputReal += "/"; |
| targetOutputReal += 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('-', arch); |
| linkerNode.LinkerOutput += cmStrCat('.', arch); |
| linkerNode.Arch = arch; |
| } |
| linkerNode.Linker = executable; |
| linkerNode.LinkerType = linkerType; |
| linkerNode.LinkerOptions += linkerOptions; |
| |
| AppendLinkDeps(target.PreBuildDependencies, linkerNode); |
| 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("Symlinking " + from + " -> " + to); |
| FastbuildExecNode postBuildExecNode; |
| postBuildExecNode.Name = "cmake_symlink_" + to; |
| postBuildExecNode.ExecOutput = |
| cmJoin({ GeneratorTarget->GetDirectory(Config), to }, "/"); |
| postBuildExecNode.ExecExecutable = cmSystemTools::GetCMakeCommand(); |
| postBuildExecNode.ExecArguments = |
| "-E cmake_symlink_executable " + |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces(from) + " " + |
| cmGlobalFastbuildGenerator::QuoteIfHasSpaces( |
| this->ConvertToFastbuildPath(postBuildExecNode.ExecOutput)); |
| res.emplace_back(std::move(postBuildExecNode)); |
| }; |
| generateSymlinkCommand(targetNames.Real, targetNames.Output); |
| generateSymlinkCommand(targetNames.Real, targetNames.SharedObject); |
| generateSymlinkCommand(targetNames.ImportReal, targetNames.ImportOutput); |
| } |
| return res; |
| } |