cmNinjaNormalTargetGenerator.cxx 46 KB

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