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"
9 #include <cmext/algorithm>
11 #include "cmsys/String.h"
13 #include "cmGeneratedFileStream.h"
14 #include "cmGeneratorExpression.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmStateTypes.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
20 #include "cmXCodeObject.h"
21 #include "cmXMLWriter.h"
23 class cmLocalGenerator;
25 cmXCodeScheme::cmXCodeScheme(cmLocalGenerator* lg, cmXCodeObject* xcObj,
27 const std::vector<std::string>& configList,
28 unsigned int xcVersion)
31 , Tests(std::move(tests))
32 , TargetName(xcObj->GetTarget()->GetName())
33 , ConfigList(configList)
34 , XcodeVersion(xcVersion)
38 void cmXCodeScheme::WriteXCodeSharedScheme(const std::string& xcProjDir,
39 const std::string& container)
41 // Create shared scheme sub-directory tree
43 std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes");
44 cmSystemTools::MakeDirectory(xcodeSchemeDir);
46 std::string xcodeSchemeFile =
47 cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme");
49 cmGeneratedFileStream fout(xcodeSchemeFile);
50 fout.SetCopyIfDifferent(true);
55 WriteXCodeXCScheme(fout, container);
58 void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout,
59 const std::string& container)
61 cmXMLWriter xout(fout);
62 xout.SetIndentationElement(std::string(3, ' '));
65 xout.StartElement("Scheme");
66 xout.BreakAttributes();
67 xout.Attribute("LastUpgradeVersion", WriteVersionString());
68 xout.Attribute("version", "1.3");
71 Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_CONFIGURATION");
72 std::string launchConfiguration =
73 !propDftCfg.IsEmpty() ? *propDftCfg : "Debug";
75 WriteBuildAction(xout, container);
76 WriteTestAction(xout, FindConfiguration("Debug"), container);
77 WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container);
78 WriteProfileAction(xout, FindConfiguration("Release"));
79 WriteAnalyzeAction(xout, FindConfiguration("Debug"));
80 WriteArchiveAction(xout, FindConfiguration("Release"));
85 void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
86 const std::string& container)
88 xout.StartElement("BuildAction");
89 xout.BreakAttributes();
90 xout.Attribute("parallelizeBuildables", "YES");
91 xout.Attribute("buildImplicitDependencies", "YES");
93 xout.StartElement("BuildActionEntries");
94 xout.StartElement("BuildActionEntry");
95 xout.BreakAttributes();
96 xout.Attribute("buildForTesting", "YES");
97 xout.Attribute("buildForRunning", "YES");
98 xout.Attribute("buildForProfiling", "YES");
99 xout.Attribute("buildForArchiving", "YES");
100 xout.Attribute("buildForAnalyzing", "YES");
102 WriteBuildableReference(xout, this->Target, container);
104 xout.EndElement(); // BuildActionEntry
105 xout.EndElement(); // BuildActionEntries
106 xout.EndElement(); // BuildAction
109 void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
110 const std::string& configuration,
111 const std::string& container)
113 xout.StartElement("TestAction");
114 xout.BreakAttributes();
115 xout.Attribute("buildConfiguration", configuration);
116 xout.Attribute("selectedDebuggerIdentifier",
117 "Xcode.DebuggerFoundation.Debugger.LLDB");
118 xout.Attribute("selectedLauncherIdentifier",
119 "Xcode.DebuggerFoundation.Launcher.LLDB");
120 xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
122 xout.StartElement("Testables");
123 for (auto test : this->Tests) {
124 xout.StartElement("TestableReference");
125 xout.BreakAttributes();
126 xout.Attribute("skipped", "NO");
127 WriteBuildableReference(xout, test, container);
128 xout.EndElement(); // TestableReference
133 xout.StartElement("MacroExpansion");
134 WriteBuildableReference(xout, this->Target, container);
135 xout.EndElement(); // MacroExpansion
138 xout.StartElement("AdditionalOptions");
141 xout.EndElement(); // TestAction
144 void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
145 const std::string& configuration,
146 const std::string& container)
148 xout.StartElement("LaunchAction");
149 xout.BreakAttributes();
150 xout.Attribute("buildConfiguration", configuration);
151 xout.Attribute("selectedDebuggerIdentifier",
152 "Xcode.DebuggerFoundation.Debugger.LLDB");
153 xout.Attribute("selectedLauncherIdentifier",
154 "Xcode.DebuggerFoundation.Launcher.LLDB");
157 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE");
158 std::string value = "0"; // == 'AUTO'
159 if (launchMode && *launchMode == "WAIT") {
162 xout.Attribute("launchStyle", value);
164 WriteCustomWorkingDirectory(xout, configuration);
166 xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
167 WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
168 "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
170 xout.Attribute("debugServiceExtension", "internal");
171 xout.Attribute("allowLocationSimulation", "YES");
172 if (cmValue gpuFrameCaptureMode = this->Target->GetTarget()->GetProperty(
173 "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE")) {
174 std::string value = *gpuFrameCaptureMode;
175 if (cmsysString_strcasecmp(value.c_str(), "Metal") == 0) {
177 } else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
180 xout.Attribute("enableGPUFrameCaptureMode", value);
183 // Diagnostics tab begin
185 bool useAddressSanitizer = WriteLaunchActionAttribute(
186 xout, "enableAddressSanitizer",
187 "XCODE_SCHEME_ADDRESS_SANITIZER"); // not allowed with
188 // enableThreadSanitizer=YES
189 WriteLaunchActionAttribute(
190 xout, "enableASanStackUseAfterReturn",
191 "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
193 bool useThreadSanitizer = false;
194 if (!useAddressSanitizer) {
195 useThreadSanitizer = WriteLaunchActionAttribute(
196 xout, "enableThreadSanitizer",
197 "XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
198 // enableAddressSanitizer=YES
201 WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
202 "XCODE_SCHEME_THREAD_SANITIZER_STOP");
204 WriteLaunchActionAttribute(xout, "enableUBSanitizer",
205 "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
207 if (cmValue value = this->Target->GetTarget()->GetProperty(
208 "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION")) {
210 xout.Attribute("enableGPUValidationMode",
211 "1"); // unset means YES, "1" means NO
215 if (cmValue value = this->Target->GetTarget()->GetProperty(
216 "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION")) {
218 xout.Attribute("enableGPUShaderValidationMode",
219 "2"); // unset means NO, "2" means YES
223 WriteLaunchActionAttribute(
224 xout, "stopOnEveryUBSanitizerIssue",
225 "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
227 WriteLaunchActionAttribute(
228 xout, "disableMainThreadChecker",
229 "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); // negative enabled!
230 WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue",
231 "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
233 if (this->Target->GetTarget()->GetPropertyAsBool(
234 "XCODE_SCHEME_DEBUG_AS_ROOT")) {
235 xout.Attribute("debugAsWhichUser", "root");
238 // Diagnostics tab end
240 if (IsExecutable(this->Target)) {
241 xout.StartElement("BuildableProductRunnable");
242 xout.BreakAttributes();
243 xout.Attribute("runnableDebuggingMode", "0");
246 xout.StartElement("MacroExpansion");
249 WriteBuildableReference(xout, this->Target, container);
251 xout.EndElement(); // MacroExpansion
256 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
258 xout.StartElement("PathRunnable");
259 xout.BreakAttributes();
261 xout.Attribute("runnableDebuggingMode", "0");
262 xout.Attribute("FilePath", *exe);
264 xout.EndElement(); // PathRunnable
269 // Arguments tab begin
271 if (cmValue argList =
272 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
273 std::vector<std::string> arguments = cmExpandedList(*argList);
274 if (!arguments.empty()) {
275 xout.StartElement("CommandLineArguments");
277 for (auto const& argument : arguments) {
278 xout.StartElement("CommandLineArgument");
279 xout.BreakAttributes();
281 xout.Attribute("argument", argument);
282 xout.Attribute("isEnabled", "YES");
284 xout.EndElement(); // CommandLineArgument
287 xout.EndElement(); // CommandLineArguments
291 if (cmValue envList =
292 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
293 std::vector<std::string> envs = cmExpandedList(*envList);
295 xout.StartElement("EnvironmentVariables");
297 for (auto env : envs) {
299 xout.StartElement("EnvironmentVariable");
300 xout.BreakAttributes();
302 std::string envValue;
303 const auto p = env.find_first_of('=');
304 if (p != std::string::npos) {
305 envValue = env.substr(p + 1);
309 xout.Attribute("key", env);
310 xout.Attribute("value", envValue);
311 xout.Attribute("isEnabled", "YES");
313 xout.EndElement(); // EnvironmentVariable
316 xout.EndElement(); // EnvironmentVariables
322 xout.StartElement("AdditionalOptions");
324 if (!useThreadSanitizer) {
325 WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
326 "XCODE_SCHEME_MALLOC_SCRIBBLE");
329 if (!useThreadSanitizer && !useAddressSanitizer) {
330 WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
331 "XCODE_SCHEME_MALLOC_GUARD_EDGES");
334 if (!useThreadSanitizer && !useAddressSanitizer) {
335 WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
336 "/usr/lib/libgmalloc.dylib",
337 "XCODE_SCHEME_GUARD_MALLOC");
340 WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
341 "XCODE_SCHEME_ZOMBIE_OBJECTS");
343 if (!useThreadSanitizer && !useAddressSanitizer) {
344 WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
345 "XCODE_SCHEME_MALLOC_STACK");
348 WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
349 "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
351 WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
352 "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
356 xout.EndElement(); // LaunchAction
359 bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
360 const std::string& attrName,
361 const std::string& varName)
363 if (Target->GetTarget()->GetPropertyAsBool(varName)) {
364 xout.Attribute(attrName.c_str(), "YES");
370 bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
371 cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
374 cmValue property = Target->GetTarget()->GetProperty(varName);
375 bool isOn = (!property && defaultValue) || cmIsOn(property);
378 xout.Attribute(attrName.c_str(), "YES");
380 xout.Attribute(attrName.c_str(), "NO");
385 bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
386 cmXMLWriter& xout, const std::string& key, const std::string& value,
387 const std::string& varName)
389 if (Target->GetTarget()->GetPropertyAsBool(varName)) {
390 xout.StartElement("AdditionalOption");
391 xout.BreakAttributes();
393 xout.Attribute("key", key);
394 xout.Attribute("value", value);
395 xout.Attribute("isEnabled", "YES");
397 xout.EndElement(); // AdditionalOption
404 void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
405 const std::string& configuration)
407 xout.StartElement("ProfileAction");
408 xout.BreakAttributes();
409 xout.Attribute("buildConfiguration", configuration);
410 xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
411 xout.Attribute("savedToolIdentifier", "");
412 WriteCustomWorkingDirectory(xout, configuration);
413 WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
414 "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
419 void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
420 const std::string& configuration)
422 xout.StartElement("AnalyzeAction");
423 xout.BreakAttributes();
424 xout.Attribute("buildConfiguration", configuration);
428 void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
429 const std::string& configuration)
431 xout.StartElement("ArchiveAction");
432 xout.BreakAttributes();
433 xout.Attribute("buildConfiguration", configuration);
434 xout.Attribute("revealArchiveInOrganizer", "YES");
438 void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
439 const cmXCodeObject* xcObj,
440 const std::string& container)
442 xout.StartElement("BuildableReference");
443 xout.BreakAttributes();
444 xout.Attribute("BuildableIdentifier", "primary");
445 xout.Attribute("BlueprintIdentifier", xcObj->GetId());
446 std::string const noConfig; // FIXME: What config to use here?
447 xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName(noConfig));
448 xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
449 xout.Attribute("ReferencedContainer", "container:" + container);
453 void cmXCodeScheme::WriteCustomWorkingDirectory(
454 cmXMLWriter& xout, const std::string& configuration)
456 std::string const& propertyValue =
457 this->Target->GetTarget()->GetSafeProperty(
458 "XCODE_SCHEME_WORKING_DIRECTORY");
459 if (propertyValue.empty()) {
460 xout.Attribute("useCustomWorkingDirectory", "NO");
462 xout.Attribute("useCustomWorkingDirectory", "YES");
464 auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
465 propertyValue, this->LocalGenerator, configuration);
466 xout.Attribute("customWorkingDirectory", customWorkingDirectory);
470 std::string cmXCodeScheme::WriteVersionString()
472 std::ostringstream v;
473 v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
477 std::string cmXCodeScheme::FindConfiguration(const std::string& name)
479 // Try to find the desired configuration by name,
480 // and if it's not found return first from the list
482 if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
483 return this->ConfigList[0];
489 bool cmXCodeScheme::IsTestable() const
491 return !this->Tests.empty() || IsExecutable(this->Target);
494 bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
496 cmGeneratorTarget* gt = target->GetTarget();
498 cmSystemTools::Error("Error no target on xobject\n");
502 return gt->GetType() == cmStateEnums::EXECUTABLE;