| /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
 |    file Copyright.txt or https://cmake.org/licensing for details.  */ | 
 | #include "cmNinjaNormalTargetGenerator.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <cassert> | 
 | #include <iterator> | 
 | #include <map> | 
 | #include <set> | 
 | #include <sstream> | 
 | #include <unordered_set> | 
 | #include <utility> | 
 |  | 
 | #include <cm/memory> | 
 | #include <cm/optional> | 
 | #include <cm/vector> | 
 |  | 
 | #include "cmComputeLinkInformation.h" | 
 | #include "cmCustomCommand.h" // IWYU pragma: keep | 
 | #include "cmCustomCommandGenerator.h" | 
 | #include "cmGeneratedFileStream.h" | 
 | #include "cmGeneratorTarget.h" | 
 | #include "cmGlobalNinjaGenerator.h" | 
 | #include "cmLinkLineComputer.h" | 
 | #include "cmLinkLineDeviceComputer.h" | 
 | #include "cmLocalCommonGenerator.h" | 
 | #include "cmLocalGenerator.h" | 
 | #include "cmLocalNinjaGenerator.h" | 
 | #include "cmMakefile.h" | 
 | #include "cmMessageType.h" | 
 | #include "cmNinjaLinkLineDeviceComputer.h" | 
 | #include "cmNinjaTypes.h" | 
 | #include "cmOSXBundleGenerator.h" | 
 | #include "cmOutputConverter.h" | 
 | #include "cmRulePlaceholderExpander.h" | 
 | #include "cmSourceFile.h" | 
 | #include "cmState.h" | 
 | #include "cmStateDirectory.h" | 
 | #include "cmStateSnapshot.h" | 
 | #include "cmStateTypes.h" | 
 | #include "cmStringAlgorithms.h" | 
 | #include "cmSystemTools.h" | 
 | #include "cmValue.h" | 
 |  | 
 | cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator( | 
 |   cmGeneratorTarget* target) | 
 |   : cmNinjaTargetGenerator(target) | 
 | { | 
 |   if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) { | 
 |     // on Windows the output dir is already needed at compile time | 
 |     // ensure the directory exists (OutDir test) | 
 |     for (auto const& config : this->GetConfigNames()) { | 
 |       this->EnsureDirectoryExists(target->GetDirectory(config)); | 
 |     } | 
 |   } | 
 |  | 
 |   this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target); | 
 |   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); | 
 | } | 
 |  | 
 | cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default; | 
 |  | 
 | void cmNinjaNormalTargetGenerator::Generate(const std::string& config) | 
 | { | 
 |   std::string lang = this->GeneratorTarget->GetLinkerLanguage(config); | 
 |   if (this->TargetLinkLanguage(config).empty()) { | 
 |     cmSystemTools::Error("CMake can not determine linker language for " | 
 |                          "target: " + | 
 |                          this->GetGeneratorTarget()->GetName()); | 
 |     return; | 
 |   } | 
 |  | 
 |   // Write the rules for each language. | 
 |   this->WriteLanguagesRules(config); | 
 |  | 
 |   // Write the build statements | 
 |   bool firstForConfig = true; | 
 |   for (auto const& fileConfig : this->GetConfigNames()) { | 
 |     if (!this->GetGlobalGenerator() | 
 |            ->GetCrossConfigs(fileConfig) | 
 |            .count(config)) { | 
 |       continue; | 
 |     } | 
 |     this->WriteObjectBuildStatements(config, fileConfig, firstForConfig); | 
 |     firstForConfig = false; | 
 |   } | 
 |  | 
 |   if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) { | 
 |     this->WriteObjectLibStatement(config); | 
 |   } else { | 
 |     firstForConfig = true; | 
 |     for (auto const& fileConfig : this->GetConfigNames()) { | 
 |       if (!this->GetGlobalGenerator() | 
 |              ->GetCrossConfigs(fileConfig) | 
 |              .count(config)) { | 
 |         continue; | 
 |       } | 
 |       // If this target has cuda language link inputs, and we need to do | 
 |       // device linking | 
 |       this->WriteDeviceLinkStatement(config, fileConfig, firstForConfig); | 
 |       this->WriteLinkStatement(config, fileConfig, firstForConfig); | 
 |       firstForConfig = false; | 
 |     } | 
 |   } | 
 |   if (this->GetGlobalGenerator()->EnableCrossConfigBuild()) { | 
 |     this->GetGlobalGenerator()->AddTargetAlias( | 
 |       this->GetTargetName(), this->GetGeneratorTarget(), "all"); | 
 |   } | 
 |  | 
 |   // Find ADDITIONAL_CLEAN_FILES | 
 |   this->AdditionalCleanFiles(config); | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteLanguagesRules( | 
 |   const std::string& config) | 
 | { | 
 | #ifdef NINJA_GEN_VERBOSE_FILES | 
 |   cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream()); | 
 |   this->GetRulesFileStream() | 
 |     << "# Rules for each languages for " | 
 |     << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) | 
 |     << " target " << this->GetTargetName() << "\n\n"; | 
 | #endif | 
 |  | 
 |   // Write rules for languages compiled in this target. | 
 |   std::set<std::string> languages; | 
 |   std::vector<cmSourceFile const*> sourceFiles; | 
 |   this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config); | 
 |   for (cmSourceFile const* sf : sourceFiles) { | 
 |     std::string const lang = sf->GetLanguage(); | 
 |     if (!lang.empty()) { | 
 |       languages.insert(lang); | 
 |     } | 
 |   } | 
 |   for (std::string const& language : languages) { | 
 |     this->WriteLanguageRules(language, config); | 
 |   } | 
 | } | 
 |  | 
 | const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const | 
 | { | 
 |   switch (this->GetGeneratorTarget()->GetType()) { | 
 |     case cmStateEnums::STATIC_LIBRARY: | 
 |       return "static library"; | 
 |     case cmStateEnums::SHARED_LIBRARY: | 
 |       return "shared library"; | 
 |     case cmStateEnums::MODULE_LIBRARY: | 
 |       if (this->GetGeneratorTarget()->IsCFBundleOnApple()) { | 
 |         return "CFBundle shared module"; | 
 |       } else { | 
 |         return "shared module"; | 
 |       } | 
 |     case cmStateEnums::EXECUTABLE: | 
 |       return "executable"; | 
 |     default: | 
 |       return nullptr; | 
 |   } | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule( | 
 |   const std::string& config) const | 
 | { | 
 |   return cmStrCat( | 
 |     this->TargetLinkLanguage(config), "_", | 
 |     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()), | 
 |     "_LINKER__", | 
 |     cmGlobalNinjaGenerator::EncodeRuleName( | 
 |       this->GetGeneratorTarget()->GetName()), | 
 |     "_", config); | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule( | 
 |   const std::string& config) const | 
 | { | 
 |   return cmStrCat( | 
 |     this->TargetLinkLanguage(config), "_", | 
 |     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()), | 
 |     "_DEVICE_LINKER__", | 
 |     cmGlobalNinjaGenerator::EncodeRuleName( | 
 |       this->GetGeneratorTarget()->GetName()), | 
 |     "_", config); | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule( | 
 |   const std::string& config) const | 
 | { | 
 |   return cmStrCat( | 
 |     this->TargetLinkLanguage(config), "_DEVICE_LINK__", | 
 |     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | 
 |     '_', config); | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule( | 
 |   const std::string& config) const | 
 | { | 
 |   return cmStrCat( | 
 |     this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__", | 
 |     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | 
 |     '_', config); | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule( | 
 |   const std::string& config) const | 
 | { | 
 |   return cmStrCat( | 
 |     this->TargetLinkLanguage(config), "_FATBINARY__", | 
 |     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), | 
 |     '_', config); | 
 | } | 
 |  | 
 | struct cmNinjaRemoveNoOpCommands | 
 | { | 
 |   bool operator()(std::string const& cmd) | 
 |   { | 
 |     return cmd.empty() || cmd[0] == ':'; | 
 |   } | 
 | }; | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule( | 
 |   bool useResponseFile, const std::string& config) | 
 | { | 
 |   cmNinjaRule rule(this->LanguageLinkerDeviceRule(config)); | 
 |   if (!this->GetGlobalGenerator()->HasRule(rule.Name)) { | 
 |     cmRulePlaceholderExpander::RuleVariables vars; | 
 |     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); | 
 |     vars.CMTargetType = | 
 |       cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) | 
 |         .c_str(); | 
 |  | 
 |     vars.Language = "CUDA"; | 
 |  | 
 |     // build response file name | 
 |     std::string responseFlag = this->GetMakefile()->GetSafeDefinition( | 
 |       "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"); | 
 |  | 
 |     if (!useResponseFile || responseFlag.empty()) { | 
 |       vars.Objects = "$in"; | 
 |       vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; | 
 |     } else { | 
 |       rule.RspFile = "$RSP_FILE"; | 
 |       responseFlag += rule.RspFile; | 
 |  | 
 |       // build response file content | 
 |       if (this->GetGlobalGenerator()->IsGCCOnWindows()) { | 
 |         rule.RspContent = "$in"; | 
 |       } else { | 
 |         rule.RspContent = "$in_newline"; | 
 |       } | 
 |       rule.RspContent += " $LINK_LIBRARIES"; | 
 |       vars.Objects = responseFlag.c_str(); | 
 |       vars.LinkLibraries = ""; | 
 |     } | 
 |  | 
 |     vars.ObjectDir = "$OBJECT_DIR"; | 
 |  | 
 |     vars.Target = "$TARGET_FILE"; | 
 |  | 
 |     vars.SONameFlag = "$SONAME_FLAG"; | 
 |     vars.TargetSOName = "$SONAME"; | 
 |     vars.TargetPDB = "$TARGET_PDB"; | 
 |     vars.TargetCompilePDB = "$TARGET_COMPILE_PDB"; | 
 |  | 
 |     vars.Flags = "$FLAGS"; | 
 |     vars.LinkFlags = "$LINK_FLAGS"; | 
 |     vars.Manifests = "$MANIFESTS"; | 
 |  | 
 |     vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS"; | 
 |  | 
 |     std::string launcher; | 
 |     cmValue val = this->GetLocalGenerator()->GetRuleLauncher( | 
 |       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); | 
 |     if (cmNonempty(val)) { | 
 |       launcher = cmStrCat(*val, ' '); | 
 |     } | 
 |  | 
 |     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( | 
 |       this->GetLocalGenerator()->CreateRulePlaceholderExpander()); | 
 |  | 
 |     // Rule for linking library/executable. | 
 |     std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd(); | 
 |     for (std::string& linkCmd : linkCmds) { | 
 |       linkCmd = cmStrCat(launcher, linkCmd); | 
 |       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | 
 |                                                    linkCmd, vars); | 
 |     } | 
 |  | 
 |     // If there is no ranlib the command will be ":".  Skip it. | 
 |     cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands()); | 
 |  | 
 |     rule.Command = | 
 |       this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config); | 
 |  | 
 |     // Write the linker rule with response file if needed. | 
 |     rule.Comment = | 
 |       cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ', | 
 |                this->GetVisibleTypeName(), '.'); | 
 |     rule.Description = | 
 |       cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ', | 
 |                this->GetVisibleTypeName(), " $TARGET_FILE"); | 
 |     rule.Restat = "$RESTAT"; | 
 |  | 
 |     this->GetGlobalGenerator()->AddRule(rule); | 
 |   } | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules( | 
 |   const std::string& config) | 
 | { | 
 |   const cmMakefile* mf = this->GetMakefile(); | 
 |  | 
 |   cmNinjaRule rule(this->LanguageLinkerCudaDeviceRule(config)); | 
 |   rule.Command = this->GetLocalGenerator()->BuildCommandLine( | 
 |     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"), | 
 |                " -arch=$ARCH $REGISTER -o=$out $in") }, | 
 |     config, config); | 
 |   rule.Comment = "Rule for CUDA device linking."; | 
 |   rule.Description = "Linking CUDA $out"; | 
 |   this->GetGlobalGenerator()->AddRule(rule); | 
 |  | 
 |   cmRulePlaceholderExpander::RuleVariables vars; | 
 |   vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); | 
 |   vars.CMTargetType = | 
 |     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); | 
 |  | 
 |   vars.Language = "CUDA"; | 
 |   vars.Object = "$out"; | 
 |   vars.Fatbinary = "$FATBIN"; | 
 |   vars.RegisterFile = "$REGISTER"; | 
 |   vars.LinkFlags = "$LINK_FLAGS"; | 
 |  | 
 |   std::string flags = this->GetFlags("CUDA", config); | 
 |   vars.Flags = flags.c_str(); | 
 |  | 
 |   std::string compileCmd = this->GetMakefile()->GetRequiredDefinition( | 
 |     "CMAKE_CUDA_DEVICE_LINK_COMPILE"); | 
 |   std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( | 
 |     this->GetLocalGenerator()->CreateRulePlaceholderExpander()); | 
 |   rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | 
 |                                                compileCmd, vars); | 
 |  | 
 |   rule.Name = this->LanguageLinkerCudaDeviceCompileRule(config); | 
 |   rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd }, | 
 |                                                              config, config); | 
 |   rule.Comment = "Rule for compiling CUDA device stubs."; | 
 |   rule.Description = "Compiling CUDA device stub $out"; | 
 |   this->GetGlobalGenerator()->AddRule(rule); | 
 |  | 
 |   rule.Name = this->LanguageLinkerCudaFatbinaryRule(config); | 
 |   rule.Command = this->GetLocalGenerator()->BuildCommandLine( | 
 |     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"), | 
 |                " -64 -cmdline=--compile-only -compress-all -link " | 
 |                "--embedded-fatbin=$out $PROFILES") }, | 
 |     config, config); | 
 |   rule.Comment = "Rule for CUDA fatbinaries."; | 
 |   rule.Description = "Creating fatbinary $out"; | 
 |   this->GetGlobalGenerator()->AddRule(rule); | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, | 
 |                                                  const std::string& config) | 
 | { | 
 |   cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType(); | 
 |  | 
 |   std::string linkRuleName = this->LanguageLinkerRule(config); | 
 |   if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) { | 
 |     cmNinjaRule rule(std::move(linkRuleName)); | 
 |     cmRulePlaceholderExpander::RuleVariables vars; | 
 |     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); | 
 |     vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str(); | 
 |  | 
 |     std::string lang = this->TargetLinkLanguage(config); | 
 |     vars.Language = lang.c_str(); | 
 |     vars.AIXExports = "$AIX_EXPORTS"; | 
 |  | 
 |     if (this->TargetLinkLanguage(config) == "Swift") { | 
 |       vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME"; | 
 |       vars.SwiftModule = "$SWIFT_MODULE"; | 
 |       vars.SwiftModuleName = "$SWIFT_MODULE_NAME"; | 
 |       vars.SwiftOutputFileMap = "$SWIFT_OUTPUT_FILE_MAP"; | 
 |       vars.SwiftSources = "$SWIFT_SOURCES"; | 
 |  | 
 |       vars.Defines = "$DEFINES"; | 
 |       vars.Flags = "$FLAGS"; | 
 |       vars.Includes = "$INCLUDES"; | 
 |     } | 
 |  | 
 |     std::string responseFlag; | 
 |  | 
 |     std::string cmakeVarLang = | 
 |       cmStrCat("CMAKE_", this->TargetLinkLanguage(config)); | 
 |  | 
 |     // build response file name | 
 |     std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; | 
 |     cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); | 
 |  | 
 |     if (flag) { | 
 |       responseFlag = *flag; | 
 |     } else { | 
 |       responseFlag = "@"; | 
 |     } | 
 |  | 
 |     if (!useResponseFile || responseFlag.empty()) { | 
 |       vars.Objects = "$in"; | 
 |       vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES"; | 
 |     } else { | 
 |       rule.RspFile = "$RSP_FILE"; | 
 |       responseFlag += rule.RspFile; | 
 |  | 
 |       // build response file content | 
 |       if (this->GetGlobalGenerator()->IsGCCOnWindows()) { | 
 |         rule.RspContent = "$in"; | 
 |       } else { | 
 |         rule.RspContent = "$in_newline"; | 
 |       } | 
 |       rule.RspContent += " $LINK_PATH $LINK_LIBRARIES"; | 
 |       if (this->TargetLinkLanguage(config) == "Swift") { | 
 |         vars.SwiftSources = responseFlag.c_str(); | 
 |       } else { | 
 |         vars.Objects = responseFlag.c_str(); | 
 |       } | 
 |       vars.LinkLibraries = ""; | 
 |     } | 
 |  | 
 |     vars.ObjectDir = "$OBJECT_DIR"; | 
 |  | 
 |     vars.Target = "$TARGET_FILE"; | 
 |  | 
 |     vars.SONameFlag = "$SONAME_FLAG"; | 
 |     vars.TargetSOName = "$SONAME"; | 
 |     vars.TargetInstallNameDir = "$INSTALLNAME_DIR"; | 
 |     vars.TargetPDB = "$TARGET_PDB"; | 
 |  | 
 |     // Setup the target version. | 
 |     std::string targetVersionMajor; | 
 |     std::string targetVersionMinor; | 
 |     { | 
 |       std::ostringstream majorStream; | 
 |       std::ostringstream minorStream; | 
 |       int major; | 
 |       int minor; | 
 |       this->GetGeneratorTarget()->GetTargetVersion(major, minor); | 
 |       majorStream << major; | 
 |       minorStream << minor; | 
 |       targetVersionMajor = majorStream.str(); | 
 |       targetVersionMinor = minorStream.str(); | 
 |     } | 
 |     vars.TargetVersionMajor = targetVersionMajor.c_str(); | 
 |     vars.TargetVersionMinor = targetVersionMinor.c_str(); | 
 |  | 
 |     vars.Flags = "$FLAGS"; | 
 |     vars.LinkFlags = "$LINK_FLAGS"; | 
 |     vars.Manifests = "$MANIFESTS"; | 
 |  | 
 |     std::string langFlags; | 
 |     if (targetType != cmStateEnums::EXECUTABLE) { | 
 |       langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS"; | 
 |       vars.LanguageCompileFlags = langFlags.c_str(); | 
 |     } | 
 |  | 
 |     std::string linkerLauncher = this->GetLinkerLauncher(config); | 
 |     if (cmNonempty(linkerLauncher)) { | 
 |       vars.Launcher = linkerLauncher.c_str(); | 
 |     } | 
 |  | 
 |     std::string launcher; | 
 |     cmValue val = this->GetLocalGenerator()->GetRuleLauncher( | 
 |       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); | 
 |     if (cmNonempty(val)) { | 
 |       launcher = cmStrCat(*val, ' '); | 
 |     } | 
 |  | 
 |     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( | 
 |       this->GetLocalGenerator()->CreateRulePlaceholderExpander()); | 
 |  | 
 |     // Rule for linking library/executable. | 
 |     std::vector<std::string> linkCmds = this->ComputeLinkCmd(config); | 
 |     for (std::string& linkCmd : linkCmds) { | 
 |       linkCmd = cmStrCat(launcher, linkCmd); | 
 |       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), | 
 |                                                    linkCmd, vars); | 
 |     } | 
 |  | 
 |     // If there is no ranlib the command will be ":".  Skip it. | 
 |     cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands()); | 
 |  | 
 |     linkCmds.insert(linkCmds.begin(), "$PRE_LINK"); | 
 |     linkCmds.emplace_back("$POST_BUILD"); | 
 |     rule.Command = | 
 |       this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config); | 
 |  | 
 |     // Write the linker rule with response file if needed. | 
 |     rule.Comment = | 
 |       cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ', | 
 |                this->GetVisibleTypeName(), '.'); | 
 |     rule.Description = | 
 |       cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ', | 
 |                this->GetVisibleTypeName(), " $TARGET_FILE"); | 
 |     rule.Restat = "$RESTAT"; | 
 |     this->GetGlobalGenerator()->AddRule(rule); | 
 |   } | 
 |  | 
 |   auto const tgtNames = this->TargetNames(config); | 
 |   if (tgtNames.Output != tgtNames.Real && | 
 |       !this->GetGeneratorTarget()->IsFrameworkOnApple()) { | 
 |     std::string cmakeCommand = | 
 |       this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); | 
 |     if (targetType == cmStateEnums::EXECUTABLE) { | 
 |       cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE"); | 
 |       { | 
 |         std::vector<std::string> cmd; | 
 |         cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out"); | 
 |         cmd.emplace_back("$POST_BUILD"); | 
 |         rule.Command = | 
 |           this->GetLocalGenerator()->BuildCommandLine(cmd, config, config); | 
 |       } | 
 |       rule.Description = "Creating executable symlink $out"; | 
 |       rule.Comment = "Rule for creating executable symlink."; | 
 |       this->GetGlobalGenerator()->AddRule(rule); | 
 |     } else { | 
 |       cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY"); | 
 |       { | 
 |         std::vector<std::string> cmd; | 
 |         cmd.push_back(cmakeCommand + | 
 |                       " -E cmake_symlink_library $in $SONAME $out"); | 
 |         cmd.emplace_back("$POST_BUILD"); | 
 |         rule.Command = | 
 |           this->GetLocalGenerator()->BuildCommandLine(cmd, config, config); | 
 |       } | 
 |       rule.Description = "Creating library symlink $out"; | 
 |       rule.Comment = "Rule for creating library symlink."; | 
 |       this->GetGlobalGenerator()->AddRule(rule); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd() | 
 | { | 
 |   std::vector<std::string> linkCmds; | 
 |  | 
 |   // this target requires separable cuda compilation | 
 |   // now build the correct command depending on if the target is | 
 |   // an executable or a dynamic library. | 
 |   std::string linkCmd; | 
 |   switch (this->GetGeneratorTarget()->GetType()) { | 
 |     case cmStateEnums::STATIC_LIBRARY: | 
 |     case cmStateEnums::SHARED_LIBRARY: | 
 |     case cmStateEnums::MODULE_LIBRARY: { | 
 |       this->GetMakefile()->GetDefExpandList("CMAKE_CUDA_DEVICE_LINK_LIBRARY", | 
 |                                             linkCmds); | 
 |     } break; | 
 |     case cmStateEnums::EXECUTABLE: { | 
 |       this->GetMakefile()->GetDefExpandList( | 
 |         "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE", linkCmds); | 
 |     } break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   return linkCmds; | 
 | } | 
 |  | 
 | std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd( | 
 |   const std::string& config) | 
 | { | 
 |   std::vector<std::string> linkCmds; | 
 |   cmMakefile* mf = this->GetMakefile(); | 
 |   { | 
 |     // If we have a rule variable prefer it. In the case of static libraries | 
 |     // this occurs when things like IPO is enabled, and we need to use the | 
 |     // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead. | 
 |     std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable( | 
 |       this->TargetLinkLanguage(config), config); | 
 |     cmValue linkCmd = mf->GetDefinition(linkCmdVar); | 
 |     if (linkCmd) { | 
 |       std::string linkCmdStr = *linkCmd; | 
 |       if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) { | 
 |         std::string ruleVar = | 
 |           cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config), | 
 |                    "_GNUtoMS_RULE"); | 
 |         if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) { | 
 |           linkCmdStr += *rule; | 
 |         } | 
 |       } | 
 |       cmExpandList(linkCmdStr, linkCmds); | 
 |       if (this->UseLWYU) { | 
 |         cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK"); | 
 |         if (lwyuCheck) { | 
 |           std::string cmakeCommand = cmStrCat( | 
 |             this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |               cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL), | 
 |             " -E __run_co_compile --lwyu="); | 
 |           cmakeCommand += | 
 |             this->GetLocalGenerator()->EscapeForShell(*lwyuCheck); | 
 |  | 
 |           std::string targetOutputReal = | 
 |             this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath( | 
 |               config, cmStateEnums::RuntimeBinaryArtifact, | 
 |               /*realname=*/true)); | 
 |           cmakeCommand += cmStrCat(" --source=", targetOutputReal); | 
 |           linkCmds.push_back(std::move(cmakeCommand)); | 
 |         } | 
 |       } | 
 |       return linkCmds; | 
 |     } | 
 |   } | 
 |   switch (this->GetGeneratorTarget()->GetType()) { | 
 |     case cmStateEnums::STATIC_LIBRARY: { | 
 |       // We have archive link commands set. First, delete the existing archive. | 
 |       { | 
 |         std::string cmakeCommand = | 
 |           this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |             cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); | 
 |         linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE"); | 
 |       } | 
 |       // TODO: Use ARCHIVE_APPEND for archives over a certain size. | 
 |       { | 
 |         std::string linkCmdVar = cmStrCat( | 
 |           "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE"); | 
 |  | 
 |         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable( | 
 |           linkCmdVar, this->TargetLinkLanguage(config), config); | 
 |  | 
 |         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar); | 
 |         cmExpandList(linkCmd, linkCmds); | 
 |       } | 
 |       { | 
 |         std::string linkCmdVar = cmStrCat( | 
 |           "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH"); | 
 |  | 
 |         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable( | 
 |           linkCmdVar, this->TargetLinkLanguage(config), config); | 
 |  | 
 |         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar); | 
 |         cmExpandList(linkCmd, linkCmds); | 
 |       } | 
 | #ifdef __APPLE__ | 
 |       // On macOS ranlib truncates the fractional part of the static archive | 
 |       // file modification time.  If the archive and at least one contained | 
 |       // object file were created within the same second this will make look | 
 |       // the archive older than the object file. On subsequent ninja runs this | 
 |       // leads to re-achiving and updating dependent targets. | 
 |       // As a work-around we touch the archive after ranlib (see #19222). | 
 |       { | 
 |         std::string cmakeCommand = | 
 |           this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |             cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); | 
 |         linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE"); | 
 |       } | 
 | #endif | 
 |     } break; | 
 |     case cmStateEnums::SHARED_LIBRARY: | 
 |     case cmStateEnums::MODULE_LIBRARY: | 
 |       break; | 
 |     case cmStateEnums::EXECUTABLE: | 
 |       if (this->TargetLinkLanguage(config) == "Swift") { | 
 |         if (this->GeneratorTarget->IsExecutableWithExports()) { | 
 |           this->Makefile->GetDefExpandList("CMAKE_EXE_EXPORTS_Swift_FLAG", | 
 |                                            linkCmds); | 
 |         } | 
 |       } | 
 |       break; | 
 |     default: | 
 |       assert(false && "Unexpected target type"); | 
 |   } | 
 |   return linkCmds; | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( | 
 |   const std::string& config, const std::string& fileConfig, | 
 |   bool firstForConfig) | 
 | { | 
 |   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); | 
 |   if (!globalGen->GetLanguageEnabled("CUDA")) { | 
 |     return; | 
 |   } | 
 |  | 
 |   cmGeneratorTarget* genTarget = this->GetGeneratorTarget(); | 
 |  | 
 |   bool requiresDeviceLinking = requireDeviceLinking( | 
 |     *this->GeneratorTarget, *this->GetLocalGenerator(), config); | 
 |   if (!requiresDeviceLinking) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // First and very important step is to make sure while inside this | 
 |   // step our link language is set to CUDA | 
 |   std::string const& objExt = | 
 |     this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION"); | 
 |  | 
 |   std::string targetOutputDir = | 
 |     cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget), | 
 |              globalGen->ConfigDirectory(config), "/"); | 
 |   targetOutputDir = globalGen->ExpandCFGIntDir(targetOutputDir, config); | 
 |  | 
 |   std::string targetOutputReal = | 
 |     this->ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt); | 
 |  | 
 |   if (firstForConfig) { | 
 |     globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); | 
 |   } | 
 |   this->DeviceLinkObject = targetOutputReal; | 
 |  | 
 |   // Write comments. | 
 |   cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream()); | 
 |   this->GetCommonFileStream() | 
 |     << "# Device Link build statements for " | 
 |     << cmState::GetTargetTypeName(genTarget->GetType()) << " target " | 
 |     << this->GetTargetName() << "\n\n"; | 
 |  | 
 |   if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") { | 
 |     std::string architecturesStr = | 
 |       this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES"); | 
 |  | 
 |     if (cmIsOff(architecturesStr)) { | 
 |       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, | 
 |                                    "CUDA_SEPARABLE_COMPILATION on Clang " | 
 |                                    "requires CUDA_ARCHITECTURES to be set."); | 
 |       return; | 
 |     } | 
 |  | 
 |     this->WriteDeviceLinkRules(config); | 
 |     this->WriteDeviceLinkStatements(config, cmExpandedList(architecturesStr), | 
 |                                     targetOutputReal); | 
 |   } else { | 
 |     this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir, | 
 |                                          targetOutputReal); | 
 |   } | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements( | 
 |   const std::string& config, const std::vector<std::string>& architectures, | 
 |   const std::string& output) | 
 | { | 
 |   // Ensure there are no duplicates. | 
 |   const cmNinjaDeps explicitDeps = [&]() -> std::vector<std::string> { | 
 |     std::unordered_set<std::string> depsSet; | 
 |     const cmNinjaDeps linkDeps = | 
 |       this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true); | 
 |     const cmNinjaDeps objects = this->GetObjects(config); | 
 |     depsSet.insert(linkDeps.begin(), linkDeps.end()); | 
 |     depsSet.insert(objects.begin(), objects.end()); | 
 |  | 
 |     std::vector<std::string> deps; | 
 |     std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps)); | 
 |     return deps; | 
 |   }(); | 
 |  | 
 |   cmGlobalNinjaGenerator* globalGen{ this->GetGlobalGenerator() }; | 
 |   const std::string objectDir = | 
 |     cmStrCat(this->GeneratorTarget->GetSupportDirectory(), | 
 |              globalGen->ConfigDirectory(config)); | 
 |   const std::string ninjaOutputDir = this->ConvertToNinjaPath(objectDir); | 
 |  | 
 |   cmNinjaBuild fatbinary(this->LanguageLinkerCudaFatbinaryRule(config)); | 
 |  | 
 |   // Link device code for each architecture. | 
 |   for (const std::string& architectureKind : architectures) { | 
 |     // Clang always generates real code, so strip the specifier. | 
 |     const std::string architecture = | 
 |       architectureKind.substr(0, architectureKind.find('-')); | 
 |     const std::string cubin = | 
 |       cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin"); | 
 |  | 
 |     cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config)); | 
 |     dlink.ExplicitDeps = explicitDeps; | 
 |     dlink.Outputs = { cubin }; | 
 |     dlink.Variables["ARCH"] = cmStrCat("sm_", architecture); | 
 |  | 
 |     // The generated register file contains macros that when expanded register | 
 |     // the device routines. Because the routines are the same for all | 
 |     // architectures the register file will be the same too. Thus generate it | 
 |     // only on the first invocation to reduce overhead. | 
 |     if (fatbinary.ExplicitDeps.empty()) { | 
 |       dlink.Variables["REGISTER"] = cmStrCat( | 
 |         "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h"); | 
 |     } | 
 |  | 
 |     fatbinary.Variables["PROFILES"] += | 
 |       cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin); | 
 |     fatbinary.ExplicitDeps.emplace_back(cubin); | 
 |  | 
 |     globalGen->WriteBuild(this->GetCommonFileStream(), dlink); | 
 |   } | 
 |  | 
 |   // Combine all architectures into a single fatbinary. | 
 |   fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") }; | 
 |   globalGen->WriteBuild(this->GetCommonFileStream(), fatbinary); | 
 |  | 
 |   // Compile the stub that registers the kernels and contains the fatbinaries. | 
 |   cmLocalNinjaGenerator* localGen{ this->GetLocalGenerator() }; | 
 |   cmNinjaBuild dcompile(this->LanguageLinkerCudaDeviceCompileRule(config)); | 
 |   dcompile.Outputs = { output }; | 
 |   dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") }; | 
 |   dcompile.Variables["FATBIN"] = localGen->ConvertToOutputFormat( | 
 |     cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL); | 
 |   dcompile.Variables["REGISTER"] = localGen->ConvertToOutputFormat( | 
 |     cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL); | 
 |  | 
 |   cmNinjaLinkLineDeviceComputer linkLineComputer( | 
 |     localGen, localGen->GetStateSnapshot().GetDirectory(), globalGen); | 
 |   linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig()); | 
 |  | 
 |   // Link libraries and paths are only used during the final executable/library | 
 |   // link. | 
 |   std::string frameworkPath; | 
 |   std::string linkPath; | 
 |   std::string linkLibs; | 
 |   localGen->GetDeviceLinkFlags(linkLineComputer, config, linkLibs, | 
 |                                dcompile.Variables["LINK_FLAGS"], frameworkPath, | 
 |                                linkPath, this->GetGeneratorTarget()); | 
 |  | 
 |   globalGen->WriteBuild(this->GetCommonFileStream(), dcompile); | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement( | 
 |   const std::string& config, const std::string& fileConfig, | 
 |   const std::string& outputDir, const std::string& output) | 
 | { | 
 |   cmGeneratorTarget* genTarget = this->GetGeneratorTarget(); | 
 |   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); | 
 |  | 
 |   std::string targetOutputImplib = this->ConvertToNinjaPath( | 
 |     genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); | 
 |  | 
 |   if (config != fileConfig) { | 
 |     std::string targetOutputFileConfigDir = | 
 |       cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget), | 
 |                globalGen->ConfigDirectory(fileConfig), "/"); | 
 |     targetOutputFileConfigDir = | 
 |       globalGen->ExpandCFGIntDir(outputDir, fileConfig); | 
 |     if (outputDir == targetOutputFileConfigDir) { | 
 |       return; | 
 |     } | 
 |  | 
 |     if (!genTarget->GetFullName(config, cmStateEnums::ImportLibraryArtifact) | 
 |            .empty() && | 
 |         !genTarget | 
 |            ->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact) | 
 |            .empty() && | 
 |         targetOutputImplib == | 
 |           this->ConvertToNinjaPath(genTarget->GetFullPath( | 
 |             fileConfig, cmStateEnums::ImportLibraryArtifact))) { | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   // Compute the comment. | 
 |   cmNinjaBuild build(this->LanguageLinkerDeviceRule(config)); | 
 |   build.Comment = | 
 |     cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output); | 
 |  | 
 |   cmNinjaVars& vars = build.Variables; | 
 |  | 
 |   // Compute outputs. | 
 |   build.Outputs.push_back(output); | 
 |   // Compute specific libraries to link with. | 
 |   build.ExplicitDeps = this->GetObjects(config); | 
 |   build.ImplicitDeps = | 
 |     this->ComputeLinkDeps(this->TargetLinkLanguage(config), config); | 
 |  | 
 |   std::string frameworkPath; | 
 |   std::string linkPath; | 
 |  | 
 |   std::string createRule = | 
 |     genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config); | 
 |   cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); | 
 |  | 
 |   vars["TARGET_FILE"] = | 
 |     localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL); | 
 |  | 
 |   cmNinjaLinkLineDeviceComputer linkLineComputer( | 
 |     this->GetLocalGenerator(), | 
 |     this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(), globalGen); | 
 |   linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig()); | 
 |  | 
 |   localGen.GetDeviceLinkFlags(linkLineComputer, config, vars["LINK_LIBRARIES"], | 
 |                               vars["LINK_FLAGS"], frameworkPath, linkPath, | 
 |                               genTarget); | 
 |  | 
 |   this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars); | 
 |  | 
 |   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]); | 
 |  | 
 |   vars["MANIFESTS"] = this->GetManifests(config); | 
 |  | 
 |   vars["LINK_PATH"] = frameworkPath + linkPath; | 
 |  | 
 |   // Compute language specific link flags. | 
 |   std::string langFlags; | 
 |   localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config); | 
 |   vars["LANGUAGE_COMPILE_FLAGS"] = langFlags; | 
 |  | 
 |   auto const tgtNames = this->TargetNames(config); | 
 |   if (genTarget->HasSOName(config)) { | 
 |     vars["SONAME_FLAG"] = | 
 |       this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config)); | 
 |     vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject, | 
 |                                                     cmOutputConverter::SHELL); | 
 |     if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) { | 
 |       std::string install_dir = | 
 |         this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config); | 
 |       if (!install_dir.empty()) { | 
 |         vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat( | 
 |           install_dir, cmOutputConverter::SHELL); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (!tgtNames.ImportLibrary.empty()) { | 
 |     const std::string impLibPath = localGen.ConvertToOutputFormat( | 
 |       targetOutputImplib, cmOutputConverter::SHELL); | 
 |     vars["TARGET_IMPLIB"] = impLibPath; | 
 |     this->EnsureParentDirectoryExists(targetOutputImplib); | 
 |   } | 
 |  | 
 |   const std::string objPath = | 
 |     cmStrCat(this->GetGeneratorTarget()->GetSupportDirectory(), | 
 |              globalGen->ConfigDirectory(config)); | 
 |  | 
 |   vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |     this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL); | 
 |   this->EnsureDirectoryExists(objPath); | 
 |  | 
 |   this->SetMsvcTargetPdbVariable(vars, config); | 
 |  | 
 |   std::string& linkLibraries = vars["LINK_LIBRARIES"]; | 
 |   std::string& link_path = vars["LINK_PATH"]; | 
 |   if (globalGen->IsGCCOnWindows()) { | 
 |     // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) | 
 |     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); | 
 |     std::replace(link_path.begin(), link_path.end(), '\\', '/'); | 
 |   } | 
 |  | 
 |   // Device linking currently doesn't support response files so | 
 |   // do not check if the user has explicitly forced a response file. | 
 |   int const commandLineLengthLimit = | 
 |     static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) - | 
 |     globalGen->GetRuleCmdLength(build.Rule); | 
 |  | 
 |   build.RspFile = this->ConvertToNinjaPath( | 
 |     cmStrCat("CMakeFiles/", genTarget->GetName(), | 
 |              globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp")); | 
 |  | 
 |   // Gather order-only dependencies. | 
 |   this->GetLocalGenerator()->AppendTargetDepends( | 
 |     this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config, | 
 |     DependOnTargetArtifact); | 
 |  | 
 |   // Write the build statement for this target. | 
 |   bool usedResponseFile = false; | 
 |   globalGen->WriteBuild(this->GetCommonFileStream(), build, | 
 |                         commandLineLengthLimit, &usedResponseFile); | 
 |   this->WriteNvidiaDeviceLinkRule(usedResponseFile, config); | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteLinkStatement( | 
 |   const std::string& config, const std::string& fileConfig, | 
 |   bool firstForConfig) | 
 | { | 
 |   cmMakefile* mf = this->GetMakefile(); | 
 |   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); | 
 |   cmGeneratorTarget* gt = this->GetGeneratorTarget(); | 
 |  | 
 |   std::string targetOutput = this->ConvertToNinjaPath(gt->GetFullPath(config)); | 
 |   std::string targetOutputReal = this->ConvertToNinjaPath( | 
 |     gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact, | 
 |                     /*realname=*/true)); | 
 |   std::string targetOutputImplib = this->ConvertToNinjaPath( | 
 |     gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); | 
 |  | 
 |   if (config != fileConfig) { | 
 |     if (targetOutput == | 
 |         this->ConvertToNinjaPath(gt->GetFullPath(fileConfig))) { | 
 |       return; | 
 |     } | 
 |     if (targetOutputReal == | 
 |         this->ConvertToNinjaPath( | 
 |           gt->GetFullPath(fileConfig, cmStateEnums::RuntimeBinaryArtifact, | 
 |                           /*realname=*/true))) { | 
 |       return; | 
 |     } | 
 |     if (!gt->GetFullName(config, cmStateEnums::ImportLibraryArtifact) | 
 |            .empty() && | 
 |         !gt->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact) | 
 |            .empty() && | 
 |         targetOutputImplib == | 
 |           this->ConvertToNinjaPath(gt->GetFullPath( | 
 |             fileConfig, cmStateEnums::ImportLibraryArtifact))) { | 
 |       return; | 
 |     } | 
 |   } | 
 |  | 
 |   auto const tgtNames = this->TargetNames(config); | 
 |   if (gt->IsAppBundleOnApple()) { | 
 |     // Create the app bundle | 
 |     std::string outpath = gt->GetDirectory(config); | 
 |     this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath, | 
 |                                               config); | 
 |  | 
 |     // Calculate the output path | 
 |     targetOutput = cmStrCat(outpath, '/', tgtNames.Output); | 
 |     targetOutput = this->ConvertToNinjaPath(targetOutput); | 
 |     targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real); | 
 |     targetOutputReal = this->ConvertToNinjaPath(targetOutputReal); | 
 |   } else if (gt->IsFrameworkOnApple()) { | 
 |     // Create the library framework. | 
 |  | 
 |     cmOSXBundleGenerator::SkipParts bundleSkipParts; | 
 |     if (globalGen->GetName() == "Ninja Multi-Config") { | 
 |       const auto postFix = this->GeneratorTarget->GetFilePostfix(config); | 
 |       // Skip creating Info.plist when there are multiple configurations, and | 
 |       // the current configuration has a postfix. The non-postfix configuration | 
 |       // Info.plist can be used by all the other configurations. | 
 |       if (!postFix.empty()) { | 
 |         bundleSkipParts.infoPlist = true; | 
 |       } | 
 |     } | 
 |  | 
 |     this->OSXBundleGenerator->CreateFramework( | 
 |       tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts); | 
 |   } else if (gt->IsCFBundleOnApple()) { | 
 |     // Create the core foundation bundle. | 
 |     this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output, | 
 |                                              gt->GetDirectory(config), config); | 
 |   } | 
 |  | 
 |   // Write comments. | 
 |   cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); | 
 |   const cmStateEnums::TargetType targetType = gt->GetType(); | 
 |   this->GetImplFileStream(fileConfig) | 
 |     << "# Link build statements for " << cmState::GetTargetTypeName(targetType) | 
 |     << " target " << this->GetTargetName() << "\n\n"; | 
 |  | 
 |   cmNinjaBuild linkBuild(this->LanguageLinkerRule(config)); | 
 |   cmNinjaVars& vars = linkBuild.Variables; | 
 |  | 
 |   // Compute the comment. | 
 |   linkBuild.Comment = | 
 |     cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal); | 
 |  | 
 |   // Compute outputs. | 
 |   linkBuild.Outputs.push_back(targetOutputReal); | 
 |   if (firstForConfig) { | 
 |     globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); | 
 |   } | 
 |  | 
 |   if (this->TargetLinkLanguage(config) == "Swift") { | 
 |     vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string { | 
 |       cmGeneratorTarget::Names targetNames = | 
 |         this->GetGeneratorTarget()->GetLibraryNames(config); | 
 |       return targetNames.Base; | 
 |     }(); | 
 |  | 
 |     vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string { | 
 |       if (cmValue name = gt->GetProperty("Swift_MODULE_NAME")) { | 
 |         return *name; | 
 |       } | 
 |       return gt->GetName(); | 
 |     }(); | 
 |  | 
 |     vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string { | 
 |       std::string directory = | 
 |         this->GetLocalGenerator()->GetCurrentBinaryDirectory(); | 
 |       if (cmValue prop = this->GetGeneratorTarget()->GetProperty( | 
 |             "Swift_MODULE_DIRECTORY")) { | 
 |         directory = *prop; | 
 |       } | 
 |  | 
 |       std::string name = module + ".swiftmodule"; | 
 |       if (cmValue prop = | 
 |             this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) { | 
 |         name = *prop; | 
 |       } | 
 |  | 
 |       return this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |         this->ConvertToNinjaPath(directory + "/" + name), | 
 |         cmOutputConverter::SHELL); | 
 |     }(vars["SWIFT_MODULE_NAME"]); | 
 |  | 
 |     const std::string map = cmStrCat(gt->GetSupportDirectory(), '/', config, | 
 |                                      '/', "output-file-map.json"); | 
 |     vars["SWIFT_OUTPUT_FILE_MAP"] = | 
 |       this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |         this->ConvertToNinjaPath(map), cmOutputConverter::SHELL); | 
 |  | 
 |     vars["SWIFT_SOURCES"] = [this, config]() -> std::string { | 
 |       std::vector<cmSourceFile const*> sources; | 
 |       std::stringstream oss; | 
 |  | 
 |       this->GetGeneratorTarget()->GetObjectSources(sources, config); | 
 |       cmLocalGenerator const* LocalGen = this->GetLocalGenerator(); | 
 |       for (const auto& source : sources) { | 
 |         oss << " " | 
 |             << LocalGen->ConvertToOutputFormat( | 
 |                  this->GetCompiledSourceNinjaPath(source), | 
 |                  cmOutputConverter::SHELL); | 
 |       } | 
 |       return oss.str(); | 
 |     }(); | 
 |  | 
 |     // Since we do not perform object builds, compute the | 
 |     // defines/flags/includes here so that they can be passed along | 
 |     // appropriately. | 
 |     vars["DEFINES"] = this->GetDefines("Swift", config); | 
 |     vars["FLAGS"] = this->GetFlags("Swift", config); | 
 |     vars["INCLUDES"] = this->GetIncludes("Swift", config); | 
 |   } | 
 |  | 
 |   // Compute specific libraries to link with. | 
 |   if (this->TargetLinkLanguage(config) == "Swift") { | 
 |     std::vector<cmSourceFile const*> sources; | 
 |     gt->GetObjectSources(sources, config); | 
 |     for (const auto& source : sources) { | 
 |       linkBuild.Outputs.push_back( | 
 |         this->ConvertToNinjaPath(this->GetObjectFilePath(source, config))); | 
 |       linkBuild.ExplicitDeps.emplace_back( | 
 |         this->GetCompiledSourceNinjaPath(source)); | 
 |     } | 
 |     linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]); | 
 |   } else { | 
 |     linkBuild.ExplicitDeps = this->GetObjects(config); | 
 |   } | 
 |  | 
 |   std::vector<std::string> extraISPCObjects = | 
 |     this->GetGeneratorTarget()->GetGeneratedISPCObjects(config); | 
 |   std::transform(extraISPCObjects.begin(), extraISPCObjects.end(), | 
 |                  std::back_inserter(linkBuild.ExplicitDeps), | 
 |                  this->MapToNinjaPath()); | 
 |  | 
 |   linkBuild.ImplicitDeps = | 
 |     this->ComputeLinkDeps(this->TargetLinkLanguage(config), config); | 
 |  | 
 |   if (!this->DeviceLinkObject.empty()) { | 
 |     linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject); | 
 |   } | 
 |  | 
 |   std::string frameworkPath; | 
 |   std::string linkPath; | 
 |  | 
 |   std::string createRule = | 
 |     gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config); | 
 |   bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE"); | 
 |   cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); | 
 |  | 
 |   vars["TARGET_FILE"] = | 
 |     localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL); | 
 |  | 
 |   std::unique_ptr<cmLinkLineComputer> linkLineComputer = | 
 |     globalGen->CreateLinkLineComputer( | 
 |       this->GetLocalGenerator(), | 
 |       this->GetLocalGenerator()->GetStateSnapshot().GetDirectory()); | 
 |   linkLineComputer->SetUseWatcomQuote(useWatcomQuote); | 
 |   linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig()); | 
 |  | 
 |   localGen.GetTargetFlags(linkLineComputer.get(), config, | 
 |                           vars["LINK_LIBRARIES"], vars["FLAGS"], | 
 |                           vars["LINK_FLAGS"], frameworkPath, linkPath, gt); | 
 |  | 
 |   // Add OS X version flags, if any. | 
 |   if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || | 
 |       this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) { | 
 |     this->AppendOSXVerFlag(vars["LINK_FLAGS"], | 
 |                            this->TargetLinkLanguage(config), "COMPATIBILITY", | 
 |                            true); | 
 |     this->AppendOSXVerFlag(vars["LINK_FLAGS"], | 
 |                            this->TargetLinkLanguage(config), "CURRENT", false); | 
 |   } | 
 |  | 
 |   this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars); | 
 |  | 
 |   this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"], | 
 |                                 config); | 
 |  | 
 |   this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags( | 
 |     vars["LINK_FLAGS"], this->GetGeneratorTarget(), | 
 |     this->TargetLinkLanguage(config)); | 
 |  | 
 |   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]); | 
 |  | 
 |   vars["MANIFESTS"] = this->GetManifests(config); | 
 |   vars["AIX_EXPORTS"] = this->GetAIXExports(config); | 
 |  | 
 |   vars["LINK_PATH"] = frameworkPath + linkPath; | 
 |  | 
 |   // Compute architecture specific link flags.  Yes, these go into a different | 
 |   // variable for executables, probably due to a mistake made when duplicating | 
 |   // code between the Makefile executable and library generators. | 
 |   if (targetType == cmStateEnums::EXECUTABLE) { | 
 |     std::string t = vars["FLAGS"]; | 
 |     localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config), | 
 |                                   config); | 
 |     vars["FLAGS"] = t; | 
 |   } else { | 
 |     std::string t = vars["ARCH_FLAGS"]; | 
 |     localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config), | 
 |                                   config); | 
 |     vars["ARCH_FLAGS"] = t; | 
 |     t.clear(); | 
 |     localGen.AddLanguageFlagsForLinking( | 
 |       t, gt, this->TargetLinkLanguage(config), config); | 
 |     vars["LANGUAGE_COMPILE_FLAGS"] = t; | 
 |   } | 
 |   if (gt->HasSOName(config)) { | 
 |     vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config)); | 
 |     vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject, | 
 |                                                     cmOutputConverter::SHELL); | 
 |     if (targetType == cmStateEnums::SHARED_LIBRARY) { | 
 |       std::string install_dir = gt->GetInstallNameDirForBuildTree(config); | 
 |       if (!install_dir.empty()) { | 
 |         vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat( | 
 |           install_dir, cmOutputConverter::SHELL); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator()); | 
 |  | 
 |   if (!tgtNames.ImportLibrary.empty()) { | 
 |     const std::string impLibPath = localGen.ConvertToOutputFormat( | 
 |       targetOutputImplib, cmOutputConverter::SHELL); | 
 |     vars["TARGET_IMPLIB"] = impLibPath; | 
 |     this->EnsureParentDirectoryExists(targetOutputImplib); | 
 |     if (gt->HasImportLibrary(config)) { | 
 |       // Some linkers may update a binary without touching its import lib. | 
 |       byproducts.ExplicitOuts.emplace_back(targetOutputImplib); | 
 |       if (firstForConfig) { | 
 |         globalGen->GetByproductsForCleanTarget(config).push_back( | 
 |           targetOutputImplib); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (!this->SetMsvcTargetPdbVariable(vars, config)) { | 
 |     // It is common to place debug symbols at a specific place, | 
 |     // so we need a plain target name in the rule available. | 
 |     std::string prefix; | 
 |     std::string base; | 
 |     std::string suffix; | 
 |     gt->GetFullNameComponents(prefix, base, suffix, config); | 
 |     std::string dbg_suffix = ".dbg"; | 
 |     // TODO: Where to document? | 
 |     if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) { | 
 |       dbg_suffix = *d; | 
 |     } | 
 |     vars["TARGET_PDB"] = base + suffix + dbg_suffix; | 
 |   } | 
 |  | 
 |   const std::string objPath = | 
 |     cmStrCat(gt->GetSupportDirectory(), globalGen->ConfigDirectory(config)); | 
 |   vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |     this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL); | 
 |   this->EnsureDirectoryExists(objPath); | 
 |  | 
 |   std::string& linkLibraries = vars["LINK_LIBRARIES"]; | 
 |   std::string& link_path = vars["LINK_PATH"]; | 
 |   if (globalGen->IsGCCOnWindows()) { | 
 |     // ar.exe can't handle backslashes in rsp files (implicitly used by gcc) | 
 |     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/'); | 
 |     std::replace(link_path.begin(), link_path.end(), '\\', '/'); | 
 |   } | 
 |  | 
 |   const std::vector<cmCustomCommand>* cmdLists[3] = { | 
 |     >->GetPreBuildCommands(), >->GetPreLinkCommands(), | 
 |     >->GetPostBuildCommands() | 
 |   }; | 
 |  | 
 |   std::vector<std::string> preLinkCmdLines; | 
 |   std::vector<std::string> postBuildCmdLines; | 
 |  | 
 |   std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines, | 
 |                                                 &preLinkCmdLines, | 
 |                                                 &postBuildCmdLines }; | 
 |  | 
 |   for (unsigned i = 0; i != 3; ++i) { | 
 |     for (cmCustomCommand const& cc : *cmdLists[i]) { | 
 |       if (config == fileConfig || | 
 |           this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(), | 
 |                                                          cc.GetBacktrace())) { | 
 |         cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(), | 
 |                                      true, config); | 
 |         localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]); | 
 |         std::vector<std::string> const& ccByproducts = ccg.GetByproducts(); | 
 |         byproducts.Add(ccByproducts); | 
 |         std::transform( | 
 |           ccByproducts.begin(), ccByproducts.end(), | 
 |           std::back_inserter(globalGen->GetByproductsForCleanTarget()), | 
 |           this->MapToNinjaPath()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // maybe create .def file from list of objects | 
 |   cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | 
 |     gt->GetModuleDefinitionInfo(config); | 
 |   if (mdi && mdi->DefFileGenerated) { | 
 |     std::string cmakeCommand = | 
 |       this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); | 
 |     std::string cmd = | 
 |       cmStrCat(cmakeCommand, " -E __create_def ", | 
 |                this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |                  mdi->DefFile, cmOutputConverter::SHELL), | 
 |                ' '); | 
 |     std::string obj_list_file = mdi->DefFile + ".objs"; | 
 |     cmd += this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |       obj_list_file, cmOutputConverter::SHELL); | 
 |  | 
 |     cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM"); | 
 |     if (cmNonempty(nm_executable)) { | 
 |       cmd += " --nm="; | 
 |       cmd += this->LocalCommonGenerator->ConvertToOutputFormat( | 
 |         *nm_executable, cmOutputConverter::SHELL); | 
 |     } | 
 |     preLinkCmdLines.push_back(std::move(cmd)); | 
 |  | 
 |     // create a list of obj files for the -E __create_def to read | 
 |     cmGeneratedFileStream fout(obj_list_file); | 
 |  | 
 |     if (mdi->WindowsExportAllSymbols) { | 
 |       cmNinjaDeps objs = this->GetObjects(config); | 
 |       for (std::string const& obj : objs) { | 
 |         if (cmHasLiteralSuffix(obj, ".obj")) { | 
 |           fout << obj << "\n"; | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     for (cmSourceFile const* src : mdi->Sources) { | 
 |       fout << src->GetFullPath() << "\n"; | 
 |     } | 
 |   } | 
 |   // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR | 
 |   // for the link commands. | 
 |   if (!preLinkCmdLines.empty()) { | 
 |     const std::string homeOutDir = localGen.ConvertToOutputFormat( | 
 |       localGen.GetBinaryDirectory(), cmOutputConverter::SHELL); | 
 |     preLinkCmdLines.push_back("cd " + homeOutDir); | 
 |   } | 
 |  | 
 |   vars["PRE_LINK"] = localGen.BuildCommandLine( | 
 |     preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget); | 
 |   std::string postBuildCmdLine = | 
 |     localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig, | 
 |                               "post-build", this->GeneratorTarget); | 
 |  | 
 |   cmNinjaVars symlinkVars; | 
 |   bool const symlinkNeeded = | 
 |     (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple()); | 
 |   if (!symlinkNeeded) { | 
 |     vars["POST_BUILD"] = postBuildCmdLine; | 
 |   } else { | 
 |     vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP; | 
 |     symlinkVars["POST_BUILD"] = postBuildCmdLine; | 
 |   } | 
 |  | 
 |   std::string cmakeVarLang = | 
 |     cmStrCat("CMAKE_", this->TargetLinkLanguage(config)); | 
 |  | 
 |   // build response file name | 
 |   std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; | 
 |  | 
 |   cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar); | 
 |  | 
 |   bool const lang_supports_response = | 
 |     !(this->TargetLinkLanguage(config) == "RC" || | 
 |       (this->TargetLinkLanguage(config) == "CUDA" && !flag)); | 
 |   int commandLineLengthLimit = -1; | 
 |   if (!lang_supports_response || !this->ForceResponseFile()) { | 
 |     commandLineLengthLimit = | 
 |       static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) - | 
 |       globalGen->GetRuleCmdLength(linkBuild.Rule); | 
 |   } | 
 |  | 
 |   linkBuild.RspFile = this->ConvertToNinjaPath( | 
 |     cmStrCat("CMakeFiles/", gt->GetName(), | 
 |              globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp")); | 
 |  | 
 |   // Gather order-only dependencies. | 
 |   this->GetLocalGenerator()->AppendTargetDepends( | 
 |     gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact); | 
 |  | 
 |   // Add order-only dependencies on versioning symlinks of shared libs we link. | 
 |   if (!this->GeneratorTarget->IsDLLPlatform()) { | 
 |     if (cmComputeLinkInformation* cli = | 
 |           this->GeneratorTarget->GetLinkInformation(config)) { | 
 |       for (auto const& item : cli->GetItems()) { | 
 |         if (item.Target && | 
 |             item.Target->GetType() == cmStateEnums::SHARED_LIBRARY && | 
 |             !item.Target->IsFrameworkOnApple()) { | 
 |           std::string const& lib = | 
 |             this->ConvertToNinjaPath(item.Target->GetFullPath(config)); | 
 |           if (std::find(linkBuild.ImplicitDeps.begin(), | 
 |                         linkBuild.ImplicitDeps.end(), | 
 |                         lib) == linkBuild.ImplicitDeps.end()) { | 
 |             linkBuild.OrderOnlyDeps.emplace_back(lib); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Ninja should restat after linking if and only if there are byproducts. | 
 |   vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1"; | 
 |  | 
 |   linkBuild.Outputs.reserve(linkBuild.Outputs.size() + | 
 |                             byproducts.ExplicitOuts.size()); | 
 |   std::move(byproducts.ExplicitOuts.begin(), byproducts.ExplicitOuts.end(), | 
 |             std::back_inserter(linkBuild.Outputs)); | 
 |   linkBuild.WorkDirOuts = std::move(byproducts.WorkDirOuts); | 
 |  | 
 |   // Write the build statement for this target. | 
 |   bool usedResponseFile = false; | 
 |   globalGen->WriteBuild(this->GetImplFileStream(fileConfig), linkBuild, | 
 |                         commandLineLengthLimit, &usedResponseFile); | 
 |   this->WriteLinkRule(usedResponseFile, config); | 
 |  | 
 |   if (symlinkNeeded) { | 
 |     if (targetType == cmStateEnums::EXECUTABLE) { | 
 |       cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE"); | 
 |       build.Comment = "Create executable symlink " + targetOutput; | 
 |       build.Outputs.push_back(targetOutput); | 
 |       if (firstForConfig) { | 
 |         globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput); | 
 |       } | 
 |       build.ExplicitDeps.push_back(targetOutputReal); | 
 |       build.Variables = std::move(symlinkVars); | 
 |       globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build); | 
 |     } else { | 
 |       cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY"); | 
 |       build.Comment = "Create library symlink " + targetOutput; | 
 |  | 
 |       std::string const soName = this->ConvertToNinjaPath( | 
 |         this->GetTargetFilePath(tgtNames.SharedObject, config)); | 
 |       // If one link has to be created. | 
 |       if (targetOutputReal == soName || targetOutput == soName) { | 
 |         symlinkVars["SONAME"] = | 
 |           this->GetLocalGenerator()->ConvertToOutputFormat( | 
 |             soName, cmOutputConverter::SHELL); | 
 |       } else { | 
 |         symlinkVars["SONAME"].clear(); | 
 |         build.Outputs.push_back(soName); | 
 |         if (firstForConfig) { | 
 |           globalGen->GetByproductsForCleanTarget(config).push_back(soName); | 
 |         } | 
 |       } | 
 |       build.Outputs.push_back(targetOutput); | 
 |       if (firstForConfig) { | 
 |         globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput); | 
 |       } | 
 |       build.ExplicitDeps.push_back(targetOutputReal); | 
 |       build.Variables = std::move(symlinkVars); | 
 |  | 
 |       globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build); | 
 |     } | 
 |   } | 
 |  | 
 |   // Add aliases for the file name and the target name. | 
 |   globalGen->AddTargetAlias(tgtNames.Output, gt, config); | 
 |   globalGen->AddTargetAlias(this->GetTargetName(), gt, config); | 
 | } | 
 |  | 
 | void cmNinjaNormalTargetGenerator::WriteObjectLibStatement( | 
 |   const std::string& config) | 
 | { | 
 |   // Write a phony output that depends on all object files. | 
 |   { | 
 |     cmNinjaBuild build("phony"); | 
 |     build.Comment = "Object library " + this->GetTargetName(); | 
 |     this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(), | 
 |                                                    build.Outputs, config); | 
 |     this->GetLocalGenerator()->AppendTargetOutputs( | 
 |       this->GetGeneratorTarget(), | 
 |       this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), config); | 
 |     build.ExplicitDeps = this->GetObjects(config); | 
 |     this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build); | 
 |   } | 
 |  | 
 |   // Add aliases for the target name. | 
 |   this->GetGlobalGenerator()->AddTargetAlias( | 
 |     this->GetTargetName(), this->GetGeneratorTarget(), config); | 
 | } | 
 |  | 
 | cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames( | 
 |   const std::string& config) const | 
 | { | 
 |   if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) { | 
 |     return this->GeneratorTarget->GetExecutableNames(config); | 
 |   } | 
 |   return this->GeneratorTarget->GetLibraryNames(config); | 
 | } | 
 |  | 
 | std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage( | 
 |   const std::string& config) const | 
 | { | 
 |   return this->GeneratorTarget->GetLinkerLanguage(config); | 
 | } |