cmNinjaNormalTargetGenerator.cxx 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164
  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. #include <algorithm>
  5. #include <cassert>
  6. #include <iterator>
  7. #include <map>
  8. #include <set>
  9. #include <sstream>
  10. #include <utility>
  11. #include <cm/memory>
  12. #include "cmAlgorithms.h"
  13. #include "cmComputeLinkInformation.h"
  14. #include "cmCustomCommand.h" // IWYU pragma: keep
  15. #include "cmCustomCommandGenerator.h"
  16. #include "cmGeneratedFileStream.h"
  17. #include "cmGeneratorTarget.h"
  18. #include "cmGlobalNinjaGenerator.h"
  19. #include "cmLinkLineComputer.h"
  20. #include "cmLinkLineDeviceComputer.h"
  21. #include "cmLocalCommonGenerator.h"
  22. #include "cmLocalGenerator.h"
  23. #include "cmLocalNinjaGenerator.h"
  24. #include "cmMakefile.h"
  25. #include "cmNinjaLinkLineDeviceComputer.h"
  26. #include "cmNinjaTypes.h"
  27. #include "cmOSXBundleGenerator.h"
  28. #include "cmOutputConverter.h"
  29. #include "cmRulePlaceholderExpander.h"
  30. #include "cmSourceFile.h"
  31. #include "cmState.h"
  32. #include "cmStateDirectory.h"
  33. #include "cmStateSnapshot.h"
  34. #include "cmStateTypes.h"
  35. #include "cmStringAlgorithms.h"
  36. #include "cmSystemTools.h"
  37. cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
  38. cmGeneratorTarget* target)
  39. : cmNinjaTargetGenerator(target)
  40. {
  41. if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) {
  42. // on Windows the output dir is already needed at compile time
  43. // ensure the directory exists (OutDir test)
  44. for (auto const& config : this->GetConfigNames()) {
  45. EnsureDirectoryExists(target->GetDirectory(config));
  46. }
  47. }
  48. this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
  49. this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
  50. }
  51. cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
  52. void cmNinjaNormalTargetGenerator::Generate(const std::string& config)
  53. {
  54. std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
  55. if (this->TargetLinkLanguage(config).empty()) {
  56. cmSystemTools::Error("CMake can not determine linker language for "
  57. "target: " +
  58. this->GetGeneratorTarget()->GetName());
  59. return;
  60. }
  61. // Write the rules for each language.
  62. this->WriteLanguagesRules(config);
  63. // Write the build statements
  64. this->WriteObjectBuildStatements(config);
  65. if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
  66. this->WriteObjectLibStatement(config);
  67. } else {
  68. // If this target has cuda language link inputs, and we need to do
  69. // device linking
  70. this->WriteDeviceLinkStatement(config);
  71. this->WriteLinkStatement(config);
  72. }
  73. // Find ADDITIONAL_CLEAN_FILES
  74. this->AdditionalCleanFiles(config);
  75. }
  76. void cmNinjaNormalTargetGenerator::WriteLanguagesRules(
  77. const std::string& config)
  78. {
  79. #ifdef NINJA_GEN_VERBOSE_FILES
  80. cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
  81. this->GetRulesFileStream()
  82. << "# Rules for each languages for "
  83. << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
  84. << " target " << this->GetTargetName() << "\n\n";
  85. #endif
  86. // Write rules for languages compiled in this target.
  87. std::set<std::string> languages;
  88. std::vector<cmSourceFile const*> sourceFiles;
  89. this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
  90. for (cmSourceFile const* sf : sourceFiles) {
  91. std::string const lang = sf->GetLanguage();
  92. if (!lang.empty()) {
  93. languages.insert(lang);
  94. }
  95. }
  96. for (std::string const& language : languages) {
  97. this->WriteLanguageRules(language);
  98. }
  99. }
  100. const char* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
  101. {
  102. switch (this->GetGeneratorTarget()->GetType()) {
  103. case cmStateEnums::STATIC_LIBRARY:
  104. return "static library";
  105. case cmStateEnums::SHARED_LIBRARY:
  106. return "shared library";
  107. case cmStateEnums::MODULE_LIBRARY:
  108. if (this->GetGeneratorTarget()->IsCFBundleOnApple()) {
  109. return "CFBundle shared module";
  110. } else {
  111. return "shared module";
  112. }
  113. case cmStateEnums::EXECUTABLE:
  114. return "executable";
  115. default:
  116. return nullptr;
  117. }
  118. }
  119. std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule(
  120. const std::string& config) const
  121. {
  122. return this->TargetLinkLanguage(config) + "_" +
  123. cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) +
  124. "_LINKER__" +
  125. cmGlobalNinjaGenerator::EncodeRuleName(
  126. this->GetGeneratorTarget()->GetName());
  127. }
  128. std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule(
  129. const std::string& config) const
  130. {
  131. return this->TargetLinkLanguage(config) + "_" +
  132. cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()) +
  133. "_DEVICE_LINKER__" +
  134. cmGlobalNinjaGenerator::EncodeRuleName(
  135. this->GetGeneratorTarget()->GetName());
  136. }
  137. struct cmNinjaRemoveNoOpCommands
  138. {
  139. bool operator()(std::string const& cmd)
  140. {
  141. return cmd.empty() || cmd[0] == ':';
  142. }
  143. };
  144. void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(
  145. bool useResponseFile, const std::string& config)
  146. {
  147. cmNinjaRule rule(this->LanguageLinkerDeviceRule(config));
  148. if (!this->GetGlobalGenerator()->HasRule(rule.Name)) {
  149. cmRulePlaceholderExpander::RuleVariables vars;
  150. vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
  151. vars.CMTargetType =
  152. cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType());
  153. vars.Language = "CUDA";
  154. // build response file name
  155. std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
  156. "CMAKE_CUDA_RESPONSE_FILE_LINK_FLAG");
  157. if (!useResponseFile || responseFlag.empty()) {
  158. vars.Objects = "$in";
  159. vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
  160. } else {
  161. rule.RspFile = "$RSP_FILE";
  162. responseFlag += rule.RspFile;
  163. // build response file content
  164. if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
  165. rule.RspContent = "$in";
  166. } else {
  167. rule.RspContent = "$in_newline";
  168. }
  169. rule.RspContent += " $LINK_LIBRARIES";
  170. vars.Objects = responseFlag.c_str();
  171. vars.LinkLibraries = "";
  172. }
  173. vars.ObjectDir = "$OBJECT_DIR";
  174. vars.Target = "$TARGET_FILE";
  175. vars.SONameFlag = "$SONAME_FLAG";
  176. vars.TargetSOName = "$SONAME";
  177. vars.TargetPDB = "$TARGET_PDB";
  178. vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
  179. vars.Flags = "$FLAGS";
  180. vars.LinkFlags = "$LINK_FLAGS";
  181. vars.Manifests = "$MANIFESTS";
  182. std::string langFlags;
  183. if (this->GetGeneratorTarget()->GetType() != cmStateEnums::EXECUTABLE) {
  184. langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
  185. vars.LanguageCompileFlags = langFlags.c_str();
  186. }
  187. std::string launcher;
  188. const char* val = this->GetLocalGenerator()->GetRuleLauncher(
  189. this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
  190. if (val && *val) {
  191. launcher = cmStrCat(val, ' ');
  192. }
  193. std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
  194. this->GetLocalGenerator()->CreateRulePlaceholderExpander());
  195. // Rule for linking library/executable.
  196. std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
  197. for (std::string& linkCmd : linkCmds) {
  198. linkCmd = cmStrCat(launcher, linkCmd);
  199. rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
  200. linkCmd, vars);
  201. }
  202. // If there is no ranlib the command will be ":". Skip it.
  203. cmEraseIf(linkCmds, cmNinjaRemoveNoOpCommands());
  204. rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
  205. // Write the linker rule with response file if needed.
  206. rule.Comment =
  207. cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
  208. this->GetVisibleTypeName(), '.');
  209. rule.Description =
  210. cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
  211. this->GetVisibleTypeName(), " $TARGET_FILE");
  212. rule.Restat = "$RESTAT";
  213. this->GetGlobalGenerator()->AddRule(rule);
  214. }
  215. }
  216. void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
  217. const std::string& config)
  218. {
  219. cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType();
  220. std::string linkRuleName = this->LanguageLinkerRule(config);
  221. if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) {
  222. cmNinjaRule rule(std::move(linkRuleName));
  223. cmRulePlaceholderExpander::RuleVariables vars;
  224. vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
  225. vars.CMTargetType = cmState::GetTargetTypeName(targetType);
  226. std::string lang = this->TargetLinkLanguage(config);
  227. vars.Language = config.c_str();
  228. if (this->TargetLinkLanguage(config) == "Swift") {
  229. vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
  230. vars.SwiftModule = "$SWIFT_MODULE";
  231. vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
  232. vars.SwiftOutputFileMap = "$SWIFT_OUTPUT_FILE_MAP";
  233. vars.SwiftSources = "$SWIFT_SOURCES";
  234. vars.Defines = "$DEFINES";
  235. vars.Flags = "$FLAGS";
  236. vars.Includes = "$INCLUDES";
  237. }
  238. std::string responseFlag;
  239. std::string cmakeVarLang =
  240. cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
  241. // build response file name
  242. std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
  243. const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar);
  244. if (flag) {
  245. responseFlag = flag;
  246. } else if (this->TargetLinkLanguage(config) != "CUDA") {
  247. responseFlag = "@";
  248. }
  249. if (!useResponseFile || responseFlag.empty()) {
  250. vars.Objects = "$in";
  251. vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
  252. } else {
  253. rule.RspFile = "$RSP_FILE";
  254. responseFlag += rule.RspFile;
  255. // build response file content
  256. if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
  257. rule.RspContent = "$in";
  258. } else {
  259. rule.RspContent = "$in_newline";
  260. }
  261. rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
  262. if (this->TargetLinkLanguage(config) == "Swift") {
  263. vars.SwiftSources = responseFlag.c_str();
  264. } else {
  265. vars.Objects = responseFlag.c_str();
  266. }
  267. vars.LinkLibraries = "";
  268. }
  269. vars.ObjectDir = "$OBJECT_DIR";
  270. vars.Target = "$TARGET_FILE";
  271. vars.SONameFlag = "$SONAME_FLAG";
  272. vars.TargetSOName = "$SONAME";
  273. vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
  274. vars.TargetPDB = "$TARGET_PDB";
  275. // Setup the target version.
  276. std::string targetVersionMajor;
  277. std::string targetVersionMinor;
  278. {
  279. std::ostringstream majorStream;
  280. std::ostringstream minorStream;
  281. int major;
  282. int minor;
  283. this->GetGeneratorTarget()->GetTargetVersion(major, minor);
  284. majorStream << major;
  285. minorStream << minor;
  286. targetVersionMajor = majorStream.str();
  287. targetVersionMinor = minorStream.str();
  288. }
  289. vars.TargetVersionMajor = targetVersionMajor.c_str();
  290. vars.TargetVersionMinor = targetVersionMinor.c_str();
  291. vars.Flags = "$FLAGS";
  292. vars.LinkFlags = "$LINK_FLAGS";
  293. vars.Manifests = "$MANIFESTS";
  294. std::string langFlags;
  295. if (targetType != cmStateEnums::EXECUTABLE) {
  296. langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
  297. vars.LanguageCompileFlags = langFlags.c_str();
  298. }
  299. std::string launcher;
  300. const char* val = this->GetLocalGenerator()->GetRuleLauncher(
  301. this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
  302. if (val && *val) {
  303. launcher = cmStrCat(val, ' ');
  304. }
  305. std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
  306. this->GetLocalGenerator()->CreateRulePlaceholderExpander());
  307. // Rule for linking library/executable.
  308. std::vector<std::string> linkCmds = this->ComputeLinkCmd(config);
  309. for (std::string& linkCmd : linkCmds) {
  310. linkCmd = cmStrCat(launcher, linkCmd);
  311. rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
  312. linkCmd, vars);
  313. }
  314. // If there is no ranlib the command will be ":". Skip it.
  315. cmEraseIf(linkCmds, cmNinjaRemoveNoOpCommands());
  316. linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
  317. linkCmds.emplace_back("$POST_BUILD");
  318. rule.Command = this->GetLocalGenerator()->BuildCommandLine(linkCmds);
  319. // Write the linker rule with response file if needed.
  320. rule.Comment =
  321. cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
  322. this->GetVisibleTypeName(), '.');
  323. rule.Description =
  324. cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
  325. this->GetVisibleTypeName(), " $TARGET_FILE");
  326. rule.Restat = "$RESTAT";
  327. this->GetGlobalGenerator()->AddRule(rule);
  328. }
  329. auto const tgtNames = this->TargetNames(config);
  330. if (tgtNames.Output != tgtNames.Real &&
  331. !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
  332. std::string cmakeCommand =
  333. this->GetLocalGenerator()->ConvertToOutputFormat(
  334. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  335. if (targetType == cmStateEnums::EXECUTABLE) {
  336. cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
  337. {
  338. std::vector<std::string> cmd;
  339. cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
  340. cmd.emplace_back("$POST_BUILD");
  341. rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
  342. }
  343. rule.Description = "Creating executable symlink $out";
  344. rule.Comment = "Rule for creating executable symlink.";
  345. this->GetGlobalGenerator()->AddRule(rule);
  346. } else {
  347. cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
  348. {
  349. std::vector<std::string> cmd;
  350. cmd.push_back(cmakeCommand +
  351. " -E cmake_symlink_library $in $SONAME $out");
  352. cmd.emplace_back("$POST_BUILD");
  353. rule.Command = this->GetLocalGenerator()->BuildCommandLine(cmd);
  354. }
  355. rule.Description = "Creating library symlink $out";
  356. rule.Comment = "Rule for creating library symlink.";
  357. this->GetGlobalGenerator()->AddRule(rule);
  358. }
  359. }
  360. }
  361. std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
  362. {
  363. std::vector<std::string> linkCmds;
  364. // this target requires separable cuda compilation
  365. // now build the correct command depending on if the target is
  366. // an executable or a dynamic library.
  367. std::string linkCmd;
  368. switch (this->GetGeneratorTarget()->GetType()) {
  369. case cmStateEnums::STATIC_LIBRARY:
  370. case cmStateEnums::SHARED_LIBRARY:
  371. case cmStateEnums::MODULE_LIBRARY: {
  372. const std::string cudaLinkCmd(
  373. this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_LIBRARY"));
  374. cmExpandList(cudaLinkCmd, linkCmds);
  375. } break;
  376. case cmStateEnums::EXECUTABLE: {
  377. const std::string cudaLinkCmd(this->GetMakefile()->GetDefinition(
  378. "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE"));
  379. cmExpandList(cudaLinkCmd, linkCmds);
  380. } break;
  381. default:
  382. break;
  383. }
  384. return linkCmds;
  385. }
  386. std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd(
  387. const std::string& config)
  388. {
  389. std::vector<std::string> linkCmds;
  390. cmMakefile* mf = this->GetMakefile();
  391. {
  392. // If we have a rule variable prefer it. In the case of static libraries
  393. // this occurs when things like IPO is enabled, and we need to use the
  394. // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
  395. std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
  396. this->TargetLinkLanguage(config), config);
  397. const char* linkCmd = mf->GetDefinition(linkCmdVar);
  398. if (linkCmd) {
  399. std::string linkCmdStr = linkCmd;
  400. if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
  401. std::string ruleVar =
  402. cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
  403. "_GNUtoMS_RULE");
  404. if (const char* rule = this->Makefile->GetDefinition(ruleVar)) {
  405. linkCmdStr += rule;
  406. }
  407. }
  408. cmExpandList(linkCmdStr, linkCmds);
  409. if (this->GetGeneratorTarget()->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
  410. std::string cmakeCommand = cmStrCat(
  411. this->GetLocalGenerator()->ConvertToOutputFormat(
  412. cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
  413. " -E __run_co_compile --lwyu=");
  414. cmGeneratorTarget& gt = *this->GetGeneratorTarget();
  415. std::string targetOutputReal = this->ConvertToNinjaPath(
  416. gt.GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
  417. /*realname=*/true));
  418. cmakeCommand += targetOutputReal;
  419. linkCmds.push_back(std::move(cmakeCommand));
  420. }
  421. return linkCmds;
  422. }
  423. }
  424. switch (this->GetGeneratorTarget()->GetType()) {
  425. case cmStateEnums::STATIC_LIBRARY: {
  426. // We have archive link commands set. First, delete the existing archive.
  427. {
  428. std::string cmakeCommand =
  429. this->GetLocalGenerator()->ConvertToOutputFormat(
  430. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  431. linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE");
  432. }
  433. // TODO: Use ARCHIVE_APPEND for archives over a certain size.
  434. {
  435. std::string linkCmdVar = cmStrCat(
  436. "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE");
  437. linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
  438. linkCmdVar, this->TargetLinkLanguage(config), config);
  439. std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
  440. cmExpandList(linkCmd, linkCmds);
  441. }
  442. {
  443. std::string linkCmdVar = cmStrCat(
  444. "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH");
  445. linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
  446. linkCmdVar, this->TargetLinkLanguage(config), config);
  447. std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
  448. cmExpandList(linkCmd, linkCmds);
  449. }
  450. #ifdef __APPLE__
  451. // On macOS ranlib truncates the fractional part of the static archive
  452. // file modification time. If the archive and at least one contained
  453. // object file were created within the same second this will make look
  454. // the archive older than the object file. On subsequent ninja runs this
  455. // leads to re-achiving and updating dependent targets.
  456. // As a work-around we touch the archive after ranlib (see #19222).
  457. {
  458. std::string cmakeCommand =
  459. this->GetLocalGenerator()->ConvertToOutputFormat(
  460. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  461. linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
  462. }
  463. #endif
  464. return linkCmds;
  465. }
  466. case cmStateEnums::SHARED_LIBRARY:
  467. case cmStateEnums::MODULE_LIBRARY:
  468. case cmStateEnums::EXECUTABLE:
  469. break;
  470. default:
  471. assert(false && "Unexpected target type");
  472. }
  473. return std::vector<std::string>();
  474. }
  475. void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
  476. const std::string& config)
  477. {
  478. cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
  479. if (!globalGen->GetLanguageEnabled("CUDA")) {
  480. return;
  481. }
  482. cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
  483. bool requiresDeviceLinking = requireDeviceLinking(
  484. *this->GeneratorTarget, *this->GetLocalGenerator(), config);
  485. if (!requiresDeviceLinking) {
  486. return;
  487. }
  488. // Now we can do device linking
  489. // First and very important step is to make sure while inside this
  490. // step our link language is set to CUDA
  491. std::string cudaLinkLanguage = "CUDA";
  492. std::string const& objExt =
  493. this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
  494. std::string const targetOutputReal = ConvertToNinjaPath(
  495. genTarget->ObjectDirectory + "cmake_device_link" + objExt);
  496. std::string const targetOutputImplib = ConvertToNinjaPath(
  497. genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
  498. this->DeviceLinkObject = targetOutputReal;
  499. // Write comments.
  500. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
  501. const cmStateEnums::TargetType targetType = genTarget->GetType();
  502. this->GetBuildFileStream() << "# Device Link build statements for "
  503. << cmState::GetTargetTypeName(targetType)
  504. << " target " << this->GetTargetName() << "\n\n";
  505. // Compute the comment.
  506. cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
  507. build.Comment =
  508. cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
  509. cmNinjaVars& vars = build.Variables;
  510. // Compute outputs.
  511. build.Outputs.push_back(targetOutputReal);
  512. // Compute specific libraries to link with.
  513. build.ExplicitDeps = this->GetObjects();
  514. build.ImplicitDeps =
  515. this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
  516. std::string frameworkPath;
  517. std::string linkPath;
  518. std::string createRule =
  519. genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
  520. const bool useWatcomQuote =
  521. this->GetMakefile()->IsOn(createRule + "_USE_WATCOM_QUOTE");
  522. cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
  523. vars["TARGET_FILE"] =
  524. localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
  525. std::unique_ptr<cmLinkLineComputer> linkLineComputer(
  526. new cmNinjaLinkLineDeviceComputer(
  527. this->GetLocalGenerator(),
  528. this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(),
  529. globalGen));
  530. linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
  531. localGen.GetTargetFlags(
  532. linkLineComputer.get(), config, vars["LINK_LIBRARIES"], vars["FLAGS"],
  533. vars["LINK_FLAGS"], frameworkPath, linkPath, genTarget);
  534. this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars);
  535. vars["LINK_FLAGS"] =
  536. cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]);
  537. vars["MANIFESTS"] = this->GetManifests(config);
  538. vars["LINK_PATH"] = frameworkPath + linkPath;
  539. // Compute architecture specific link flags. Yes, these go into a different
  540. // variable for executables, probably due to a mistake made when duplicating
  541. // code between the Makefile executable and library generators.
  542. if (targetType == cmStateEnums::EXECUTABLE) {
  543. std::string t = vars["FLAGS"];
  544. localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, config);
  545. vars["FLAGS"] = t;
  546. } else {
  547. std::string t = vars["ARCH_FLAGS"];
  548. localGen.AddArchitectureFlags(t, genTarget, cudaLinkLanguage, config);
  549. vars["ARCH_FLAGS"] = t;
  550. t.clear();
  551. localGen.AddLanguageFlagsForLinking(t, genTarget, cudaLinkLanguage,
  552. config);
  553. vars["LANGUAGE_COMPILE_FLAGS"] = t;
  554. }
  555. auto const tgtNames = this->TargetNames(config);
  556. if (genTarget->HasSOName(config)) {
  557. vars["SONAME_FLAG"] =
  558. this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
  559. vars["SONAME"] = tgtNames.SharedObject;
  560. if (targetType == cmStateEnums::SHARED_LIBRARY) {
  561. std::string install_dir =
  562. this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
  563. if (!install_dir.empty()) {
  564. vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
  565. install_dir, cmOutputConverter::SHELL);
  566. }
  567. }
  568. }
  569. if (!tgtNames.ImportLibrary.empty()) {
  570. const std::string impLibPath = localGen.ConvertToOutputFormat(
  571. targetOutputImplib, cmOutputConverter::SHELL);
  572. vars["TARGET_IMPLIB"] = impLibPath;
  573. EnsureParentDirectoryExists(impLibPath);
  574. }
  575. const std::string objPath = GetGeneratorTarget()->GetSupportDirectory();
  576. vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
  577. this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
  578. EnsureDirectoryExists(objPath);
  579. this->SetMsvcTargetPdbVariable(vars, config);
  580. if (globalGen->IsGCCOnWindows()) {
  581. // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
  582. std::string& linkLibraries = vars["LINK_LIBRARIES"];
  583. std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
  584. std::string& link_path = vars["LINK_PATH"];
  585. std::replace(link_path.begin(), link_path.end(), '\\', '/');
  586. }
  587. // Device linking currently doesn't support response files so
  588. // do not check if the user has explicitly forced a response file.
  589. int const commandLineLengthLimit =
  590. static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
  591. globalGen->GetRuleCmdLength(this->LanguageLinkerDeviceRule(config));
  592. build.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") +
  593. genTarget->GetName() + ".rsp");
  594. // Gather order-only dependencies.
  595. this->GetLocalGenerator()->AppendTargetDepends(this->GetGeneratorTarget(),
  596. build.OrderOnlyDeps, config);
  597. // Write the build statement for this target.
  598. bool usedResponseFile = false;
  599. globalGen->WriteBuild(this->GetBuildFileStream(), build,
  600. commandLineLengthLimit, &usedResponseFile);
  601. this->WriteDeviceLinkRule(usedResponseFile, config);
  602. }
  603. void cmNinjaNormalTargetGenerator::WriteLinkStatement(
  604. const std::string& config)
  605. {
  606. cmMakefile* mf = this->GetMakefile();
  607. cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
  608. cmGeneratorTarget* gt = this->GetGeneratorTarget();
  609. std::string targetOutput = ConvertToNinjaPath(gt->GetFullPath(config));
  610. std::string targetOutputReal = ConvertToNinjaPath(
  611. gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
  612. /*realname=*/true));
  613. std::string targetOutputImplib = ConvertToNinjaPath(
  614. gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
  615. auto const tgtNames = this->TargetNames(config);
  616. if (gt->IsAppBundleOnApple()) {
  617. // Create the app bundle
  618. std::string outpath = gt->GetDirectory(config);
  619. this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath,
  620. config);
  621. // Calculate the output path
  622. targetOutput = cmStrCat(outpath, '/', tgtNames.Output);
  623. targetOutput = this->ConvertToNinjaPath(targetOutput);
  624. targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real);
  625. targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
  626. } else if (gt->IsFrameworkOnApple()) {
  627. // Create the library framework.
  628. this->OSXBundleGenerator->CreateFramework(
  629. tgtNames.Output, gt->GetDirectory(config), config);
  630. } else if (gt->IsCFBundleOnApple()) {
  631. // Create the core foundation bundle.
  632. this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,
  633. gt->GetDirectory(config), config);
  634. }
  635. // Write comments.
  636. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
  637. const cmStateEnums::TargetType targetType = gt->GetType();
  638. this->GetBuildFileStream()
  639. << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
  640. << " target " << this->GetTargetName() << "\n\n";
  641. cmNinjaBuild linkBuild(this->LanguageLinkerRule(config));
  642. cmNinjaVars& vars = linkBuild.Variables;
  643. // Compute the comment.
  644. linkBuild.Comment =
  645. cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
  646. // Compute outputs.
  647. linkBuild.Outputs.push_back(targetOutputReal);
  648. if (this->TargetLinkLanguage(config) == "Swift") {
  649. vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
  650. cmGeneratorTarget::Names targetNames =
  651. this->GetGeneratorTarget()->GetLibraryNames(config);
  652. return targetNames.Base;
  653. }();
  654. vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string {
  655. if (const char* name = gt->GetProperty("Swift_MODULE_NAME")) {
  656. return name;
  657. }
  658. return gt->GetName();
  659. }();
  660. vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string {
  661. std::string directory =
  662. this->GetLocalGenerator()->GetCurrentBinaryDirectory();
  663. if (const char* prop = this->GetGeneratorTarget()->GetProperty(
  664. "Swift_MODULE_DIRECTORY")) {
  665. directory = prop;
  666. }
  667. std::string name = module + ".swiftmodule";
  668. if (const char* prop =
  669. this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) {
  670. name = prop;
  671. }
  672. return this->GetLocalGenerator()->ConvertToOutputFormat(
  673. this->ConvertToNinjaPath(directory + "/" + name),
  674. cmOutputConverter::SHELL);
  675. }(vars["SWIFT_MODULE_NAME"]);
  676. vars["SWIFT_OUTPUT_FILE_MAP"] =
  677. this->GetLocalGenerator()->ConvertToOutputFormat(
  678. this->ConvertToNinjaPath(gt->GetSupportDirectory() +
  679. "/output-file-map.json"),
  680. cmOutputConverter::SHELL);
  681. vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
  682. std::vector<cmSourceFile const*> sources;
  683. std::stringstream oss;
  684. this->GetGeneratorTarget()->GetObjectSources(sources, config);
  685. cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
  686. for (const auto& source : sources) {
  687. oss << " "
  688. << LocalGen->ConvertToOutputFormat(
  689. this->ConvertToNinjaPath(this->GetSourceFilePath(source)),
  690. cmOutputConverter::SHELL);
  691. }
  692. return oss.str();
  693. }();
  694. // Since we do not perform object builds, compute the
  695. // defines/flags/includes here so that they can be passed along
  696. // appropriately.
  697. vars["DEFINES"] = this->GetDefines("Swift", config);
  698. vars["FLAGS"] = this->GetFlags("Swift", config);
  699. vars["INCLUDES"] = this->GetIncludes("Swift", config);
  700. }
  701. // Compute specific libraries to link with.
  702. if (this->TargetLinkLanguage(config) == "Swift") {
  703. std::vector<cmSourceFile const*> sources;
  704. gt->GetObjectSources(sources, config);
  705. for (const auto& source : sources) {
  706. linkBuild.Outputs.push_back(
  707. this->ConvertToNinjaPath(this->GetObjectFilePath(source)));
  708. linkBuild.ExplicitDeps.push_back(
  709. this->ConvertToNinjaPath(this->GetSourceFilePath(source)));
  710. }
  711. linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
  712. } else {
  713. linkBuild.ExplicitDeps = this->GetObjects();
  714. }
  715. linkBuild.ImplicitDeps =
  716. this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
  717. if (!this->DeviceLinkObject.empty()) {
  718. linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
  719. }
  720. std::string frameworkPath;
  721. std::string linkPath;
  722. std::string createRule =
  723. gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
  724. bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
  725. cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
  726. vars["TARGET_FILE"] =
  727. localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
  728. std::unique_ptr<cmLinkLineComputer> linkLineComputer(
  729. globalGen->CreateLinkLineComputer(
  730. this->GetLocalGenerator(),
  731. this->GetLocalGenerator()->GetStateSnapshot().GetDirectory()));
  732. linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
  733. localGen.GetTargetFlags(linkLineComputer.get(), config,
  734. vars["LINK_LIBRARIES"], vars["FLAGS"],
  735. vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
  736. // Add OS X version flags, if any.
  737. if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
  738. this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
  739. this->AppendOSXVerFlag(vars["LINK_FLAGS"],
  740. this->TargetLinkLanguage(config), "COMPATIBILITY",
  741. true);
  742. this->AppendOSXVerFlag(vars["LINK_FLAGS"],
  743. this->TargetLinkLanguage(config), "CURRENT", false);
  744. }
  745. this->addPoolNinjaVariable("JOB_POOL_LINK", gt, vars);
  746. this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"],
  747. config);
  748. vars["LINK_FLAGS"] =
  749. cmGlobalNinjaGenerator::EncodeLiteral(vars["LINK_FLAGS"]);
  750. vars["MANIFESTS"] = this->GetManifests(config);
  751. vars["LINK_PATH"] = frameworkPath + linkPath;
  752. std::string lwyuFlags;
  753. if (gt->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
  754. lwyuFlags = " -Wl,--no-as-needed";
  755. }
  756. // Compute architecture specific link flags. Yes, these go into a different
  757. // variable for executables, probably due to a mistake made when duplicating
  758. // code between the Makefile executable and library generators.
  759. if (targetType == cmStateEnums::EXECUTABLE) {
  760. std::string t = vars["FLAGS"];
  761. localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
  762. config);
  763. t += lwyuFlags;
  764. vars["FLAGS"] = t;
  765. } else {
  766. std::string t = vars["ARCH_FLAGS"];
  767. localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
  768. config);
  769. vars["ARCH_FLAGS"] = t;
  770. t.clear();
  771. t += lwyuFlags;
  772. localGen.AddLanguageFlagsForLinking(
  773. t, gt, this->TargetLinkLanguage(config), config);
  774. vars["LANGUAGE_COMPILE_FLAGS"] = t;
  775. }
  776. if (gt->HasSOName(config)) {
  777. vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config));
  778. vars["SONAME"] = tgtNames.SharedObject;
  779. if (targetType == cmStateEnums::SHARED_LIBRARY) {
  780. std::string install_dir = gt->GetInstallNameDirForBuildTree(config);
  781. if (!install_dir.empty()) {
  782. vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
  783. install_dir, cmOutputConverter::SHELL);
  784. }
  785. }
  786. }
  787. cmNinjaDeps byproducts;
  788. if (!tgtNames.ImportLibrary.empty()) {
  789. const std::string impLibPath = localGen.ConvertToOutputFormat(
  790. targetOutputImplib, cmOutputConverter::SHELL);
  791. vars["TARGET_IMPLIB"] = impLibPath;
  792. EnsureParentDirectoryExists(impLibPath);
  793. if (gt->HasImportLibrary(config)) {
  794. byproducts.push_back(targetOutputImplib);
  795. }
  796. }
  797. if (!this->SetMsvcTargetPdbVariable(vars, config)) {
  798. // It is common to place debug symbols at a specific place,
  799. // so we need a plain target name in the rule available.
  800. std::string prefix;
  801. std::string base;
  802. std::string suffix;
  803. gt->GetFullNameComponents(prefix, base, suffix);
  804. std::string dbg_suffix = ".dbg";
  805. // TODO: Where to document?
  806. if (mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
  807. dbg_suffix = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX");
  808. }
  809. vars["TARGET_PDB"] = base + suffix + dbg_suffix;
  810. }
  811. const std::string objPath = gt->GetSupportDirectory();
  812. vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
  813. this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
  814. EnsureDirectoryExists(objPath);
  815. if (globalGen->IsGCCOnWindows()) {
  816. // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
  817. std::string& linkLibraries = vars["LINK_LIBRARIES"];
  818. std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
  819. std::string& link_path = vars["LINK_PATH"];
  820. std::replace(link_path.begin(), link_path.end(), '\\', '/');
  821. }
  822. const std::vector<cmCustomCommand>* cmdLists[3] = {
  823. &gt->GetPreBuildCommands(), &gt->GetPreLinkCommands(),
  824. &gt->GetPostBuildCommands()
  825. };
  826. std::vector<std::string> preLinkCmdLines;
  827. std::vector<std::string> postBuildCmdLines;
  828. std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
  829. &preLinkCmdLines,
  830. &postBuildCmdLines };
  831. for (unsigned i = 0; i != 3; ++i) {
  832. for (cmCustomCommand const& cc : *cmdLists[i]) {
  833. cmCustomCommandGenerator ccg(cc, config, this->GetLocalGenerator());
  834. localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
  835. std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
  836. std::transform(ccByproducts.begin(), ccByproducts.end(),
  837. std::back_inserter(byproducts), MapToNinjaPath());
  838. }
  839. }
  840. // maybe create .def file from list of objects
  841. cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
  842. gt->GetModuleDefinitionInfo(config);
  843. if (mdi && mdi->DefFileGenerated) {
  844. std::string cmakeCommand =
  845. this->GetLocalGenerator()->ConvertToOutputFormat(
  846. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  847. std::string cmd =
  848. cmStrCat(cmakeCommand, " -E __create_def ",
  849. this->GetLocalGenerator()->ConvertToOutputFormat(
  850. mdi->DefFile, cmOutputConverter::SHELL),
  851. ' ');
  852. std::string obj_list_file = mdi->DefFile + ".objs";
  853. cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
  854. obj_list_file, cmOutputConverter::SHELL);
  855. const char* nm_executable = GetMakefile()->GetDefinition("CMAKE_NM");
  856. if (nm_executable && *nm_executable) {
  857. cmd += " --nm=";
  858. cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
  859. nm_executable, cmOutputConverter::SHELL);
  860. }
  861. preLinkCmdLines.push_back(std::move(cmd));
  862. // create a list of obj files for the -E __create_def to read
  863. cmGeneratedFileStream fout(obj_list_file);
  864. if (mdi->WindowsExportAllSymbols) {
  865. cmNinjaDeps objs = this->GetObjects();
  866. for (std::string const& obj : objs) {
  867. if (cmHasLiteralSuffix(obj, ".obj")) {
  868. fout << obj << "\n";
  869. }
  870. }
  871. }
  872. for (cmSourceFile const* src : mdi->Sources) {
  873. fout << src->GetFullPath() << "\n";
  874. }
  875. }
  876. // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
  877. // for the link commands.
  878. if (!preLinkCmdLines.empty()) {
  879. const std::string homeOutDir = localGen.ConvertToOutputFormat(
  880. localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
  881. preLinkCmdLines.push_back("cd " + homeOutDir);
  882. }
  883. vars["PRE_LINK"] = localGen.BuildCommandLine(preLinkCmdLines, "pre-link",
  884. this->GeneratorTarget);
  885. std::string postBuildCmdLine = localGen.BuildCommandLine(
  886. postBuildCmdLines, "post-build", this->GeneratorTarget);
  887. cmNinjaVars symlinkVars;
  888. bool const symlinkNeeded =
  889. (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple());
  890. if (!symlinkNeeded) {
  891. vars["POST_BUILD"] = postBuildCmdLine;
  892. } else {
  893. vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
  894. symlinkVars["POST_BUILD"] = postBuildCmdLine;
  895. }
  896. std::string cmakeVarLang =
  897. cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
  898. // build response file name
  899. std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
  900. const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar);
  901. bool const lang_supports_response =
  902. !(this->TargetLinkLanguage(config) == "RC" ||
  903. (this->TargetLinkLanguage(config) == "CUDA" && !flag));
  904. int commandLineLengthLimit = -1;
  905. if (!lang_supports_response || !this->ForceResponseFile()) {
  906. commandLineLengthLimit =
  907. static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
  908. globalGen->GetRuleCmdLength(linkBuild.Rule);
  909. }
  910. linkBuild.RspFile = this->ConvertToNinjaPath(std::string("CMakeFiles/") +
  911. gt->GetName() + ".rsp");
  912. // Gather order-only dependencies.
  913. this->GetLocalGenerator()->AppendTargetDepends(gt, linkBuild.OrderOnlyDeps,
  914. config);
  915. // Add order-only dependencies on versioning symlinks of shared libs we link.
  916. if (!this->GeneratorTarget->IsDLLPlatform()) {
  917. if (cmComputeLinkInformation* cli =
  918. this->GeneratorTarget->GetLinkInformation(config)) {
  919. for (auto const& item : cli->GetItems()) {
  920. if (item.Target &&
  921. item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
  922. !item.Target->IsFrameworkOnApple()) {
  923. std::string const& lib =
  924. this->ConvertToNinjaPath(item.Target->GetFullPath(config));
  925. if (std::find(linkBuild.ImplicitDeps.begin(),
  926. linkBuild.ImplicitDeps.end(),
  927. lib) == linkBuild.ImplicitDeps.end()) {
  928. linkBuild.OrderOnlyDeps.emplace_back(lib);
  929. }
  930. }
  931. }
  932. }
  933. }
  934. // Ninja should restat after linking if and only if there are byproducts.
  935. vars["RESTAT"] = byproducts.empty() ? "" : "1";
  936. for (std::string const& o : byproducts) {
  937. globalGen->SeenCustomCommandOutput(o);
  938. linkBuild.Outputs.push_back(o);
  939. }
  940. // Write the build statement for this target.
  941. bool usedResponseFile = false;
  942. globalGen->WriteBuild(this->GetBuildFileStream(), linkBuild,
  943. commandLineLengthLimit, &usedResponseFile);
  944. this->WriteLinkRule(usedResponseFile, config);
  945. if (symlinkNeeded) {
  946. if (targetType == cmStateEnums::EXECUTABLE) {
  947. cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
  948. build.Comment = "Create executable symlink " + targetOutput;
  949. build.Outputs.push_back(targetOutput);
  950. build.ExplicitDeps.push_back(targetOutputReal);
  951. build.Variables = std::move(symlinkVars);
  952. globalGen->WriteBuild(this->GetBuildFileStream(), build);
  953. } else {
  954. cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
  955. build.Comment = "Create library symlink " + targetOutput;
  956. std::string const soName = this->ConvertToNinjaPath(
  957. this->GetTargetFilePath(tgtNames.SharedObject, config));
  958. // If one link has to be created.
  959. if (targetOutputReal == soName || targetOutput == soName) {
  960. symlinkVars["SONAME"] =
  961. this->GetLocalGenerator()->ConvertToOutputFormat(
  962. soName, cmOutputConverter::SHELL);
  963. } else {
  964. symlinkVars["SONAME"].clear();
  965. build.Outputs.push_back(soName);
  966. }
  967. build.Outputs.push_back(targetOutput);
  968. build.ExplicitDeps.push_back(targetOutputReal);
  969. build.Variables = std::move(symlinkVars);
  970. globalGen->WriteBuild(this->GetBuildFileStream(), build);
  971. }
  972. }
  973. // Add aliases for the file name and the target name.
  974. globalGen->AddTargetAlias(tgtNames.Output, gt, config);
  975. globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
  976. }
  977. void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
  978. const std::string& config)
  979. {
  980. // Write a phony output that depends on all object files.
  981. {
  982. cmNinjaBuild build("phony");
  983. build.Comment = "Object library " + this->GetTargetName();
  984. this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
  985. build.Outputs, config);
  986. build.ExplicitDeps = this->GetObjects();
  987. this->GetGlobalGenerator()->WriteBuild(this->GetBuildFileStream(), build);
  988. }
  989. // Add aliases for the target name.
  990. this->GetGlobalGenerator()->AddTargetAlias(
  991. this->GetTargetName(), this->GetGeneratorTarget(), config);
  992. }
  993. cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
  994. const std::string& config) const
  995. {
  996. if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
  997. return this->GeneratorTarget->GetExecutableNames(config);
  998. }
  999. return this->GeneratorTarget->GetLibraryNames(config);
  1000. }
  1001. std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage(
  1002. const std::string& config) const
  1003. {
  1004. return this->GeneratorTarget->GetLinkerLanguage(config);
  1005. }