cmNinjaNormalTargetGenerator.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2011 Peter Collingbourne <[email protected]>
  4. Copyright 2011 Nicolas Despres <[email protected]>
  5. Distributed under the OSI-approved BSD License (the "License");
  6. see accompanying file Copyright.txt for details.
  7. This software is distributed WITHOUT ANY WARRANTY; without even the
  8. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. See the License for more information.
  10. ============================================================================*/
  11. #include "cmNinjaNormalTargetGenerator.h"
  12. #include "cmLocalNinjaGenerator.h"
  13. #include "cmGlobalNinjaGenerator.h"
  14. #include "cmSourceFile.h"
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmMakefile.h"
  17. #include <assert.h>
  18. cmNinjaNormalTargetGenerator::
  19. cmNinjaNormalTargetGenerator(cmTarget* target)
  20. : cmNinjaTargetGenerator(target)
  21. , TargetNameOut()
  22. , TargetNameSO()
  23. , TargetNameReal()
  24. , TargetNameImport()
  25. , TargetNamePDB()
  26. {
  27. this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName());
  28. if (target->GetType() == cmTarget::EXECUTABLE)
  29. target->GetExecutableNames(this->TargetNameOut,
  30. this->TargetNameReal,
  31. this->TargetNameImport,
  32. this->TargetNamePDB,
  33. GetLocalGenerator()->GetConfigName());
  34. else
  35. target->GetLibraryNames(this->TargetNameOut,
  36. this->TargetNameSO,
  37. this->TargetNameReal,
  38. this->TargetNameImport,
  39. this->TargetNamePDB,
  40. GetLocalGenerator()->GetConfigName());
  41. if(target->GetType() != cmTarget::OBJECT_LIBRARY)
  42. {
  43. // on Windows the output dir is already needed at compile time
  44. // ensure the directory exists (OutDir test)
  45. EnsureDirectoryExists(target->GetDirectory(this->GetConfigName()));
  46. }
  47. }
  48. cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
  49. {
  50. }
  51. void cmNinjaNormalTargetGenerator::Generate()
  52. {
  53. if (!this->TargetLinkLanguage) {
  54. cmSystemTools::Error("CMake can not determine linker language for target:",
  55. this->GetTarget()->GetName());
  56. return;
  57. }
  58. // Write the rules for each language.
  59. this->WriteLanguagesRules();
  60. // Write the build statements
  61. this->WriteObjectBuildStatements();
  62. if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY)
  63. {
  64. this->WriteObjectLibStatement();
  65. }
  66. else
  67. {
  68. this->WriteLinkRule(false);
  69. #ifdef _WIN32 // TODO response file support only Linux
  70. this->WriteLinkRule(true);
  71. #endif
  72. this->WriteLinkStatement();
  73. }
  74. }
  75. void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
  76. {
  77. #ifdef NINJA_GEN_VERBOSE_FILES
  78. cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
  79. this->GetRulesFileStream()
  80. << "# Rules for each languages for "
  81. << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
  82. << " target "
  83. << this->GetTargetName()
  84. << "\n\n";
  85. #endif
  86. std::set<cmStdString> languages;
  87. this->GetTarget()->GetLanguages(languages);
  88. for(std::set<cmStdString>::const_iterator l = languages.begin();
  89. l != languages.end();
  90. ++l)
  91. this->WriteLanguageRules(*l);
  92. }
  93. const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
  94. {
  95. switch (this->GetTarget()->GetType()) {
  96. case cmTarget::STATIC_LIBRARY:
  97. return "static library";
  98. case cmTarget::SHARED_LIBRARY:
  99. return "shared library";
  100. case cmTarget::MODULE_LIBRARY:
  101. return "shared module";
  102. case cmTarget::EXECUTABLE:
  103. return "executable";
  104. default:
  105. return 0;
  106. }
  107. }
  108. std::string
  109. cmNinjaNormalTargetGenerator
  110. ::LanguageLinkerRule() const
  111. {
  112. return std::string(this->TargetLinkLanguage)
  113. + "_"
  114. + cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
  115. + "_LINKER";
  116. }
  117. void
  118. cmNinjaNormalTargetGenerator
  119. ::WriteLinkRule(bool useResponseFile)
  120. {
  121. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  122. std::string ruleName = this->LanguageLinkerRule();
  123. if (useResponseFile)
  124. ruleName += "_RSPFILE";
  125. // Select whether to use a response file for objects.
  126. std::string rspfile;
  127. if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
  128. cmLocalGenerator::RuleVariables vars;
  129. vars.RuleLauncher = "RULE_LAUNCH_LINK";
  130. vars.CMTarget = this->GetTarget();
  131. vars.Language = this->TargetLinkLanguage;
  132. std::string responseFlag;
  133. if (!useResponseFile) {
  134. vars.Objects = "$in";
  135. } else {
  136. // handle response file
  137. std::string cmakeLinkVar = std::string("CMAKE_") +
  138. this->TargetLinkLanguage + "_RESPONSE_FILE_LINK_FLAG";
  139. const char * flag = GetMakefile()->GetDefinition(cmakeLinkVar.c_str());
  140. if(flag) {
  141. responseFlag = flag;
  142. } else {
  143. responseFlag = "@";
  144. }
  145. rspfile = "$out.rsp";
  146. responseFlag += rspfile;
  147. vars.Objects = responseFlag.c_str();
  148. }
  149. vars.ObjectDir = "$OBJECT_DIR";
  150. vars.Target = "$out";
  151. vars.SONameFlag = "$SONAME_FLAG";
  152. vars.TargetSOName = "$SONAME";
  153. vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
  154. vars.TargetPDB = "$TARGET_PDB";
  155. // Setup the target version.
  156. std::string targetVersionMajor;
  157. std::string targetVersionMinor;
  158. {
  159. cmOStringStream majorStream;
  160. cmOStringStream minorStream;
  161. int major;
  162. int minor;
  163. this->GetTarget()->GetTargetVersion(major, minor);
  164. majorStream << major;
  165. minorStream << minor;
  166. targetVersionMajor = majorStream.str();
  167. targetVersionMinor = minorStream.str();
  168. }
  169. vars.TargetVersionMajor = targetVersionMajor.c_str();
  170. vars.TargetVersionMinor = targetVersionMinor.c_str();
  171. vars.LinkLibraries = "$LINK_LIBRARIES";
  172. vars.Flags = "$FLAGS";
  173. vars.LinkFlags = "$LINK_FLAGS";
  174. std::string langFlags;
  175. if (targetType != cmTarget::EXECUTABLE) {
  176. this->GetLocalGenerator()->AddLanguageFlags(langFlags,
  177. this->TargetLinkLanguage,
  178. this->GetConfigName());
  179. langFlags += " $ARCH_FLAGS";
  180. vars.LanguageCompileFlags = langFlags.c_str();
  181. }
  182. // Rule for linking library/executable.
  183. std::vector<std::string> linkCmds = this->ComputeLinkCmd();
  184. for(std::vector<std::string>::iterator i = linkCmds.begin();
  185. i != linkCmds.end();
  186. ++i)
  187. {
  188. this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
  189. }
  190. linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
  191. linkCmds.push_back("$POST_BUILD");
  192. std::string linkCmd =
  193. this->GetLocalGenerator()->BuildCommandLine(linkCmds);
  194. // Write the linker rule with response file if needed.
  195. std::ostringstream comment;
  196. comment << "Rule for linking " << this->TargetLinkLanguage << " "
  197. << this->GetVisibleTypeName() << ".";
  198. std::ostringstream description;
  199. description << "Linking " << this->TargetLinkLanguage << " "
  200. << this->GetVisibleTypeName() << " $out";
  201. this->GetGlobalGenerator()->AddRule(ruleName,
  202. linkCmd,
  203. description.str(),
  204. comment.str(),
  205. /*depfile*/ "",
  206. rspfile);
  207. }
  208. if (this->TargetNameOut != this->TargetNameReal) {
  209. std::string cmakeCommand =
  210. this->GetLocalGenerator()->ConvertToOutputFormat(
  211. this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
  212. cmLocalGenerator::SHELL);
  213. if (targetType == cmTarget::EXECUTABLE)
  214. this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
  215. cmakeCommand +
  216. " -E cmake_symlink_executable"
  217. " $in $out && $POST_BUILD",
  218. "Creating executable symlink $out",
  219. "Rule for creating executable symlink.");
  220. else
  221. this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
  222. cmakeCommand +
  223. " -E cmake_symlink_library"
  224. " $in $SONAME $out && $POST_BUILD",
  225. "Creating library symlink $out",
  226. "Rule for creating library symlink.");
  227. }
  228. }
  229. std::vector<std::string>
  230. cmNinjaNormalTargetGenerator
  231. ::ComputeLinkCmd()
  232. {
  233. std::vector<std::string> linkCmds;
  234. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  235. switch (targetType) {
  236. case cmTarget::STATIC_LIBRARY: {
  237. // Check if you have a non archive way to create the static library.
  238. {
  239. std::string linkCmdVar = "CMAKE_";
  240. linkCmdVar += this->TargetLinkLanguage;
  241. linkCmdVar += "_CREATE_STATIC_LIBRARY";
  242. if (const char *linkCmd =
  243. this->GetMakefile()->GetDefinition(linkCmdVar.c_str()))
  244. {
  245. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  246. return linkCmds;
  247. }
  248. }
  249. // We have archive link commands set. First, delete the existing archive.
  250. std::string cmakeCommand =
  251. this->GetLocalGenerator()->ConvertToOutputFormat(
  252. this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
  253. cmLocalGenerator::SHELL);
  254. linkCmds.push_back(cmakeCommand + " -E remove $out");
  255. // TODO: Use ARCHIVE_APPEND for archives over a certain size.
  256. {
  257. std::string linkCmdVar = "CMAKE_";
  258. linkCmdVar += this->TargetLinkLanguage;
  259. linkCmdVar += "_ARCHIVE_CREATE";
  260. const char *linkCmd =
  261. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  262. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  263. }
  264. {
  265. std::string linkCmdVar = "CMAKE_";
  266. linkCmdVar += this->TargetLinkLanguage;
  267. linkCmdVar += "_ARCHIVE_FINISH";
  268. const char *linkCmd =
  269. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  270. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  271. }
  272. return linkCmds;
  273. }
  274. case cmTarget::SHARED_LIBRARY:
  275. case cmTarget::MODULE_LIBRARY:
  276. case cmTarget::EXECUTABLE: {
  277. std::string linkCmdVar = "CMAKE_";
  278. linkCmdVar += this->TargetLinkLanguage;
  279. switch (targetType) {
  280. case cmTarget::SHARED_LIBRARY:
  281. linkCmdVar += "_CREATE_SHARED_LIBRARY";
  282. break;
  283. case cmTarget::MODULE_LIBRARY:
  284. linkCmdVar += "_CREATE_SHARED_MODULE";
  285. break;
  286. case cmTarget::EXECUTABLE:
  287. linkCmdVar += "_LINK_EXECUTABLE";
  288. break;
  289. default:
  290. assert(0 && "Unexpected target type");
  291. }
  292. const char *linkCmd =
  293. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  294. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  295. return linkCmds;
  296. }
  297. default:
  298. assert(0 && "Unexpected target type");
  299. }
  300. return std::vector<std::string>();
  301. }
  302. void cmNinjaNormalTargetGenerator::WriteLinkStatement()
  303. {
  304. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  305. // Write comments.
  306. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
  307. this->GetBuildFileStream()
  308. << "# Link build statements for "
  309. << cmTarget::GetTargetTypeName(targetType)
  310. << " target "
  311. << this->GetTargetName()
  312. << "\n\n";
  313. cmNinjaDeps emptyDeps;
  314. cmNinjaVars vars;
  315. std::string targetOutput = ConvertToNinjaPath(
  316. this->GetTarget()->GetFullPath(this->GetConfigName()).c_str());
  317. std::string targetOutputReal = ConvertToNinjaPath(
  318. this->GetTarget()->GetFullPath(this->GetConfigName(),
  319. /*implib=*/false,
  320. /*realpath=*/true).c_str());
  321. std::string targetOutputImplib = ConvertToNinjaPath(
  322. this->GetTarget()->GetFullPath(this->GetConfigName(),
  323. /*implib=*/true).c_str());
  324. // Compute the comment.
  325. std::ostringstream comment;
  326. comment << "Link the " << this->GetVisibleTypeName() << " "
  327. << targetOutputReal;
  328. // Compute outputs.
  329. cmNinjaDeps outputs;
  330. outputs.push_back(targetOutputReal);
  331. // Compute specific libraries to link with.
  332. cmNinjaDeps explicitDeps = this->GetObjects(),
  333. implicitDeps = this->ComputeLinkDeps();
  334. this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"],
  335. vars["FLAGS"],
  336. vars["LINK_FLAGS"],
  337. *this->GetTarget());
  338. this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]);
  339. // Compute architecture specific link flags. Yes, these go into a different
  340. // variable for executables, probably due to a mistake made when duplicating
  341. // code between the Makefile executable and library generators.
  342. std::string flags = (targetType == cmTarget::EXECUTABLE
  343. ? vars["FLAGS"]
  344. : vars["ARCH_FLAGS"]);
  345. this->GetLocalGenerator()->AddArchitectureFlags(flags,
  346. this->GetTarget(),
  347. this->TargetLinkLanguage,
  348. this->GetConfigName());
  349. if (targetType == cmTarget::EXECUTABLE) {
  350. vars["FLAGS"] = flags;
  351. } else {
  352. vars["ARCH_FLAGS"] = flags;
  353. }
  354. if (this->GetTarget()->HasSOName(this->GetConfigName())) {
  355. vars["SONAME_FLAG"] =
  356. this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage);
  357. vars["SONAME"] = this->TargetNameSO;
  358. if (targetType == cmTarget::SHARED_LIBRARY) {
  359. std::string install_name_dir = this->GetTarget()
  360. ->GetInstallNameDirForBuildTree(this->GetConfigName());
  361. if (!install_name_dir.empty()) {
  362. vars["INSTALLNAME_DIR"] =
  363. this->GetLocalGenerator()->Convert(install_name_dir.c_str(),
  364. cmLocalGenerator::NONE,
  365. cmLocalGenerator::SHELL, false);
  366. }
  367. }
  368. }
  369. std::string path;
  370. if (!this->TargetNameImport.empty()) {
  371. path = this->GetLocalGenerator()->ConvertToOutputFormat(
  372. targetOutputImplib.c_str(), cmLocalGenerator::SHELL);
  373. vars["TARGET_IMPLIB"] = path;
  374. EnsureParentDirectoryExists(path);
  375. }
  376. // TODO move to GetTargetPDB
  377. cmMakefile* mf = this->GetMakefile();
  378. if (mf->GetDefinition("MSVC_C_ARCHITECTURE_ID") ||
  379. mf->GetDefinition("MSVC_CXX_ARCHITECTURE_ID"))
  380. {
  381. path = this->GetTargetPDB();
  382. vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
  383. ConvertToNinjaPath(path.c_str()).c_str(),
  384. cmLocalGenerator::SHELL);
  385. EnsureParentDirectoryExists(path);
  386. }
  387. if (mf->IsOn("CMAKE_COMPILER_IS_MINGW"))
  388. {
  389. path = GetTarget()->GetSupportDirectory();
  390. vars["OBJECT_DIR"] = ConvertToNinjaPath(path.c_str());
  391. EnsureDirectoryExists(path);
  392. }
  393. std::vector<cmCustomCommand> *cmdLists[3] = {
  394. &this->GetTarget()->GetPreBuildCommands(),
  395. &this->GetTarget()->GetPreLinkCommands(),
  396. &this->GetTarget()->GetPostBuildCommands()
  397. };
  398. std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
  399. std::vector<std::string> *cmdLineLists[3] = {
  400. &preLinkCmdLines,
  401. &preLinkCmdLines,
  402. &postBuildCmdLines
  403. };
  404. for (unsigned i = 0; i != 3; ++i) {
  405. for (std::vector<cmCustomCommand>::const_iterator
  406. ci = cmdLists[i]->begin();
  407. ci != cmdLists[i]->end(); ++ci) {
  408. this->GetLocalGenerator()->AppendCustomCommandLines(&*ci,
  409. *cmdLineLists[i]);
  410. }
  411. }
  412. // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for
  413. // the link commands.
  414. if (!preLinkCmdLines.empty()) {
  415. path = this->GetLocalGenerator()->ConvertToOutputFormat(
  416. this->GetMakefile()->GetHomeOutputDirectory(),
  417. cmLocalGenerator::SHELL);
  418. preLinkCmdLines.push_back("cd " + path);
  419. }
  420. vars["PRE_LINK"] =
  421. this->GetLocalGenerator()->BuildCommandLine(preLinkCmdLines);
  422. std::string postBuildCmdLine =
  423. this->GetLocalGenerator()->BuildCommandLine(postBuildCmdLines);
  424. cmNinjaVars symlinkVars;
  425. if (targetOutput == targetOutputReal) {
  426. vars["POST_BUILD"] = postBuildCmdLine;
  427. } else {
  428. vars["POST_BUILD"] = ":";
  429. symlinkVars["POST_BUILD"] = postBuildCmdLine;
  430. }
  431. int cmdLineLimit = -1;
  432. #ifdef _WIN32
  433. cmdLineLimit = 8100;
  434. #else
  435. // TODO
  436. #endif
  437. // Write the build statement for this target.
  438. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  439. comment.str(),
  440. this->LanguageLinkerRule(),
  441. outputs,
  442. explicitDeps,
  443. implicitDeps,
  444. emptyDeps,
  445. vars,
  446. cmdLineLimit);
  447. if (targetOutput != targetOutputReal) {
  448. if (targetType == cmTarget::EXECUTABLE) {
  449. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  450. "Create executable symlink " + targetOutput,
  451. "CMAKE_SYMLINK_EXECUTABLE",
  452. cmNinjaDeps(1, targetOutput),
  453. cmNinjaDeps(1, targetOutputReal),
  454. emptyDeps,
  455. emptyDeps,
  456. symlinkVars);
  457. } else {
  458. symlinkVars["SONAME"] = this->GetTargetFilePath(this->TargetNameSO);
  459. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  460. "Create library symlink " + targetOutput,
  461. "CMAKE_SYMLINK_LIBRARY",
  462. cmNinjaDeps(1, targetOutput),
  463. cmNinjaDeps(1, targetOutputReal),
  464. emptyDeps,
  465. emptyDeps,
  466. symlinkVars);
  467. }
  468. }
  469. if (!this->TargetNameImport.empty()) {
  470. // Since using multiple outputs would mess up the $out variable, use an
  471. // alias for the import library.
  472. cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
  473. "Alias for import library.",
  474. cmNinjaDeps(1, targetOutputImplib),
  475. cmNinjaDeps(1, targetOutputReal));
  476. }
  477. // Add aliases for the file name and the target name.
  478. this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut,
  479. this->GetTarget());
  480. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
  481. this->GetTarget());
  482. }
  483. //----------------------------------------------------------------------------
  484. void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
  485. {
  486. // Write a phony output that depends on all object files.
  487. cmNinjaDeps outputs;
  488. this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
  489. cmNinjaDeps depends = this->GetObjects();
  490. cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
  491. "Object library "
  492. + this->GetTargetName(),
  493. outputs,
  494. depends);
  495. // Add aliases for the target name.
  496. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
  497. this->GetTarget());
  498. }