cmXCodeScheme.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  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 "cmXCodeScheme.h"
  4. #include <iomanip>
  5. #include <sstream>
  6. #include <utility>
  7. #include <cmext/algorithm>
  8. #include <cmext/string_view>
  9. #include "cmsys/String.h"
  10. #include "cmGeneratedFileStream.h"
  11. #include "cmGeneratorExpression.h"
  12. #include "cmGeneratorTarget.h"
  13. #include "cmList.h"
  14. #include "cmStateTypes.h"
  15. #include "cmStringAlgorithms.h"
  16. #include "cmSystemTools.h"
  17. #include "cmValue.h"
  18. #include "cmXCodeObject.h"
  19. #include "cmXMLWriter.h"
  20. class cmLocalGenerator;
  21. cmXCodeScheme::cmXCodeScheme(cmLocalGenerator* lg, cmXCodeObject* xcObj,
  22. TestObjects tests,
  23. const std::vector<std::string>& configList,
  24. unsigned int xcVersion)
  25. : LocalGenerator(lg)
  26. , Target(xcObj)
  27. , Tests(std::move(tests))
  28. , TargetName(xcObj->GetTarget()->GetName())
  29. , ConfigList(configList)
  30. , XcodeVersion(xcVersion)
  31. {
  32. }
  33. void cmXCodeScheme::WriteXCodeSharedScheme(const std::string& xcProjDir,
  34. const std::string& container)
  35. {
  36. // Create shared scheme sub-directory tree
  37. //
  38. std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes");
  39. cmSystemTools::MakeDirectory(xcodeSchemeDir);
  40. std::string xcodeSchemeFile =
  41. cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme");
  42. cmGeneratedFileStream fout(xcodeSchemeFile);
  43. fout.SetCopyIfDifferent(true);
  44. if (!fout) {
  45. return;
  46. }
  47. WriteXCodeXCScheme(fout, container);
  48. }
  49. void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout,
  50. const std::string& container)
  51. {
  52. cmXMLWriter xout(fout);
  53. xout.SetIndentationElement(std::string(3, ' '));
  54. xout.StartDocument();
  55. xout.StartElement("Scheme");
  56. xout.BreakAttributes();
  57. xout.Attribute("LastUpgradeVersion", WriteVersionString());
  58. xout.Attribute("version", "1.3");
  59. cmValue propDftCfg =
  60. Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_CONFIGURATION");
  61. std::string launchConfiguration =
  62. !propDftCfg.IsEmpty() ? *propDftCfg : "Debug";
  63. cmValue propTstCfg =
  64. Target->GetTarget()->GetProperty("XCODE_SCHEME_TEST_CONFIGURATION");
  65. std::string testConfiguration =
  66. !propTstCfg.IsEmpty() ? *propTstCfg : "Debug";
  67. WriteBuildAction(xout, container);
  68. WriteTestAction(xout, FindConfiguration(testConfiguration), container);
  69. WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container);
  70. WriteProfileAction(xout, FindConfiguration("Release"), container);
  71. WriteAnalyzeAction(xout, FindConfiguration("Debug"));
  72. WriteArchiveAction(xout, FindConfiguration("Release"));
  73. xout.EndElement();
  74. }
  75. void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
  76. const std::string& container)
  77. {
  78. xout.StartElement("BuildAction");
  79. xout.BreakAttributes();
  80. xout.Attribute("parallelizeBuildables", "YES");
  81. xout.Attribute("buildImplicitDependencies", "YES");
  82. xout.StartElement("BuildActionEntries");
  83. xout.StartElement("BuildActionEntry");
  84. xout.BreakAttributes();
  85. xout.Attribute("buildForTesting", "YES");
  86. xout.Attribute("buildForRunning", "YES");
  87. xout.Attribute("buildForProfiling", "YES");
  88. xout.Attribute("buildForArchiving", "YES");
  89. xout.Attribute("buildForAnalyzing", "YES");
  90. WriteBuildableReference(xout, this->Target, container);
  91. xout.EndElement(); // BuildActionEntry
  92. xout.EndElement(); // BuildActionEntries
  93. xout.EndElement(); // BuildAction
  94. }
  95. void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
  96. const std::string& configuration,
  97. const std::string& container)
  98. {
  99. xout.StartElement("TestAction");
  100. xout.BreakAttributes();
  101. xout.Attribute("buildConfiguration", configuration);
  102. xout.Attribute("selectedDebuggerIdentifier",
  103. "Xcode.DebuggerFoundation.Debugger.LLDB");
  104. xout.Attribute("selectedLauncherIdentifier",
  105. "Xcode.DebuggerFoundation.Launcher.LLDB");
  106. xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
  107. WriteCustomLLDBInitFile(xout, configuration);
  108. xout.StartElement("Testables");
  109. for (auto const* test : this->Tests) {
  110. xout.StartElement("TestableReference");
  111. xout.BreakAttributes();
  112. xout.Attribute("skipped", "NO");
  113. WriteBuildableReference(xout, test, container);
  114. xout.EndElement(); // TestableReference
  115. }
  116. xout.EndElement();
  117. if (IsTestable()) {
  118. xout.StartElement("MacroExpansion");
  119. WriteBuildableReference(xout, this->Target, container);
  120. xout.EndElement(); // MacroExpansion
  121. }
  122. xout.StartElement("AdditionalOptions");
  123. xout.EndElement();
  124. xout.EndElement(); // TestAction
  125. }
  126. void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
  127. const std::string& configuration,
  128. const std::string& container)
  129. {
  130. xout.StartElement("LaunchAction");
  131. xout.BreakAttributes();
  132. xout.Attribute("buildConfiguration", configuration);
  133. xout.Attribute("selectedDebuggerIdentifier",
  134. "Xcode.DebuggerFoundation.Debugger.LLDB");
  135. xout.Attribute("selectedLauncherIdentifier",
  136. "Xcode.DebuggerFoundation.Launcher.LLDB");
  137. {
  138. cmValue launchMode =
  139. this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE");
  140. std::string value = "0"; // == 'AUTO'
  141. if (launchMode && *launchMode == "WAIT"_s) {
  142. value = "1";
  143. }
  144. xout.Attribute("launchStyle", value);
  145. }
  146. WriteCustomWorkingDirectory(xout, configuration);
  147. WriteCustomLLDBInitFile(xout, configuration);
  148. xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
  149. WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
  150. "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
  151. true);
  152. xout.Attribute("debugServiceExtension", "internal");
  153. xout.Attribute("allowLocationSimulation", "YES");
  154. if (cmValue gpuFrameCaptureMode = this->Target->GetTarget()->GetProperty(
  155. "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE")) {
  156. std::string value = *gpuFrameCaptureMode;
  157. if (cmsysString_strcasecmp(value.c_str(), "Metal") == 0) {
  158. value = "1";
  159. } else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
  160. value = "3";
  161. }
  162. xout.Attribute("enableGPUFrameCaptureMode", value);
  163. }
  164. // Diagnostics tab begin
  165. bool useAddressSanitizer = WriteLaunchActionAttribute(
  166. xout, "enableAddressSanitizer",
  167. "XCODE_SCHEME_ADDRESS_SANITIZER"); // not allowed with
  168. // enableThreadSanitizer=YES
  169. WriteLaunchActionAttribute(
  170. xout, "enableASanStackUseAfterReturn",
  171. "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
  172. bool useThreadSanitizer = false;
  173. if (!useAddressSanitizer) {
  174. useThreadSanitizer = WriteLaunchActionAttribute(
  175. xout, "enableThreadSanitizer",
  176. "XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
  177. // enableAddressSanitizer=YES
  178. }
  179. WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
  180. "XCODE_SCHEME_THREAD_SANITIZER_STOP");
  181. WriteLaunchActionAttribute(xout, "enableUBSanitizer",
  182. "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
  183. if (cmValue value = this->Target->GetTarget()->GetProperty(
  184. "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION")) {
  185. if (value.IsOff()) {
  186. xout.Attribute("enableGPUValidationMode",
  187. "1"); // unset means YES, "1" means NO
  188. }
  189. }
  190. if (cmValue value = this->Target->GetTarget()->GetProperty(
  191. "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION")) {
  192. if (value.IsOn()) {
  193. xout.Attribute("enableGPUShaderValidationMode",
  194. "2"); // unset means NO, "2" means YES
  195. }
  196. }
  197. WriteLaunchActionAttribute(
  198. xout, "stopOnEveryUBSanitizerIssue",
  199. "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
  200. WriteLaunchActionAttribute(
  201. xout, "disableMainThreadChecker",
  202. "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); // negative enabled!
  203. WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue",
  204. "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
  205. if (this->Target->GetTarget()->GetPropertyAsBool(
  206. "XCODE_SCHEME_DEBUG_AS_ROOT")) {
  207. xout.Attribute("debugAsWhichUser", "root");
  208. }
  209. // Diagnostics tab end
  210. if (IsExecutable(this->Target)) {
  211. WriteBuildableProductRunnable(xout, this->Target, container);
  212. } else {
  213. xout.StartElement("MacroExpansion");
  214. WriteBuildableReference(xout, this->Target, container);
  215. xout.EndElement();
  216. }
  217. // Info tab begin
  218. if (cmValue exe =
  219. this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
  220. xout.StartElement("PathRunnable");
  221. xout.BreakAttributes();
  222. xout.Attribute("runnableDebuggingMode", "0");
  223. xout.Attribute("FilePath", *exe);
  224. xout.EndElement(); // PathRunnable
  225. }
  226. // Info tab end
  227. // Arguments tab begin
  228. if (cmValue argList =
  229. this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
  230. cmList arguments{ *argList };
  231. if (!arguments.empty()) {
  232. xout.StartElement("CommandLineArguments");
  233. for (auto const& argument : arguments) {
  234. xout.StartElement("CommandLineArgument");
  235. xout.BreakAttributes();
  236. xout.Attribute("argument", argument);
  237. xout.Attribute("isEnabled", "YES");
  238. xout.EndElement(); // CommandLineArgument
  239. }
  240. xout.EndElement(); // CommandLineArguments
  241. }
  242. }
  243. if (cmValue envList =
  244. this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
  245. cmList envs{ *envList };
  246. if (!envs.empty()) {
  247. xout.StartElement("EnvironmentVariables");
  248. for (auto env : envs) {
  249. xout.StartElement("EnvironmentVariable");
  250. xout.BreakAttributes();
  251. std::string envValue;
  252. const auto p = env.find_first_of('=');
  253. if (p != std::string::npos) {
  254. envValue = env.substr(p + 1);
  255. env.resize(p);
  256. }
  257. xout.Attribute("key", env);
  258. xout.Attribute("value", envValue);
  259. xout.Attribute("isEnabled", "YES");
  260. xout.EndElement(); // EnvironmentVariable
  261. }
  262. xout.EndElement(); // EnvironmentVariables
  263. }
  264. }
  265. // Arguments tab end
  266. xout.StartElement("AdditionalOptions");
  267. if (!useThreadSanitizer) {
  268. WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
  269. "XCODE_SCHEME_MALLOC_SCRIBBLE");
  270. }
  271. if (!useThreadSanitizer && !useAddressSanitizer) {
  272. WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
  273. "XCODE_SCHEME_MALLOC_GUARD_EDGES");
  274. }
  275. if (!useThreadSanitizer && !useAddressSanitizer) {
  276. WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
  277. "/usr/lib/libgmalloc.dylib",
  278. "XCODE_SCHEME_GUARD_MALLOC");
  279. }
  280. WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
  281. "XCODE_SCHEME_ZOMBIE_OBJECTS");
  282. if (!useThreadSanitizer && !useAddressSanitizer) {
  283. WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
  284. "XCODE_SCHEME_MALLOC_STACK");
  285. }
  286. WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
  287. "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
  288. WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
  289. "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
  290. xout.EndElement();
  291. xout.EndElement(); // LaunchAction
  292. }
  293. bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
  294. const std::string& attrName,
  295. const std::string& varName)
  296. {
  297. if (Target->GetTarget()->GetPropertyAsBool(varName)) {
  298. xout.Attribute(attrName.c_str(), "YES");
  299. return true;
  300. }
  301. return false;
  302. }
  303. bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
  304. cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
  305. bool defaultValue)
  306. {
  307. cmValue property = Target->GetTarget()->GetProperty(varName);
  308. bool isOn = (!property && defaultValue) || property.IsOn();
  309. if (isOn) {
  310. xout.Attribute(attrName.c_str(), "YES");
  311. } else {
  312. xout.Attribute(attrName.c_str(), "NO");
  313. }
  314. return isOn;
  315. }
  316. bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
  317. cmXMLWriter& xout, const std::string& key, const std::string& value,
  318. const std::string& varName)
  319. {
  320. if (Target->GetTarget()->GetPropertyAsBool(varName)) {
  321. xout.StartElement("AdditionalOption");
  322. xout.BreakAttributes();
  323. xout.Attribute("key", key);
  324. xout.Attribute("value", value);
  325. xout.Attribute("isEnabled", "YES");
  326. xout.EndElement(); // AdditionalOption
  327. return true;
  328. }
  329. return false;
  330. }
  331. void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
  332. const std::string& configuration,
  333. const std::string& container)
  334. {
  335. xout.StartElement("ProfileAction");
  336. xout.BreakAttributes();
  337. xout.Attribute("buildConfiguration", configuration);
  338. xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
  339. xout.Attribute("savedToolIdentifier", "");
  340. WriteCustomWorkingDirectory(xout, configuration);
  341. WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
  342. "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
  343. true);
  344. if (IsExecutable(this->Target)) {
  345. WriteBuildableProductRunnable(xout, this->Target, container);
  346. }
  347. xout.EndElement();
  348. }
  349. void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
  350. const std::string& configuration)
  351. {
  352. xout.StartElement("AnalyzeAction");
  353. xout.BreakAttributes();
  354. xout.Attribute("buildConfiguration", configuration);
  355. xout.EndElement();
  356. }
  357. void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
  358. const std::string& configuration)
  359. {
  360. xout.StartElement("ArchiveAction");
  361. xout.BreakAttributes();
  362. xout.Attribute("buildConfiguration", configuration);
  363. xout.Attribute("revealArchiveInOrganizer", "YES");
  364. xout.EndElement();
  365. }
  366. void cmXCodeScheme::WriteBuildableProductRunnable(cmXMLWriter& xout,
  367. const cmXCodeObject* xcObj,
  368. const std::string& container)
  369. {
  370. xout.StartElement("BuildableProductRunnable");
  371. xout.BreakAttributes();
  372. xout.Attribute("runnableDebuggingMode", "0");
  373. WriteBuildableReference(xout, xcObj, container);
  374. xout.EndElement();
  375. }
  376. void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
  377. const cmXCodeObject* xcObj,
  378. const std::string& container)
  379. {
  380. xout.StartElement("BuildableReference");
  381. xout.BreakAttributes();
  382. xout.Attribute("BuildableIdentifier", "primary");
  383. xout.Attribute("BlueprintIdentifier", xcObj->GetId());
  384. std::string const noConfig; // FIXME: What config to use here?
  385. xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName(noConfig));
  386. xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
  387. xout.Attribute("ReferencedContainer", cmStrCat("container:", container));
  388. xout.EndElement();
  389. }
  390. void cmXCodeScheme::WriteCustomWorkingDirectory(
  391. cmXMLWriter& xout, const std::string& configuration)
  392. {
  393. std::string const& propertyValue =
  394. this->Target->GetTarget()->GetSafeProperty(
  395. "XCODE_SCHEME_WORKING_DIRECTORY");
  396. if (propertyValue.empty()) {
  397. xout.Attribute("useCustomWorkingDirectory", "NO");
  398. } else {
  399. xout.Attribute("useCustomWorkingDirectory", "YES");
  400. auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
  401. propertyValue, this->LocalGenerator, configuration);
  402. xout.Attribute("customWorkingDirectory", customWorkingDirectory);
  403. }
  404. }
  405. void cmXCodeScheme::WriteCustomLLDBInitFile(cmXMLWriter& xout,
  406. const std::string& configuration)
  407. {
  408. std::string const& propertyValue =
  409. this->Target->GetTarget()->GetSafeProperty("XCODE_SCHEME_LLDB_INIT_FILE");
  410. if (!propertyValue.empty()) {
  411. auto customLLDBInitFile = cmGeneratorExpression::Evaluate(
  412. propertyValue, this->LocalGenerator, configuration);
  413. xout.Attribute("customLLDBInitFile", customLLDBInitFile);
  414. }
  415. }
  416. std::string cmXCodeScheme::WriteVersionString()
  417. {
  418. std::ostringstream v;
  419. v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
  420. return v.str();
  421. }
  422. std::string cmXCodeScheme::FindConfiguration(const std::string& name)
  423. {
  424. // Try to find the desired configuration by name,
  425. // and if it's not found return first from the list
  426. //
  427. if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
  428. return this->ConfigList[0];
  429. }
  430. return name;
  431. }
  432. bool cmXCodeScheme::IsTestable() const
  433. {
  434. return !this->Tests.empty() || IsExecutable(this->Target);
  435. }
  436. bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
  437. {
  438. cmGeneratorTarget* gt = target->GetTarget();
  439. if (!gt) {
  440. cmSystemTools::Error("Error no target on xobject\n");
  441. return false;
  442. }
  443. return gt->GetType() == cmStateEnums::EXECUTABLE;
  444. }