cmGlobalGhsMultiGenerator.cxx 16 KB

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