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");
70 WriteBuildAction(xout, container);
71 WriteTestAction(xout, FindConfiguration("Debug"), container);
72 WriteLaunchAction(xout, FindConfiguration("Debug"), container);
73 WriteProfileAction(xout, FindConfiguration("Release"));
74 WriteAnalyzeAction(xout, FindConfiguration("Debug"));
75 WriteArchiveAction(xout, FindConfiguration("Release"));
80 void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
81 const std::string& container)
83 xout.StartElement("BuildAction");
84 xout.BreakAttributes();
85 xout.Attribute("parallelizeBuildables", "YES");
86 xout.Attribute("buildImplicitDependencies", "YES");
88 xout.StartElement("BuildActionEntries");
89 xout.StartElement("BuildActionEntry");
90 xout.BreakAttributes();
91 xout.Attribute("buildForTesting", "YES");
92 xout.Attribute("buildForRunning", "YES");
93 xout.Attribute("buildForProfiling", "YES");
94 xout.Attribute("buildForArchiving", "YES");
95 xout.Attribute("buildForAnalyzing", "YES");
97 WriteBuildableReference(xout, this->Target, container);
99 xout.EndElement(); // BuildActionEntry
100 xout.EndElement(); // BuildActionEntries
101 xout.EndElement(); // BuildAction
104 void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
105 const std::string& configuration,
106 const std::string& container)
108 xout.StartElement("TestAction");
109 xout.BreakAttributes();
110 xout.Attribute("buildConfiguration", configuration);
111 xout.Attribute("selectedDebuggerIdentifier",
112 "Xcode.DebuggerFoundation.Debugger.LLDB");
113 xout.Attribute("selectedLauncherIdentifier",
114 "Xcode.DebuggerFoundation.Launcher.LLDB");
115 xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
117 xout.StartElement("Testables");
118 for (auto test : this->Tests) {
119 xout.StartElement("TestableReference");
120 xout.BreakAttributes();
121 xout.Attribute("skipped", "NO");
122 WriteBuildableReference(xout, test, container);
123 xout.EndElement(); // TestableReference
128 xout.StartElement("MacroExpansion");
129 WriteBuildableReference(xout, this->Target, container);
130 xout.EndElement(); // MacroExpansion
133 xout.StartElement("AdditionalOptions");
136 xout.EndElement(); // TestAction
139 void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
140 const std::string& configuration,
141 const std::string& container)
143 xout.StartElement("LaunchAction");
144 xout.BreakAttributes();
145 xout.Attribute("buildConfiguration", configuration);
146 xout.Attribute("selectedDebuggerIdentifier",
147 "Xcode.DebuggerFoundation.Debugger.LLDB");
148 xout.Attribute("selectedLauncherIdentifier",
149 "Xcode.DebuggerFoundation.Launcher.LLDB");
150 xout.Attribute("launchStyle", "0");
151 WriteCustomWorkingDirectory(xout, configuration);
153 xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
154 WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
155 "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
157 xout.Attribute("debugServiceExtension", "internal");
158 xout.Attribute("allowLocationSimulation", "YES");
159 if (cmValue gpuFrameCaptureMode = this->Target->GetTarget()->GetProperty(
160 "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE")) {
161 std::string value = *gpuFrameCaptureMode;
162 if (cmsysString_strcasecmp(value.c_str(), "Metal") == 0) {
164 } else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
167 xout.Attribute("enableGPUFrameCaptureMode", value);
170 // Diagnostics tab begin
172 bool useAddressSanitizer = WriteLaunchActionAttribute(
173 xout, "enableAddressSanitizer",
174 "XCODE_SCHEME_ADDRESS_SANITIZER"); // not allowed with
175 // enableThreadSanitizer=YES
176 WriteLaunchActionAttribute(
177 xout, "enableASanStackUseAfterReturn",
178 "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
180 bool useThreadSanitizer = false;
181 if (!useAddressSanitizer) {
182 useThreadSanitizer = WriteLaunchActionAttribute(
183 xout, "enableThreadSanitizer",
184 "XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
185 // enableAddressSanitizer=YES
188 WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
189 "XCODE_SCHEME_THREAD_SANITIZER_STOP");
191 WriteLaunchActionAttribute(xout, "enableUBSanitizer",
192 "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
193 WriteLaunchActionAttribute(
194 xout, "stopOnEveryUBSanitizerIssue",
195 "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
197 WriteLaunchActionAttribute(
198 xout, "disableMainThreadChecker",
199 "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); // negative enabled!
200 WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue",
201 "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
203 if (this->Target->GetTarget()->GetPropertyAsBool(
204 "XCODE_SCHEME_DEBUG_AS_ROOT")) {
205 xout.Attribute("debugAsWhichUser", "root");
208 // Diagnostics tab end
210 if (IsExecutable(this->Target)) {
211 xout.StartElement("BuildableProductRunnable");
212 xout.BreakAttributes();
213 xout.Attribute("runnableDebuggingMode", "0");
216 xout.StartElement("MacroExpansion");
219 WriteBuildableReference(xout, this->Target, container);
221 xout.EndElement(); // MacroExpansion
226 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
228 xout.StartElement("PathRunnable");
229 xout.BreakAttributes();
231 xout.Attribute("runnableDebuggingMode", "0");
232 xout.Attribute("FilePath", *exe);
234 xout.EndElement(); // PathRunnable
239 // Arguments tab begin
241 if (cmValue argList =
242 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
243 std::vector<std::string> arguments = cmExpandedList(*argList);
244 if (!arguments.empty()) {
245 xout.StartElement("CommandLineArguments");
247 for (auto const& argument : arguments) {
248 xout.StartElement("CommandLineArgument");
249 xout.BreakAttributes();
251 xout.Attribute("argument", argument);
252 xout.Attribute("isEnabled", "YES");
254 xout.EndElement(); // CommandLineArgument
257 xout.EndElement(); // CommandLineArguments
261 if (cmValue envList =
262 this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
263 std::vector<std::string> envs = cmExpandedList(*envList);
265 xout.StartElement("EnvironmentVariables");
267 for (auto env : envs) {
269 xout.StartElement("EnvironmentVariable");
270 xout.BreakAttributes();
272 std::string envValue;
273 const auto p = env.find_first_of('=');
274 if (p != std::string::npos) {
275 envValue = env.substr(p + 1);
279 xout.Attribute("key", env);
280 xout.Attribute("value", envValue);
281 xout.Attribute("isEnabled", "YES");
283 xout.EndElement(); // EnvironmentVariable
286 xout.EndElement(); // EnvironmentVariables
292 xout.StartElement("AdditionalOptions");
294 if (!useThreadSanitizer) {
295 WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
296 "XCODE_SCHEME_MALLOC_SCRIBBLE");
299 if (!useThreadSanitizer && !useAddressSanitizer) {
300 WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
301 "XCODE_SCHEME_MALLOC_GUARD_EDGES");
304 if (!useThreadSanitizer && !useAddressSanitizer) {
305 WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
306 "/usr/lib/libgmalloc.dylib",
307 "XCODE_SCHEME_GUARD_MALLOC");
310 WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
311 "XCODE_SCHEME_ZOMBIE_OBJECTS");
313 if (!useThreadSanitizer && !useAddressSanitizer) {
314 WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
315 "XCODE_SCHEME_MALLOC_STACK");
318 WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
319 "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
321 WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
322 "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
326 xout.EndElement(); // LaunchAction
329 bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
330 const std::string& attrName,
331 const std::string& varName)
333 if (Target->GetTarget()->GetPropertyAsBool(varName)) {
334 xout.Attribute(attrName.c_str(), "YES");
340 bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
341 cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
344 cmValue property = Target->GetTarget()->GetProperty(varName);
345 bool isOn = (!property && defaultValue) || cmIsOn(property);
348 xout.Attribute(attrName.c_str(), "YES");
350 xout.Attribute(attrName.c_str(), "NO");
355 bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
356 cmXMLWriter& xout, const std::string& key, const std::string& value,
357 const std::string& varName)
359 if (Target->GetTarget()->GetPropertyAsBool(varName)) {
360 xout.StartElement("AdditionalOption");
361 xout.BreakAttributes();
363 xout.Attribute("key", key);
364 xout.Attribute("value", value);
365 xout.Attribute("isEnabled", "YES");
367 xout.EndElement(); // AdditionalOption
374 void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
375 const std::string& configuration)
377 xout.StartElement("ProfileAction");
378 xout.BreakAttributes();
379 xout.Attribute("buildConfiguration", configuration);
380 xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
381 xout.Attribute("savedToolIdentifier", "");
382 WriteCustomWorkingDirectory(xout, configuration);
383 WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
384 "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
389 void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
390 const std::string& configuration)
392 xout.StartElement("AnalyzeAction");
393 xout.BreakAttributes();
394 xout.Attribute("buildConfiguration", configuration);
398 void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
399 const std::string& configuration)
401 xout.StartElement("ArchiveAction");
402 xout.BreakAttributes();
403 xout.Attribute("buildConfiguration", configuration);
404 xout.Attribute("revealArchiveInOrganizer", "YES");
408 void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
409 const cmXCodeObject* xcObj,
410 const std::string& container)
412 xout.StartElement("BuildableReference");
413 xout.BreakAttributes();
414 xout.Attribute("BuildableIdentifier", "primary");
415 xout.Attribute("BlueprintIdentifier", xcObj->GetId());
416 std::string const noConfig; // FIXME: What config to use here?
417 xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName(noConfig));
418 xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
419 xout.Attribute("ReferencedContainer", "container:" + container);
423 void cmXCodeScheme::WriteCustomWorkingDirectory(
424 cmXMLWriter& xout, const std::string& configuration)
426 std::string const& propertyValue =
427 this->Target->GetTarget()->GetSafeProperty(
428 "XCODE_SCHEME_WORKING_DIRECTORY");
429 if (propertyValue.empty()) {
430 xout.Attribute("useCustomWorkingDirectory", "NO");
432 xout.Attribute("useCustomWorkingDirectory", "YES");
434 auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
435 propertyValue, this->LocalGenerator, configuration);
436 xout.Attribute("customWorkingDirectory", customWorkingDirectory);
440 std::string cmXCodeScheme::WriteVersionString()
442 std::ostringstream v;
443 v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
447 std::string cmXCodeScheme::FindConfiguration(const std::string& name)
449 // Try to find the desired configuration by name,
450 // and if it's not found return first from the list
452 if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
453 return this->ConfigList[0];
459 bool cmXCodeScheme::IsTestable() const
461 return !this->Tests.empty() || IsExecutable(this->Target);
464 bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
466 cmGeneratorTarget* gt = target->GetTarget();
468 cmSystemTools::Error("Error no target on xobject\n");
472 return gt->GetType() == cmStateEnums::EXECUTABLE;