cmXCodeScheme.cxx 17 KB

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