resolve cyclic dependency with zstd
[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   cmValue propDftCfg =
71     Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_CONFIGURATION");
72   std::string launchConfiguration =
73     !propDftCfg.IsEmpty() ? *propDftCfg : "Debug";
74
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"));
81
82   xout.EndElement();
83 }
84
85 void cmXCodeScheme::WriteBuildAction(cmXMLWriter& xout,
86                                      const std::string& container)
87 {
88   xout.StartElement("BuildAction");
89   xout.BreakAttributes();
90   xout.Attribute("parallelizeBuildables", "YES");
91   xout.Attribute("buildImplicitDependencies", "YES");
92
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");
101
102   WriteBuildableReference(xout, this->Target, container);
103
104   xout.EndElement(); // BuildActionEntry
105   xout.EndElement(); // BuildActionEntries
106   xout.EndElement(); // BuildAction
107 }
108
109 void cmXCodeScheme::WriteTestAction(cmXMLWriter& xout,
110                                     const std::string& configuration,
111                                     const std::string& container)
112 {
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");
121
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
129   }
130   xout.EndElement();
131
132   if (IsTestable()) {
133     xout.StartElement("MacroExpansion");
134     WriteBuildableReference(xout, this->Target, container);
135     xout.EndElement(); // MacroExpansion
136   }
137
138   xout.StartElement("AdditionalOptions");
139   xout.EndElement();
140
141   xout.EndElement(); // TestAction
142 }
143
144 void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
145                                       const std::string& configuration,
146                                       const std::string& container)
147 {
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");
155   {
156     cmValue launchMode =
157       this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE");
158     std::string value = "0"; // == 'AUTO'
159     if (launchMode && *launchMode == "WAIT") {
160       value = "1";
161     }
162     xout.Attribute("launchStyle", value);
163   }
164   WriteCustomWorkingDirectory(xout, configuration);
165
166   xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
167   WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
168                                     "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
169                                     true);
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) {
176       value = "1";
177     } else if (cmsysString_strcasecmp(value.c_str(), "Disabled") == 0) {
178       value = "3";
179     }
180     xout.Attribute("enableGPUFrameCaptureMode", value);
181   }
182
183   // Diagnostics tab begin
184
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");
192
193   bool useThreadSanitizer = false;
194   if (!useAddressSanitizer) {
195     useThreadSanitizer = WriteLaunchActionAttribute(
196       xout, "enableThreadSanitizer",
197       "XCODE_SCHEME_THREAD_SANITIZER"); // not allowed with
198                                         // enableAddressSanitizer=YES
199   }
200
201   WriteLaunchActionAttribute(xout, "stopOnEveryThreadSanitizerIssue",
202                              "XCODE_SCHEME_THREAD_SANITIZER_STOP");
203
204   WriteLaunchActionAttribute(xout, "enableUBSanitizer",
205                              "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
206
207   if (cmValue value = this->Target->GetTarget()->GetProperty(
208         "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION")) {
209     if (value.IsOff()) {
210       xout.Attribute("enableGPUValidationMode",
211                      "1"); // unset means YES, "1" means NO
212     }
213   }
214
215   if (cmValue value = this->Target->GetTarget()->GetProperty(
216         "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION")) {
217     if (value.IsOn()) {
218       xout.Attribute("enableGPUShaderValidationMode",
219                      "2"); // unset means NO, "2" means YES
220     }
221   }
222
223   WriteLaunchActionAttribute(
224     xout, "stopOnEveryUBSanitizerIssue",
225     "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
226
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");
232
233   if (this->Target->GetTarget()->GetPropertyAsBool(
234         "XCODE_SCHEME_DEBUG_AS_ROOT")) {
235     xout.Attribute("debugAsWhichUser", "root");
236   }
237
238   // Diagnostics tab end
239
240   if (IsExecutable(this->Target)) {
241     xout.StartElement("BuildableProductRunnable");
242     xout.BreakAttributes();
243     xout.Attribute("runnableDebuggingMode", "0");
244
245   } else {
246     xout.StartElement("MacroExpansion");
247   }
248
249   WriteBuildableReference(xout, this->Target, container);
250
251   xout.EndElement(); // MacroExpansion
252
253   // Info tab begin
254
255   if (cmValue exe =
256         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
257
258     xout.StartElement("PathRunnable");
259     xout.BreakAttributes();
260
261     xout.Attribute("runnableDebuggingMode", "0");
262     xout.Attribute("FilePath", *exe);
263
264     xout.EndElement(); // PathRunnable
265   }
266
267   // Info tab end
268
269   // Arguments tab begin
270
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");
276
277       for (auto const& argument : arguments) {
278         xout.StartElement("CommandLineArgument");
279         xout.BreakAttributes();
280
281         xout.Attribute("argument", argument);
282         xout.Attribute("isEnabled", "YES");
283
284         xout.EndElement(); // CommandLineArgument
285       }
286
287       xout.EndElement(); // CommandLineArguments
288     }
289   }
290
291   if (cmValue envList =
292         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
293     std::vector<std::string> envs = cmExpandedList(*envList);
294     if (!envs.empty()) {
295       xout.StartElement("EnvironmentVariables");
296
297       for (auto env : envs) {
298
299         xout.StartElement("EnvironmentVariable");
300         xout.BreakAttributes();
301
302         std::string envValue;
303         const auto p = env.find_first_of('=');
304         if (p != std::string::npos) {
305           envValue = env.substr(p + 1);
306           env.resize(p);
307         }
308
309         xout.Attribute("key", env);
310         xout.Attribute("value", envValue);
311         xout.Attribute("isEnabled", "YES");
312
313         xout.EndElement(); // EnvironmentVariable
314       }
315
316       xout.EndElement(); // EnvironmentVariables
317     }
318   }
319
320   // Arguments tab end
321
322   xout.StartElement("AdditionalOptions");
323
324   if (!useThreadSanitizer) {
325     WriteLaunchActionAdditionalOption(xout, "MallocScribble", "",
326                                       "XCODE_SCHEME_MALLOC_SCRIBBLE");
327   }
328
329   if (!useThreadSanitizer && !useAddressSanitizer) {
330     WriteLaunchActionAdditionalOption(xout, "MallocGuardEdges", "",
331                                       "XCODE_SCHEME_MALLOC_GUARD_EDGES");
332   }
333
334   if (!useThreadSanitizer && !useAddressSanitizer) {
335     WriteLaunchActionAdditionalOption(xout, "DYLD_INSERT_LIBRARIES",
336                                       "/usr/lib/libgmalloc.dylib",
337                                       "XCODE_SCHEME_GUARD_MALLOC");
338   }
339
340   WriteLaunchActionAdditionalOption(xout, "NSZombieEnabled", "YES",
341                                     "XCODE_SCHEME_ZOMBIE_OBJECTS");
342
343   if (!useThreadSanitizer && !useAddressSanitizer) {
344     WriteLaunchActionAdditionalOption(xout, "MallocStackLogging", "",
345                                       "XCODE_SCHEME_MALLOC_STACK");
346   }
347
348   WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_APIS", "",
349                                     "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
350
351   WriteLaunchActionAdditionalOption(xout, "DYLD_PRINT_LIBRARIES", "",
352                                     "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
353
354   xout.EndElement();
355
356   xout.EndElement(); // LaunchAction
357 }
358
359 bool cmXCodeScheme::WriteLaunchActionAttribute(cmXMLWriter& xout,
360                                                const std::string& attrName,
361                                                const std::string& varName)
362 {
363   if (Target->GetTarget()->GetPropertyAsBool(varName)) {
364     xout.Attribute(attrName.c_str(), "YES");
365     return true;
366   }
367   return false;
368 }
369
370 bool cmXCodeScheme::WriteLaunchActionBooleanAttribute(
371   cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
372   bool defaultValue)
373 {
374   cmValue property = Target->GetTarget()->GetProperty(varName);
375   bool isOn = (!property && defaultValue) || cmIsOn(property);
376
377   if (isOn) {
378     xout.Attribute(attrName.c_str(), "YES");
379   } else {
380     xout.Attribute(attrName.c_str(), "NO");
381   }
382   return isOn;
383 }
384
385 bool cmXCodeScheme::WriteLaunchActionAdditionalOption(
386   cmXMLWriter& xout, const std::string& key, const std::string& value,
387   const std::string& varName)
388 {
389   if (Target->GetTarget()->GetPropertyAsBool(varName)) {
390     xout.StartElement("AdditionalOption");
391     xout.BreakAttributes();
392
393     xout.Attribute("key", key);
394     xout.Attribute("value", value);
395     xout.Attribute("isEnabled", "YES");
396
397     xout.EndElement(); // AdditionalOption
398
399     return true;
400   }
401   return false;
402 }
403
404 void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
405                                        const std::string& configuration)
406 {
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",
415                                     true);
416   xout.EndElement();
417 }
418
419 void cmXCodeScheme::WriteAnalyzeAction(cmXMLWriter& xout,
420                                        const std::string& configuration)
421 {
422   xout.StartElement("AnalyzeAction");
423   xout.BreakAttributes();
424   xout.Attribute("buildConfiguration", configuration);
425   xout.EndElement();
426 }
427
428 void cmXCodeScheme::WriteArchiveAction(cmXMLWriter& xout,
429                                        const std::string& configuration)
430 {
431   xout.StartElement("ArchiveAction");
432   xout.BreakAttributes();
433   xout.Attribute("buildConfiguration", configuration);
434   xout.Attribute("revealArchiveInOrganizer", "YES");
435   xout.EndElement();
436 }
437
438 void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
439                                             const cmXCodeObject* xcObj,
440                                             const std::string& container)
441 {
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);
450   xout.EndElement();
451 }
452
453 void cmXCodeScheme::WriteCustomWorkingDirectory(
454   cmXMLWriter& xout, const std::string& configuration)
455 {
456   std::string const& propertyValue =
457     this->Target->GetTarget()->GetSafeProperty(
458       "XCODE_SCHEME_WORKING_DIRECTORY");
459   if (propertyValue.empty()) {
460     xout.Attribute("useCustomWorkingDirectory", "NO");
461   } else {
462     xout.Attribute("useCustomWorkingDirectory", "YES");
463
464     auto customWorkingDirectory = cmGeneratorExpression::Evaluate(
465       propertyValue, this->LocalGenerator, configuration);
466     xout.Attribute("customWorkingDirectory", customWorkingDirectory);
467   }
468 }
469
470 std::string cmXCodeScheme::WriteVersionString()
471 {
472   std::ostringstream v;
473   v << std::setfill('0') << std::setw(4) << this->XcodeVersion * 10;
474   return v.str();
475 }
476
477 std::string cmXCodeScheme::FindConfiguration(const std::string& name)
478 {
479   // Try to find the desired configuration by name,
480   // and if it's not found return first from the list
481   //
482   if (!cm::contains(this->ConfigList, name) && !this->ConfigList.empty()) {
483     return this->ConfigList[0];
484   }
485
486   return name;
487 }
488
489 bool cmXCodeScheme::IsTestable() const
490 {
491   return !this->Tests.empty() || IsExecutable(this->Target);
492 }
493
494 bool cmXCodeScheme::IsExecutable(const cmXCodeObject* target)
495 {
496   cmGeneratorTarget* gt = target->GetTarget();
497   if (!gt) {
498     cmSystemTools::Error("Error no target on xobject\n");
499     return false;
500   }
501
502   return gt->GetType() == cmStateEnums::EXECUTABLE;
503 }