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