1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmNinjaNormalTargetGenerator.h"
4 
5 #include <algorithm>
6 #include <cassert>
7 #include <iterator>
8 #include <map>
9 #include <set>
10 #include <sstream>
11 #include <unordered_set>
12 #include <utility>
13 
14 #include <cm/memory>
15 #include <cm/optional>
16 #include <cm/vector>
17 
18 #include "cmComputeLinkInformation.h"
19 #include "cmCustomCommand.h" // IWYU pragma: keep
20 #include "cmCustomCommandGenerator.h"
21 #include "cmGeneratedFileStream.h"
22 #include "cmGeneratorTarget.h"
23 #include "cmGlobalNinjaGenerator.h"
24 #include "cmLinkLineComputer.h"
25 #include "cmLinkLineDeviceComputer.h"
26 #include "cmLocalCommonGenerator.h"
27 #include "cmLocalGenerator.h"
28 #include "cmLocalNinjaGenerator.h"
29 #include "cmMakefile.h"
30 #include "cmMessageType.h"
31 #include "cmNinjaLinkLineDeviceComputer.h"
32 #include "cmNinjaTypes.h"
33 #include "cmOSXBundleGenerator.h"
34 #include "cmOutputConverter.h"
35 #include "cmRulePlaceholderExpander.h"
36 #include "cmSourceFile.h"
37 #include "cmState.h"
38 #include "cmStateDirectory.h"
39 #include "cmStateSnapshot.h"
40 #include "cmStateTypes.h"
41 #include "cmStringAlgorithms.h"
42 #include "cmSystemTools.h"
43 #include "cmValue.h"
44 
cmNinjaNormalTargetGenerator(cmGeneratorTarget * target)45 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
46   cmGeneratorTarget* target)
47   : cmNinjaTargetGenerator(target)
48 {
49   if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) {
50     // on Windows the output dir is already needed at compile time
51     // ensure the directory exists (OutDir test)
52     for (auto const& config : this->GetConfigNames()) {
53       this->EnsureDirectoryExists(target->GetDirectory(config));
54     }
55   }
56 
57   this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
58   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
59 }
60 
61 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
62 
Generate(const std::string & config)63 void cmNinjaNormalTargetGenerator::Generate(const std::string& config)
64 {
65   std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
66   if (this->TargetLinkLanguage(config).empty()) {
67     cmSystemTools::Error("CMake can not determine linker language for "
68                          "target: " +
69                          this->GetGeneratorTarget()->GetName());
70     return;
71   }
72 
73   // Write the rules for each language.
74   this->WriteLanguagesRules(config);
75 
76   // Write the build statements
77   bool firstForConfig = true;
78   for (auto const& fileConfig : this->GetConfigNames()) {
79     if (!this->GetGlobalGenerator()
80            ->GetCrossConfigs(fileConfig)
81            .count(config)) {
82       continue;
83     }
84     this->WriteObjectBuildStatements(config, fileConfig, firstForConfig);
85     firstForConfig = false;
86   }
87 
88   if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
89     this->WriteObjectLibStatement(config);
90   } else {
91     firstForConfig = true;
92     for (auto const& fileConfig : this->GetConfigNames()) {
93       if (!this->GetGlobalGenerator()
94              ->GetCrossConfigs(fileConfig)
95              .count(config)) {
96         continue;
97       }
98       // If this target has cuda language link inputs, and we need to do
99       // device linking
100       this->WriteDeviceLinkStatement(config, fileConfig, firstForConfig);
101       this->WriteLinkStatement(config, fileConfig, firstForConfig);
102       firstForConfig = false;
103     }
104   }
105   if (this->GetGlobalGenerator()->EnableCrossConfigBuild()) {
106     this->GetGlobalGenerator()->AddTargetAlias(
107       this->GetTargetName(), this->GetGeneratorTarget(), "all");
108   }
109 
110   // Find ADDITIONAL_CLEAN_FILES
111   this->AdditionalCleanFiles(config);
112 }
113 
WriteLanguagesRules(const std::string & config)114 void cmNinjaNormalTargetGenerator::WriteLanguagesRules(
115   const std::string& config)
116 {
117 #ifdef NINJA_GEN_VERBOSE_FILES
118   cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
119   this->GetRulesFileStream()
120     << "# Rules for each languages for "
121     << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
122     << " target " << this->GetTargetName() << "\n\n";
123 #endif
124 
125   // Write rules for languages compiled in this target.
126   std::set<std::string> languages;
127   std::vector<cmSourceFile const*> sourceFiles;
128   this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
129   for (cmSourceFile const* sf : sourceFiles) {
130     std::string const lang = sf->GetLanguage();
131     if (!lang.empty()) {
132       languages.insert(lang);
133     }
134   }
135   for (std::string const& language : languages) {
136     this->WriteLanguageRules(language, config);
137   }
138 }
139 
GetVisibleTypeName() const140 const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
141 {
142   switch (this->GetGeneratorTarget()->GetType()) {
143     case cmStateEnums::STATIC_LIBRARY:
144       return "static library";
145     case cmStateEnums::SHARED_LIBRARY:
146       return "shared library";
147     case cmStateEnums::MODULE_LIBRARY:
148       if (this->GetGeneratorTarget()->IsCFBundleOnApple()) {
149         return "CFBundle shared module";
150       } else {
151         return "shared module";
152       }
153     case cmStateEnums::EXECUTABLE:
154       return "executable";
155     default:
156       return nullptr;
157   }
158 }
159 
LanguageLinkerRule(const std::string & config) const160 std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule(
161   const std::string& config) const
162 {
163   return cmStrCat(
164     this->TargetLinkLanguage(config), "_",
165     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
166     "_LINKER__",
167     cmGlobalNinjaGenerator::EncodeRuleName(
168       this->GetGeneratorTarget()->GetName()),
169     "_", config);
170 }
171 
LanguageLinkerDeviceRule(const std::string & config) const172 std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule(
173   const std::string& config) const
174 {
175   return cmStrCat(
176     this->TargetLinkLanguage(config), "_",
177     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
178     "_DEVICE_LINKER__",
179     cmGlobalNinjaGenerator::EncodeRuleName(
180       this->GetGeneratorTarget()->GetName()),
181     "_", config);
182 }
183 
LanguageLinkerCudaDeviceRule(const std::string & config) const184 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule(
185   const std::string& config) const
186 {
187   return cmStrCat(
188     this->TargetLinkLanguage(config), "_DEVICE_LINK__",
189     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
190     '_', config);
191 }
192 
LanguageLinkerCudaDeviceCompileRule(const std::string & config) const193 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule(
194   const std::string& config) const
195 {
196   return cmStrCat(
197     this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__",
198     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
199     '_', config);
200 }
201 
LanguageLinkerCudaFatbinaryRule(const std::string & config) const202 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
203   const std::string& config) const
204 {
205   return cmStrCat(
206     this->TargetLinkLanguage(config), "_FATBINARY__",
207     cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
208     '_', config);
209 }
210 
211 struct cmNinjaRemoveNoOpCommands
212 {
operator ()cmNinjaRemoveNoOpCommands213   bool operator()(std::string const& cmd)
214   {
215     return cmd.empty() || cmd[0] == ':';
216   }
217 };
218 
WriteNvidiaDeviceLinkRule(bool useResponseFile,const std::string & config)219 void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
220   bool useResponseFile, const std::string& config)
221 {
222   cmNinjaRule rule(this->LanguageLinkerDeviceRule(config));
223   if (!this->GetGlobalGenerator()->HasRule(rule.Name)) {
224     cmRulePlaceholderExpander::RuleVariables vars;
225     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
226     vars.CMTargetType =
227       cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
228         .c_str();
229 
230     vars.Language = "CUDA";
231 
232     // build response file name
233     std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
234       "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG");
235 
236     if (!useResponseFile || responseFlag.empty()) {
237       vars.Objects = "$in";
238       vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
239     } else {
240       rule.RspFile = "$RSP_FILE";
241       responseFlag += rule.RspFile;
242 
243       // build response file content
244       if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
245         rule.RspContent = "$in";
246       } else {
247         rule.RspContent = "$in_newline";
248       }
249       rule.RspContent += " $LINK_LIBRARIES";
250       vars.Objects = responseFlag.c_str();
251       vars.LinkLibraries = "";
252     }
253 
254     vars.ObjectDir = "$OBJECT_DIR";
255 
256     vars.Target = "$TARGET_FILE";
257 
258     vars.SONameFlag = "$SONAME_FLAG";
259     vars.TargetSOName = "$SONAME";
260     vars.TargetPDB = "$TARGET_PDB";
261     vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
262 
263     vars.Flags = "$FLAGS";
264     vars.LinkFlags = "$LINK_FLAGS";
265     vars.Manifests = "$MANIFESTS";
266 
267     vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
268 
269     std::string launcher;
270     cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
271       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
272     if (cmNonempty(val)) {
273       launcher = cmStrCat(*val, ' ');
274     }
275 
276     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
277       this->GetLocalGenerator()->CreateRulePlaceholderExpander());
278 
279     // Rule for linking library/executable.
280     std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
281     for (std::string& linkCmd : linkCmds) {
282       linkCmd = cmStrCat(launcher, linkCmd);
283       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
284                                                    linkCmd, vars);
285     }
286 
287     // If there is no ranlib the command will be ":".  Skip it.
288     cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
289 
290     rule.Command =
291       this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
292 
293     // Write the linker rule with response file if needed.
294     rule.Comment =
295       cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
296                this->GetVisibleTypeName(), '.');
297     rule.Description =
298       cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
299                this->GetVisibleTypeName(), " $TARGET_FILE");
300     rule.Restat = "$RESTAT";
301 
302     this->GetGlobalGenerator()->AddRule(rule);
303   }
304 }
305 
WriteDeviceLinkRules(const std::string & config)306 void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
307   const std::string& config)
308 {
309   const cmMakefile* mf = this->GetMakefile();
310 
311   cmNinjaRule rule(this->LanguageLinkerCudaDeviceRule(config));
312   rule.Command = this->GetLocalGenerator()->BuildCommandLine(
313     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
314                " -arch=$ARCH $REGISTER -o=$out $in") },
315     config, config);
316   rule.Comment = "Rule for CUDA device linking.";
317   rule.Description = "Linking CUDA $out";
318   this->GetGlobalGenerator()->AddRule(rule);
319 
320   cmRulePlaceholderExpander::RuleVariables vars;
321   vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
322   vars.CMTargetType =
323     cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
324 
325   vars.Language = "CUDA";
326   vars.Object = "$out";
327   vars.Fatbinary = "$FATBIN";
328   vars.RegisterFile = "$REGISTER";
329 
330   std::string flags = this->GetFlags("CUDA", config);
331   vars.Flags = flags.c_str();
332 
333   std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(
334     "CMAKE_CUDA_DEVICE_LINK_COMPILE");
335   std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
336     this->GetLocalGenerator()->CreateRulePlaceholderExpander());
337   rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
338                                                compileCmd, vars);
339 
340   rule.Name = this->LanguageLinkerCudaDeviceCompileRule(config);
341   rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd },
342                                                              config, config);
343   rule.Comment = "Rule for compiling CUDA device stubs.";
344   rule.Description = "Compiling CUDA device stub $out";
345   this->GetGlobalGenerator()->AddRule(rule);
346 
347   rule.Name = this->LanguageLinkerCudaFatbinaryRule(config);
348   rule.Command = this->GetLocalGenerator()->BuildCommandLine(
349     { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
350                " -64 -cmdline=--compile-only -compress-all -link "
351                "--embedded-fatbin=$out $PROFILES") },
352     config, config);
353   rule.Comment = "Rule for CUDA fatbinaries.";
354   rule.Description = "Creating fatbinary $out";
355   this->GetGlobalGenerator()->AddRule(rule);
356 }
357 
WriteLinkRule(bool useResponseFile,const std::string & config)358 void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
359                                                  const std::string& config)
360 {
361   cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType();
362 
363   std::string linkRuleName = this->LanguageLinkerRule(config);
364   if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) {
365     cmNinjaRule rule(std::move(linkRuleName));
366     cmRulePlaceholderExpander::RuleVariables vars;
367     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
368     vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
369 
370     std::string lang = this->TargetLinkLanguage(config);
371     vars.Language = lang.c_str();
372     vars.AIXExports = "$AIX_EXPORTS";
373 
374     if (this->TargetLinkLanguage(config) == "Swift") {
375       vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
376       vars.SwiftModule = "$SWIFT_MODULE";
377       vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
378       vars.SwiftOutputFileMap = "$SWIFT_OUTPUT_FILE_MAP";
379       vars.SwiftSources = "$SWIFT_SOURCES";
380 
381       vars.Defines = "$DEFINES";
382       vars.Flags = "$FLAGS";
383       vars.Includes = "$INCLUDES";
384     }
385 
386     std::string responseFlag;
387 
388     std::string cmakeVarLang =
389       cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
390 
391     // build response file name
392     std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
393     cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
394 
395     if (flag) {
396       responseFlag = *flag;
397     } else {
398       responseFlag = "@";
399     }
400 
401     if (!useResponseFile || responseFlag.empty()) {
402       vars.Objects = "$in";
403       vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
404     } else {
405       rule.RspFile = "$RSP_FILE";
406       responseFlag += rule.RspFile;
407 
408       // build response file content
409       if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
410         rule.RspContent = "$in";
411       } else {
412         rule.RspContent = "$in_newline";
413       }
414       rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
415       if (this->TargetLinkLanguage(config) == "Swift") {
416         vars.SwiftSources = responseFlag.c_str();
417       } else {
418         vars.Objects = responseFlag.c_str();
419       }
420       vars.LinkLibraries = "";
421     }
422 
423     vars.ObjectDir = "$OBJECT_DIR";
424 
425     vars.Target = "$TARGET_FILE";
426 
427     vars.SONameFlag = "$SONAME_FLAG";
428     vars.TargetSOName = "$SONAME";
429     vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
430     vars.TargetPDB = "$TARGET_PDB";
431 
432     // Setup the target version.
433     std::string targetVersionMajor;
434     std::string targetVersionMinor;
435     {
436       std::ostringstream majorStream;
437       std::ostringstream minorStream;
438       int major;
439       int minor;
440       this->GetGeneratorTarget()->GetTargetVersion(major, minor);
441       majorStream << major;
442       minorStream << minor;
443       targetVersionMajor = majorStream.str();
444       targetVersionMinor = minorStream.str();
445     }
446     vars.TargetVersionMajor = targetVersionMajor.c_str();
447     vars.TargetVersionMinor = targetVersionMinor.c_str();
448 
449     vars.Flags = "$FLAGS";
450     vars.LinkFlags = "$LINK_FLAGS";
451     vars.Manifests = "$MANIFESTS";
452 
453     std::string langFlags;
454     if (targetType != cmStateEnums::EXECUTABLE) {
455       langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
456       vars.LanguageCompileFlags = langFlags.c_str();
457     }
458 
459     std::string linkerLauncher = this->GetLinkerLauncher(config);
460     if (cmNonempty(linkerLauncher)) {
461       vars.Launcher = linkerLauncher.c_str();
462     }
463 
464     std::string launcher;
465     cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
466       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
467     if (cmNonempty(val)) {
468       launcher = cmStrCat(*val, ' ');
469     }
470 
471     std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
472       this->GetLocalGenerator()->CreateRulePlaceholderExpander());
473 
474     // Rule for linking library/executable.
475     std::vector<std::string> linkCmds = this->ComputeLinkCmd(config);
476     for (std::string& linkCmd : linkCmds) {
477       linkCmd = cmStrCat(launcher, linkCmd);
478       rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
479                                                    linkCmd, vars);
480     }
481 
482     // If there is no ranlib the command will be ":".  Skip it.
483     cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
484 
485     linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
486     linkCmds.emplace_back("$POST_BUILD");
487     rule.Command =
488       this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
489 
490     // Write the linker rule with response file if needed.
491     rule.Comment =
492       cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
493                this->GetVisibleTypeName(), '.');
494     rule.Description =
495       cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
496                this->GetVisibleTypeName(), " $TARGET_FILE");
497     rule.Restat = "$RESTAT";
498     this->GetGlobalGenerator()->AddRule(rule);
499   }
500 
501   auto const tgtNames = this->TargetNames(config);
502   if (tgtNames.Output != tgtNames.Real &&
503       !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
504     std::string cmakeCommand =
505       this->GetLocalGenerator()->ConvertToOutputFormat(
506         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
507     if (targetType == cmStateEnums::EXECUTABLE) {
508       cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
509       {
510         std::vector<std::string> cmd;
511         cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
512         cmd.emplace_back("$POST_BUILD");
513         rule.Command =
514           this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
515       }
516       rule.Description = "Creating executable symlink $out";
517       rule.Comment = "Rule for creating executable symlink.";
518       this->GetGlobalGenerator()->AddRule(rule);
519     } else {
520       cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
521       {
522         std::vector<std::string> cmd;
523         cmd.push_back(cmakeCommand +
524                       " -E cmake_symlink_library $in $SONAME $out");
525         cmd.emplace_back("$POST_BUILD");
526         rule.Command =
527           this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
528       }
529       rule.Description = "Creating library symlink $out";
530       rule.Comment = "Rule for creating library symlink.";
531       this->GetGlobalGenerator()->AddRule(rule);
532     }
533   }
534 }
535 
ComputeDeviceLinkCmd()536 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
537 {
538   std::vector<std::string> linkCmds;
539 
540   // this target requires separable cuda compilation
541   // now build the correct command depending on if the target is
542   // an executable or a dynamic library.
543   std::string linkCmd;
544   switch (this->GetGeneratorTarget()->GetType()) {
545     case cmStateEnums::STATIC_LIBRARY:
546     case cmStateEnums::SHARED_LIBRARY:
547     case cmStateEnums::MODULE_LIBRARY: {
548       this->GetMakefile()->GetDefExpandList("CMAKE_CUDA_DEVICE_LINK_LIBRARY",
549                                             linkCmds);
550     } break;
551     case cmStateEnums::EXECUTABLE: {
552       this->GetMakefile()->GetDefExpandList(
553         "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE", linkCmds);
554     } break;
555     default:
556       break;
557   }
558   return linkCmds;
559 }
560 
ComputeLinkCmd(const std::string & config)561 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd(
562   const std::string& config)
563 {
564   std::vector<std::string> linkCmds;
565   cmMakefile* mf = this->GetMakefile();
566   {
567     // If we have a rule variable prefer it. In the case of static libraries
568     // this occurs when things like IPO is enabled, and we need to use the
569     // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
570     std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
571       this->TargetLinkLanguage(config), config);
572     cmValue linkCmd = mf->GetDefinition(linkCmdVar);
573     if (linkCmd) {
574       std::string linkCmdStr = *linkCmd;
575       if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
576         std::string ruleVar =
577           cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
578                    "_GNUtoMS_RULE");
579         if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
580           linkCmdStr += *rule;
581         }
582       }
583       cmExpandList(linkCmdStr, linkCmds);
584       if (this->UseLWYU) {
585         cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
586         if (lwyuCheck) {
587           std::string cmakeCommand = cmStrCat(
588             this->GetLocalGenerator()->ConvertToOutputFormat(
589               cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
590             " -E __run_co_compile --lwyu=");
591           cmakeCommand +=
592             this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
593 
594           std::string targetOutputReal =
595             this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath(
596               config, cmStateEnums::RuntimeBinaryArtifact,
597               /*realname=*/true));
598           cmakeCommand += cmStrCat(" --source=", targetOutputReal);
599           linkCmds.push_back(std::move(cmakeCommand));
600         }
601       }
602       return linkCmds;
603     }
604   }
605   switch (this->GetGeneratorTarget()->GetType()) {
606     case cmStateEnums::STATIC_LIBRARY: {
607       // We have archive link commands set. First, delete the existing archive.
608       {
609         std::string cmakeCommand =
610           this->GetLocalGenerator()->ConvertToOutputFormat(
611             cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
612         linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE");
613       }
614       // TODO: Use ARCHIVE_APPEND for archives over a certain size.
615       {
616         std::string linkCmdVar = cmStrCat(
617           "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE");
618 
619         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
620           linkCmdVar, this->TargetLinkLanguage(config), config);
621 
622         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
623         cmExpandList(linkCmd, linkCmds);
624       }
625       {
626         std::string linkCmdVar = cmStrCat(
627           "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH");
628 
629         linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
630           linkCmdVar, this->TargetLinkLanguage(config), config);
631 
632         std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
633         cmExpandList(linkCmd, linkCmds);
634       }
635 #ifdef __APPLE__
636       // On macOS ranlib truncates the fractional part of the static archive
637       // file modification time.  If the archive and at least one contained
638       // object file were created within the same second this will make look
639       // the archive older than the object file. On subsequent ninja runs this
640       // leads to re-achiving and updating dependent targets.
641       // As a work-around we touch the archive after ranlib (see #19222).
642       {
643         std::string cmakeCommand =
644           this->GetLocalGenerator()->ConvertToOutputFormat(
645             cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
646         linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
647       }
648 #endif
649     } break;
650     case cmStateEnums::SHARED_LIBRARY:
651     case cmStateEnums::MODULE_LIBRARY:
652       break;
653     case cmStateEnums::EXECUTABLE:
654       if (this->TargetLinkLanguage(config) == "Swift") {
655         if (this->GeneratorTarget->IsExecutableWithExports()) {
656           this->Makefile->GetDefExpandList("CMAKE_EXE_EXPORTS_Swift_FLAG",
657                                            linkCmds);
658         }
659       }
660       break;
661     default:
662       assert(false && "Unexpected target type");
663   }
664   return linkCmds;
665 }
666 
WriteDeviceLinkStatement(const std::string & config,const std::string & fileConfig,bool firstForConfig)667 void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
668   const std::string& config, const std::string& fileConfig,
669   bool firstForConfig)
670 {
671   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
672   if (!globalGen->GetLanguageEnabled("CUDA")) {
673     return;
674   }
675 
676   cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
677 
678   bool requiresDeviceLinking = requireDeviceLinking(
679     *this->GeneratorTarget, *this->GetLocalGenerator(), config);
680   if (!requiresDeviceLinking) {
681     return;
682   }
683 
684   // First and very important step is to make sure while inside this
685   // step our link language is set to CUDA
686   std::string const& objExt =
687     this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
688 
689   std::string targetOutputDir =
690     cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget),
691              globalGen->ConfigDirectory(config), "/");
692   targetOutputDir = globalGen->ExpandCFGIntDir(targetOutputDir, config);
693 
694   std::string targetOutputReal =
695     this->ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt);
696 
697   if (firstForConfig) {
698     globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
699   }
700   this->DeviceLinkObject = targetOutputReal;
701 
702   // Write comments.
703   cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
704   this->GetCommonFileStream()
705     << "# Device Link build statements for "
706     << cmState::GetTargetTypeName(genTarget->GetType()) << " target "
707     << this->GetTargetName() << "\n\n";
708 
709   if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
710     std::string architecturesStr =
711       this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
712 
713     if (cmIsOff(architecturesStr)) {
714       this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
715                                    "CUDA_SEPARABLE_COMPILATION on Clang "
716                                    "requires CUDA_ARCHITECTURES to be set.");
717       return;
718     }
719 
720     this->WriteDeviceLinkRules(config);
721     this->WriteDeviceLinkStatements(config, cmExpandedList(architecturesStr),
722                                     targetOutputReal);
723   } else {
724     this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir,
725                                          targetOutputReal);
726   }
727 }
728 
WriteDeviceLinkStatements(const std::string & config,const std::vector<std::string> & architectures,const std::string & output)729 void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
730   const std::string& config, const std::vector<std::string>& architectures,
731   const std::string& output)
732 {
733   // Ensure there are no duplicates.
734   const cmNinjaDeps explicitDeps = [&]() -> std::vector<std::string> {
735     std::unordered_set<std::string> depsSet;
736     const cmNinjaDeps linkDeps =
737       this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true);
738     const cmNinjaDeps objects = this->GetObjects(config);
739     depsSet.insert(linkDeps.begin(), linkDeps.end());
740     depsSet.insert(objects.begin(), objects.end());
741 
742     std::vector<std::string> deps;
743     std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
744     return deps;
745   }();
746 
747   const std::string objectDir =
748     cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
749              this->GetGlobalGenerator()->ConfigDirectory(config));
750   const std::string ninjaOutputDir = this->ConvertToNinjaPath(objectDir);
751 
752   cmNinjaBuild fatbinary(this->LanguageLinkerCudaFatbinaryRule(config));
753 
754   // Link device code for each architecture.
755   for (const std::string& architectureKind : architectures) {
756     // Clang always generates real code, so strip the specifier.
757     const std::string architecture =
758       architectureKind.substr(0, architectureKind.find('-'));
759     const std::string cubin =
760       cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
761 
762     cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config));
763     dlink.ExplicitDeps = explicitDeps;
764     dlink.Outputs = { cubin };
765     dlink.Variables["ARCH"] = cmStrCat("sm_", architecture);
766 
767     // The generated register file contains macros that when expanded register
768     // the device routines. Because the routines are the same for all
769     // architectures the register file will be the same too. Thus generate it
770     // only on the first invocation to reduce overhead.
771     if (fatbinary.ExplicitDeps.empty()) {
772       dlink.Variables["REGISTER"] = cmStrCat(
773         "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
774     }
775 
776     fatbinary.Variables["PROFILES"] +=
777       cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
778     fatbinary.ExplicitDeps.emplace_back(cubin);
779 
780     this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), dlink);
781   }
782 
783   // Combine all architectures into a single fatbinary.
784   fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
785   this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(),
786                                          fatbinary);
787 
788   // Compile the stub that registers the kernels and contains the fatbinaries.
789   cmNinjaBuild dcompile(this->LanguageLinkerCudaDeviceCompileRule(config));
790   dcompile.Outputs = { output };
791   dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
792   dcompile.Variables["FATBIN"] =
793     this->GetLocalGenerator()->ConvertToOutputFormat(
794       cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL);
795   dcompile.Variables["REGISTER"] =
796     this->GetLocalGenerator()->ConvertToOutputFormat(
797       cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL);
798   this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(),
799                                          dcompile);
800 }
801 
WriteNvidiaDeviceLinkStatement(const std::string & config,const std::string & fileConfig,const std::string & outputDir,const std::string & output)802 void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement(
803   const std::string& config, const std::string& fileConfig,
804   const std::string& outputDir, const std::string& output)
805 {
806   cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
807   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
808 
809   std::string targetOutputImplib = this->ConvertToNinjaPath(
810     genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
811 
812   if (config != fileConfig) {
813     std::string targetOutputFileConfigDir =
814       cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget),
815                globalGen->ConfigDirectory(fileConfig), "/");
816     targetOutputFileConfigDir =
817       globalGen->ExpandCFGIntDir(outputDir, fileConfig);
818     if (outputDir == targetOutputFileConfigDir) {
819       return;
820     }
821 
822     if (!genTarget->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
823            .empty() &&
824         !genTarget
825            ->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
826            .empty() &&
827         targetOutputImplib ==
828           this->ConvertToNinjaPath(genTarget->GetFullPath(
829             fileConfig, cmStateEnums::ImportLibraryArtifact))) {
830       return;
831     }
832   }
833 
834   // Compute the comment.
835   cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
836   build.Comment =
837     cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output);
838 
839   cmNinjaVars& vars = build.Variables;
840 
841   // Compute outputs.
842   build.Outputs.push_back(output);
843   // Compute specific libraries to link with.
844   build.ExplicitDeps = this->GetObjects(config);
845   build.ImplicitDeps =
846     this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
847 
848   std::string frameworkPath;
849   std::string linkPath;
850 
851   std::string createRule =
852     genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
853   const bool useWatcomQuote =
854     this->GetMakefile()->IsOn(createRule + "_USE_WATCOM_QUOTE");
855   cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
856 
857   vars["TARGET_FILE"] =
858     localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL);
859 
860   std::unique_ptr<cmLinkLineComputer> linkLineComputer(
861     new cmNinjaLinkLineDeviceComputer(
862       this->GetLocalGenerator(),
863       this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(),
864       globalGen));
865   linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
866   linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig());
867 
868   localGen.GetDeviceLinkFlags(linkLineComputer.get(), config,
869                               vars["LINK_LIBRARIES"], vars["LINK_FLAGS"],
870                               frameworkPath, linkPath, genTarget);
871 
872   this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars);
873 
874   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
875 
876   vars["MANIFESTS"] = this->GetManifests(config);
877 
878   vars["LINK_PATH"] = frameworkPath + linkPath;
879 
880   // Compute language specific link flags.
881   std::string langFlags;
882   localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config);
883   vars["LANGUAGE_COMPILE_FLAGS"] = langFlags;
884 
885   auto const tgtNames = this->TargetNames(config);
886   if (genTarget->HasSOName(config)) {
887     vars["SONAME_FLAG"] =
888       this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
889     vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
890                                                     cmOutputConverter::SHELL);
891     if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
892       std::string install_dir =
893         this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
894       if (!install_dir.empty()) {
895         vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
896           install_dir, cmOutputConverter::SHELL);
897       }
898     }
899   }
900 
901   if (!tgtNames.ImportLibrary.empty()) {
902     const std::string impLibPath = localGen.ConvertToOutputFormat(
903       targetOutputImplib, cmOutputConverter::SHELL);
904     vars["TARGET_IMPLIB"] = impLibPath;
905     this->EnsureParentDirectoryExists(targetOutputImplib);
906   }
907 
908   const std::string objPath =
909     cmStrCat(this->GetGeneratorTarget()->GetSupportDirectory(),
910              globalGen->ConfigDirectory(config));
911 
912   vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
913     this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
914   this->EnsureDirectoryExists(objPath);
915 
916   this->SetMsvcTargetPdbVariable(vars, config);
917 
918   std::string& linkLibraries = vars["LINK_LIBRARIES"];
919   std::string& link_path = vars["LINK_PATH"];
920   if (globalGen->IsGCCOnWindows()) {
921     // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
922     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
923     std::replace(link_path.begin(), link_path.end(), '\\', '/');
924   }
925 
926   // Device linking currently doesn't support response files so
927   // do not check if the user has explicitly forced a response file.
928   int const commandLineLengthLimit =
929     static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
930     globalGen->GetRuleCmdLength(build.Rule);
931 
932   build.RspFile = this->ConvertToNinjaPath(
933     cmStrCat("CMakeFiles/", genTarget->GetName(),
934              globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
935 
936   // Gather order-only dependencies.
937   this->GetLocalGenerator()->AppendTargetDepends(
938     this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config,
939     DependOnTargetArtifact);
940 
941   // Write the build statement for this target.
942   bool usedResponseFile = false;
943   globalGen->WriteBuild(this->GetCommonFileStream(), build,
944                         commandLineLengthLimit, &usedResponseFile);
945   this->WriteNvidiaDeviceLinkRule(usedResponseFile, config);
946 }
947 
WriteLinkStatement(const std::string & config,const std::string & fileConfig,bool firstForConfig)948 void cmNinjaNormalTargetGenerator::WriteLinkStatement(
949   const std::string& config, const std::string& fileConfig,
950   bool firstForConfig)
951 {
952   cmMakefile* mf = this->GetMakefile();
953   cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
954   cmGeneratorTarget* gt = this->GetGeneratorTarget();
955 
956   std::string targetOutput = this->ConvertToNinjaPath(gt->GetFullPath(config));
957   std::string targetOutputReal = this->ConvertToNinjaPath(
958     gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
959                     /*realname=*/true));
960   std::string targetOutputImplib = this->ConvertToNinjaPath(
961     gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
962 
963   if (config != fileConfig) {
964     if (targetOutput ==
965         this->ConvertToNinjaPath(gt->GetFullPath(fileConfig))) {
966       return;
967     }
968     if (targetOutputReal ==
969         this->ConvertToNinjaPath(
970           gt->GetFullPath(fileConfig, cmStateEnums::RuntimeBinaryArtifact,
971                           /*realname=*/true))) {
972       return;
973     }
974     if (!gt->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
975            .empty() &&
976         !gt->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
977            .empty() &&
978         targetOutputImplib ==
979           this->ConvertToNinjaPath(gt->GetFullPath(
980             fileConfig, cmStateEnums::ImportLibraryArtifact))) {
981       return;
982     }
983   }
984 
985   auto const tgtNames = this->TargetNames(config);
986   if (gt->IsAppBundleOnApple()) {
987     // Create the app bundle
988     std::string outpath = gt->GetDirectory(config);
989     this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath,
990                                               config);
991 
992     // Calculate the output path
993     targetOutput = cmStrCat(outpath, '/', tgtNames.Output);
994     targetOutput = this->ConvertToNinjaPath(targetOutput);
995     targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real);
996     targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
997   } else if (gt->IsFrameworkOnApple()) {
998     // Create the library framework.
999 
1000     cmOSXBundleGenerator::SkipParts bundleSkipParts;
1001     if (globalGen->GetName() == "Ninja Multi-Config") {
1002       const auto postFix = this->GeneratorTarget->GetFilePostfix(config);
1003       // Skip creating Info.plist when there are multiple configurations, and
1004       // the current configuration has a postfix. The non-postfix configuration
1005       // Info.plist can be used by all the other configurations.
1006       if (!postFix.empty()) {
1007         bundleSkipParts.infoPlist = true;
1008       }
1009     }
1010 
1011     this->OSXBundleGenerator->CreateFramework(
1012       tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
1013   } else if (gt->IsCFBundleOnApple()) {
1014     // Create the core foundation bundle.
1015     this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,
1016                                              gt->GetDirectory(config), config);
1017   }
1018 
1019   // Write comments.
1020   cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
1021   const cmStateEnums::TargetType targetType = gt->GetType();
1022   this->GetImplFileStream(fileConfig)
1023     << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
1024     << " target " << this->GetTargetName() << "\n\n";
1025 
1026   cmNinjaBuild linkBuild(this->LanguageLinkerRule(config));
1027   cmNinjaVars& vars = linkBuild.Variables;
1028 
1029   // Compute the comment.
1030   linkBuild.Comment =
1031     cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
1032 
1033   // Compute outputs.
1034   linkBuild.Outputs.push_back(targetOutputReal);
1035   if (firstForConfig) {
1036     globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
1037   }
1038 
1039   if (this->TargetLinkLanguage(config) == "Swift") {
1040     vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
1041       cmGeneratorTarget::Names targetNames =
1042         this->GetGeneratorTarget()->GetLibraryNames(config);
1043       return targetNames.Base;
1044     }();
1045 
1046     vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string {
1047       if (cmValue name = gt->GetProperty("Swift_MODULE_NAME")) {
1048         return *name;
1049       }
1050       return gt->GetName();
1051     }();
1052 
1053     vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string {
1054       std::string directory =
1055         this->GetLocalGenerator()->GetCurrentBinaryDirectory();
1056       if (cmValue prop = this->GetGeneratorTarget()->GetProperty(
1057             "Swift_MODULE_DIRECTORY")) {
1058         directory = *prop;
1059       }
1060 
1061       std::string name = module + ".swiftmodule";
1062       if (cmValue prop =
1063             this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) {
1064         name = *prop;
1065       }
1066 
1067       return this->GetLocalGenerator()->ConvertToOutputFormat(
1068         this->ConvertToNinjaPath(directory + "/" + name),
1069         cmOutputConverter::SHELL);
1070     }(vars["SWIFT_MODULE_NAME"]);
1071 
1072     const std::string map = cmStrCat(gt->GetSupportDirectory(), '/', config,
1073                                      '/', "output-file-map.json");
1074     vars["SWIFT_OUTPUT_FILE_MAP"] =
1075       this->GetLocalGenerator()->ConvertToOutputFormat(
1076         this->ConvertToNinjaPath(map), cmOutputConverter::SHELL);
1077 
1078     vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
1079       std::vector<cmSourceFile const*> sources;
1080       std::stringstream oss;
1081 
1082       this->GetGeneratorTarget()->GetObjectSources(sources, config);
1083       cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
1084       for (const auto& source : sources) {
1085         oss << " "
1086             << LocalGen->ConvertToOutputFormat(
1087                  this->GetCompiledSourceNinjaPath(source),
1088                  cmOutputConverter::SHELL);
1089       }
1090       return oss.str();
1091     }();
1092 
1093     // Since we do not perform object builds, compute the
1094     // defines/flags/includes here so that they can be passed along
1095     // appropriately.
1096     vars["DEFINES"] = this->GetDefines("Swift", config);
1097     vars["FLAGS"] = this->GetFlags("Swift", config);
1098     vars["INCLUDES"] = this->GetIncludes("Swift", config);
1099   }
1100 
1101   // Compute specific libraries to link with.
1102   if (this->TargetLinkLanguage(config) == "Swift") {
1103     std::vector<cmSourceFile const*> sources;
1104     gt->GetObjectSources(sources, config);
1105     for (const auto& source : sources) {
1106       linkBuild.Outputs.push_back(
1107         this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
1108       linkBuild.ExplicitDeps.emplace_back(
1109         this->GetCompiledSourceNinjaPath(source));
1110     }
1111     linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
1112   } else {
1113     linkBuild.ExplicitDeps = this->GetObjects(config);
1114   }
1115 
1116   std::vector<std::string> extraISPCObjects =
1117     this->GetGeneratorTarget()->GetGeneratedISPCObjects(config);
1118   std::transform(extraISPCObjects.begin(), extraISPCObjects.end(),
1119                  std::back_inserter(linkBuild.ExplicitDeps),
1120                  this->MapToNinjaPath());
1121 
1122   linkBuild.ImplicitDeps =
1123     this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1124 
1125   if (!this->DeviceLinkObject.empty()) {
1126     linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
1127   }
1128 
1129   std::string frameworkPath;
1130   std::string linkPath;
1131 
1132   std::string createRule =
1133     gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1134   bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
1135   cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1136 
1137   vars["TARGET_FILE"] =
1138     localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
1139 
1140   std::unique_ptr<cmLinkLineComputer> linkLineComputer =
1141     globalGen->CreateLinkLineComputer(
1142       this->GetLocalGenerator(),
1143       this->GetLocalGenerator()->GetStateSnapshot().GetDirectory());
1144   linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
1145   linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig());
1146 
1147   localGen.GetTargetFlags(linkLineComputer.get(), config,
1148                           vars["LINK_LIBRARIES"], vars["FLAGS"],
1149                           vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
1150 
1151   // Add OS X version flags, if any.
1152   if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
1153       this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
1154     this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1155                            this->TargetLinkLanguage(config), "COMPATIBILITY",
1156                            true);
1157     this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1158                            this->TargetLinkLanguage(config), "CURRENT", false);
1159   }
1160 
1161   this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars);
1162 
1163   this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"],
1164                                 config);
1165 
1166   this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
1167     vars["LINK_FLAGS"], this->GetGeneratorTarget(),
1168     this->TargetLinkLanguage(config));
1169 
1170   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
1171 
1172   vars["MANIFESTS"] = this->GetManifests(config);
1173   vars["AIX_EXPORTS"] = this->GetAIXExports(config);
1174 
1175   vars["LINK_PATH"] = frameworkPath + linkPath;
1176 
1177   // Compute architecture specific link flags.  Yes, these go into a different
1178   // variable for executables, probably due to a mistake made when duplicating
1179   // code between the Makefile executable and library generators.
1180   if (targetType == cmStateEnums::EXECUTABLE) {
1181     std::string t = vars["FLAGS"];
1182     localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1183                                   config);
1184     vars["FLAGS"] = t;
1185   } else {
1186     std::string t = vars["ARCH_FLAGS"];
1187     localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1188                                   config);
1189     vars["ARCH_FLAGS"] = t;
1190     t.clear();
1191     localGen.AddLanguageFlagsForLinking(
1192       t, gt, this->TargetLinkLanguage(config), config);
1193     vars["LANGUAGE_COMPILE_FLAGS"] = t;
1194   }
1195   if (gt->HasSOName(config)) {
1196     vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config));
1197     vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1198                                                     cmOutputConverter::SHELL);
1199     if (targetType == cmStateEnums::SHARED_LIBRARY) {
1200       std::string install_dir = gt->GetInstallNameDirForBuildTree(config);
1201       if (!install_dir.empty()) {
1202         vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1203           install_dir, cmOutputConverter::SHELL);
1204       }
1205     }
1206   }
1207 
1208   cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
1209 
1210   if (!tgtNames.ImportLibrary.empty()) {
1211     const std::string impLibPath = localGen.ConvertToOutputFormat(
1212       targetOutputImplib, cmOutputConverter::SHELL);
1213     vars["TARGET_IMPLIB"] = impLibPath;
1214     this->EnsureParentDirectoryExists(targetOutputImplib);
1215     if (gt->HasImportLibrary(config)) {
1216       // Some linkers may update a binary without touching its import lib.
1217       byproducts.ExplicitOuts.emplace_back(targetOutputImplib);
1218       if (firstForConfig) {
1219         globalGen->GetByproductsForCleanTarget(config).push_back(
1220           targetOutputImplib);
1221       }
1222     }
1223   }
1224 
1225   if (!this->SetMsvcTargetPdbVariable(vars, config)) {
1226     // It is common to place debug symbols at a specific place,
1227     // so we need a plain target name in the rule available.
1228     std::string prefix;
1229     std::string base;
1230     std::string suffix;
1231     gt->GetFullNameComponents(prefix, base, suffix, config);
1232     std::string dbg_suffix = ".dbg";
1233     // TODO: Where to document?
1234     if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
1235       dbg_suffix = *d;
1236     }
1237     vars["TARGET_PDB"] = base + suffix + dbg_suffix;
1238   }
1239 
1240   const std::string objPath =
1241     cmStrCat(gt->GetSupportDirectory(), globalGen->ConfigDirectory(config));
1242   vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1243     this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1244   this->EnsureDirectoryExists(objPath);
1245 
1246   std::string& linkLibraries = vars["LINK_LIBRARIES"];
1247   std::string& link_path = vars["LINK_PATH"];
1248   if (globalGen->IsGCCOnWindows()) {
1249     // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1250     std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1251     std::replace(link_path.begin(), link_path.end(), '\\', '/');
1252   }
1253 
1254   const std::vector<cmCustomCommand>* cmdLists[3] = {
1255     &gt->GetPreBuildCommands(), &gt->GetPreLinkCommands(),
1256     &gt->GetPostBuildCommands()
1257   };
1258 
1259   std::vector<std::string> preLinkCmdLines;
1260   std::vector<std::string> postBuildCmdLines;
1261 
1262   std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
1263                                                 &preLinkCmdLines,
1264                                                 &postBuildCmdLines };
1265 
1266   for (unsigned i = 0; i != 3; ++i) {
1267     for (cmCustomCommand const& cc : *cmdLists[i]) {
1268       if (config == fileConfig ||
1269           this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(),
1270                                                          cc.GetBacktrace())) {
1271         cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(),
1272                                      true, config);
1273         localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
1274         std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
1275         byproducts.Add(ccByproducts);
1276         std::transform(
1277           ccByproducts.begin(), ccByproducts.end(),
1278           std::back_inserter(globalGen->GetByproductsForCleanTarget()),
1279           this->MapToNinjaPath());
1280       }
1281     }
1282   }
1283 
1284   // maybe create .def file from list of objects
1285   cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
1286     gt->GetModuleDefinitionInfo(config);
1287   if (mdi && mdi->DefFileGenerated) {
1288     std::string cmakeCommand =
1289       this->GetLocalGenerator()->ConvertToOutputFormat(
1290         cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
1291     std::string cmd =
1292       cmStrCat(cmakeCommand, " -E __create_def ",
1293                this->GetLocalGenerator()->ConvertToOutputFormat(
1294                  mdi->DefFile, cmOutputConverter::SHELL),
1295                ' ');
1296     std::string obj_list_file = mdi->DefFile + ".objs";
1297     cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
1298       obj_list_file, cmOutputConverter::SHELL);
1299 
1300     cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
1301     if (cmNonempty(nm_executable)) {
1302       cmd += " --nm=";
1303       cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
1304         *nm_executable, cmOutputConverter::SHELL);
1305     }
1306     preLinkCmdLines.push_back(std::move(cmd));
1307 
1308     // create a list of obj files for the -E __create_def to read
1309     cmGeneratedFileStream fout(obj_list_file);
1310 
1311     if (mdi->WindowsExportAllSymbols) {
1312       cmNinjaDeps objs = this->GetObjects(config);
1313       for (std::string const& obj : objs) {
1314         if (cmHasLiteralSuffix(obj, ".obj")) {
1315           fout << obj << "\n";
1316         }
1317       }
1318     }
1319 
1320     for (cmSourceFile const* src : mdi->Sources) {
1321       fout << src->GetFullPath() << "\n";
1322     }
1323   }
1324   // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
1325   // for the link commands.
1326   if (!preLinkCmdLines.empty()) {
1327     const std::string homeOutDir = localGen.ConvertToOutputFormat(
1328       localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
1329     preLinkCmdLines.push_back("cd " + homeOutDir);
1330   }
1331 
1332   vars["PRE_LINK"] = localGen.BuildCommandLine(
1333     preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget);
1334   std::string postBuildCmdLine =
1335     localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig,
1336                               "post-build", this->GeneratorTarget);
1337 
1338   cmNinjaVars symlinkVars;
1339   bool const symlinkNeeded =
1340     (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple());
1341   if (!symlinkNeeded) {
1342     vars["POST_BUILD"] = postBuildCmdLine;
1343   } else {
1344     vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
1345     symlinkVars["POST_BUILD"] = postBuildCmdLine;
1346   }
1347 
1348   std::string cmakeVarLang =
1349     cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
1350 
1351   // build response file name
1352   std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
1353 
1354   cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1355 
1356   bool const lang_supports_response =
1357     !(this->TargetLinkLanguage(config) == "RC" ||
1358       (this->TargetLinkLanguage(config) == "CUDA" && !flag));
1359   int commandLineLengthLimit = -1;
1360   if (!lang_supports_response || !this->ForceResponseFile()) {
1361     commandLineLengthLimit =
1362       static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1363       globalGen->GetRuleCmdLength(linkBuild.Rule);
1364   }
1365 
1366   linkBuild.RspFile = this->ConvertToNinjaPath(
1367     cmStrCat("CMakeFiles/", gt->GetName(),
1368              globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1369 
1370   // Gather order-only dependencies.
1371   this->GetLocalGenerator()->AppendTargetDepends(
1372     gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact);
1373 
1374   // Add order-only dependencies on versioning symlinks of shared libs we link.
1375   if (!this->GeneratorTarget->IsDLLPlatform()) {
1376     if (cmComputeLinkInformation* cli =
1377           this->GeneratorTarget->GetLinkInformation(config)) {
1378       for (auto const& item : cli->GetItems()) {
1379         if (item.Target &&
1380             item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
1381             !item.Target->IsFrameworkOnApple()) {
1382           std::string const& lib =
1383             this->ConvertToNinjaPath(item.Target->GetFullPath(config));
1384           if (std::find(linkBuild.ImplicitDeps.begin(),
1385                         linkBuild.ImplicitDeps.end(),
1386                         lib) == linkBuild.ImplicitDeps.end()) {
1387             linkBuild.OrderOnlyDeps.emplace_back(lib);
1388           }
1389         }
1390       }
1391     }
1392   }
1393 
1394   // Ninja should restat after linking if and only if there are byproducts.
1395   vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
1396 
1397   linkBuild.Outputs.reserve(linkBuild.Outputs.size() +
1398                             byproducts.ExplicitOuts.size());
1399   std::move(byproducts.ExplicitOuts.begin(), byproducts.ExplicitOuts.end(),
1400             std::back_inserter(linkBuild.Outputs));
1401   linkBuild.WorkDirOuts = std::move(byproducts.WorkDirOuts);
1402 
1403   // Write the build statement for this target.
1404   bool usedResponseFile = false;
1405   globalGen->WriteBuild(this->GetImplFileStream(fileConfig), linkBuild,
1406                         commandLineLengthLimit, &usedResponseFile);
1407   this->WriteLinkRule(usedResponseFile, config);
1408 
1409   if (symlinkNeeded) {
1410     if (targetType == cmStateEnums::EXECUTABLE) {
1411       cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
1412       build.Comment = "Create executable symlink " + targetOutput;
1413       build.Outputs.push_back(targetOutput);
1414       if (firstForConfig) {
1415         globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1416       }
1417       build.ExplicitDeps.push_back(targetOutputReal);
1418       build.Variables = std::move(symlinkVars);
1419       globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1420     } else {
1421       cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
1422       build.Comment = "Create library symlink " + targetOutput;
1423 
1424       std::string const soName = this->ConvertToNinjaPath(
1425         this->GetTargetFilePath(tgtNames.SharedObject, config));
1426       // If one link has to be created.
1427       if (targetOutputReal == soName || targetOutput == soName) {
1428         symlinkVars["SONAME"] =
1429           this->GetLocalGenerator()->ConvertToOutputFormat(
1430             soName, cmOutputConverter::SHELL);
1431       } else {
1432         symlinkVars["SONAME"].clear();
1433         build.Outputs.push_back(soName);
1434         if (firstForConfig) {
1435           globalGen->GetByproductsForCleanTarget(config).push_back(soName);
1436         }
1437       }
1438       build.Outputs.push_back(targetOutput);
1439       if (firstForConfig) {
1440         globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1441       }
1442       build.ExplicitDeps.push_back(targetOutputReal);
1443       build.Variables = std::move(symlinkVars);
1444 
1445       globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1446     }
1447   }
1448 
1449   // Add aliases for the file name and the target name.
1450   globalGen->AddTargetAlias(tgtNames.Output, gt, config);
1451   globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
1452 }
1453 
WriteObjectLibStatement(const std::string & config)1454 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
1455   const std::string& config)
1456 {
1457   // Write a phony output that depends on all object files.
1458   {
1459     cmNinjaBuild build("phony");
1460     build.Comment = "Object library " + this->GetTargetName();
1461     this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1462                                                    build.Outputs, config);
1463     this->GetLocalGenerator()->AppendTargetOutputs(
1464       this->GetGeneratorTarget(),
1465       this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), config);
1466     build.ExplicitDeps = this->GetObjects(config);
1467     this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1468   }
1469 
1470   // Add aliases for the target name.
1471   this->GetGlobalGenerator()->AddTargetAlias(
1472     this->GetTargetName(), this->GetGeneratorTarget(), config);
1473 }
1474 
TargetNames(const std::string & config) const1475 cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
1476   const std::string& config) const
1477 {
1478   if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
1479     return this->GeneratorTarget->GetExecutableNames(config);
1480   }
1481   return this->GeneratorTarget->GetLibraryNames(config);
1482 }
1483 
TargetLinkLanguage(const std::string & config) const1484 std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage(
1485   const std::string& config) const
1486 {
1487   return this->GeneratorTarget->GetLinkerLanguage(config);
1488 }
1489