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"
11 #include <unordered_set>
15 #include <cm/optional>
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"
38 #include "cmStateDirectory.h"
39 #include "cmStateSnapshot.h"
40 #include "cmStateTypes.h"
41 #include "cmStringAlgorithms.h"
42 #include "cmSystemTools.h"
45 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
46 cmGeneratorTarget* target)
47 : cmNinjaTargetGenerator(target)
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));
57 this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
58 this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
61 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
63 void cmNinjaNormalTargetGenerator::Generate(const std::string& config)
65 std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
66 if (this->TargetLinkLanguage(config).empty()) {
67 cmSystemTools::Error("CMake can not determine linker language for "
69 this->GetGeneratorTarget()->GetName());
73 // Write the rules for each language.
74 this->WriteLanguagesRules(config);
76 // Write the build statements
77 bool firstForConfig = true;
78 for (auto const& fileConfig : this->GetConfigNames()) {
79 if (!this->GetGlobalGenerator()
80 ->GetCrossConfigs(fileConfig)
84 this->WriteObjectBuildStatements(config, fileConfig, firstForConfig);
85 firstForConfig = false;
88 if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
89 this->WriteObjectLibStatement(config);
91 firstForConfig = true;
92 for (auto const& fileConfig : this->GetConfigNames()) {
93 if (!this->GetGlobalGenerator()
94 ->GetCrossConfigs(fileConfig)
98 // If this target has cuda language link inputs, and we need to do
100 this->WriteDeviceLinkStatement(config, fileConfig, firstForConfig);
101 this->WriteLinkStatement(config, fileConfig, firstForConfig);
102 firstForConfig = false;
105 if (this->GetGlobalGenerator()->EnableCrossConfigBuild()) {
106 this->GetGlobalGenerator()->AddTargetAlias(
107 this->GetTargetName(), this->GetGeneratorTarget(), "all");
110 // Find ADDITIONAL_CLEAN_FILES
111 this->AdditionalCleanFiles(config);
114 void cmNinjaNormalTargetGenerator::WriteLanguagesRules(
115 const std::string& config)
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";
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 if (this->HaveRequiredLanguages(sourceFiles, languages)) {
130 for (std::string const& language : languages) {
131 this->WriteLanguageRules(language, config);
136 const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
138 switch (this->GetGeneratorTarget()->GetType()) {
139 case cmStateEnums::STATIC_LIBRARY:
140 return "static library";
141 case cmStateEnums::SHARED_LIBRARY:
142 return "shared library";
143 case cmStateEnums::MODULE_LIBRARY:
144 if (this->GetGeneratorTarget()->IsCFBundleOnApple()) {
145 return "CFBundle shared module";
147 return "shared module";
149 case cmStateEnums::EXECUTABLE:
156 std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule(
157 const std::string& config) const
160 this->TargetLinkLanguage(config), "_",
161 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
163 cmGlobalNinjaGenerator::EncodeRuleName(
164 this->GetGeneratorTarget()->GetName()),
168 std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule(
169 const std::string& config) const
172 this->TargetLinkLanguage(config), "_",
173 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
175 cmGlobalNinjaGenerator::EncodeRuleName(
176 this->GetGeneratorTarget()->GetName()),
180 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule(
181 const std::string& config) const
184 this->TargetLinkLanguage(config), "_DEVICE_LINK__",
185 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
189 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule(
190 const std::string& config) const
193 this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__",
194 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
198 std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
199 const std::string& config) const
202 this->TargetLinkLanguage(config), "_FATBINARY__",
203 cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
207 struct cmNinjaRemoveNoOpCommands
209 bool operator()(std::string const& cmd)
211 return cmd.empty() || cmd[0] == ':';
215 void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
216 bool useResponseFile, const std::string& config)
218 cmNinjaRule rule(this->LanguageLinkerDeviceRule(config));
219 if (!this->GetGlobalGenerator()->HasRule(rule.Name)) {
220 cmRulePlaceholderExpander::RuleVariables vars;
221 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
223 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
226 vars.Language = "CUDA";
228 // build response file name
229 std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
230 "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG");
232 if (!useResponseFile || responseFlag.empty()) {
233 vars.Objects = "$in";
234 vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
236 rule.RspFile = "$RSP_FILE";
237 responseFlag += rule.RspFile;
239 // build response file content
240 if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
241 rule.RspContent = "$in";
243 rule.RspContent = "$in_newline";
245 rule.RspContent += " $LINK_LIBRARIES";
246 vars.Objects = responseFlag.c_str();
247 vars.LinkLibraries = "";
250 vars.ObjectDir = "$OBJECT_DIR";
252 vars.Target = "$TARGET_FILE";
254 vars.SONameFlag = "$SONAME_FLAG";
255 vars.TargetSOName = "$SONAME";
256 vars.TargetPDB = "$TARGET_PDB";
257 vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
259 vars.Flags = "$FLAGS";
260 vars.LinkFlags = "$LINK_FLAGS";
261 vars.Manifests = "$MANIFESTS";
263 vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
265 std::string launcher;
266 cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
267 this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
268 if (cmNonempty(val)) {
269 launcher = cmStrCat(*val, ' ');
272 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
273 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
275 // Rule for linking library/executable.
276 std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
277 for (std::string& linkCmd : linkCmds) {
278 linkCmd = cmStrCat(launcher, linkCmd);
279 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
283 // If there is no ranlib the command will be ":". Skip it.
284 cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
287 this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
289 // Write the linker rule with response file if needed.
291 cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
292 this->GetVisibleTypeName(), '.');
294 cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
295 this->GetVisibleTypeName(), " $TARGET_FILE");
296 rule.Restat = "$RESTAT";
298 this->GetGlobalGenerator()->AddRule(rule);
302 void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
303 const std::string& config)
305 const cmMakefile* mf = this->GetMakefile();
307 cmNinjaRule rule(this->LanguageLinkerCudaDeviceRule(config));
308 rule.Command = this->GetLocalGenerator()->BuildCommandLine(
309 { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
310 " -arch=$ARCH $REGISTER -o=$out $in") },
312 rule.Comment = "Rule for CUDA device linking.";
313 rule.Description = "Linking CUDA $out";
314 this->GetGlobalGenerator()->AddRule(rule);
316 cmRulePlaceholderExpander::RuleVariables vars;
317 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
319 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
321 vars.Language = "CUDA";
322 vars.Object = "$out";
323 vars.Fatbinary = "$FATBIN";
324 vars.RegisterFile = "$REGISTER";
325 vars.LinkFlags = "$LINK_FLAGS";
327 std::string flags = this->GetFlags("CUDA", config);
328 vars.Flags = flags.c_str();
330 std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(
331 "CMAKE_CUDA_DEVICE_LINK_COMPILE");
332 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
333 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
334 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
337 rule.Name = this->LanguageLinkerCudaDeviceCompileRule(config);
338 rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd },
340 rule.Comment = "Rule for compiling CUDA device stubs.";
341 rule.Description = "Compiling CUDA device stub $out";
342 this->GetGlobalGenerator()->AddRule(rule);
344 rule.Name = this->LanguageLinkerCudaFatbinaryRule(config);
345 rule.Command = this->GetLocalGenerator()->BuildCommandLine(
346 { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
347 " -64 -cmdline=--compile-only -compress-all -link "
348 "--embedded-fatbin=$out $PROFILES") },
350 rule.Comment = "Rule for CUDA fatbinaries.";
351 rule.Description = "Creating fatbinary $out";
352 this->GetGlobalGenerator()->AddRule(rule);
355 void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
356 const std::string& config)
358 cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType();
360 std::string linkRuleName = this->LanguageLinkerRule(config);
361 if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) {
362 cmNinjaRule rule(std::move(linkRuleName));
363 cmRulePlaceholderExpander::RuleVariables vars;
364 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
365 vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
367 std::string lang = this->TargetLinkLanguage(config);
368 vars.Language = lang.c_str();
369 vars.AIXExports = "$AIX_EXPORTS";
371 if (this->TargetLinkLanguage(config) == "Swift") {
372 vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
373 vars.SwiftModule = "$SWIFT_MODULE";
374 vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
375 vars.SwiftOutputFileMap = "$SWIFT_OUTPUT_FILE_MAP";
376 vars.SwiftSources = "$SWIFT_SOURCES";
378 vars.Defines = "$DEFINES";
379 vars.Flags = "$FLAGS";
380 vars.Includes = "$INCLUDES";
383 std::string responseFlag;
385 std::string cmakeVarLang =
386 cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
388 // build response file name
389 std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
390 cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
393 responseFlag = *flag;
398 if (!useResponseFile || responseFlag.empty()) {
399 vars.Objects = "$in";
400 vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
402 rule.RspFile = "$RSP_FILE";
403 responseFlag += rule.RspFile;
405 // build response file content
406 if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
407 rule.RspContent = "$in";
409 rule.RspContent = "$in_newline";
411 rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
412 if (this->TargetLinkLanguage(config) == "Swift") {
413 vars.SwiftSources = responseFlag.c_str();
415 vars.Objects = responseFlag.c_str();
417 vars.LinkLibraries = "";
420 vars.ObjectDir = "$OBJECT_DIR";
422 vars.Target = "$TARGET_FILE";
424 vars.SONameFlag = "$SONAME_FLAG";
425 vars.TargetSOName = "$SONAME";
426 vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
427 vars.TargetPDB = "$TARGET_PDB";
429 // Setup the target version.
430 std::string targetVersionMajor;
431 std::string targetVersionMinor;
433 std::ostringstream majorStream;
434 std::ostringstream minorStream;
437 this->GetGeneratorTarget()->GetTargetVersion(major, minor);
438 majorStream << major;
439 minorStream << minor;
440 targetVersionMajor = majorStream.str();
441 targetVersionMinor = minorStream.str();
443 vars.TargetVersionMajor = targetVersionMajor.c_str();
444 vars.TargetVersionMinor = targetVersionMinor.c_str();
446 vars.Flags = "$FLAGS";
447 vars.LinkFlags = "$LINK_FLAGS";
448 vars.Manifests = "$MANIFESTS";
450 std::string langFlags;
451 if (targetType != cmStateEnums::EXECUTABLE) {
452 langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
453 vars.LanguageCompileFlags = langFlags.c_str();
456 std::string linkerLauncher = this->GetLinkerLauncher(config);
457 if (cmNonempty(linkerLauncher)) {
458 vars.Launcher = linkerLauncher.c_str();
461 std::string launcher;
462 cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
463 this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
464 if (cmNonempty(val)) {
465 launcher = cmStrCat(*val, ' ');
468 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
469 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
471 // Rule for linking library/executable.
472 std::vector<std::string> linkCmds = this->ComputeLinkCmd(config);
473 for (std::string& linkCmd : linkCmds) {
474 linkCmd = cmStrCat(launcher, linkCmd);
475 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
479 // If there is no ranlib the command will be ":". Skip it.
480 cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
482 linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
483 linkCmds.emplace_back("$POST_BUILD");
485 this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
487 // Write the linker rule with response file if needed.
489 cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
490 this->GetVisibleTypeName(), '.');
492 cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
493 this->GetVisibleTypeName(), " $TARGET_FILE");
494 rule.Restat = "$RESTAT";
495 this->GetGlobalGenerator()->AddRule(rule);
498 auto const tgtNames = this->TargetNames(config);
499 if (tgtNames.Output != tgtNames.Real &&
500 !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
501 std::string cmakeCommand =
502 this->GetLocalGenerator()->ConvertToOutputFormat(
503 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
504 if (targetType == cmStateEnums::EXECUTABLE) {
505 cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
507 std::vector<std::string> cmd;
508 cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
509 cmd.emplace_back("$POST_BUILD");
511 this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
513 rule.Description = "Creating executable symlink $out";
514 rule.Comment = "Rule for creating executable symlink.";
515 this->GetGlobalGenerator()->AddRule(rule);
517 cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
519 std::vector<std::string> cmd;
520 cmd.push_back(cmakeCommand +
521 " -E cmake_symlink_library $in $SONAME $out");
522 cmd.emplace_back("$POST_BUILD");
524 this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
526 rule.Description = "Creating library symlink $out";
527 rule.Comment = "Rule for creating library symlink.";
528 this->GetGlobalGenerator()->AddRule(rule);
533 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
535 std::vector<std::string> linkCmds;
537 // this target requires separable cuda compilation
538 // now build the correct command depending on if the target is
539 // an executable or a dynamic library.
540 switch (this->GetGeneratorTarget()->GetType()) {
541 case cmStateEnums::STATIC_LIBRARY:
542 case cmStateEnums::SHARED_LIBRARY:
543 case cmStateEnums::MODULE_LIBRARY: {
544 this->GetMakefile()->GetDefExpandList("CMAKE_CUDA_DEVICE_LINK_LIBRARY",
547 case cmStateEnums::EXECUTABLE: {
548 this->GetMakefile()->GetDefExpandList(
549 "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE", linkCmds);
557 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd(
558 const std::string& config)
560 std::vector<std::string> linkCmds;
561 cmMakefile* mf = this->GetMakefile();
563 // If we have a rule variable prefer it. In the case of static libraries
564 // this occurs when things like IPO is enabled, and we need to use the
565 // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
566 std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
567 this->TargetLinkLanguage(config), config);
568 cmValue linkCmd = mf->GetDefinition(linkCmdVar);
570 std::string linkCmdStr = *linkCmd;
571 if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
572 std::string ruleVar =
573 cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
575 if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
579 cmExpandList(linkCmdStr, linkCmds);
581 cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
583 std::string cmakeCommand = cmStrCat(
584 this->GetLocalGenerator()->ConvertToOutputFormat(
585 cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
586 " -E __run_co_compile --lwyu=");
588 this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
590 std::string targetOutputReal =
591 this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath(
592 config, cmStateEnums::RuntimeBinaryArtifact,
594 cmakeCommand += cmStrCat(" --source=", targetOutputReal);
595 linkCmds.push_back(std::move(cmakeCommand));
601 switch (this->GetGeneratorTarget()->GetType()) {
602 case cmStateEnums::STATIC_LIBRARY: {
603 // We have archive link commands set. First, delete the existing archive.
605 std::string cmakeCommand =
606 this->GetLocalGenerator()->ConvertToOutputFormat(
607 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
608 linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE");
610 // TODO: Use ARCHIVE_APPEND for archives over a certain size.
612 std::string linkCmdVar = cmStrCat(
613 "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE");
615 linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
616 linkCmdVar, this->TargetLinkLanguage(config), config);
618 std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
619 cmExpandList(linkCmd, linkCmds);
622 std::string linkCmdVar = cmStrCat(
623 "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH");
625 linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
626 linkCmdVar, this->TargetLinkLanguage(config), config);
628 std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
629 cmExpandList(linkCmd, linkCmds);
632 // On macOS ranlib truncates the fractional part of the static archive
633 // file modification time. If the archive and at least one contained
634 // object file were created within the same second this will make look
635 // the archive older than the object file. On subsequent ninja runs this
636 // leads to re-achiving and updating dependent targets.
637 // As a work-around we touch the archive after ranlib (see #19222).
639 std::string cmakeCommand =
640 this->GetLocalGenerator()->ConvertToOutputFormat(
641 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
642 linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
646 case cmStateEnums::SHARED_LIBRARY:
647 case cmStateEnums::MODULE_LIBRARY:
649 case cmStateEnums::EXECUTABLE:
650 if (this->TargetLinkLanguage(config) == "Swift") {
651 if (this->GeneratorTarget->IsExecutableWithExports()) {
652 this->Makefile->GetDefExpandList("CMAKE_EXE_EXPORTS_Swift_FLAG",
658 assert(false && "Unexpected target type");
663 void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
664 const std::string& config, const std::string& fileConfig,
667 cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
668 if (!globalGen->GetLanguageEnabled("CUDA")) {
672 cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
674 bool requiresDeviceLinking = requireDeviceLinking(
675 *this->GeneratorTarget, *this->GetLocalGenerator(), config);
676 if (!requiresDeviceLinking) {
680 // First and very important step is to make sure while inside this
681 // step our link language is set to CUDA
682 std::string const& objExt =
683 this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
685 std::string targetOutputDir =
686 cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget),
687 globalGen->ConfigDirectory(config), "/");
688 targetOutputDir = globalGen->ExpandCFGIntDir(targetOutputDir, config);
690 std::string targetOutputReal =
691 this->ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt);
693 if (firstForConfig) {
694 globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
696 this->DeviceLinkObject = targetOutputReal;
699 cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
700 this->GetCommonFileStream()
701 << "# Device Link build statements for "
702 << cmState::GetTargetTypeName(genTarget->GetType()) << " target "
703 << this->GetTargetName() << "\n\n";
705 if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
706 std::string architecturesStr =
707 this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
709 if (cmIsOff(architecturesStr)) {
710 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
711 "CUDA_SEPARABLE_COMPILATION on Clang "
712 "requires CUDA_ARCHITECTURES to be set.");
716 this->WriteDeviceLinkRules(config);
717 this->WriteDeviceLinkStatements(config, cmExpandedList(architecturesStr),
720 this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir,
725 void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
726 const std::string& config, const std::vector<std::string>& architectures,
727 const std::string& output)
729 // Ensure there are no duplicates.
730 const cmNinjaDeps explicitDeps = [&]() -> std::vector<std::string> {
731 std::unordered_set<std::string> depsSet;
732 const cmNinjaDeps linkDeps =
733 this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true);
734 const cmNinjaDeps objects = this->GetObjects(config);
735 depsSet.insert(linkDeps.begin(), linkDeps.end());
736 depsSet.insert(objects.begin(), objects.end());
738 std::vector<std::string> deps;
739 std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
743 cmGlobalNinjaGenerator* globalGen{ this->GetGlobalGenerator() };
744 const std::string objectDir =
745 cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
746 globalGen->ConfigDirectory(config));
747 const std::string ninjaOutputDir = this->ConvertToNinjaPath(objectDir);
749 cmNinjaBuild fatbinary(this->LanguageLinkerCudaFatbinaryRule(config));
751 // Link device code for each architecture.
752 for (const std::string& architectureKind : architectures) {
753 // Clang always generates real code, so strip the specifier.
754 const std::string architecture =
755 architectureKind.substr(0, architectureKind.find('-'));
756 const std::string cubin =
757 cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
759 cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config));
760 dlink.ExplicitDeps = explicitDeps;
761 dlink.Outputs = { cubin };
762 dlink.Variables["ARCH"] = cmStrCat("sm_", architecture);
764 // The generated register file contains macros that when expanded register
765 // the device routines. Because the routines are the same for all
766 // architectures the register file will be the same too. Thus generate it
767 // only on the first invocation to reduce overhead.
768 if (fatbinary.ExplicitDeps.empty()) {
769 dlink.Variables["REGISTER"] = cmStrCat(
770 "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
773 fatbinary.Variables["PROFILES"] +=
774 cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
775 fatbinary.ExplicitDeps.emplace_back(cubin);
777 globalGen->WriteBuild(this->GetCommonFileStream(), dlink);
780 // Combine all architectures into a single fatbinary.
781 fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
782 globalGen->WriteBuild(this->GetCommonFileStream(), fatbinary);
784 // Compile the stub that registers the kernels and contains the fatbinaries.
785 cmLocalNinjaGenerator* localGen{ this->GetLocalGenerator() };
786 cmNinjaBuild dcompile(this->LanguageLinkerCudaDeviceCompileRule(config));
787 dcompile.Outputs = { output };
788 dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
789 dcompile.Variables["FATBIN"] = localGen->ConvertToOutputFormat(
790 cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL);
791 dcompile.Variables["REGISTER"] = localGen->ConvertToOutputFormat(
792 cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL);
794 cmNinjaLinkLineDeviceComputer linkLineComputer(
795 localGen, localGen->GetStateSnapshot().GetDirectory(), globalGen);
796 linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
798 // Link libraries and paths are only used during the final executable/library
800 std::string frameworkPath;
801 std::string linkPath;
802 std::string linkLibs;
803 localGen->GetDeviceLinkFlags(linkLineComputer, config, linkLibs,
804 dcompile.Variables["LINK_FLAGS"], frameworkPath,
805 linkPath, this->GetGeneratorTarget());
807 globalGen->WriteBuild(this->GetCommonFileStream(), dcompile);
810 void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement(
811 const std::string& config, const std::string& fileConfig,
812 const std::string& outputDir, const std::string& output)
814 cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
815 cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
817 std::string targetOutputImplib = this->ConvertToNinjaPath(
818 genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
820 if (config != fileConfig) {
821 std::string targetOutputFileConfigDir =
822 cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget),
823 globalGen->ConfigDirectory(fileConfig), "/");
824 targetOutputFileConfigDir =
825 globalGen->ExpandCFGIntDir(outputDir, fileConfig);
826 if (outputDir == targetOutputFileConfigDir) {
830 if (!genTarget->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
833 ->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
835 targetOutputImplib ==
836 this->ConvertToNinjaPath(genTarget->GetFullPath(
837 fileConfig, cmStateEnums::ImportLibraryArtifact))) {
842 // Compute the comment.
843 cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
845 cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output);
847 cmNinjaVars& vars = build.Variables;
850 build.Outputs.push_back(output);
851 // Compute specific libraries to link with.
852 build.ExplicitDeps = this->GetObjects(config);
854 this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
856 std::string frameworkPath;
857 std::string linkPath;
859 std::string createRule =
860 genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
861 cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
863 vars["TARGET_FILE"] =
864 localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL);
866 cmNinjaLinkLineDeviceComputer linkLineComputer(
867 this->GetLocalGenerator(),
868 this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(), globalGen);
869 linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
871 localGen.GetDeviceLinkFlags(linkLineComputer, config, vars["LINK_LIBRARIES"],
872 vars["LINK_FLAGS"], frameworkPath, linkPath,
875 this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars);
877 vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
879 vars["MANIFESTS"] = this->GetManifests(config);
881 vars["LINK_PATH"] = frameworkPath + linkPath;
883 // Compute language specific link flags.
884 std::string langFlags;
885 localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config);
886 vars["LANGUAGE_COMPILE_FLAGS"] = langFlags;
888 auto const tgtNames = this->TargetNames(config);
889 if (genTarget->HasSOName(config)) {
890 vars["SONAME_FLAG"] =
891 this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
892 vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
893 cmOutputConverter::SHELL);
894 if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
895 std::string install_dir =
896 this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
897 if (!install_dir.empty()) {
898 vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
899 install_dir, cmOutputConverter::SHELL);
904 if (!tgtNames.ImportLibrary.empty()) {
905 const std::string impLibPath = localGen.ConvertToOutputFormat(
906 targetOutputImplib, cmOutputConverter::SHELL);
907 vars["TARGET_IMPLIB"] = impLibPath;
908 this->EnsureParentDirectoryExists(targetOutputImplib);
911 const std::string objPath =
912 cmStrCat(this->GetGeneratorTarget()->GetSupportDirectory(),
913 globalGen->ConfigDirectory(config));
915 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
916 this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
917 this->EnsureDirectoryExists(objPath);
919 this->SetMsvcTargetPdbVariable(vars, config);
921 std::string& linkLibraries = vars["LINK_LIBRARIES"];
922 std::string& link_path = vars["LINK_PATH"];
923 if (globalGen->IsGCCOnWindows()) {
924 // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
925 std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
926 std::replace(link_path.begin(), link_path.end(), '\\', '/');
929 // Device linking currently doesn't support response files so
930 // do not check if the user has explicitly forced a response file.
931 int const commandLineLengthLimit =
932 static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
933 globalGen->GetRuleCmdLength(build.Rule);
935 build.RspFile = this->ConvertToNinjaPath(
936 cmStrCat("CMakeFiles/", genTarget->GetName(),
937 globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
939 // Gather order-only dependencies.
940 this->GetLocalGenerator()->AppendTargetDepends(
941 this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config,
942 DependOnTargetArtifact);
944 // Write the build statement for this target.
945 bool usedResponseFile = false;
946 globalGen->WriteBuild(this->GetCommonFileStream(), build,
947 commandLineLengthLimit, &usedResponseFile);
948 this->WriteNvidiaDeviceLinkRule(usedResponseFile, config);
951 void cmNinjaNormalTargetGenerator::WriteLinkStatement(
952 const std::string& config, const std::string& fileConfig,
955 cmMakefile* mf = this->GetMakefile();
956 cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
957 cmGeneratorTarget* gt = this->GetGeneratorTarget();
959 std::string targetOutput = this->ConvertToNinjaPath(gt->GetFullPath(config));
960 std::string targetOutputReal = this->ConvertToNinjaPath(
961 gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
963 std::string targetOutputImplib = this->ConvertToNinjaPath(
964 gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
966 if (config != fileConfig) {
968 this->ConvertToNinjaPath(gt->GetFullPath(fileConfig))) {
971 if (targetOutputReal ==
972 this->ConvertToNinjaPath(
973 gt->GetFullPath(fileConfig, cmStateEnums::RuntimeBinaryArtifact,
974 /*realname=*/true))) {
977 if (!gt->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
979 !gt->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
981 targetOutputImplib ==
982 this->ConvertToNinjaPath(gt->GetFullPath(
983 fileConfig, cmStateEnums::ImportLibraryArtifact))) {
988 auto const tgtNames = this->TargetNames(config);
989 if (gt->IsAppBundleOnApple()) {
990 // Create the app bundle
991 std::string outpath = gt->GetDirectory(config);
992 this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath,
995 // Calculate the output path
996 targetOutput = cmStrCat(outpath, '/', tgtNames.Output);
997 targetOutput = this->ConvertToNinjaPath(targetOutput);
998 targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real);
999 targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
1000 } else if (gt->IsFrameworkOnApple()) {
1001 // Create the library framework.
1003 cmOSXBundleGenerator::SkipParts bundleSkipParts;
1004 if (globalGen->GetName() == "Ninja Multi-Config") {
1005 const auto postFix = this->GeneratorTarget->GetFilePostfix(config);
1006 // Skip creating Info.plist when there are multiple configurations, and
1007 // the current configuration has a postfix. The non-postfix configuration
1008 // Info.plist can be used by all the other configurations.
1009 if (!postFix.empty()) {
1010 bundleSkipParts.infoPlist = true;
1014 this->OSXBundleGenerator->CreateFramework(
1015 tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
1016 } else if (gt->IsCFBundleOnApple()) {
1017 // Create the core foundation bundle.
1018 this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,
1019 gt->GetDirectory(config), config);
1023 cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
1024 const cmStateEnums::TargetType targetType = gt->GetType();
1025 this->GetImplFileStream(fileConfig)
1026 << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
1027 << " target " << this->GetTargetName() << "\n\n";
1029 cmNinjaBuild linkBuild(this->LanguageLinkerRule(config));
1030 cmNinjaVars& vars = linkBuild.Variables;
1032 // Compute the comment.
1034 cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
1037 linkBuild.Outputs.push_back(targetOutputReal);
1038 if (firstForConfig) {
1039 globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
1042 if (this->TargetLinkLanguage(config) == "Swift") {
1043 vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
1044 cmGeneratorTarget::Names targetNames =
1045 this->GetGeneratorTarget()->GetLibraryNames(config);
1046 return targetNames.Base;
1049 vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string {
1050 if (cmValue name = gt->GetProperty("Swift_MODULE_NAME")) {
1053 return gt->GetName();
1056 vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string {
1057 std::string directory =
1058 this->GetLocalGenerator()->GetCurrentBinaryDirectory();
1059 if (cmValue prop = this->GetGeneratorTarget()->GetProperty(
1060 "Swift_MODULE_DIRECTORY")) {
1064 std::string name = module + ".swiftmodule";
1066 this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) {
1070 return this->GetLocalGenerator()->ConvertToOutputFormat(
1071 this->ConvertToNinjaPath(directory + "/" + name),
1072 cmOutputConverter::SHELL);
1073 }(vars["SWIFT_MODULE_NAME"]);
1075 const std::string map = cmStrCat(gt->GetSupportDirectory(), '/', config,
1076 '/', "output-file-map.json");
1077 vars["SWIFT_OUTPUT_FILE_MAP"] =
1078 this->GetLocalGenerator()->ConvertToOutputFormat(
1079 this->ConvertToNinjaPath(map), cmOutputConverter::SHELL);
1081 vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
1082 std::vector<cmSourceFile const*> sources;
1083 std::stringstream oss;
1085 this->GetGeneratorTarget()->GetObjectSources(sources, config);
1086 cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
1087 for (const auto& source : sources) {
1088 const std::string sourcePath = source->GetLanguage() == "Swift"
1089 ? this->GetCompiledSourceNinjaPath(source)
1090 : this->GetObjectFilePath(source, config);
1092 << LocalGen->ConvertToOutputFormat(sourcePath,
1093 cmOutputConverter::SHELL);
1098 // Since we do not perform object builds, compute the
1099 // defines/flags/includes here so that they can be passed along
1101 vars["DEFINES"] = this->GetDefines("Swift", config);
1102 vars["FLAGS"] = this->GetFlags("Swift", config);
1103 vars["INCLUDES"] = this->GetIncludes("Swift", config);
1106 // Compute specific libraries to link with.
1107 if (this->TargetLinkLanguage(config) == "Swift") {
1108 std::vector<cmSourceFile const*> sources;
1109 gt->GetObjectSources(sources, config);
1110 for (const auto& source : sources) {
1111 if (source->GetLanguage() == "Swift") {
1112 linkBuild.Outputs.push_back(
1113 this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
1114 linkBuild.ExplicitDeps.emplace_back(
1115 this->GetCompiledSourceNinjaPath(source));
1117 linkBuild.ExplicitDeps.emplace_back(
1118 this->GetObjectFilePath(source, config));
1121 linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
1123 linkBuild.ExplicitDeps = this->GetObjects(config);
1126 std::vector<std::string> extraISPCObjects =
1127 this->GetGeneratorTarget()->GetGeneratedISPCObjects(config);
1128 std::transform(extraISPCObjects.begin(), extraISPCObjects.end(),
1129 std::back_inserter(linkBuild.ExplicitDeps),
1130 this->MapToNinjaPath());
1132 linkBuild.ImplicitDeps =
1133 this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1135 if (!this->DeviceLinkObject.empty()) {
1136 linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
1139 std::string frameworkPath;
1140 std::string linkPath;
1142 std::string createRule =
1143 gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1144 bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
1145 cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1147 vars["TARGET_FILE"] =
1148 localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
1150 std::unique_ptr<cmLinkLineComputer> linkLineComputer =
1151 globalGen->CreateLinkLineComputer(
1152 this->GetLocalGenerator(),
1153 this->GetLocalGenerator()->GetStateSnapshot().GetDirectory());
1154 linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
1155 linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig());
1157 localGen.GetTargetFlags(linkLineComputer.get(), config,
1158 vars["LINK_LIBRARIES"], vars["FLAGS"],
1159 vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
1161 // Add OS X version flags, if any.
1162 if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
1163 this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
1164 this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1165 this->TargetLinkLanguage(config), "COMPATIBILITY",
1167 this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1168 this->TargetLinkLanguage(config), "CURRENT", false);
1171 this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars);
1173 this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
1174 vars["LINK_FLAGS"], this->GetGeneratorTarget(),
1175 this->TargetLinkLanguage(config));
1177 vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
1179 vars["MANIFESTS"] = this->GetManifests(config);
1180 vars["AIX_EXPORTS"] = this->GetAIXExports(config);
1182 vars["LINK_PATH"] = frameworkPath + linkPath;
1184 // Compute architecture specific link flags. Yes, these go into a different
1185 // variable for executables, probably due to a mistake made when duplicating
1186 // code between the Makefile executable and library generators.
1187 if (targetType == cmStateEnums::EXECUTABLE) {
1188 std::string t = vars["FLAGS"];
1189 localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1193 std::string t = vars["ARCH_FLAGS"];
1194 localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1196 vars["ARCH_FLAGS"] = t;
1198 localGen.AddLanguageFlagsForLinking(
1199 t, gt, this->TargetLinkLanguage(config), config);
1200 vars["LANGUAGE_COMPILE_FLAGS"] = t;
1202 if (gt->HasSOName(config)) {
1203 vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config));
1204 vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1205 cmOutputConverter::SHELL);
1206 if (targetType == cmStateEnums::SHARED_LIBRARY) {
1207 std::string install_dir = gt->GetInstallNameDirForBuildTree(config);
1208 if (!install_dir.empty()) {
1209 vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1210 install_dir, cmOutputConverter::SHELL);
1215 cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
1217 if (!tgtNames.ImportLibrary.empty()) {
1218 const std::string impLibPath = localGen.ConvertToOutputFormat(
1219 targetOutputImplib, cmOutputConverter::SHELL);
1220 vars["TARGET_IMPLIB"] = impLibPath;
1221 this->EnsureParentDirectoryExists(targetOutputImplib);
1222 if (gt->HasImportLibrary(config)) {
1223 // Some linkers may update a binary without touching its import lib.
1224 byproducts.ExplicitOuts.emplace_back(targetOutputImplib);
1225 if (firstForConfig) {
1226 globalGen->GetByproductsForCleanTarget(config).push_back(
1227 targetOutputImplib);
1232 if (!this->SetMsvcTargetPdbVariable(vars, config)) {
1233 // It is common to place debug symbols at a specific place,
1234 // so we need a plain target name in the rule available.
1238 gt->GetFullNameComponents(prefix, base, suffix, config);
1239 std::string dbg_suffix = ".dbg";
1240 // TODO: Where to document?
1241 if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
1244 vars["TARGET_PDB"] = base + suffix + dbg_suffix;
1247 const std::string objPath =
1248 cmStrCat(gt->GetSupportDirectory(), globalGen->ConfigDirectory(config));
1249 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1250 this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1251 this->EnsureDirectoryExists(objPath);
1253 std::string& linkLibraries = vars["LINK_LIBRARIES"];
1254 std::string& link_path = vars["LINK_PATH"];
1255 if (globalGen->IsGCCOnWindows()) {
1256 // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1257 std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1258 std::replace(link_path.begin(), link_path.end(), '\\', '/');
1261 const std::vector<cmCustomCommand>* cmdLists[3] = {
1262 >->GetPreBuildCommands(), >->GetPreLinkCommands(),
1263 >->GetPostBuildCommands()
1266 std::vector<std::string> preLinkCmdLines;
1267 std::vector<std::string> postBuildCmdLines;
1269 std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
1271 &postBuildCmdLines };
1273 for (unsigned i = 0; i != 3; ++i) {
1274 for (cmCustomCommand const& cc : *cmdLists[i]) {
1275 if (config == fileConfig ||
1276 this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(),
1277 cc.GetBacktrace())) {
1278 cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(),
1280 localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
1281 std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
1282 byproducts.Add(ccByproducts);
1284 ccByproducts.begin(), ccByproducts.end(),
1285 std::back_inserter(globalGen->GetByproductsForCleanTarget()),
1286 this->MapToNinjaPath());
1291 // maybe create .def file from list of objects
1292 cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
1293 gt->GetModuleDefinitionInfo(config);
1294 if (mdi && mdi->DefFileGenerated) {
1295 std::string cmakeCommand =
1296 this->GetLocalGenerator()->ConvertToOutputFormat(
1297 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
1299 cmStrCat(cmakeCommand, " -E __create_def ",
1300 this->GetLocalGenerator()->ConvertToOutputFormat(
1301 mdi->DefFile, cmOutputConverter::SHELL),
1303 std::string obj_list_file = mdi->DefFile + ".objs";
1304 cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
1305 obj_list_file, cmOutputConverter::SHELL);
1307 cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
1308 if (cmNonempty(nm_executable)) {
1310 cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
1311 *nm_executable, cmOutputConverter::SHELL);
1313 preLinkCmdLines.push_back(std::move(cmd));
1315 // create a list of obj files for the -E __create_def to read
1316 cmGeneratedFileStream fout(obj_list_file);
1318 if (mdi->WindowsExportAllSymbols) {
1319 cmNinjaDeps objs = this->GetObjects(config);
1320 for (std::string const& obj : objs) {
1321 if (cmHasLiteralSuffix(obj, ".obj")) {
1322 fout << obj << "\n";
1327 for (cmSourceFile const* src : mdi->Sources) {
1328 fout << src->GetFullPath() << "\n";
1331 // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
1332 // for the link commands.
1333 if (!preLinkCmdLines.empty()) {
1334 const std::string homeOutDir = localGen.ConvertToOutputFormat(
1335 localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
1336 preLinkCmdLines.push_back("cd " + homeOutDir);
1339 vars["PRE_LINK"] = localGen.BuildCommandLine(
1340 preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget);
1341 std::string postBuildCmdLine =
1342 localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig,
1343 "post-build", this->GeneratorTarget);
1345 cmNinjaVars symlinkVars;
1346 bool const symlinkNeeded =
1347 (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple());
1348 if (!symlinkNeeded) {
1349 vars["POST_BUILD"] = postBuildCmdLine;
1351 vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
1352 symlinkVars["POST_BUILD"] = postBuildCmdLine;
1355 std::string cmakeVarLang =
1356 cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
1358 // build response file name
1359 std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
1361 cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1363 bool const lang_supports_response =
1364 !(this->TargetLinkLanguage(config) == "RC" ||
1365 (this->TargetLinkLanguage(config) == "CUDA" && !flag));
1366 int commandLineLengthLimit = -1;
1367 if (!lang_supports_response || !this->ForceResponseFile()) {
1368 commandLineLengthLimit =
1369 static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1370 globalGen->GetRuleCmdLength(linkBuild.Rule);
1373 linkBuild.RspFile = this->ConvertToNinjaPath(
1374 cmStrCat("CMakeFiles/", gt->GetName(),
1375 globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1377 // Gather order-only dependencies.
1378 this->GetLocalGenerator()->AppendTargetDepends(
1379 gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact);
1381 // Add order-only dependencies on versioning symlinks of shared libs we link.
1382 if (!this->GeneratorTarget->IsDLLPlatform()) {
1383 if (cmComputeLinkInformation* cli =
1384 this->GeneratorTarget->GetLinkInformation(config)) {
1385 for (auto const& item : cli->GetItems()) {
1387 item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
1388 !item.Target->IsFrameworkOnApple()) {
1389 std::string const& lib =
1390 this->ConvertToNinjaPath(item.Target->GetFullPath(config));
1391 if (std::find(linkBuild.ImplicitDeps.begin(),
1392 linkBuild.ImplicitDeps.end(),
1393 lib) == linkBuild.ImplicitDeps.end()) {
1394 linkBuild.OrderOnlyDeps.emplace_back(lib);
1401 // Ninja should restat after linking if and only if there are byproducts.
1402 vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
1404 linkBuild.Outputs.reserve(linkBuild.Outputs.size() +
1405 byproducts.ExplicitOuts.size());
1406 std::move(byproducts.ExplicitOuts.begin(), byproducts.ExplicitOuts.end(),
1407 std::back_inserter(linkBuild.Outputs));
1408 linkBuild.WorkDirOuts = std::move(byproducts.WorkDirOuts);
1410 // Write the build statement for this target.
1411 bool usedResponseFile = false;
1412 globalGen->WriteBuild(this->GetImplFileStream(fileConfig), linkBuild,
1413 commandLineLengthLimit, &usedResponseFile);
1414 this->WriteLinkRule(usedResponseFile, config);
1416 if (symlinkNeeded) {
1417 if (targetType == cmStateEnums::EXECUTABLE) {
1418 cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
1419 build.Comment = "Create executable symlink " + targetOutput;
1420 build.Outputs.push_back(targetOutput);
1421 if (firstForConfig) {
1422 globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1424 build.ExplicitDeps.push_back(targetOutputReal);
1425 build.Variables = std::move(symlinkVars);
1426 globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1428 cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
1429 build.Comment = "Create library symlink " + targetOutput;
1431 std::string const soName = this->ConvertToNinjaPath(
1432 this->GetTargetFilePath(tgtNames.SharedObject, config));
1433 // If one link has to be created.
1434 if (targetOutputReal == soName || targetOutput == soName) {
1435 symlinkVars["SONAME"] =
1436 this->GetLocalGenerator()->ConvertToOutputFormat(
1437 soName, cmOutputConverter::SHELL);
1439 symlinkVars["SONAME"].clear();
1440 build.Outputs.push_back(soName);
1441 if (firstForConfig) {
1442 globalGen->GetByproductsForCleanTarget(config).push_back(soName);
1445 build.Outputs.push_back(targetOutput);
1446 if (firstForConfig) {
1447 globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1449 build.ExplicitDeps.push_back(targetOutputReal);
1450 build.Variables = std::move(symlinkVars);
1452 globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1456 // Add aliases for the file name and the target name.
1457 globalGen->AddTargetAlias(tgtNames.Output, gt, config);
1458 globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
1461 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
1462 const std::string& config)
1464 // Write a phony output that depends on all object files.
1466 cmNinjaBuild build("phony");
1467 build.Comment = "Object library " + this->GetTargetName();
1468 this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1469 build.Outputs, config);
1470 this->GetLocalGenerator()->AppendTargetOutputs(
1471 this->GetGeneratorTarget(),
1472 this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), config);
1473 build.ExplicitDeps = this->GetObjects(config);
1474 this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1477 // Add aliases for the target name.
1478 this->GetGlobalGenerator()->AddTargetAlias(
1479 this->GetTargetName(), this->GetGeneratorTarget(), config);
1482 cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
1483 const std::string& config) const
1485 if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
1486 return this->GeneratorTarget->GetExecutableNames(config);
1488 return this->GeneratorTarget->GetLibraryNames(config);
1491 std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage(
1492 const std::string& config) const
1494 return this->GeneratorTarget->GetLinkerLanguage(config);