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 >->GetPreBuildCommands(), >->GetPreLinkCommands(),
1256 >->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