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"
15 #include "cmAlgorithms.h"
16 #include "cmComputeLinkInformation.h"
17 #include "cmCustomCommand.h" // IWYU pragma: keep
18 #include "cmCustomCommandGenerator.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmGeneratorTarget.h"
21 #include "cmGlobalNinjaGenerator.h"
22 #include "cmLinkLineComputer.h"
23 #include "cmLinkLineDeviceComputer.h"
24 #include "cmLocalCommonGenerator.h"
25 #include "cmLocalGenerator.h"
26 #include "cmLocalNinjaGenerator.h"
27 #include "cmMakefile.h"
28 #include "cmNinjaLinkLineDeviceComputer.h"
29 #include "cmNinjaTypes.h"
30 #include "cmOSXBundleGenerator.h"
31 #include "cmOutputConverter.h"
32 #include "cmRulePlaceholderExpander.h"
33 #include "cmSourceFile.h"
35 #include "cmStateDirectory.h"
36 #include "cmStateSnapshot.h"
37 #include "cmStateTypes.h"
38 #include "cmStringAlgorithms.h"
39 #include "cmSystemTools.h"
41 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
42 cmGeneratorTarget* target)
43 : cmNinjaTargetGenerator(target)
44 , TargetLinkLanguage("")
46 this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName());
47 if (target->GetType() == cmStateEnums::EXECUTABLE) {
48 this->TargetNames = this->GetGeneratorTarget()->GetExecutableNames(
49 GetLocalGenerator()->GetConfigName());
51 this->TargetNames = this->GetGeneratorTarget()->GetLibraryNames(
52 GetLocalGenerator()->GetConfigName());
55 if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) {
56 // on Windows the output dir is already needed at compile time
57 // ensure the directory exists (OutDir test)
58 EnsureDirectoryExists(target->GetDirectory(this->GetConfigName()));
61 this->OSXBundleGenerator =
62 cm::make_unique<cmOSXBundleGenerator>(target, this->GetConfigName());
63 this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
66 cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
68 void cmNinjaNormalTargetGenerator::Generate()
70 if (this->TargetLinkLanguage.empty()) {
71 cmSystemTools::Error("CMake can not determine linker language for "
73 this->GetGeneratorTarget()->GetName());
77 // Write the rules for each language.
78 this->WriteLanguagesRules();
80 // Write the build statements
81 this->WriteObjectBuildStatements();
83 if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
84 this->WriteObjectLibStatement();
86 // If this target has cuda language link inputs, and we need to do
88 this->WriteDeviceLinkStatement();
89 this->WriteLinkStatement();
92 // Find ADDITIONAL_CLEAN_FILES
93 this->AdditionalCleanFiles();
96 void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
98 #ifdef NINJA_GEN_VERBOSE_FILES
99 cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
100 this->GetRulesFileStream()
101 << "# Rules for each languages for "
102 << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
103 << " target " << this->GetTargetName() << "\n\n";
106 // Write rules for languages compiled in this target.
107 std::set<std::string> languages;
108 std::vector<cmSourceFile const*> sourceFiles;
109 this->GetGeneratorTarget()->GetObjectSources(
110 sourceFiles, this->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"));
111 for (cmSourceFile const* sf : sourceFiles) {
112 std::string const lang = sf->GetLanguage();
114 languages.insert(lang);
117 for (std::string const& language : languages) {
118 this->WriteLanguageRules(language);
122 const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
124 switch (this->GetGeneratorTarget()->GetType()) {
125 case cmStateEnums::STATIC_LIBRARY:
126 return "static library";
127 case cmStateEnums::SHARED_LIBRARY:
128 return "shared library";
129 case cmStateEnums::MODULE_LIBRARY:
130 if (this->GetGeneratorTarget()->IsCFBundleOnApple()) {
131 return "CFBundle shared module";
133 return "shared module";
135 case cmStateEnums::EXECUTABLE:
142 std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule() const
144 return this->TargetLinkLanguage + "_" +
145 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) +
147 cmGlobalNinjaGenerator::EncodeRuleName(
148 this->GetGeneratorTarget()->GetName());
151 std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule() const
153 return this->TargetLinkLanguage + "_" +
154 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) +
156 cmGlobalNinjaGenerator::EncodeRuleName(
157 this->GetGeneratorTarget()->GetName());
160 struct cmNinjaRemoveNoOpCommands
162 bool operator()(std::string const& cmd)
164 return cmd.empty() || cmd[0] == ':';
168 void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(bool useResponseFile)
170 cmNinjaRule rule(this->LanguageLinkerDeviceRule());
171 if (!this->GetGlobalGenerator()->HasRule(rule.Name)) {
172 cmRulePlaceholderExpander::RuleVariables vars;
173 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
175 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType());
177 vars.Language = "CUDA";
179 // build response file name
180 std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
181 "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG");
183 if (!useResponseFile || responseFlag.empty()) {
184 vars.Objects = "$in";
185 vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
187 rule.RspFile = "$RSP_FILE";
188 responseFlag += rule.RspFile;
190 // build response file content
191 if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
192 rule.RspContent = "$in";
194 rule.RspContent = "$in_newline";
196 rule.RspContent += " $LINK_LIBRARIES";
197 vars.Objects = responseFlag.c_str();
198 vars.LinkLibraries = "";
201 vars.ObjectDir = "$OBJECT_DIR";
203 vars.Target = "$TARGET_FILE";
205 vars.SONameFlag = "$SONAME_FLAG";
206 vars.TargetSOName = "$SONAME";
207 vars.TargetPDB = "$TARGET_PDB";
208 vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
210 vars.Flags = "$FLAGS";
211 vars.LinkFlags = "$LINK_FLAGS";
212 vars.Manifests = "$MANIFESTS";
214 std::string langFlags;
215 if (this->GetGeneratorTarget()->GetType() != cmStateEnums::EXECUTABLE) {
216 langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
217 vars.LanguageCompileFlags = langFlags.c_str();
220 std::string launcher;
221 const char* val = this->GetLocalGenerator()->GetRuleLauncher(
222 this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
224 launcher = cmStrCat(val, ' ');
227 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
228 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
230 // Rule for linking library/executable.
231 std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
232 for (std::string& linkCmd : linkCmds) {
233 linkCmd = cmStrCat(launcher, linkCmd);
234 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
238 // If there is no ranlib the command will be ":". Skip it.
239 cmEraseIf(linkCmds, cmNinjaRemoveNoOpCommands());
241 rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
243 // Write the linker rule with response file if needed.
244 rule.Comment = cmStrCat("Rule for linking ", this->TargetLinkLanguage, ' ',
245 this->GetVisibleTypeName(), '.');
246 rule.Description = cmStrCat("Linking ", this->TargetLinkLanguage, ' ',
247 this->GetVisibleTypeName(), " $TARGET_FILE");
248 rule.Restat = "$RESTAT";
250 this->GetGlobalGenerator()->AddRule(rule);
254 void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile)
256 cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType();
258 std::string linkRuleName = this->LanguageLinkerRule();
259 if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) {
260 cmNinjaRule rule(std::move(linkRuleName));
261 cmRulePlaceholderExpander::RuleVariables vars;
262 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
263 vars.CMTargetType = cmState::GetTargetTypeName(targetType);
265 vars.Language = this->TargetLinkLanguage.c_str();
267 if (this->TargetLinkLanguage == "Swift") {
268 vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
269 vars.SwiftModule = "$SWIFT_MODULE";
270 vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
271 vars.SwiftOutputFileMap = "$SWIFT_OUTPUT_FILE_MAP";
272 vars.SwiftSources = "$SWIFT_SOURCES";
274 vars.Defines = "$DEFINES";
275 vars.Flags = "$FLAGS";
276 vars.Includes = "$INCLUDES";
279 std::string responseFlag;
281 std::string cmakeVarLang = cmStrCat("CMAKE_", this->TargetLinkLanguage);
283 // build response file name
284 std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
285 const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar);
293 if (!useResponseFile || responseFlag.empty()) {
294 vars.Objects = "$in";
295 vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
297 rule.RspFile = "$RSP_FILE";
298 responseFlag += rule.RspFile;
300 // build response file content
301 if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
302 rule.RspContent = "$in";
304 rule.RspContent = "$in_newline";
306 rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
307 if (this->TargetLinkLanguage == "Swift") {
308 vars.SwiftSources = responseFlag.c_str();
310 vars.Objects = responseFlag.c_str();
312 vars.LinkLibraries = "";
315 vars.ObjectDir = "$OBJECT_DIR";
317 vars.Target = "$TARGET_FILE";
319 vars.SONameFlag = "$SONAME_FLAG";
320 vars.TargetSOName = "$SONAME";
321 vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
322 vars.TargetPDB = "$TARGET_PDB";
324 // Setup the target version.
325 std::string targetVersionMajor;
326 std::string targetVersionMinor;
328 std::ostringstream majorStream;
329 std::ostringstream minorStream;
332 this->GetGeneratorTarget()->GetTargetVersion(major, minor);
333 majorStream << major;
334 minorStream << minor;
335 targetVersionMajor = majorStream.str();
336 targetVersionMinor = minorStream.str();
338 vars.TargetVersionMajor = targetVersionMajor.c_str();
339 vars.TargetVersionMinor = targetVersionMinor.c_str();
341 vars.Flags = "$FLAGS";
342 vars.LinkFlags = "$LINK_FLAGS";
343 vars.Manifests = "$MANIFESTS";
345 std::string langFlags;
346 if (targetType != cmStateEnums::EXECUTABLE) {
347 langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
348 vars.LanguageCompileFlags = langFlags.c_str();
351 std::string launcher;
352 const char* val = this->GetLocalGenerator()->GetRuleLauncher(
353 this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
355 launcher = cmStrCat(val, ' ');
358 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
359 this->GetLocalGenerator()->CreateRulePlaceholderExpander());
361 // Rule for linking library/executable.
362 std::vector<std::string> linkCmds = this->ComputeLinkCmd();
363 for (std::string& linkCmd : linkCmds) {
364 linkCmd = cmStrCat(launcher, linkCmd);
365 rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
369 // If there is no ranlib the command will be ":". Skip it.
370 cmEraseIf(linkCmds, cmNinjaRemoveNoOpCommands());
372 linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
373 linkCmds.emplace_back("$POST_BUILD");
374 rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
376 // Write the linker rule with response file if needed.
377 rule.Comment = cmStrCat("Rule for linking ", this->TargetLinkLanguage, ' ',
378 this->GetVisibleTypeName(), '.');
379 rule.Description = cmStrCat("Linking ", this->TargetLinkLanguage, ' ',
380 this->GetVisibleTypeName(), " $TARGET_FILE");
381 rule.Restat = "$RESTAT";
382 this->GetGlobalGenerator()->AddRule(rule);
385 if (this->TargetNames.Output != this->TargetNames.Real &&
386 !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
387 std::string cmakeCommand =
388 this->GetLocalGenerator()->ConvertToOutputFormat(
389 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
390 if (targetType == cmStateEnums::EXECUTABLE) {
391 cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
393 std::vector<std::string> cmd;
394 cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
395 cmd.emplace_back("$POST_BUILD");
396 rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
398 rule.Description = "Creating executable symlink $out";
399 rule.Comment = "Rule for creating executable symlink.";
400 this->GetGlobalGenerator()->AddRule(rule);
402 cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
404 std::vector<std::string> cmd;
405 cmd.push_back(cmakeCommand +
406 " -E cmake_symlink_library $in $SONAME $out");
407 cmd.emplace_back("$POST_BUILD");
408 rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
410 rule.Description = "Creating library symlink $out";
411 rule.Comment = "Rule for creating library symlink.";
412 this->GetGlobalGenerator()->AddRule(rule);
417 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
419 std::vector<std::string> linkCmds;
421 // this target requires separable cuda compilation
422 // now build the correct command depending on if the target is
423 // an executable or a dynamic library.
425 switch (this->GetGeneratorTarget()->GetType()) {
426 case cmStateEnums::STATIC_LIBRARY:
427 case cmStateEnums::SHARED_LIBRARY:
428 case cmStateEnums::MODULE_LIBRARY: {
429 const std::string cudaLinkCmd(
430 this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_LIBRARY"));
431 cmExpandList(cudaLinkCmd, linkCmds);
433 case cmStateEnums::EXECUTABLE: {
434 const std::string cudaLinkCmd(this->GetMakefile()->GetDefinition(
435 "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE"));
436 cmExpandList(cudaLinkCmd, linkCmds);
444 std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd()
446 std::vector<std::string> linkCmds;
447 cmMakefile* mf = this->GetMakefile();
449 // If we have a rule variable prefer it. In the case of static libraries
450 // this occurs when things like IPO is enabled, and we need to use the
451 // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
452 std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
453 this->TargetLinkLanguage, this->GetConfigName());
454 const char* linkCmd = mf->GetDefinition(linkCmdVar);
456 std::string linkCmdStr = linkCmd;
457 if (this->GetGeneratorTarget()->HasImplibGNUtoMS(this->ConfigName)) {
458 std::string ruleVar = cmStrCat(
459 "CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
461 if (const char* rule = this->Makefile->GetDefinition(ruleVar)) {
465 cmExpandList(linkCmdStr, linkCmds);
466 if (this->GetGeneratorTarget()->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
467 std::string cmakeCommand = cmStrCat(
468 this->GetLocalGenerator()->ConvertToOutputFormat(
469 cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
470 " -E __run_co_compile --lwyu=");
471 cmGeneratorTarget& gt = *this->GetGeneratorTarget();
472 const std::string cfgName = this->GetConfigName();
473 std::string targetOutputReal = this->ConvertToNinjaPath(
474 gt.GetFullPath(cfgName, cmStateEnums::RuntimeBinaryArtifact,
476 cmakeCommand += targetOutputReal;
477 linkCmds.push_back(std::move(cmakeCommand));
482 switch (this->GetGeneratorTarget()->GetType()) {
483 case cmStateEnums::STATIC_LIBRARY: {
484 // We have archive link commands set. First, delete the existing archive.
486 std::string cmakeCommand =
487 this->GetLocalGenerator()->ConvertToOutputFormat(
488 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
489 linkCmds.push_back(cmakeCommand + " -E remove $TARGET_FILE");
491 // TODO: Use ARCHIVE_APPEND for archives over a certain size.
493 std::string linkCmdVar =
494 cmStrCat("CMAKE_", this->TargetLinkLanguage, "_ARCHIVE_CREATE");
496 linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
497 linkCmdVar, this->TargetLinkLanguage, this->GetConfigName());
499 std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
500 cmExpandList(linkCmd, linkCmds);
503 std::string linkCmdVar =
504 cmStrCat("CMAKE_", this->TargetLinkLanguage, "_ARCHIVE_FINISH");
506 linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
507 linkCmdVar, this->TargetLinkLanguage, this->GetConfigName());
509 std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
510 cmExpandList(linkCmd, linkCmds);
513 // On macOS ranlib truncates the fractional part of the static archive
514 // file modification time. If the archive and at least one contained
515 // object file were created within the same second this will make look
516 // the archive older than the object file. On subsequent ninja runs this
517 // leads to re-achiving and updating dependent targets.
518 // As a work-around we touch the archive after ranlib (see #19222).
520 std::string cmakeCommand =
521 this->GetLocalGenerator()->ConvertToOutputFormat(
522 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
523 linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
528 case cmStateEnums::SHARED_LIBRARY:
529 case cmStateEnums::MODULE_LIBRARY:
530 case cmStateEnums::EXECUTABLE:
533 assert(false && "Unexpected target type");
535 return std::vector<std::string>();
538 void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement()
540 cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
541 if (!globalGen->GetLanguageEnabled("CUDA")) {
545 cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
547 bool requiresDeviceLinking = requireDeviceLinking(
548 *this->GeneratorTarget, *this->GetLocalGenerator(), this->ConfigName);
549 if (!requiresDeviceLinking) {
553 // Now we can do device linking
555 // First and very important step is to make sure while inside this
556 // step our link language is set to CUDA
557 std::string cudaLinkLanguage = "CUDA";
558 std::string const& objExt =
559 this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
561 std::string const cfgName = this->GetConfigName();
562 std::string const targetOutputReal = ConvertToNinjaPath(
563 genTarget->ObjectDirectory + "cmake_device_link" + objExt);
565 std::string const targetOutputImplib = ConvertToNinjaPath(
566 genTarget->GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact));
568 this->DeviceLinkObject = targetOutputReal;
571 cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
572 const cmStateEnums::TargetType targetType = genTarget->GetType();
573 this->GetBuildFileStream() << "# Device Link build statements for "
574 << cmState::GetTargetTypeName(targetType)
575 << " target " << this->GetTargetName() << "\n\n";
577 // Compute the comment.
578 cmNinjaBuild build(this->LanguageLinkerDeviceRule());
580 cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
582 cmNinjaVars& vars = build.Variables;
585 build.Outputs.push_back(targetOutputReal);
586 // Compute specific libraries to link with.
587 build.ExplicitDeps = this->GetObjects();
588 build.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
590 std::string frameworkPath;
591 std::string linkPath;
593 std::string createRule = genTarget->GetCreateRuleVariable(
594 this->TargetLinkLanguage, this->GetConfigName());
595 const bool useWatcomQuote =
596 this->GetMakefile()->IsOn(createRule + "_USE_WATCOM_QUOTE");
597 cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
599 vars["TARGET_FILE"] =
600 localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
602 std::unique_ptr<cmLinkLineComputer> linkLineComputer(
603 new cmNinjaLinkLineDeviceComputer(
604 this->GetLocalGenerator(),
605 this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(),
607 linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
609 localGen.GetTargetFlags(
610 linkLineComputer.get(), this->GetConfigName(), vars["LINK_LIBRARIES"],
611 vars["FLAGS"], vars["LINK_FLAGS"], frameworkPath, linkPath, genTarget);
613 this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars);
616 cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]);
618 vars["MANIFESTS"] = this->GetManifests();
620 vars["LINK_PATH"] = frameworkPath + linkPath;
622 // Compute architecture specific link flags. Yes, these go into a different
623 // variable for executables, probably due to a mistake made when duplicating
624 // code between the Makefile executable and library generators.
625 if (targetType == cmStateEnums::EXECUTABLE) {
626 std::string t = vars["FLAGS"];
627 localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, cfgName);
630 std::string t = vars["ARCH_FLAGS"];
631 localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, cfgName);
632 vars["ARCH_FLAGS"] = t;
634 localGen.AddLanguageFlagsForLinking(t, genTarget, cudaLinkLanguage,
636 vars["LANGUAGE_COMPILE_FLAGS"] = t;
638 if (genTarget->HasSOName(cfgName)) {
639 vars["SONAME_FLAG"] =
640 this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage);
641 vars["SONAME"] = this->TargetNames.SharedObject;
642 if (targetType == cmStateEnums::SHARED_LIBRARY) {
643 std::string install_dir =
644 this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName);
645 if (!install_dir.empty()) {
646 vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
647 install_dir, cmOutputConverter::SHELL);
652 if (!this->TargetNames.ImportLibrary.empty()) {
653 const std::string impLibPath = localGen.ConvertToOutputFormat(
654 targetOutputImplib, cmOutputConverter::SHELL);
655 vars["TARGET_IMPLIB"] = impLibPath;
656 EnsureParentDirectoryExists(impLibPath);
659 const std::string objPath = GetGeneratorTarget()->GetSupportDirectory();
660 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
661 this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
662 EnsureDirectoryExists(objPath);
664 this->SetMsvcTargetPdbVariable(vars);
666 if (globalGen->IsGCCOnWindows()) {
667 // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
668 std::string& linkLibraries = vars["LINK_LIBRARIES"];
669 std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
670 std::string& link_path = vars["LINK_PATH"];
671 std::replace(link_path.begin(), link_path.end(), '\\', '/');
674 // Device linking currently doesn't support response files so
675 // do not check if the user has explicitly forced a response file.
676 int const commandLineLengthLimit =
677 static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
678 globalGen->GetRuleCmdLength(this->LanguageLinkerDeviceRule());
680 build.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") +
681 genTarget->GetName() + ".rsp");
683 // Gather order-only dependencies.
684 this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(),
685 build.OrderOnlyDeps);
687 // Write the build statement for this target.
688 bool usedResponseFile = false;
689 globalGen->WriteBuild(this->GetBuildFileStream(), build,
690 commandLineLengthLimit, &usedResponseFile);
691 this->WriteDeviceLinkRule(usedResponseFile);
694 void cmNinjaNormalTargetGenerator::WriteLinkStatement()
696 cmMakefile* mf = this->GetMakefile();
697 cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
698 cmGeneratorTarget* gt = this->GetGeneratorTarget();
700 const std::string cfgName = this->GetConfigName();
701 std::string targetOutput = ConvertToNinjaPath(gt->GetFullPath(cfgName));
702 std::string targetOutputReal = ConvertToNinjaPath(
703 gt->GetFullPath(cfgName, cmStateEnums::RuntimeBinaryArtifact,
705 std::string targetOutputImplib = ConvertToNinjaPath(
706 gt->GetFullPath(cfgName, cmStateEnums::ImportLibraryArtifact));
708 if (gt->IsAppBundleOnApple()) {
709 // Create the app bundle
710 std::string outpath = gt->GetDirectory(cfgName);
711 this->OSXBundleGenerator->CreateAppBundle(this->TargetNames.Output,
714 // Calculate the output path
715 targetOutput = cmStrCat(outpath, '/', this->TargetNames.Output);
716 targetOutput = this->ConvertToNinjaPath(targetOutput);
717 targetOutputReal = cmStrCat(outpath, '/', this->TargetNames.Real);
718 targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
719 } else if (gt->IsFrameworkOnApple()) {
720 // Create the library framework.
721 this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
722 gt->GetDirectory(cfgName));
723 } else if (gt->IsCFBundleOnApple()) {
724 // Create the core foundation bundle.
725 this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output,
726 gt->GetDirectory(cfgName));
730 cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
731 const cmStateEnums::TargetType targetType = gt->GetType();
732 this->GetBuildFileStream()
733 << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
734 << " target " << this->GetTargetName() << "\n\n";
736 cmNinjaBuild linkBuild(this->LanguageLinkerRule());
737 cmNinjaVars& vars = linkBuild.Variables;
739 // Compute the comment.
741 cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
744 linkBuild.Outputs.push_back(targetOutputReal);
746 if (this->TargetLinkLanguage == "Swift") {
747 vars["SWIFT_LIBRARY_NAME"] = [this]() -> std::string {
748 cmGeneratorTarget::Names targetNames =
749 this->GetGeneratorTarget()->GetLibraryNames(this->GetConfigName());
750 return targetNames.Base;
753 vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string {
754 if (const char* name = gt->GetProperty("Swift_MODULE_NAME")) {
757 return gt->GetName();
760 vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string {
761 std::string directory =
762 this->GetLocalGenerator()->GetCurrentBinaryDirectory();
763 if (const char* prop = this->GetGeneratorTarget()->GetProperty(
764 "Swift_MODULE_DIRECTORY")) {
768 std::string name = module + ".swiftmodule";
769 if (const char* prop =
770 this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) {
774 return this->GetLocalGenerator()->ConvertToOutputFormat(
775 this->ConvertToNinjaPath(directory + "/" + name),
776 cmOutputConverter::SHELL);
777 }(vars["SWIFT_MODULE_NAME"]);
779 vars["SWIFT_OUTPUT_FILE_MAP"] =
780 this->GetLocalGenerator()->ConvertToOutputFormat(
781 this->ConvertToNinjaPath(gt->GetSupportDirectory() +
782 "/output-file-map.json"),
783 cmOutputConverter::SHELL);
785 vars["SWIFT_SOURCES"] = [this]() -> std::string {
786 std::vector<cmSourceFile const*> sources;
787 std::stringstream oss;
789 this->GetGeneratorTarget()->GetObjectSources(sources,
790 this->GetConfigName());
791 cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
792 for (const auto& source : sources) {
794 << LocalGen->ConvertToOutputFormat(
795 this->ConvertToNinjaPath(this->GetSourceFilePath(source)),
796 cmOutputConverter::SHELL);
801 // Since we do not perform object builds, compute the
802 // defines/flags/includes here so that they can be passed along
804 vars["DEFINES"] = this->GetDefines("Swift");
805 vars["FLAGS"] = this->GetFlags("Swift");
806 vars["INCLUDES"] = this->GetIncludes("Swift");
809 // Compute specific libraries to link with.
810 if (this->TargetLinkLanguage == "Swift") {
811 std::vector<cmSourceFile const*> sources;
812 gt->GetObjectSources(sources, this->GetConfigName());
813 for (const auto& source : sources) {
814 linkBuild.Outputs.push_back(
815 this->ConvertToNinjaPath(this->GetObjectFilePath(source)));
816 linkBuild.ExplicitDeps.push_back(
817 this->ConvertToNinjaPath(this->GetSourceFilePath(source)));
820 linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
822 linkBuild.ExplicitDeps = this->GetObjects();
824 linkBuild.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
826 if (!this->DeviceLinkObject.empty()) {
827 linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
830 std::string frameworkPath;
831 std::string linkPath;
833 std::string createRule =
834 gt->GetCreateRuleVariable(this->TargetLinkLanguage, this->GetConfigName());
835 bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
836 cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
838 vars["TARGET_FILE"] =
839 localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
841 std::unique_ptr<cmLinkLineComputer> linkLineComputer(
842 globalGen->CreateLinkLineComputer(
843 this->GetLocalGenerator(),
844 this->GetLocalGenerator()->GetStateSnapshot().GetDirectory()));
845 linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
847 localGen.GetTargetFlags(linkLineComputer.get(), this->GetConfigName(),
848 vars["LINK_LIBRARIES"], vars["FLAGS"],
849 vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
851 // Add OS X version flags, if any.
852 if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
853 this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
854 this->AppendOSXVerFlag(vars["LINK_FLAGS"], this->TargetLinkLanguage,
855 "COMPATIBILITY", true);
856 this->AppendOSXVerFlag(vars["LINK_FLAGS"], this->TargetLinkLanguage,
860 this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars);
862 this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"]);
864 cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]);
866 vars["MANIFESTS"] = this->GetManifests();
868 vars["LINK_PATH"] = frameworkPath + linkPath;
869 std::string lwyuFlags;
870 if (gt->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
871 lwyuFlags = " -Wl,--no-as-needed";
874 // Compute architecture specific link flags. Yes, these go into a different
875 // variable for executables, probably due to a mistake made when duplicating
876 // code between the Makefile executable and library generators.
877 if (targetType == cmStateEnums::EXECUTABLE) {
878 std::string t = vars["FLAGS"];
879 localGen.AddArchitectureFlags(t, gt, TargetLinkLanguage, cfgName);
883 std::string t = vars["ARCH_FLAGS"];
884 localGen.AddArchitectureFlags(t, gt, TargetLinkLanguage, cfgName);
885 vars["ARCH_FLAGS"] = t;
888 localGen.AddLanguageFlagsForLinking(t, gt, TargetLinkLanguage, cfgName);
889 vars["LANGUAGE_COMPILE_FLAGS"] = t;
891 if (gt->HasSOName(cfgName)) {
892 vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage);
893 vars["SONAME"] = this->TargetNames.SharedObject;
894 if (targetType == cmStateEnums::SHARED_LIBRARY) {
895 std::string install_dir = gt->GetInstallNameDirForBuildTree(cfgName);
896 if (!install_dir.empty()) {
897 vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
898 install_dir, cmOutputConverter::SHELL);
903 cmNinjaDeps byproducts;
905 if (!this->TargetNames.ImportLibrary.empty()) {
906 const std::string impLibPath = localGen.ConvertToOutputFormat(
907 targetOutputImplib, cmOutputConverter::SHELL);
908 vars["TARGET_IMPLIB"] = impLibPath;
909 EnsureParentDirectoryExists(impLibPath);
910 if (gt->HasImportLibrary(cfgName)) {
911 byproducts.push_back(targetOutputImplib);
915 if (!this->SetMsvcTargetPdbVariable(vars)) {
916 // It is common to place debug symbols at a specific place,
917 // so we need a plain target name in the rule available.
921 gt->GetFullNameComponents(prefix, base, suffix);
922 std::string dbg_suffix = ".dbg";
923 // TODO: Where to document?
924 if (mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
925 dbg_suffix = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX");
927 vars["TARGET_PDB"] = base + suffix + dbg_suffix;
930 const std::string objPath = gt->GetSupportDirectory();
931 vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
932 this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
933 EnsureDirectoryExists(objPath);
935 if (globalGen->IsGCCOnWindows()) {
936 // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
937 std::string& linkLibraries = vars["LINK_LIBRARIES"];
938 std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
939 std::string& link_path = vars["LINK_PATH"];
940 std::replace(link_path.begin(), link_path.end(), '\\', '/');
943 const std::vector<cmCustomCommand>* cmdLists[3] = {
944 >->GetPreBuildCommands(), >->GetPreLinkCommands(),
945 >->GetPostBuildCommands()
948 std::vector<std::string> preLinkCmdLines;
949 std::vector<std::string> postBuildCmdLines;
950 std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
952 &postBuildCmdLines };
954 for (unsigned i = 0; i != 3; ++i) {
955 for (cmCustomCommand const& cc : *cmdLists[i]) {
956 cmCustomCommandGenerator ccg(cc, cfgName, this->GetLocalGenerator());
957 localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
958 std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
959 std::transform(ccByproducts.begin(), ccByproducts.end(),
960 std::back_inserter(byproducts), MapToNinjaPath());
964 // maybe create .def file from list of objects
965 cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
966 gt->GetModuleDefinitionInfo(this->GetConfigName());
967 if (mdi && mdi->DefFileGenerated) {
968 std::string cmakeCommand =
969 this->GetLocalGenerator()->ConvertToOutputFormat(
970 cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
972 cmStrCat(cmakeCommand, " -E __create_def ",
973 this->GetLocalGenerator()->ConvertToOutputFormat(
974 mdi->DefFile, cmOutputConverter::SHELL),
976 std::string obj_list_file = mdi->DefFile + ".objs";
977 cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
978 obj_list_file, cmOutputConverter::SHELL);
980 const char* nm_executable = GetMakefile()->GetDefinition("CMAKE_NM");
981 if (nm_executable && *nm_executable) {
983 cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
984 nm_executable, cmOutputConverter::SHELL);
986 preLinkCmdLines.push_back(std::move(cmd));
988 // create a list of obj files for the -E __create_def to read
989 cmGeneratedFileStream fout(obj_list_file);
991 if (mdi->WindowsExportAllSymbols) {
992 cmNinjaDeps objs = this->GetObjects();
993 for (std::string const& obj : objs) {
994 if (cmHasLiteralSuffix(obj, ".obj")) {
1000 for (cmSourceFile const* src : mdi->Sources) {
1001 fout << src->GetFullPath() << "\n";
1004 // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
1005 // for the link commands.
1006 if (!preLinkCmdLines.empty()) {
1007 const std::string homeOutDir = localGen.ConvertToOutputFormat(
1008 localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
1009 preLinkCmdLines.push_back("cd " + homeOutDir);
1012 vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines, "pre-link",
1013 this->GeneratorTarget);
1014 std::string postBuildCmdLine = localGen.BuildCommandLine(
1015 postBuildCmdLines, "post-build", this->GeneratorTarget);
1017 cmNinjaVars symlinkVars;
1018 bool const symlinkNeeded =
1019 (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple());
1020 if (!symlinkNeeded) {
1021 vars["POST_BUILD"] = postBuildCmdLine;
1023 vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
1024 symlinkVars["POST_BUILD"] = postBuildCmdLine;
1027 std::string cmakeVarLang = cmStrCat("CMAKE_", this->TargetLinkLanguage);
1029 // build response file name
1030 std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
1032 const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar);
1034 bool const lang_supports_response =
1035 !(this->TargetLinkLanguage == "RC" ||
1036 (this->TargetLinkLanguage == "CUDA" && !flag));
1037 int commandLineLengthLimit = -1;
1038 if (!lang_supports_response || !this->ForceResponseFile()) {
1039 commandLineLengthLimit =
1040 static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1041 globalGen->GetRuleCmdLength(linkBuild.Rule);
1044 linkBuild.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") +
1045 gt->GetName() + ".rsp");
1047 // Gather order-only dependencies.
1048 this->GetLocalGenerator()->AppendTargetDepends(gt, linkBuild.OrderOnlyDeps);
1050 // Add order-only dependencies on versioning symlinks of shared libs we link.
1051 if (!this->GeneratorTarget->IsDLLPlatform()) {
1052 if (cmComputeLinkInformation* cli =
1053 this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) {
1054 for (auto const& item : cli->GetItems()) {
1056 item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
1057 !item.Target->IsFrameworkOnApple()) {
1058 std::string const& lib = this->ConvertToNinjaPath(
1059 item.Target->GetFullPath(this->GetConfigName()));
1060 if (std::find(linkBuild.ImplicitDeps.begin(),
1061 linkBuild.ImplicitDeps.end(),
1062 lib) == linkBuild.ImplicitDeps.end()) {
1063 linkBuild.OrderOnlyDeps.emplace_back(lib);
1070 // Ninja should restat after linking if and only if there are byproducts.
1071 vars["RESTAT"] = byproducts.empty() ? "" : "1";
1073 for (std::string const& o : byproducts) {
1074 globalGen->SeenCustomCommandOutput(o);
1075 linkBuild.Outputs.push_back(o);
1078 // Write the build statement for this target.
1079 bool usedResponseFile = false;
1080 globalGen->WriteBuild(this->GetBuildFileStream(), linkBuild,
1081 commandLineLengthLimit, &usedResponseFile);
1082 this->WriteLinkRule(usedResponseFile);
1084 if (symlinkNeeded) {
1085 if (targetType == cmStateEnums::EXECUTABLE) {
1086 cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
1087 build.Comment = "Create executable symlink " + targetOutput;
1088 build.Outputs.push_back(targetOutput);
1089 build.ExplicitDeps.push_back(targetOutputReal);
1090 build.Variables = std::move(symlinkVars);
1091 globalGen->WriteBuild(this->GetBuildFileStream(), build);
1093 cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
1094 build.Comment = "Create library symlink " + targetOutput;
1096 std::string const soName = this->ConvertToNinjaPath(
1097 this->GetTargetFilePath(this->TargetNames.SharedObject));
1098 // If one link has to be created.
1099 if (targetOutputReal == soName || targetOutput == soName) {
1100 symlinkVars["SONAME"] =
1101 this->GetLocalGenerator()->ConvertToOutputFormat(
1102 soName, cmOutputConverter::SHELL);
1104 symlinkVars["SONAME"].clear();
1105 build.Outputs.push_back(soName);
1107 build.Outputs.push_back(targetOutput);
1108 build.ExplicitDeps.push_back(targetOutputReal);
1109 build.Variables = std::move(symlinkVars);
1111 globalGen->WriteBuild(this->GetBuildFileStream(), build);
1115 // Add aliases for the file name and the target name.
1116 globalGen->AddTargetAlias(this->TargetNames.Output, gt);
1117 globalGen->AddTargetAlias(this->GetTargetName(), gt);
1120 void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
1122 // Write a phony output that depends on all object files.
1124 cmNinjaBuild build("phony");
1125 build.Comment = "Object library " + this->GetTargetName();
1126 this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1128 build.ExplicitDeps = this->GetObjects();
1129 this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build);
1132 // Add aliases for the target name.
1133 this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
1134 this->GetGeneratorTarget());