cmNinjaNormalTargetGenerator.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  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. std::string outpath = target->GetDirectory(this->GetConfigName());
  46. cmSystemTools::MakeDirectory(outpath.c_str());
  47. }
  48. }
  49. cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator()
  50. {
  51. }
  52. void cmNinjaNormalTargetGenerator::Generate()
  53. {
  54. if (!this->TargetLinkLanguage) {
  55. cmSystemTools::Error("CMake can not determine linker language for target:",
  56. this->GetTarget()->GetName());
  57. return;
  58. }
  59. // Write the rules for each language.
  60. this->WriteLanguagesRules();
  61. // Write the build statements
  62. this->WriteObjectBuildStatements();
  63. if(this->GetTarget()->GetType() == cmTarget::OBJECT_LIBRARY)
  64. {
  65. this->WriteObjectLibStatement();
  66. }
  67. else
  68. {
  69. this->WriteLinkRule();
  70. this->WriteLinkStatement();
  71. }
  72. this->GetBuildFileStream() << "\n";
  73. this->GetRulesFileStream() << "\n";
  74. }
  75. void cmNinjaNormalTargetGenerator::WriteLanguagesRules()
  76. {
  77. cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
  78. this->GetRulesFileStream()
  79. << "# Rules for each languages for "
  80. << cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
  81. << " target "
  82. << this->GetTargetName()
  83. << "\n\n";
  84. std::set<cmStdString> languages;
  85. this->GetTarget()->GetLanguages(languages);
  86. for(std::set<cmStdString>::const_iterator l = languages.begin();
  87. l != languages.end();
  88. ++l)
  89. this->WriteLanguageRules(*l);
  90. }
  91. const char *cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
  92. {
  93. switch (this->GetTarget()->GetType()) {
  94. case cmTarget::STATIC_LIBRARY:
  95. return "static library";
  96. case cmTarget::SHARED_LIBRARY:
  97. return "shared library";
  98. case cmTarget::MODULE_LIBRARY:
  99. return "shared module";
  100. case cmTarget::EXECUTABLE:
  101. return "executable";
  102. default:
  103. return 0;
  104. }
  105. }
  106. std::string
  107. cmNinjaNormalTargetGenerator
  108. ::LanguageLinkerRule() const
  109. {
  110. return std::string(this->TargetLinkLanguage)
  111. + "_"
  112. + cmTarget::GetTargetTypeName(this->GetTarget()->GetType())
  113. + "_LINKER";
  114. }
  115. void
  116. cmNinjaNormalTargetGenerator
  117. ::WriteLinkRule()
  118. {
  119. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  120. std::string ruleName = this->LanguageLinkerRule();
  121. if (!this->GetGlobalGenerator()->HasRule(ruleName)) {
  122. cmLocalGenerator::RuleVariables vars;
  123. vars.RuleLauncher = "RULE_LAUNCH_LINK";
  124. vars.CMTarget = this->GetTarget();
  125. vars.Language = this->TargetLinkLanguage;
  126. vars.Objects = "$in";
  127. std::string objdir =
  128. this->GetLocalGenerator()->GetHomeRelativeOutputPath();
  129. objdir += objdir.empty() ? "" : "/";
  130. objdir += cmake::GetCMakeFilesDirectoryPostSlash();
  131. objdir += this->GetTargetName();
  132. objdir += ".dir";
  133. objdir = this->GetLocalGenerator()->Convert(objdir.c_str(),
  134. cmLocalGenerator::START_OUTPUT,
  135. cmLocalGenerator::SHELL);
  136. vars.ObjectDir = objdir.c_str();
  137. vars.Target = "$out";
  138. vars.TargetSOName = "$SONAME";
  139. vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
  140. vars.TargetPDB = "$TARGET_PDB";
  141. // Setup the target version.
  142. std::string targetVersionMajor;
  143. std::string targetVersionMinor;
  144. {
  145. cmOStringStream majorStream;
  146. cmOStringStream minorStream;
  147. int major;
  148. int minor;
  149. this->GetTarget()->GetTargetVersion(major, minor);
  150. majorStream << major;
  151. minorStream << minor;
  152. targetVersionMajor = majorStream.str();
  153. targetVersionMinor = minorStream.str();
  154. }
  155. vars.TargetVersionMajor = targetVersionMajor.c_str();
  156. vars.TargetVersionMinor = targetVersionMinor.c_str();
  157. vars.LinkLibraries = "$LINK_LIBRARIES";
  158. vars.Flags = "$FLAGS";
  159. vars.LinkFlags = "$LINK_FLAGS";
  160. std::string langFlags;
  161. this->GetLocalGenerator()->AddLanguageFlags(langFlags,
  162. this->TargetLinkLanguage,
  163. this->GetConfigName());
  164. if (targetType != cmTarget::EXECUTABLE)
  165. langFlags += " $ARCH_FLAGS";
  166. vars.LanguageCompileFlags = langFlags.c_str();
  167. // Rule for linking library.
  168. std::vector<std::string> linkCmds = this->ComputeLinkCmd();
  169. for(std::vector<std::string>::iterator i = linkCmds.begin();
  170. i != linkCmds.end();
  171. ++i)
  172. {
  173. this->GetLocalGenerator()->ExpandRuleVariables(*i, vars);
  174. }
  175. linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
  176. linkCmds.push_back("$POST_BUILD");
  177. std::string linkCmd =
  178. this->GetLocalGenerator()->BuildCommandLine(linkCmds);
  179. // Write the linker rule.
  180. std::ostringstream comment;
  181. comment << "Rule for linking " << this->TargetLinkLanguage << " "
  182. << this->GetVisibleTypeName() << ".";
  183. std::ostringstream description;
  184. description << "Linking " << this->TargetLinkLanguage << " "
  185. << this->GetVisibleTypeName() << " $out";
  186. this->GetGlobalGenerator()->AddRule(ruleName,
  187. linkCmd,
  188. description.str(),
  189. comment.str());
  190. }
  191. if (this->TargetNameOut != this->TargetNameReal) {
  192. std::string cmakeCommand =
  193. this->GetLocalGenerator()->ConvertToOutputFormat(
  194. this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
  195. cmLocalGenerator::SHELL);
  196. if (targetType == cmTarget::EXECUTABLE)
  197. this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_EXECUTABLE",
  198. cmakeCommand +
  199. " -E cmake_symlink_executable"
  200. " $in $out && $POST_BUILD",
  201. "Creating executable symlink $out",
  202. "Rule for creating executable symlink.");
  203. else
  204. this->GetGlobalGenerator()->AddRule("CMAKE_SYMLINK_LIBRARY",
  205. cmakeCommand +
  206. " -E cmake_symlink_library"
  207. " $in $SONAME $out && $POST_BUILD",
  208. "Creating library symlink $out",
  209. "Rule for creating library symlink.");
  210. }
  211. }
  212. std::vector<std::string>
  213. cmNinjaNormalTargetGenerator
  214. ::ComputeLinkCmd()
  215. {
  216. std::vector<std::string> linkCmds;
  217. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  218. switch (targetType) {
  219. case cmTarget::STATIC_LIBRARY: {
  220. // Check if you have a non archive way to create the static library.
  221. {
  222. std::string linkCmdVar = "CMAKE_";
  223. linkCmdVar += this->TargetLinkLanguage;
  224. linkCmdVar += "_CREATE_STATIC_LIBRARY";
  225. if (const char *linkCmd =
  226. this->GetMakefile()->GetDefinition(linkCmdVar.c_str()))
  227. {
  228. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  229. return linkCmds;
  230. }
  231. }
  232. // We have archive link commands set. First, delete the existing archive.
  233. std::string cmakeCommand =
  234. this->GetLocalGenerator()->ConvertToOutputFormat(
  235. this->GetMakefile()->GetRequiredDefinition("CMAKE_COMMAND"),
  236. cmLocalGenerator::SHELL);
  237. linkCmds.push_back(cmakeCommand + " -E remove $out");
  238. // TODO: Use ARCHIVE_APPEND for archives over a certain size.
  239. {
  240. std::string linkCmdVar = "CMAKE_";
  241. linkCmdVar += this->TargetLinkLanguage;
  242. linkCmdVar += "_ARCHIVE_CREATE";
  243. const char *linkCmd =
  244. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  245. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  246. }
  247. {
  248. std::string linkCmdVar = "CMAKE_";
  249. linkCmdVar += this->TargetLinkLanguage;
  250. linkCmdVar += "_ARCHIVE_FINISH";
  251. const char *linkCmd =
  252. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  253. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  254. }
  255. return linkCmds;
  256. }
  257. case cmTarget::SHARED_LIBRARY:
  258. case cmTarget::MODULE_LIBRARY:
  259. case cmTarget::EXECUTABLE: {
  260. std::string linkCmdVar = "CMAKE_";
  261. linkCmdVar += this->TargetLinkLanguage;
  262. switch (targetType) {
  263. case cmTarget::SHARED_LIBRARY:
  264. linkCmdVar += "_CREATE_SHARED_LIBRARY";
  265. break;
  266. case cmTarget::MODULE_LIBRARY:
  267. linkCmdVar += "_CREATE_SHARED_MODULE";
  268. break;
  269. case cmTarget::EXECUTABLE:
  270. linkCmdVar += "_LINK_EXECUTABLE";
  271. break;
  272. default:
  273. assert(0 && "Unexpected target type");
  274. }
  275. const char *linkCmd =
  276. this->GetMakefile()->GetRequiredDefinition(linkCmdVar.c_str());
  277. cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
  278. return linkCmds;
  279. }
  280. default:
  281. assert(0 && "Unexpected target type");
  282. }
  283. return std::vector<std::string>();
  284. }
  285. void cmNinjaNormalTargetGenerator::WriteLinkStatement()
  286. {
  287. cmTarget::TargetType targetType = this->GetTarget()->GetType();
  288. // Write comments.
  289. cmGlobalNinjaGenerator::WriteDivider(this->GetBuildFileStream());
  290. this->GetBuildFileStream()
  291. << "# Link build statements for "
  292. << cmTarget::GetTargetTypeName(targetType)
  293. << " target "
  294. << this->GetTargetName()
  295. << "\n\n";
  296. cmNinjaDeps emptyDeps;
  297. cmNinjaVars vars;
  298. std::string targetOutput = ConvertToNinjaPath(
  299. this->GetTarget()->GetFullPath(this->GetConfigName()).c_str());
  300. std::string targetOutputReal = ConvertToNinjaPath(
  301. this->GetTarget()->GetFullPath(this->GetConfigName(),
  302. /*implib=*/false,
  303. /*realpath=*/true).c_str());
  304. std::string targetOutputImplib = ConvertToNinjaPath(
  305. this->GetTarget()->GetFullPath(this->GetConfigName(),
  306. /*implib=*/true).c_str());
  307. // Compute the comment.
  308. std::ostringstream comment;
  309. comment << "Link the " << this->GetVisibleTypeName() << " "
  310. << targetOutputReal;
  311. // Compute outputs.
  312. cmNinjaDeps outputs;
  313. outputs.push_back(targetOutputReal);
  314. // Compute specific libraries to link with.
  315. cmNinjaDeps explicitDeps = this->GetObjects(),
  316. implicitDeps = this->ComputeLinkDeps();
  317. this->GetLocalGenerator()->GetTargetFlags(vars["LINK_LIBRARIES"],
  318. vars["FLAGS"],
  319. vars["LINK_FLAGS"],
  320. *this->GetTarget());
  321. this->AddModuleDefinitionFlag(vars["LINK_FLAGS"]);
  322. // Compute architecture specific link flags. Yes, these go into a different
  323. // variable for executables, probably due to a mistake made when duplicating
  324. // code between the Makefile executable and library generators.
  325. this->GetLocalGenerator()
  326. ->AddArchitectureFlags(targetType == cmTarget::EXECUTABLE
  327. ? vars["FLAGS"]
  328. : vars["ARCH_FLAGS"],
  329. this->GetTarget(),
  330. this->TargetLinkLanguage,
  331. this->GetConfigName());
  332. vars["SONAME"] = this->TargetNameSO;
  333. if (targetType == cmTarget::SHARED_LIBRARY) {
  334. std::string install_name_dir =
  335. this->GetTarget()->GetInstallNameDirForBuildTree(this->GetConfigName());
  336. if (!install_name_dir.empty()) {
  337. vars["INSTALLNAME_DIR"] =
  338. this->GetLocalGenerator()->Convert(install_name_dir.c_str(),
  339. cmLocalGenerator::NONE,
  340. cmLocalGenerator::SHELL, false);
  341. }
  342. }
  343. if (!this->TargetNameImport.empty()) {
  344. vars["TARGET_IMPLIB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
  345. targetOutputImplib.c_str(), cmLocalGenerator::SHELL);
  346. }
  347. vars["TARGET_PDB"] = this->GetLocalGenerator()->ConvertToOutputFormat(
  348. this->GetTargetPDB().c_str(), cmLocalGenerator::SHELL);
  349. std::vector<cmCustomCommand> *cmdLists[3] = {
  350. &this->GetTarget()->GetPreBuildCommands(),
  351. &this->GetTarget()->GetPreLinkCommands(),
  352. &this->GetTarget()->GetPostBuildCommands()
  353. };
  354. std::vector<std::string> preLinkCmdLines, postBuildCmdLines;
  355. std::vector<std::string> *cmdLineLists[3] = {
  356. &preLinkCmdLines,
  357. &preLinkCmdLines,
  358. &postBuildCmdLines
  359. };
  360. for (unsigned i = 0; i != 3; ++i) {
  361. for (std::vector<cmCustomCommand>::const_iterator
  362. ci = cmdLists[i]->begin();
  363. ci != cmdLists[i]->end(); ++ci) {
  364. this->GetLocalGenerator()->AppendCustomCommandLines(&*ci,
  365. *cmdLineLists[i]);
  366. }
  367. }
  368. // If we have any PRE_LINK commands, we need to go back to HOME_OUTPUT for
  369. // the link commands.
  370. if (!preLinkCmdLines.empty()) {
  371. std::string path = this->GetLocalGenerator()->ConvertToOutputFormat(
  372. this->GetMakefile()->GetHomeOutputDirectory(),
  373. cmLocalGenerator::SHELL);
  374. preLinkCmdLines.push_back("cd " + path);
  375. }
  376. vars["PRE_LINK"] =
  377. this->GetLocalGenerator()->BuildCommandLine(preLinkCmdLines);
  378. std::string postBuildCmdLine =
  379. this->GetLocalGenerator()->BuildCommandLine(postBuildCmdLines);
  380. cmNinjaVars symlinkVars;
  381. if (targetOutput == targetOutputReal) {
  382. vars["POST_BUILD"] = postBuildCmdLine;
  383. } else {
  384. vars["POST_BUILD"] = ":";
  385. symlinkVars["POST_BUILD"] = postBuildCmdLine;
  386. }
  387. // Write the build statement for this target.
  388. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  389. comment.str(),
  390. this->LanguageLinkerRule(),
  391. outputs,
  392. explicitDeps,
  393. implicitDeps,
  394. emptyDeps,
  395. vars);
  396. if (targetOutput != targetOutputReal) {
  397. if (targetType == cmTarget::EXECUTABLE) {
  398. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  399. "Create executable symlink " + targetOutput,
  400. "CMAKE_SYMLINK_EXECUTABLE",
  401. cmNinjaDeps(1, targetOutput),
  402. cmNinjaDeps(1, targetOutputReal),
  403. emptyDeps,
  404. emptyDeps,
  405. symlinkVars);
  406. } else {
  407. symlinkVars["SONAME"] = this->GetTargetFilePath(this->TargetNameSO);
  408. cmGlobalNinjaGenerator::WriteBuild(this->GetBuildFileStream(),
  409. "Create library symlink " + targetOutput,
  410. "CMAKE_SYMLINK_LIBRARY",
  411. cmNinjaDeps(1, targetOutput),
  412. cmNinjaDeps(1, targetOutputReal),
  413. emptyDeps,
  414. emptyDeps,
  415. symlinkVars);
  416. }
  417. }
  418. if (!this->TargetNameImport.empty()) {
  419. // Since using multiple outputs would mess up the $out variable, use an
  420. // alias for the import library.
  421. cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
  422. "Alias for import library.",
  423. cmNinjaDeps(1, targetOutputImplib),
  424. cmNinjaDeps(1, targetOutputReal));
  425. }
  426. // Add aliases for the file name and the target name.
  427. this->GetGlobalGenerator()->AddTargetAlias(this->TargetNameOut,
  428. this->GetTarget());
  429. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
  430. this->GetTarget());
  431. }
  432. //----------------------------------------------------------------------------
  433. void cmNinjaNormalTargetGenerator::WriteObjectLibStatement()
  434. {
  435. // Write a phony output that depends on all object files.
  436. cmNinjaDeps outputs;
  437. this->GetLocalGenerator()->AppendTargetOutputs(this->GetTarget(), outputs);
  438. cmNinjaDeps depends = this->GetObjects();
  439. cmGlobalNinjaGenerator::WritePhonyBuild(this->GetBuildFileStream(),
  440. "Object library "
  441. + this->GetTargetName(),
  442. outputs,
  443. depends);
  444. // Add aliases for the target name.
  445. this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
  446. this->GetTarget());
  447. }