1
0

cmNinjaNormalTargetGenerator.cxx 47 KB

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