cmXCodeScheme.cxx 16 KB

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