cmGlobalGhsMultiGenerator.cxx 15 KB

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