adc500a0764419592b640db92739779f17f42f25
[platform/upstream/cmake.git] / Source / cmXCodeScheme.cxx
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
5 #include <iomanip>
6 #include <sstream>
7 #include <utility>
8
9 #include <cmext/algorithm>
10
11 #include "cmsys/String.h"
12
13 #include "cmGeneratedFileStream.h"
14 #include "cmGeneratorExpression.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmStateTypes.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
19 #include "cmValue.h"
20 #include "cmXCodeObject.h"
21 #include "cmXMLWriter.h"
22
23 class cmLocalGenerator;
24
25 cmXCodeScheme::cmXCodeScheme(cmLocalGenerator* lg, cmXCodeObject* xcObj,
26                              TestObjects tests,
27                              const std::vector<std::string>& configList,
28                              unsigned int xcVersion)
29   : LocalGenerator(lg)
30   , Target(xcObj)
31   , Tests(std::move(tests))
32   , TargetName(xcObj->GetTarget()->GetName())
33   , ConfigList(configList)
34   , XcodeVersion(xcVersion)
35 {
36 }
37
38 void cmXCodeScheme::WriteXCodeSharedScheme(const std::string& xcProjDir,
39                                            const std::string& container)
40 {
41   // Create shared scheme sub-directory tree
42   //
43   std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes");
44   cmSystemTools::MakeDirectory(xcodeSchemeDir);
45
46   std::string xcodeSchemeFile =
47     cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme");
48
49   cmGeneratedFileStream fout(xcodeSchemeFile);
50   fout.SetCopyIfDifferent(true);
51   if (!fout) {
52     return;
53   }
54
55   WriteXCodeXCScheme(fout, container);
56 }
57
58 void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout,
59                                        const std::string& container)
60 {
61   cmXMLWriter xout(fout);
62   xout.SetIndentationElement(std::string(3, ' '));
63   xout.StartDocument();
64
65   xout.StartElement("Scheme");
66   xout.BreakAttributes();
67   xout.Attribute("LastUpgradeVersion", WriteVersionString());
68   xout.Attribute("version", "1.3");
69
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"));
76
77   xout.EndElement();
78 }
79
80 void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
81                                      const std::string& container)
82 {
83   xout.StartElement("BuildAction");
84   xout.BreakAttributes();
85   xout.Attribute("parallelizeBuildables", "YES");
86   xout.Attribute("buildImplicitDependencies", "YES");
87
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");
96
97   WriteBuildableReference(xout, this->Target, container);
98
99   xout.EndElement(); // BuildActionEntry
100   xout.EndElement(); // BuildActionEntries
101   xout.EndElement(); // BuildAction
102 }
103
104 void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
105                                     const std::string& configuration,
106                                     const std::string& container)
107 {
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");
116
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
124   }
125   xout.EndElement();
126
127   if (IsTestable()) {
128     xout.StartElement("MacroExpansion");
129     WriteBuildableReference(xout, this->Target, container);
130     xout.EndElement(); // MacroExpansion
131   }
132
133   xout.StartElement("AdditionalOptions");
134   xout.EndElement();
135
136   xout.EndElement(); // TestAction
137 }
138
139 void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
140                                       const std::string& configuration,
141                                       const std::string& container)
142 {
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);
152
153   xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
154   WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
155                                     "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
156                                     true);
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) {
163       value = "1";
164     } else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
165       value = "3";
166     }
167     xout.Attribute("enableGPUFrameCaptureMode", value);
168   }
169
170   // Diagnostics tab begin
171
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");
179
180   bool useThreadSanitizer = false;
181   if (!useAddressSanitizer) {
182     useThreadSanitizer = WriteLaunchActionAttribute(
183       xout, "enableThreadSanitizer",
184       "XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
185                                         // enableAddressSanitizer=YES
186   }
187
188   WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
189                              "XCODE_SCHEME_THREAD_SANITIZER_STOP");
190
191   WriteLaunchActionAttribute(xout, "enableUBSanitizer",
192                              "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
193   WriteLaunchActionAttribute(
194     xout, "stopOnEveryUBSanitizerIssue",
195     "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
196
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");
202
203   if (this->Target->GetTarget()->GetPropertyAsBool(
204         "XCODE_SCHEME_DEBUG_AS_ROOT")) {
205     xout.Attribute("debugAsWhichUser", "root");
206   }
207
208   // Diagnostics tab end
209
210   if (IsExecutable(this->Target)) {
211     xout.StartElement("BuildableProductRunnable");
212     xout.BreakAttributes();
213     xout.Attribute("runnableDebuggingMode", "0");
214
215   } else {
216     xout.StartElement("MacroExpansion");
217   }
218
219   WriteBuildableReference(xout, this->Target, container);
220
221   xout.EndElement(); // MacroExpansion
222
223   // Info tab begin
224
225   if (cmValue exe =
226         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
227
228     xout.StartElement("PathRunnable");
229     xout.BreakAttributes();
230
231     xout.Attribute("runnableDebuggingMode", "0");
232     xout.Attribute("FilePath", *exe);
233
234     xout.EndElement(); // PathRunnable
235   }
236
237   // Info tab end
238
239   // Arguments tab begin
240
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");
246
247       for (auto const& argument : arguments) {
248         xout.StartElement("CommandLineArgument");
249         xout.BreakAttributes();
250
251         xout.Attribute("argument", argument);
252         xout.Attribute("isEnabled", "YES");
253
254         xout.EndElement(); // CommandLineArgument
255       }
256
257       xout.EndElement(); // CommandLineArguments
258     }
259   }
260
261   if (cmValue envList =
262         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
263     std::vector<std::string> envs = cmExpandedList(*envList);
264     if (!envs.empty()) {
265       xout.StartElement("EnvironmentVariables");
266
267       for (auto env : envs) {
268
269         xout.StartElement("EnvironmentVariable");
270         xout.BreakAttributes();
271
272         std::string envValue;
273         const auto p = env.find_first_of('=');
274         if (p != std::string::npos) {
275           envValue = env.substr(p + 1);
276           env.resize(p);
277         }
278
279         xout.Attribute("key", env);
280         xout.Attribute("value", envValue);
281         xout.Attribute("isEnabled", "YES");
282
283         xout.EndElement(); // EnvironmentVariable
284       }
285
286       xout.EndElement(); // EnvironmentVariables
287     }
288   }
289
290   // Arguments tab end
291
292   xout.StartElement("AdditionalOptions");
293
294   if (!useThreadSanitizer) {
295     WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
296                                       "XCODE_SCHEME_MALLOC_SCRIBBLE");
297   }
298
299   if (!useThreadSanitizer && !useAddressSanitizer) {
300     WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
301                                       "XCODE_SCHEME_MALLOC_GUARD_EDGES");
302   }
303
304   if (!useThreadSanitizer && !useAddressSanitizer) {
305     WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
306                                       "/usr/lib/libgmalloc.dylib",
307                                       "XCODE_SCHEME_GUARD_MALLOC");
308   }
309
310   WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
311                                     "XCODE_SCHEME_ZOMBIE_OBJECTS");
312
313   if (!useThreadSanitizer && !useAddressSanitizer) {
314     WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
315                                       "XCODE_SCHEME_MALLOC_STACK");
316   }
317
318   WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
319                                     "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
320
321   WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
322                                     "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
323
324   xout.EndElement();
325
326   xout.EndElement(); // LaunchAction
327 }
328
329 bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
330                                                const std::string& attrName,
331                                                const std::string& varName)
332 {
333   if (Target->GetTarget()->GetPropertyAsBool(varName)) {
334     xout.Attribute(attrName.c_str(), "YES");
335     return true;
336   }
337   return false;
338 }
339
340 bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
341   cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
342   bool defaultValue)
343 {
344   cmValue property = Target->GetTarget()->GetProperty(varName);
345   bool isOn = (!property && defaultValue) || cmIsOn(property);
346
347   if (isOn) {
348     xout.Attribute(attrName.c_str(), "YES");
349   } else {
350     xout.Attribute(attrName.c_str(), "NO");
351   }
352   return isOn;
353 }
354
355 bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
356   cmXMLWriter& xout, const std::string& key, const std::string& value,
357   const std::string& varName)
358 {
359   if (Target->GetTarget()->GetPropertyAsBool(varName)) {
360     xout.StartElement("AdditionalOption");
361     xout.BreakAttributes();
362
363     xout.Attribute("key", key);
364     xout.Attribute("value", value);
365     xout.Attribute("isEnabled", "YES");
366
367     xout.EndElement(); // AdditionalOption
368
369     return true;
370   }
371   return false;
372 }
373
374 void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
375                                        const std::string& configuration)
376 {
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",
385                                     true);
386   xout.EndElement();
387 }
388
389 void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
390                                        const std::string& configuration)
391 {
392   xout.StartElement("AnalyzeAction");
393   xout.BreakAttributes();
394   xout.Attribute("buildConfiguration", configuration);
395   xout.EndElement();
396 }
397
398 void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
399                                        const std::string& configuration)
400 {
401   xout.StartElement("ArchiveAction");
402   xout.BreakAttributes();
403   xout.Attribute("buildConfiguration", configuration);
404   xout.Attribute("revealArchiveInOrganizer", "YES");
405   xout.EndElement();
406 }
407
408 void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
409                                             const cmXCodeObject* xcObj,
410                                             const std::string& container)
411 {
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);
420   xout.EndElement();
421 }
422
423 void cmXCodeScheme::WriteCustomWorkingDirectory(
424   cmXMLWriter& xout, const std::string& configuration)
425 {
426   std::string const& propertyValue =
427     this->Target->GetTarget()->GetSafeProperty(
428       "XCODE_SCHEME_WORKING_DIRECTORY");
429   if (propertyValue.empty()) {
430     xout.Attribute("useCustomWorkingDirectory", "NO");
431   } else {
432     xout.Attribute("useCustomWorkingDirectory", "YES");
433
434     auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
435       propertyValue, this->LocalGenerator, configuration);
436     xout.Attribute("customWorkingDirectory", customWorkingDirectory);
437   }
438 }
439
440 std::string cmXCodeScheme::WriteVersionString()
441 {
442   std::ostringstream v;
443   v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
444   return v.str();
445 }
446
447 std::string cmXCodeScheme::FindConfiguration(const std::string& name)
448 {
449   // Try to find the desired configuration by name,
450   // and if it's not found return first from the list
451   //
452   if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
453     return this->ConfigList[0];
454   }
455
456   return name;
457 }
458
459 bool cmXCodeScheme::IsTestable() const
460 {
461   return !this->Tests.empty() || IsExecutable(this->Target);
462 }
463
464 bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
465 {
466   cmGeneratorTarget* gt = target->GetTarget();
467   if (!gt) {
468     cmSystemTools::Error("Error no target on xobject\n");
469     return false;
470   }
471
472   return gt->GetType() == cmStateEnums::EXECUTABLE;
473 }