cmNinjaNormalTargetGenerator.cxx 46 KB

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