cmGlobalGhsMultiGenerator.cxx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmGlobalGhsMultiGenerator.h"
  4. #include <algorithm>
  5. #include <functional>
  6. #include <map>
  7. #include <sstream>
  8. #include <utility>
  9. #include <cm/memory>
  10. #include <cm/string>
  11. #include <cmext/algorithm>
  12. #include <cmext/memory>
  13. #include "cmCustomCommand.h"
  14. #include "cmCustomCommandLines.h"
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmGeneratorTarget.h"
  17. #include "cmGhsMultiGpj.h"
  18. #include "cmList.h"
  19. #include "cmLocalGenerator.h"
  20. #include "cmLocalGhsMultiGenerator.h"
  21. #include "cmMakefile.h"
  22. #include "cmMessageType.h"
  23. #include "cmSourceFile.h"
  24. #include "cmState.h"
  25. #include "cmStateTypes.h"
  26. #include "cmStringAlgorithms.h"
  27. #include "cmSystemTools.h"
  28. #include "cmTarget.h"
  29. #include "cmValue.h"
  30. #include "cmVersion.h"
  31. #include "cmake.h"
  32. char const* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
  33. #ifdef __linux__
  34. char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
  35. #elif defined(_WIN32)
  36. char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
  37. #endif
  38. char const* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET =
  39. "RERUN_CMAKE";
  40. cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
  41. : cmGlobalGenerator(cm)
  42. {
  43. cm->GetState()->SetGhsMultiIDE(true);
  44. }
  45. cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default;
  46. std::unique_ptr<cmLocalGenerator>
  47. cmGlobalGhsMultiGenerator::CreateLocalGenerator(cmMakefile* mf)
  48. {
  49. return std::unique_ptr<cmLocalGenerator>(
  50. cm::make_unique<cmLocalGhsMultiGenerator>(this, mf));
  51. }
  52. cmDocumentationEntry cmGlobalGhsMultiGenerator::GetDocumentation()
  53. {
  54. return {
  55. GetActualName(),
  56. "Generates Green Hills MULTI files (experimental, work-in-progress)."
  57. };
  58. }
  59. void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory(
  60. cmGeneratorTarget* gt) const
  61. {
  62. // Compute full path to object file directory for this target.
  63. std::string dir = cmStrCat(gt->GetSupportDirectory(), '/');
  64. gt->ObjectDirectory = dir;
  65. }
  66. bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
  67. bool build, cmMakefile* mf)
  68. {
  69. /* In build mode nothing to be done.
  70. * Toolset already determined and build tool absolute path is cached.
  71. */
  72. if (build) {
  73. return true;
  74. }
  75. /* Determine the absolute directory for the toolset */
  76. std::string tsp;
  77. this->GetToolset(mf, tsp, ts);
  78. /* no toolset was found */
  79. if (tsp.empty()) {
  80. return false;
  81. }
  82. /* set the build tool to use */
  83. std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
  84. DEFAULT_BUILD_PROGRAM);
  85. cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
  86. /* check if the toolset changed from last generate */
  87. if (cmNonempty(prevTool) && !cmSystemTools::ComparePath(gbuild, *prevTool)) {
  88. std::string const& e =
  89. cmStrCat("toolset build tool: ", gbuild,
  90. "\n"
  91. "Does not match the previously used build tool: ",
  92. *prevTool,
  93. "\n"
  94. "Either remove the CMakeCache.txt file and CMakeFiles "
  95. "directory or choose a different binary directory.");
  96. mf->IssueMessage(MessageType::FATAL_ERROR, e);
  97. return false;
  98. }
  99. /* store the toolset that is being used for this build */
  100. mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild, "build program to use",
  101. cmStateEnums::INTERNAL, true);
  102. mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp);
  103. return true;
  104. }
  105. bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
  106. cmMakefile* mf)
  107. {
  108. /* set primary target */
  109. cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET");
  110. if (t.IsOff()) {
  111. /* Use the value from `-A` or use `arm` */
  112. std::string arch = "arm";
  113. if (!cmIsOff(p)) {
  114. arch = p;
  115. }
  116. cmValue platform = mf->GetDefinition("GHS_TARGET_PLATFORM");
  117. std::string tgt = cmStrCat(arch, '_', platform, ".tgt");
  118. /* update the primary target name*/
  119. mf->AddDefinition("GHS_PRIMARY_TARGET", tgt);
  120. }
  121. return true;
  122. }
  123. void cmGlobalGhsMultiGenerator::EnableLanguage(
  124. std::vector<std::string> const& l, cmMakefile* mf, bool optional)
  125. {
  126. mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI");
  127. mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
  128. this->cmGlobalGenerator::EnableLanguage(l, mf, optional);
  129. }
  130. bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/)
  131. {
  132. // The GHS generator only knows how to lookup its build tool
  133. // during generation of the project files, but this
  134. // can only be done after the toolset is specified.
  135. return true;
  136. }
  137. void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsp,
  138. std::string const& ts)
  139. {
  140. /* Determine tsp - full path of the toolset from ts (toolset hint via -T) */
  141. std::string root = mf->GetSafeDefinition("GHS_TOOLSET_ROOT");
  142. // Check if `-T` was set by user
  143. if (ts.empty()) {
  144. // Enter toolset search mode
  145. std::vector<std::string> output;
  146. // Make sure root exists...
  147. if (!cmSystemTools::PathExists(root)) {
  148. std::string msg =
  149. "GHS_TOOLSET_ROOT directory \"" + root + "\" does not exist.";
  150. mf->IssueMessage(MessageType::FATAL_ERROR, msg);
  151. tsp = "";
  152. return;
  153. }
  154. // Add a directory separator
  155. if (root.back() != '/') {
  156. root += "/";
  157. }
  158. // Get all compiler directories in toolset root
  159. cmSystemTools::Glob(root, "comp_[^;]+", output);
  160. if (output.empty()) {
  161. // No compiler directories found
  162. std::string msg =
  163. "No GHS toolsets found in GHS_TOOLSET_ROOT \"" + root + "\".";
  164. mf->IssueMessage(MessageType::FATAL_ERROR, msg);
  165. tsp = "";
  166. } else {
  167. // Use latest? version
  168. tsp = root + output.back();
  169. }
  170. } else {
  171. // Toolset was provided by user
  172. std::string tryPath;
  173. // NOTE: CollapseFullPath() will determine if user toolset was full path or
  174. // or relative path.
  175. tryPath = cmSystemTools::CollapseFullPath(ts, root);
  176. if (!cmSystemTools::FileExists(tryPath)) {
  177. std::string msg = "GHS toolset \"" + tryPath + "\" does not exist.";
  178. mf->IssueMessage(MessageType::FATAL_ERROR, msg);
  179. tsp = "";
  180. } else {
  181. tsp = tryPath;
  182. }
  183. }
  184. }
  185. void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
  186. {
  187. /* clang-format off */
  188. fout << "#!gbuild\n"
  189. "#\n"
  190. "# CMAKE generated file: DO NOT EDIT!\n"
  191. "# Generated by \"" << GetActualName() << "\""
  192. " Generator, CMake Version " << cmVersion::GetMajorVersion() << '.'
  193. << cmVersion::GetMinorVersion() << "\n"
  194. "#\n\n";
  195. /* clang-format on */
  196. }
  197. void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout)
  198. {
  199. fout << "Commands {\n"
  200. " Custom_Rule_Command {\n"
  201. " name = \"Custom Rule Command\"\n"
  202. " exec = \""
  203. #ifdef _WIN32
  204. "cmd.exe"
  205. #else
  206. "/bin/sh"
  207. #endif
  208. "\"\n"
  209. " options = {\"SpecialOptions\"}\n"
  210. " }\n"
  211. "}\n"
  212. "\n\n"
  213. "FileTypes {\n"
  214. " CmakeRule {\n"
  215. " name = \"Custom Rule\"\n"
  216. " action = \"&Run\"\n"
  217. " extensions = {\""
  218. #ifdef _WIN32
  219. "bat"
  220. #else
  221. "sh"
  222. #endif
  223. "\"}\n"
  224. " grepable = false\n"
  225. " command = \"Custom Rule Command\"\n"
  226. " commandLine = \"$COMMAND "
  227. #ifdef _WIN32
  228. "/c"
  229. #endif
  230. " $INPUTFILE\"\n"
  231. " progress = \"Processing Custom Rule\"\n"
  232. " promoteToFirstPass = true\n"
  233. " outputType = \"None\"\n"
  234. " color = \"#800080\"\n"
  235. " }\n"
  236. "}\n";
  237. }
  238. void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout)
  239. {
  240. fout << "FileTypes {\n"
  241. " CmakeTarget {\n"
  242. " name = \"Custom Target\"\n"
  243. " action = \"&Execute\"\n"
  244. " grepable = false\n"
  245. " outputType = \"None\"\n"
  246. " color = \"#800080\"\n"
  247. " }\n"
  248. "}\n";
  249. }
  250. void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout,
  251. cmLocalGenerator* root)
  252. {
  253. this->WriteFileHeader(fout);
  254. this->WriteMacros(fout, root);
  255. this->WriteHighLevelDirectives(fout, root);
  256. GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
  257. fout << "# Top Level Project File\n";
  258. // Specify BSP option if supplied by user
  259. // -- not all platforms require this entry in the project file
  260. cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME");
  261. if (!bspName.IsOff()) {
  262. fout << " -bsp " << *bspName << '\n';
  263. }
  264. // Specify OS DIR if supplied by user
  265. // -- not all platforms require this entry in the project file
  266. cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR");
  267. if (!osDir.IsOff()) {
  268. cmValue osDirOption =
  269. root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION");
  270. fout << " ";
  271. if (osDirOption.IsOff()) {
  272. fout << "";
  273. } else {
  274. fout << *osDirOption;
  275. }
  276. fout << "\"" << osDir << "\"\n";
  277. }
  278. }
  279. void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout,
  280. bool filterPredefined)
  281. {
  282. std::set<std::string> predefinedTargets;
  283. predefinedTargets.insert(this->GetInstallTargetName());
  284. predefinedTargets.insert(this->GetAllTargetName());
  285. predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET));
  286. // All known targets
  287. for (cmGeneratorTarget const* target : this->ProjectTargets) {
  288. if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
  289. target->GetType() == cmStateEnums::MODULE_LIBRARY ||
  290. target->GetType() == cmStateEnums::SHARED_LIBRARY ||
  291. (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
  292. target->GetName() != this->GetInstallTargetName())) {
  293. continue;
  294. }
  295. /* Check if the current target is a predefined CMake target */
  296. bool predefinedTarget =
  297. predefinedTargets.find(target->GetName()) != predefinedTargets.end();
  298. if ((filterPredefined && predefinedTarget) ||
  299. (!filterPredefined && !predefinedTarget)) {
  300. fout << target->GetName() + ".tgt" + FILE_EXTENSION << " [Project]\n";
  301. }
  302. }
  303. }
  304. void cmGlobalGhsMultiGenerator::WriteProjectLine(
  305. std::ostream& fout, cmGeneratorTarget const* target,
  306. std::string& rootBinaryDir)
  307. {
  308. cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME");
  309. cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
  310. /* If either value is not valid then this particular target is an
  311. * unsupported target type and should be skipped.
  312. */
  313. if (projFile && projType) {
  314. std::string path = cmSystemTools::RelativePath(rootBinaryDir, *projFile);
  315. fout << path;
  316. fout << ' ' << *projType << '\n';
  317. }
  318. }
  319. void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root)
  320. {
  321. std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
  322. // All known targets
  323. for (cmGeneratorTarget const* target : this->ProjectTargets) {
  324. if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
  325. target->GetType() == cmStateEnums::MODULE_LIBRARY ||
  326. target->GetType() == cmStateEnums::SHARED_LIBRARY ||
  327. (target->GetType() == cmStateEnums::GLOBAL_TARGET &&
  328. target->GetName() != this->GetInstallTargetName())) {
  329. continue;
  330. }
  331. // create target build file
  332. std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION);
  333. std::string fname = cmStrCat(rootBinaryDir, '/', name);
  334. cmGeneratedFileStream fbld(fname);
  335. fbld.SetCopyIfDifferent(true);
  336. this->WriteFileHeader(fbld);
  337. GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld);
  338. std::vector<cmGeneratorTarget const*> build;
  339. if (this->ComputeTargetBuildOrder(target, build)) {
  340. cmSystemTools::Error(
  341. cmStrCat("The inter-target dependency graph for target [",
  342. target->GetName(), "] had a cycle.\n"));
  343. } else {
  344. for (auto& tgt : build) {
  345. this->WriteProjectLine(fbld, tgt, rootBinaryDir);
  346. }
  347. }
  348. fbld.Close();
  349. }
  350. }
  351. void cmGlobalGhsMultiGenerator::Generate()
  352. {
  353. std::string fname;
  354. // first do the superclass method
  355. this->cmGlobalGenerator::Generate();
  356. // output top-level projects
  357. for (auto& it : this->ProjectMap) {
  358. this->OutputTopLevelProject(it.second[0], it.second);
  359. }
  360. // create custom rule BOD file
  361. fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
  362. "/CMakeFiles/custom_rule.bod";
  363. cmGeneratedFileStream frule(fname);
  364. frule.SetCopyIfDifferent(true);
  365. this->WriteFileHeader(frule);
  366. this->WriteCustomRuleBOD(frule);
  367. frule.Close();
  368. // create custom target BOD file
  369. fname = this->GetCMakeInstance()->GetHomeOutputDirectory() +
  370. "/CMakeFiles/custom_target.bod";
  371. cmGeneratedFileStream ftarget(fname);
  372. ftarget.SetCopyIfDifferent(true);
  373. this->WriteFileHeader(ftarget);
  374. this->WriteCustomTargetBOD(ftarget);
  375. ftarget.Close();
  376. }
  377. void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
  378. cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
  379. {
  380. std::string fname;
  381. if (generators.empty()) {
  382. return;
  383. }
  384. // Collect all targets under this root generator and the transitive
  385. // closure of their dependencies.
  386. TargetDependSet const projectTargets =
  387. this->GetTargetsForProject(root, generators);
  388. OrderedTargetDependSet sortedProjectTargets(projectTargets, "");
  389. this->ProjectTargets.clear();
  390. for (cmGeneratorTarget const* t : sortedProjectTargets) {
  391. /* save list of all targets in sorted order */
  392. this->ProjectTargets.push_back(t);
  393. }
  394. /* Name top-level projects as filename.top.gpj to avoid name clashes
  395. * with target projects. This avoid the issue where the project has
  396. * the same name as the executable target.
  397. */
  398. fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
  399. root->GetProjectName(), ".top", FILE_EXTENSION);
  400. cmGeneratedFileStream top(fname);
  401. top.SetCopyIfDifferent(true);
  402. this->WriteTopLevelProject(top, root);
  403. this->WriteTargets(root);
  404. this->WriteSubProjects(top, true);
  405. this->WriteSubProjects(top, false);
  406. top.Close();
  407. }
  408. std::vector<cmGlobalGenerator::GeneratedMakeCommand>
  409. cmGlobalGhsMultiGenerator::GenerateBuildCommand(
  410. std::string const& makeProgram, std::string const& projectName,
  411. std::string const& projectDir, std::vector<std::string> const& targetNames,
  412. std::string const& /*config*/, int jobs, bool verbose,
  413. cmBuildOptions /*buildOptions*/, std::vector<std::string> const& makeOptions)
  414. {
  415. GeneratedMakeCommand makeCommand;
  416. makeCommand.Add(this->SelectMakeProgram(makeProgram));
  417. if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
  418. if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
  419. makeCommand.Add("-parallel");
  420. } else {
  421. makeCommand.Add(std::string("-parallel=") + std::to_string(jobs));
  422. }
  423. }
  424. /* determine the top-project file in the project directory */
  425. std::string proj = projectName + ".top" + FILE_EXTENSION;
  426. std::vector<std::string> files;
  427. cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
  428. if (!files.empty()) {
  429. /* use the real top-level project in the directory */
  430. proj = files.at(0);
  431. }
  432. makeCommand.Add("-top", proj);
  433. /* determine targets to build */
  434. bool build_all = false;
  435. if (!targetNames.empty()) {
  436. for (auto const& tname : targetNames) {
  437. if (!tname.empty()) {
  438. if (tname == "clean") {
  439. makeCommand.Add("-clean");
  440. } else {
  441. makeCommand.Add(tname + ".tgt.gpj");
  442. }
  443. } else {
  444. build_all = true;
  445. }
  446. }
  447. } else {
  448. build_all = true;
  449. }
  450. if (build_all) {
  451. /* transform name to default build */;
  452. std::string all = std::string(this->GetAllTargetName()) + ".tgt.gpj";
  453. makeCommand.Add(all);
  454. }
  455. if (verbose) {
  456. makeCommand.Add("-commands");
  457. }
  458. makeCommand.Add(makeOptions.begin(), makeOptions.end());
  459. return { std::move(makeCommand) };
  460. }
  461. void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout,
  462. cmLocalGenerator* root)
  463. {
  464. fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n';
  465. cmValue ghsGpjMacros = root->GetMakefile()->GetDefinition("GHS_GPJ_MACROS");
  466. if (ghsGpjMacros) {
  467. cmList expandedList{ *ghsGpjMacros };
  468. for (std::string const& arg : expandedList) {
  469. fout << "macro " << arg << '\n';
  470. }
  471. }
  472. }
  473. void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(
  474. std::ostream& fout, cmLocalGenerator* root)
  475. {
  476. /* put primary target and customization files into project file */
  477. cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET");
  478. /* clang-format off */
  479. fout << "primaryTarget=" << tgt << "\n"
  480. "customization=" << root->GetBinaryDirectory()
  481. << "/CMakeFiles/custom_rule.bod\n"
  482. "customization=" << root->GetBinaryDirectory()
  483. << "/CMakeFiles/custom_target.bod" << '\n';
  484. /* clang-format on */
  485. cmValue const customization =
  486. root->GetMakefile()->GetDefinition("GHS_CUSTOMIZATION");
  487. if (cmNonempty(customization)) {
  488. fout << "customization="
  489. << cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n';
  490. this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
  491. }
  492. }
  493. std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string str)
  494. {
  495. cm::erase(str, '"');
  496. return str;
  497. }
  498. bool cmGlobalGhsMultiGenerator::TargetCompare::operator()(
  499. cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
  500. {
  501. // Make sure a given named target is ordered first,
  502. // e.g. to set ALL_BUILD as the default active project.
  503. // When the empty string is named this is a no-op.
  504. if (r->GetName() == this->First) {
  505. return false;
  506. }
  507. if (l->GetName() == this->First) {
  508. return true;
  509. }
  510. return l->GetName() < r->GetName();
  511. }
  512. cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  513. TargetDependSet const& targets, std::string const& first)
  514. : derived(TargetCompare(first))
  515. {
  516. this->insert(targets.begin(), targets.end());
  517. }
  518. bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
  519. cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build)
  520. {
  521. std::vector<cmGeneratorTarget const*> t{ tgt };
  522. return this->ComputeTargetBuildOrder(t, build);
  523. }
  524. bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder(
  525. std::vector<cmGeneratorTarget const*>& tgt,
  526. std::vector<cmGeneratorTarget const*>& build)
  527. {
  528. std::set<cmGeneratorTarget const*> temp;
  529. std::set<cmGeneratorTarget const*> perm;
  530. for (auto const* const ti : tgt) {
  531. bool r = this->VisitTarget(temp, perm, build, ti);
  532. if (r) {
  533. return r;
  534. }
  535. }
  536. return false;
  537. }
  538. bool cmGlobalGhsMultiGenerator::VisitTarget(
  539. std::set<cmGeneratorTarget const*>& temp,
  540. std::set<cmGeneratorTarget const*>& perm,
  541. std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti)
  542. {
  543. /* check if permanent mark is set*/
  544. if (perm.find(ti) == perm.end()) {
  545. /* set temporary mark; check if revisit*/
  546. if (temp.insert(ti).second) {
  547. /* sort targets lexicographically to ensure that nodes are always visited
  548. * in the same order */
  549. OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti),
  550. "");
  551. for (auto const& di : sortedTargets) {
  552. if (this->VisitTarget(temp, perm, order, di)) {
  553. return true;
  554. }
  555. }
  556. /* mark as complete; insert into beginning of list*/
  557. perm.insert(ti);
  558. order.push_back(ti);
  559. return false;
  560. }
  561. /* revisiting item - not a DAG */
  562. return true;
  563. }
  564. /* already complete */
  565. return false;
  566. }
  567. bool cmGlobalGhsMultiGenerator::AddCheckTarget()
  568. {
  569. // Skip the target if no regeneration is to be done.
  570. if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
  571. return false;
  572. }
  573. // Get the generators.
  574. std::vector<std::unique_ptr<cmLocalGenerator>> const& generators =
  575. this->LocalGenerators;
  576. auto& lg =
  577. cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]);
  578. // The name of the output file for the custom command.
  579. this->StampFile = lg.GetBinaryDirectory() + std::string("/CMakeFiles/") +
  580. CHECK_BUILD_SYSTEM_TARGET;
  581. // Add a custom rule to re-run CMake if any input files changed.
  582. {
  583. // Collect the input files used to generate all targets in this
  584. // project.
  585. std::vector<std::string> listFiles;
  586. for (auto const& gen : generators) {
  587. cm::append(listFiles, gen->GetMakefile()->GetListFiles());
  588. }
  589. // Add the cache file.
  590. listFiles.emplace_back(cmStrCat(
  591. this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt"));
  592. // Print not implemented warning.
  593. if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) {
  594. std::ostringstream msg;
  595. msg << "Any pre-check scripts, such as those generated for file(GLOB "
  596. "CONFIGURE_DEPENDS), will not be run by gbuild.";
  597. this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
  598. msg.str());
  599. }
  600. // Sort the list of input files and remove duplicates.
  601. std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
  602. auto newEnd = std::unique(listFiles.begin(), listFiles.end());
  603. listFiles.erase(newEnd, listFiles.end());
  604. // Create a rule to re-run CMake and create output file.
  605. cmCustomCommandLines commandLines;
  606. commandLines.emplace_back(
  607. cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "rm", "-f",
  608. this->StampFile }));
  609. std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
  610. std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
  611. commandLines.emplace_back(
  612. cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB }));
  613. commandLines.emplace_back(cmMakeCommandLine(
  614. { cmSystemTools::GetCMakeCommand(), "-E", "touch", this->StampFile }));
  615. /* Create the target(Exclude from ALL_BUILD).
  616. *
  617. * The build tool, currently, does not support rereading the project files
  618. * if they get updated. So do not run this target as part of ALL_BUILD.
  619. */
  620. auto cc = cm::make_unique<cmCustomCommand>();
  621. cmTarget* tgt =
  622. lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc));
  623. auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
  624. auto* gt = ptr.get();
  625. lg.AddGeneratorTarget(std::move(ptr));
  626. // Add the rule.
  627. cc = cm::make_unique<cmCustomCommand>();
  628. cc->SetOutputs(this->StampFile);
  629. cc->SetDepends(listFiles);
  630. cc->SetCommandLines(commandLines);
  631. cc->SetComment("Checking Build System");
  632. cc->SetEscapeOldStyle(false);
  633. cc->SetStdPipesUTF8(true);
  634. if (cmSourceFile* file =
  635. lg.AddCustomCommandToOutput(std::move(cc), true)) {
  636. gt->AddSource(file->ResolveFullPath());
  637. } else {
  638. cmSystemTools::Error("Error adding rule for " + this->StampFile);
  639. }
  640. // Organize in the "predefined targets" folder:
  641. if (this->UseFolderProperty()) {
  642. tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
  643. }
  644. }
  645. return true;
  646. }
  647. void cmGlobalGhsMultiGenerator::AddAllTarget()
  648. {
  649. // Add a special target that depends on ALL projects for easy build
  650. // of one configuration only.
  651. for (auto const& it : this->ProjectMap) {
  652. std::vector<cmLocalGenerator*> const& gen = it.second;
  653. // add the ALL_BUILD to the first local generator of each project
  654. if (!gen.empty()) {
  655. // Use no actual command lines so that the target itself is not
  656. // considered always out of date.
  657. auto cc = cm::make_unique<cmCustomCommand>();
  658. cc->SetEscapeOldStyle(false);
  659. cc->SetComment("Build all projects");
  660. cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(),
  661. true, std::move(cc));
  662. gen[0]->AddGeneratorTarget(
  663. cm::make_unique<cmGeneratorTarget>(allBuild, gen[0]));
  664. // Organize in the "predefined targets" folder:
  665. if (this->UseFolderProperty()) {
  666. allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
  667. }
  668. // Now make all targets depend on the ALL_BUILD target
  669. for (cmLocalGenerator const* i : gen) {
  670. for (auto const& tgt : i->GetGeneratorTargets()) {
  671. // Skip global or imported targets
  672. if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
  673. tgt->IsImported()) {
  674. continue;
  675. }
  676. // Skip Exclude From All Targets
  677. if (!this->IsExcluded(gen[0], tgt.get())) {
  678. allBuild->AddUtility(tgt->GetName(), false);
  679. }
  680. }
  681. }
  682. }
  683. }
  684. }
  685. void cmGlobalGhsMultiGenerator::AddExtraIDETargets()
  686. {
  687. // Add a special target that depends on ALL projects.
  688. this->AddAllTarget();
  689. /* Add Custom Target to check if CMake needs to be rerun.
  690. *
  691. * The build tool, currently, does not support rereading the project files
  692. * if they get updated. So do not make the other targets dependent on this
  693. * check.
  694. */
  695. this->AddCheckTarget();
  696. }