cmNinjaNormalTargetGenerator.cxx 17 KB

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