cmNinjaNormalTargetGenerator.cxx 18 KB

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