cmGlobalGhsMultiGenerator.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmGlobalGhsMultiGenerator.h"
  4. #include "cmDocumentationEntry.h"
  5. #include "cmGeneratedFileStream.h"
  6. #include "cmGeneratorTarget.h"
  7. #include "cmGhsMultiGpj.h"
  8. #include "cmLocalGenerator.h"
  9. #include "cmLocalGhsMultiGenerator.h"
  10. #include "cmMakefile.h"
  11. #include "cmState.h"
  12. #include "cmStateTypes.h"
  13. #include "cmSystemTools.h"
  14. #include "cmVersion.h"
  15. #include "cmake.h"
  16. #include <algorithm>
  17. #include <map>
  18. #include <ostream>
  19. #include <string.h>
  20. #include <utility>
  21. const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj";
  22. #ifdef __linux__
  23. const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild";
  24. const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "/usr/ghs";
  25. #elif defined(_WIN32)
  26. const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe";
  27. const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "C:/ghs";
  28. #endif
  29. cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm)
  30. : cmGlobalGenerator(cm)
  31. {
  32. cm->GetState()->SetGhsMultiIDE(true);
  33. }
  34. cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default;
  35. cmLocalGenerator* cmGlobalGhsMultiGenerator::CreateLocalGenerator(
  36. cmMakefile* mf)
  37. {
  38. return new cmLocalGhsMultiGenerator(this, mf);
  39. }
  40. void cmGlobalGhsMultiGenerator::GetDocumentation(cmDocumentationEntry& entry)
  41. {
  42. entry.Name = GetActualName();
  43. entry.Brief =
  44. "Generates Green Hills MULTI files (experimental, work-in-progress).";
  45. }
  46. void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory(
  47. cmGeneratorTarget* gt) const
  48. {
  49. // Compute full path to object file directory for this target.
  50. std::string dir;
  51. dir += gt->LocalGenerator->GetCurrentBinaryDirectory();
  52. dir += "/";
  53. dir += gt->LocalGenerator->GetTargetDirectory(gt);
  54. dir += "/";
  55. gt->ObjectDirectory = dir;
  56. }
  57. bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts,
  58. cmMakefile* mf)
  59. {
  60. std::string tsp; /* toolset path */
  61. this->GetToolset(mf, tsp, ts);
  62. /* no toolset was found */
  63. if (tsp.empty()) {
  64. return false;
  65. }
  66. if (ts.empty()) {
  67. std::string message;
  68. message =
  69. "Green Hills MULTI: -T <toolset> not specified; defaulting to \"";
  70. message += tsp;
  71. message += "\"";
  72. cmSystemTools::Message(message);
  73. /* store the full toolset for later use
  74. * -- already done if -T<toolset> was specified
  75. */
  76. mf->AddCacheDefinition("CMAKE_GENERATOR_TOOLSET", tsp.c_str(),
  77. "Location of generator toolset.",
  78. cmStateEnums::INTERNAL);
  79. }
  80. /* set the build tool to use */
  81. std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
  82. DEFAULT_BUILD_PROGRAM);
  83. const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
  84. /* check if the toolset changed from last generate */
  85. if (prevTool != nullptr && (gbuild != prevTool)) {
  86. std::string message = "toolset build tool: ";
  87. message += gbuild;
  88. message += "\nDoes not match the previously used build tool: ";
  89. message += prevTool;
  90. message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
  91. "directory or choose a different binary directory.";
  92. cmSystemTools::Error(message);
  93. return false;
  94. }
  95. /* store the toolset that is being used for this build */
  96. mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(),
  97. "build program to use", cmStateEnums::INTERNAL, true);
  98. mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp.c_str());
  99. return true;
  100. }
  101. bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p,
  102. cmMakefile* mf)
  103. {
  104. if (p.empty()) {
  105. cmSystemTools::Message(
  106. "Green Hills MULTI: -A <arch> not specified; defaulting to \"arm\"");
  107. std::string arch = "arm";
  108. /* store the platform name for later use
  109. * -- already done if -A<arch> was specified
  110. */
  111. mf->AddCacheDefinition("CMAKE_GENERATOR_PLATFORM", arch.c_str(),
  112. "Name of generator platform.",
  113. cmStateEnums::INTERNAL);
  114. }
  115. const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM");
  116. if (tgtPlatform == nullptr) {
  117. cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not "
  118. "specified; defaulting to \"integrity\"");
  119. tgtPlatform = "integrity";
  120. }
  121. /* store the platform name for later use */
  122. mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform,
  123. "Name of GHS target platform.",
  124. cmStateEnums::INTERNAL);
  125. return true;
  126. }
  127. void cmGlobalGhsMultiGenerator::EnableLanguage(
  128. std::vector<std::string> const& l, cmMakefile* mf, bool optional)
  129. {
  130. mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI");
  131. mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
  132. this->cmGlobalGenerator::EnableLanguage(l, mf, optional);
  133. }
  134. bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/)
  135. {
  136. // The GHS generator only knows how to lookup its build tool
  137. // during generation of the project files, but this
  138. // can only be done after the toolset is specified.
  139. return true;
  140. }
  141. void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsd,
  142. const std::string& ts)
  143. {
  144. const char* ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT");
  145. if (!ghsRoot || ghsRoot[0] == '\0') {
  146. ghsRoot = DEFAULT_TOOLSET_ROOT;
  147. }
  148. tsd = ghsRoot;
  149. if (ts.empty()) {
  150. std::vector<std::string> output;
  151. // Use latest? version
  152. if (tsd.back() != '/') {
  153. tsd += "/";
  154. }
  155. cmSystemTools::Glob(tsd, "comp_[^;]+", output);
  156. if (output.empty()) {
  157. std::string msg =
  158. "No GHS toolsets found in GHS_TOOLSET_ROOT \"" + tsd + "\".";
  159. cmSystemTools::Error(msg);
  160. tsd = "";
  161. } else {
  162. tsd += output.back();
  163. }
  164. } else {
  165. std::string tryPath;
  166. /* CollapseCombinedPath will check if ts is an absolute path */
  167. tryPath = cmSystemTools::CollapseCombinedPath(tsd, ts);
  168. if (!cmSystemTools::FileExists(tryPath)) {
  169. std::string msg = "GHS toolset \"" + tryPath + "\" not found.";
  170. cmSystemTools::Error(msg);
  171. tsd = "";
  172. } else {
  173. tsd = tryPath;
  174. }
  175. }
  176. }
  177. void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout)
  178. {
  179. fout << "#!gbuild" << std::endl;
  180. fout << "#" << std::endl
  181. << "# CMAKE generated file: DO NOT EDIT!" << std::endl
  182. << "# Generated by \"" << GetActualName() << "\""
  183. << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
  184. << cmVersion::GetMinorVersion() << std::endl
  185. << "#" << std::endl
  186. << std::endl;
  187. }
  188. void cmGlobalGhsMultiGenerator::WriteTopLevelProject(
  189. std::ostream& fout, cmLocalGenerator* root,
  190. std::vector<cmLocalGenerator*>& generators)
  191. {
  192. WriteFileHeader(fout);
  193. this->WriteMacros(fout);
  194. this->WriteHighLevelDirectives(fout);
  195. GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout);
  196. fout << "# Top Level Project File" << std::endl;
  197. // Specify BSP option if supplied by user
  198. // -- not all platforms require this entry in the project file
  199. // integrity platforms require this field; use default if needed
  200. std::string platform;
  201. if (const char* p =
  202. this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM")) {
  203. platform = p;
  204. }
  205. std::string bspName;
  206. if (char const* bspCache =
  207. this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME")) {
  208. bspName = bspCache;
  209. this->GetCMakeInstance()->MarkCliAsUsed("GHS_BSP_NAME");
  210. } else {
  211. bspName = "IGNORE";
  212. }
  213. if (platform.find("integrity") != std::string::npos &&
  214. cmSystemTools::IsOff(bspName.c_str())) {
  215. const char* a =
  216. this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
  217. bspName = "sim";
  218. bspName += (a ? a : "");
  219. }
  220. if (!cmSystemTools::IsOff(bspName.c_str())) {
  221. fout << " -bsp " << bspName << std::endl;
  222. }
  223. // Specify OS DIR if supplied by user
  224. // -- not all platforms require this entry in the project file
  225. std::string osDir;
  226. std::string osDirOption;
  227. if (char const* osDirCache =
  228. this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR")) {
  229. osDir = osDirCache;
  230. }
  231. if (char const* osDirOptionCache =
  232. this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION")) {
  233. osDirOption = osDirOptionCache;
  234. }
  235. if (!cmSystemTools::IsOff(osDir.c_str()) ||
  236. platform.find("integrity") != std::string::npos) {
  237. std::replace(osDir.begin(), osDir.end(), '\\', '/');
  238. fout << " " << osDirOption << "\"" << osDir << "\"" << std::endl;
  239. }
  240. WriteSubProjects(fout, root, generators);
  241. }
  242. void cmGlobalGhsMultiGenerator::WriteSubProjects(
  243. std::ostream& fout, cmLocalGenerator* root,
  244. std::vector<cmLocalGenerator*>& generators)
  245. {
  246. // Collect all targets under this root generator and the transitive
  247. // closure of their dependencies.
  248. TargetDependSet projectTargets;
  249. TargetDependSet originalTargets;
  250. this->GetTargetSets(projectTargets, originalTargets, root, generators);
  251. OrderedTargetDependSet orderedProjectTargets(projectTargets, "");
  252. // write out all the sub-projects
  253. std::string rootBinaryDir = root->GetCurrentBinaryDirectory();
  254. for (cmGeneratorTarget const* target : orderedProjectTargets) {
  255. if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
  256. continue;
  257. }
  258. const char* projName = target->GetProperty("GENERATOR_FILE_NAME");
  259. const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
  260. if (projName && projType) {
  261. cmLocalGenerator* lg = target->GetLocalGenerator();
  262. std::string dir = lg->GetCurrentBinaryDirectory();
  263. dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir);
  264. if (dir == ".") {
  265. dir.clear();
  266. } else {
  267. if (dir.back() != '/') {
  268. dir += "/";
  269. }
  270. }
  271. if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) {
  272. fout << "{comment} ";
  273. }
  274. std::string projFile = dir + projName + FILE_EXTENSION;
  275. fout << projFile;
  276. fout << " " << projType << std::endl;
  277. if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) {
  278. // create reference project
  279. std::string fname = dir;
  280. fname += target->GetName();
  281. fname += "REF";
  282. fname += FILE_EXTENSION;
  283. cmGeneratedFileStream fref(fname);
  284. fref.SetCopyIfDifferent(true);
  285. this->WriteFileHeader(fref);
  286. GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref);
  287. fref << " :reference=" << projFile << std::endl;
  288. fref.Close();
  289. }
  290. }
  291. }
  292. }
  293. void cmGlobalGhsMultiGenerator::Generate()
  294. {
  295. // first do the superclass method
  296. this->cmGlobalGenerator::Generate();
  297. // output top-level projects
  298. for (auto& it : this->ProjectMap) {
  299. this->OutputTopLevelProject(it.second[0], it.second);
  300. }
  301. }
  302. void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
  303. cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
  304. {
  305. if (generators.empty()) {
  306. return;
  307. }
  308. /* Name top-level projects as filename.top.gpj to avoid name clashes
  309. * with target projects. This avoid the issue where the project has
  310. * the same name as the executable target.
  311. */
  312. std::string fname = root->GetCurrentBinaryDirectory();
  313. fname += "/";
  314. fname += root->GetProjectName();
  315. fname += ".top";
  316. fname += FILE_EXTENSION;
  317. cmGeneratedFileStream fout(fname);
  318. fout.SetCopyIfDifferent(true);
  319. this->WriteTopLevelProject(fout, root, generators);
  320. fout.Close();
  321. }
  322. std::vector<cmGlobalGenerator::GeneratedMakeCommand>
  323. cmGlobalGhsMultiGenerator::GenerateBuildCommand(
  324. const std::string& makeProgram, const std::string& projectName,
  325. const std::string& projectDir, std::vector<std::string> const& targetNames,
  326. const std::string& /*config*/, bool /*fast*/, int jobs, bool /*verbose*/,
  327. std::vector<std::string> const& makeOptions)
  328. {
  329. GeneratedMakeCommand makeCommand = {};
  330. std::string gbuild;
  331. if (const char* gbuildCached =
  332. this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) {
  333. gbuild = gbuildCached;
  334. }
  335. makeCommand.Add(this->SelectMakeProgram(makeProgram, gbuild));
  336. if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
  337. makeCommand.Add("-parallel");
  338. if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
  339. makeCommand.Add(std::to_string(jobs));
  340. }
  341. }
  342. makeCommand.Add(makeOptions.begin(), makeOptions.end());
  343. /* determine which top-project file to use */
  344. std::string proj = projectName + ".top" + FILE_EXTENSION;
  345. std::vector<std::string> files;
  346. cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files);
  347. if (!files.empty()) {
  348. auto p = std::find(files.begin(), files.end(), proj);
  349. if (p == files.end()) {
  350. proj = files.at(0);
  351. }
  352. }
  353. makeCommand.Add("-top", proj);
  354. if (!targetNames.empty()) {
  355. if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
  356. targetNames.end()) {
  357. makeCommand.Add("-clean");
  358. } else {
  359. for (const auto& tname : targetNames) {
  360. if (!tname.empty()) {
  361. if (tname.compare(tname.size() - 4, 4, ".gpj") == 0) {
  362. makeCommand.Add(tname);
  363. } else {
  364. makeCommand.Add(tname + ".gpj");
  365. }
  366. }
  367. }
  368. }
  369. }
  370. return { makeCommand };
  371. }
  372. void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout)
  373. {
  374. char const* ghsGpjMacros =
  375. this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
  376. if (nullptr != ghsGpjMacros) {
  377. std::vector<std::string> expandedList;
  378. cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList);
  379. for (std::string const& arg : expandedList) {
  380. fout << "macro " << arg << std::endl;
  381. }
  382. }
  383. }
  384. void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout)
  385. {
  386. /* set primary target */
  387. std::string tgt;
  388. const char* t =
  389. this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET");
  390. if (t) {
  391. tgt = t;
  392. this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET");
  393. } else {
  394. const char* a =
  395. this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
  396. const char* p =
  397. this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM");
  398. tgt = (a ? a : "");
  399. tgt += "_";
  400. tgt += (p ? p : "");
  401. tgt += ".tgt";
  402. }
  403. fout << "primaryTarget=" << tgt << std::endl;
  404. char const* const customization =
  405. this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION");
  406. if (nullptr != customization && strlen(customization) > 0) {
  407. fout << "customization=" << trimQuotes(customization) << std::endl;
  408. this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION");
  409. }
  410. }
  411. std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str)
  412. {
  413. std::string result;
  414. result.reserve(str.size());
  415. for (const char* ch = str.c_str(); *ch != '\0'; ++ch) {
  416. if (*ch != '"') {
  417. result += *ch;
  418. }
  419. }
  420. return result;
  421. }
  422. bool cmGlobalGhsMultiGenerator::TargetCompare::operator()(
  423. cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
  424. {
  425. // Make sure a given named target is ordered first,
  426. // e.g. to set ALL_BUILD as the default active project.
  427. // When the empty string is named this is a no-op.
  428. if (r->GetName() == this->First) {
  429. return false;
  430. }
  431. if (l->GetName() == this->First) {
  432. return true;
  433. }
  434. return l->GetName() < r->GetName();
  435. }
  436. cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet(
  437. TargetDependSet const& targets, std::string const& first)
  438. : derived(TargetCompare(first))
  439. {
  440. this->insert(targets.begin(), targets.end());
  441. }