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 "cmGlobalXCodeGenerator.h"
4 
5 #include <algorithm>
6 #include <cassert>
7 #include <cstdio>
8 #include <cstring>
9 #include <iomanip>
10 #include <sstream>
11 #include <unordered_set>
12 #include <utility>
13 
14 #include <cm/memory>
15 #include <cmext/algorithm>
16 #include <cmext/string_view>
17 
18 #include "cmsys/RegularExpression.hxx"
19 
20 #include "cmCMakePath.h"
21 #include "cmComputeLinkInformation.h"
22 #include "cmCryptoHash.h"
23 #include "cmCustomCommand.h"
24 #include "cmCustomCommandGenerator.h"
25 #include "cmCustomCommandLines.h"
26 #include "cmDocumentationEntry.h"
27 #include "cmGeneratedFileStream.h"
28 #include "cmGeneratorExpression.h"
29 #include "cmGeneratorTarget.h"
30 #include "cmGlobalGeneratorFactory.h"
31 #include "cmLocalGenerator.h"
32 #include "cmLocalXCodeGenerator.h"
33 #include "cmMakefile.h"
34 #include "cmMessageType.h"
35 #include "cmOutputConverter.h"
36 #include "cmSourceFile.h"
37 #include "cmSourceGroup.h"
38 #include "cmState.h"
39 #include "cmStateTypes.h"
40 #include "cmStringAlgorithms.h"
41 #include "cmSystemTools.h"
42 #include "cmTarget.h"
43 #include "cmXCode21Object.h"
44 #include "cmXCodeObject.h"
45 #include "cmXCodeScheme.h"
46 #include "cmake.h"
47 
48 struct cmLinkImplementation;
49 
50 #if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
51 #  include <CoreFoundation/CoreFoundation.h>
52 #  if !TARGET_OS_IPHONE
53 #    define HAVE_APPLICATION_SERVICES
54 #    include <ApplicationServices/ApplicationServices.h>
55 #  endif
56 #endif
57 
58 #if !defined(CMAKE_BOOTSTRAP)
59 #  include "cmXMLParser.h"
60 
61 // parse the xml file storing the installed version of Xcode on
62 // the machine
63 class cmXcodeVersionParser : public cmXMLParser
64 {
65 public:
cmXcodeVersionParser()66   cmXcodeVersionParser()
67     : Version("1.5")
68   {
69   }
StartElement(const std::string &,const char **)70   void StartElement(const std::string&, const char**) override
71   {
72     this->Data = "";
73   }
EndElement(const std::string & name)74   void EndElement(const std::string& name) override
75   {
76     if (name == "key") {
77       this->Key = this->Data;
78     } else if (name == "string") {
79       if (this->Key == "CFBundleShortVersionString") {
80         this->Version = this->Data;
81       }
82     }
83   }
CharacterDataHandler(const char * data,int length)84   void CharacterDataHandler(const char* data, int length) override
85   {
86     this->Data.append(data, length);
87   }
88   std::string Version;
89   std::string Key;
90   std::string Data;
91 };
92 #endif
93 
94 // Builds either an object list or a space-separated string from the
95 // given inputs.
96 class cmGlobalXCodeGenerator::BuildObjectListOrString
97 {
98   cmGlobalXCodeGenerator* Generator;
99   cmXCodeObject* Group;
100   bool Empty;
101   std::string String;
102 
103 public:
BuildObjectListOrString(cmGlobalXCodeGenerator * gen,bool buildObjectList)104   BuildObjectListOrString(cmGlobalXCodeGenerator* gen, bool buildObjectList)
105     : Generator(gen)
106     , Group(nullptr)
107     , Empty(true)
108   {
109     if (buildObjectList) {
110       this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
111     }
112   }
113 
IsEmpty() const114   bool IsEmpty() const { return this->Empty; }
115 
Add(const std::string & newString)116   void Add(const std::string& newString)
117   {
118     this->Empty = false;
119 
120     if (this->Group) {
121       this->Group->AddObject(this->Generator->CreateString(newString));
122     } else {
123       this->String += newString;
124       this->String += ' ';
125     }
126   }
127 
GetString() const128   const std::string& GetString() const { return this->String; }
129 
CreateList()130   cmXCodeObject* CreateList()
131   {
132     if (this->Group) {
133       return this->Group;
134     }
135     return this->Generator->CreateString(this->String);
136   }
137 };
138 
139 class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
140 {
141 public:
142   std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
143     const std::string& name, bool allowArch, cmake* cm) const override;
144 
GetDocumentation(cmDocumentationEntry & entry) const145   void GetDocumentation(cmDocumentationEntry& entry) const override
146   {
147     cmGlobalXCodeGenerator::GetDocumentation(entry);
148   }
149 
GetGeneratorNames() const150   std::vector<std::string> GetGeneratorNames() const override
151   {
152     std::vector<std::string> names;
153     names.push_back(cmGlobalXCodeGenerator::GetActualName());
154     return names;
155   }
156 
GetGeneratorNamesWithPlatform() const157   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
158   {
159     return std::vector<std::string>();
160   }
161 
SupportsToolset() const162   bool SupportsToolset() const override { return true; }
SupportsPlatform() const163   bool SupportsPlatform() const override { return false; }
164 
GetKnownPlatforms() const165   std::vector<std::string> GetKnownPlatforms() const override
166   {
167     return std::vector<std::string>();
168   }
169 
GetDefaultPlatformName() const170   std::string GetDefaultPlatformName() const override { return std::string(); }
171 };
172 
cmGlobalXCodeGenerator(cmake * cm,std::string const & version_string,unsigned int version_number)173 cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(
174   cmake* cm, std::string const& version_string, unsigned int version_number)
175   : cmGlobalGenerator(cm)
176 {
177   this->VersionString = version_string;
178   this->XcodeVersion = version_number;
179   if (this->XcodeVersion >= 120) {
180     this->XcodeBuildSystem = BuildSystem::Twelve;
181   } else {
182     this->XcodeBuildSystem = BuildSystem::One;
183   }
184 
185   this->RootObject = nullptr;
186   this->MainGroupChildren = nullptr;
187   this->FrameworkGroup = nullptr;
188   this->CurrentMakefile = nullptr;
189   this->CurrentLocalGenerator = nullptr;
190   this->XcodeBuildCommandInitialized = false;
191 
192   this->ObjectDirArchDefault = "$(CURRENT_ARCH)";
193   this->ObjectDirArch = this->ObjectDirArchDefault;
194 
195   cm->GetState()->SetIsGeneratorMultiConfig(true);
196 }
197 
NewFactory()198 std::unique_ptr<cmGlobalGeneratorFactory> cmGlobalXCodeGenerator::NewFactory()
199 {
200   return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory);
201 }
202 
203 std::unique_ptr<cmGlobalGenerator>
CreateGlobalGenerator(const std::string & name,bool,cmake * cm) const204 cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(const std::string& name,
205                                                        bool /*allowArch*/,
206                                                        cmake* cm) const
207 {
208   if (name != GetActualName()) {
209     return std::unique_ptr<cmGlobalGenerator>();
210   }
211 #if !defined(CMAKE_BOOTSTRAP)
212   cmXcodeVersionParser parser;
213   std::string versionFile;
214   {
215     std::string out;
216     bool commandResult = cmSystemTools::RunSingleCommand(
217       "xcode-select --print-path", &out, nullptr, nullptr, nullptr,
218       cmSystemTools::OUTPUT_NONE);
219     if (commandResult) {
220       std::string::size_type pos = out.find(".app/");
221       if (pos != std::string::npos) {
222         versionFile = out.substr(0, pos + 5) + "Contents/version.plist";
223       }
224     }
225   }
226   if (!versionFile.empty() && cmSystemTools::FileExists(versionFile)) {
227     parser.ParseFile(versionFile.c_str());
228   } else if (cmSystemTools::FileExists(
229                "/Applications/Xcode.app/Contents/version.plist")) {
230     parser.ParseFile("/Applications/Xcode.app/Contents/version.plist");
231   } else {
232     parser.ParseFile(
233       "/Developer/Applications/Xcode.app/Contents/version.plist");
234   }
235   std::string const& version_string = parser.Version;
236 
237   // Compute an integer form of the version number.
238   unsigned int v[2] = { 0, 0 };
239   sscanf(version_string.c_str(), "%u.%u", &v[0], &v[1]);
240   unsigned int version_number = 10 * v[0] + v[1];
241 
242   if (version_number < 50) {
243     cm->IssueMessage(MessageType::FATAL_ERROR,
244                      "Xcode " + version_string + " not supported.");
245     return std::unique_ptr<cmGlobalGenerator>();
246   }
247 
248   return std::unique_ptr<cmGlobalGenerator>(
249     cm::make_unique<cmGlobalXCodeGenerator>(cm, version_string,
250                                             version_number));
251 #else
252   std::cerr << "CMake should be built with cmake to use Xcode, "
253                "default to Xcode 1.5\n";
254   return std::unique_ptr<cmGlobalGenerator>(
255     cm::make_unique<cmGlobalXCodeGenerator>(cm));
256 #endif
257 }
258 
FindMakeProgram(cmMakefile * mf)259 bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf)
260 {
261   // The Xcode generator knows how to lookup its build tool
262   // directly instead of needing a helper module to do it, so we
263   // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
264   if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
265     mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetXcodeBuildCommand());
266   }
267   return true;
268 }
269 
GetXcodeBuildCommand()270 std::string const& cmGlobalXCodeGenerator::GetXcodeBuildCommand()
271 {
272   if (!this->XcodeBuildCommandInitialized) {
273     this->XcodeBuildCommandInitialized = true;
274     this->XcodeBuildCommand = this->FindXcodeBuildCommand();
275   }
276   return this->XcodeBuildCommand;
277 }
278 
FindXcodeBuildCommand()279 std::string cmGlobalXCodeGenerator::FindXcodeBuildCommand()
280 {
281   std::string makeProgram = cmSystemTools::FindProgram("xcodebuild");
282   if (makeProgram.empty()) {
283     makeProgram = "xcodebuild";
284   }
285   return makeProgram;
286 }
287 
SetSystemName(std::string const & s,cmMakefile * mf)288 bool cmGlobalXCodeGenerator::SetSystemName(std::string const& s,
289                                            cmMakefile* mf)
290 {
291   this->SystemName = s;
292   return this->cmGlobalGenerator::SetSystemName(s, mf);
293 }
294 
295 namespace {
cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b)296 cm::string_view cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b)
297 {
298   switch (b) {
299     case cmGlobalXCodeGenerator::BuildSystem::One:
300       return "1"_s;
301     case cmGlobalXCodeGenerator::BuildSystem::Twelve:
302       return "12"_s;
303   }
304   return {};
305 }
306 }
307 
SetGeneratorToolset(std::string const & ts,bool build,cmMakefile * mf)308 bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts,
309                                                  bool build, cmMakefile* mf)
310 {
311   if (!this->ParseGeneratorToolset(ts, mf)) {
312     return false;
313   }
314   if (build) {
315     return true;
316   }
317   if (!this->GeneratorToolset.empty()) {
318     mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset);
319   }
320   mf->AddDefinition("CMAKE_XCODE_BUILD_SYSTEM",
321                     cmXcodeBuildSystemString(this->XcodeBuildSystem));
322   return true;
323 }
324 
ParseGeneratorToolset(std::string const & ts,cmMakefile * mf)325 bool cmGlobalXCodeGenerator::ParseGeneratorToolset(std::string const& ts,
326                                                    cmMakefile* mf)
327 {
328   std::vector<std::string> const fields = cmTokenize(ts, ",");
329   auto fi = fields.cbegin();
330   if (fi == fields.cend()) {
331     return true;
332   }
333 
334   // The first field may be the Xcode GCC_VERSION.
335   if (fi->find('=') == fi->npos) {
336     this->GeneratorToolset = *fi;
337     ++fi;
338   }
339 
340   std::unordered_set<std::string> handled;
341 
342   // The rest of the fields must be key=value pairs.
343   for (; fi != fields.cend(); ++fi) {
344     std::string::size_type pos = fi->find('=');
345     if (pos == fi->npos) {
346       /* clang-format off */
347       std::string const& e = cmStrCat(
348         "Generator\n"
349         "  ", this->GetName(), "\n"
350         "given toolset specification\n"
351         "  ", ts, "\n"
352         "that contains a field after the first ',' with no '='."
353         );
354       /* clang-format on */
355       mf->IssueMessage(MessageType::FATAL_ERROR, e);
356       return false;
357     }
358     std::string const key = fi->substr(0, pos);
359     std::string const value = fi->substr(pos + 1);
360     if (!handled.insert(key).second) {
361       /* clang-format off */
362       std::string const& e = cmStrCat(
363         "Generator\n"
364         "  ", this->GetName(), "\n"
365         "given toolset specification\n"
366         "  ", ts, "\n"
367         "that contains duplicate field key '", key, "'."
368         );
369       /* clang-format on */
370       mf->IssueMessage(MessageType::FATAL_ERROR, e);
371       return false;
372     }
373     if (!this->ProcessGeneratorToolsetField(key, value, mf)) {
374       return false;
375     }
376   }
377 
378   return true;
379 }
380 
ProcessGeneratorToolsetField(std::string const & key,std::string const & value,cmMakefile * mf)381 bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField(
382   std::string const& key, std::string const& value, cmMakefile* mf)
383 {
384   if (key == "buildsystem") {
385     if (value == "1"_s) {
386       this->XcodeBuildSystem = BuildSystem::One;
387     } else if (value == "12"_s) {
388       this->XcodeBuildSystem = BuildSystem::Twelve;
389     } else {
390       /* clang-format off */
391       std::string const& e = cmStrCat(
392         "Generator\n"
393         "  ",  this->GetName(), "\n"
394         "toolset specification field\n"
395         "  buildsystem=", value, "\n"
396         "value is unknown.  It must be '1' or '12'."
397         );
398       /* clang-format on */
399       mf->IssueMessage(MessageType::FATAL_ERROR, e);
400       return false;
401     }
402     if (this->XcodeBuildSystem == BuildSystem::Twelve &&
403         this->XcodeVersion < 120) {
404       /* clang-format off */
405       std::string const& e = cmStrCat(
406         "Generator\n"
407         "  ",  this->GetName(), "\n"
408         "toolset specification field\n"
409         "  buildsystem=", value, "\n"
410         "is not allowed with Xcode ", this->VersionString, '.'
411         );
412       /* clang-format on */
413       mf->IssueMessage(MessageType::FATAL_ERROR, e);
414       return false;
415     }
416     return true;
417   }
418   /* clang-format off */
419   std::string const& e = cmStrCat(
420     "Generator\n"
421     "  ", this->GetName(), "\n"
422     "given toolset specification that contains invalid field '", key, "'."
423     );
424   /* clang-format on */
425   mf->IssueMessage(MessageType::FATAL_ERROR, e);
426   return false;
427 }
428 
EnableLanguage(std::vector<std::string> const & lang,cmMakefile * mf,bool optional)429 void cmGlobalXCodeGenerator::EnableLanguage(
430   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
431 {
432   mf->AddDefinition("XCODE", "1");
433   mf->AddDefinition("XCODE_VERSION", this->VersionString);
434   mf->InitCMAKE_CONFIGURATION_TYPES("Debug;Release;MinSizeRel;RelWithDebInfo");
435   mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
436   this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
437   this->ComputeArchitectures(mf);
438 }
439 
Open(const std::string & bindir,const std::string & projectName,bool dryRun)440 bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
441                                   const std::string& projectName, bool dryRun)
442 {
443   bool ret = false;
444 
445 #ifdef HAVE_APPLICATION_SERVICES
446   std::string url = bindir + "/" + projectName + ".xcodeproj";
447 
448   if (dryRun) {
449     return cmSystemTools::FileExists(url, false);
450   }
451 
452   CFStringRef cfStr = CFStringCreateWithCString(
453     kCFAllocatorDefault, url.c_str(), kCFStringEncodingUTF8);
454   if (cfStr) {
455     CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfStr,
456                                                    kCFURLPOSIXPathStyle, true);
457     if (cfUrl) {
458       OSStatus err = LSOpenCFURLRef(cfUrl, nullptr);
459       ret = err == noErr;
460       CFRelease(cfUrl);
461     }
462     CFRelease(cfStr);
463   }
464 #endif
465 
466   return ret;
467 }
468 
469 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
GenerateBuildCommand(const std::string & makeProgram,const std::string & projectName,const std::string &,std::vector<std::string> const & targetNames,const std::string & config,bool,int jobs,bool,std::vector<std::string> const & makeOptions)470 cmGlobalXCodeGenerator::GenerateBuildCommand(
471   const std::string& makeProgram, const std::string& projectName,
472   const std::string& /*projectDir*/,
473   std::vector<std::string> const& targetNames, const std::string& config,
474   bool /*fast*/, int jobs, bool /*verbose*/,
475   std::vector<std::string> const& makeOptions)
476 {
477   GeneratedMakeCommand makeCommand;
478   // now build the test
479   makeCommand.Add(
480     this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()));
481 
482   if (!projectName.empty()) {
483     makeCommand.Add("-project");
484     std::string projectArg = cmStrCat(projectName, ".xcodeproj");
485     makeCommand.Add(projectArg);
486   }
487   if (cm::contains(targetNames, "clean")) {
488     makeCommand.Add("clean");
489     makeCommand.Add("-target", "ALL_BUILD");
490   } else {
491     makeCommand.Add("build");
492     if (targetNames.empty() ||
493         ((targetNames.size() == 1) && targetNames.front().empty())) {
494       makeCommand.Add("-target", "ALL_BUILD");
495     } else {
496       for (const auto& tname : targetNames) {
497         if (!tname.empty()) {
498           makeCommand.Add("-target", tname);
499         }
500       }
501     }
502   }
503 
504   if ((this->XcodeBuildSystem >= BuildSystem::Twelve) ||
505       (jobs != cmake::NO_BUILD_PARALLEL_LEVEL)) {
506     makeCommand.Add("-parallelizeTargets");
507   }
508   makeCommand.Add("-configuration", (config.empty() ? "Debug" : config));
509 
510   if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) &&
511       (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) {
512     makeCommand.Add("-jobs", std::to_string(jobs));
513   }
514 
515   if (this->XcodeVersion >= 70) {
516     makeCommand.Add("-hideShellScriptEnvironment");
517   }
518   makeCommand.Add(makeOptions.begin(), makeOptions.end());
519   return { std::move(makeCommand) };
520 }
521 
522 //! Create a local generator appropriate to this Global Generator
CreateLocalGenerator(cmMakefile * mf)523 std::unique_ptr<cmLocalGenerator> cmGlobalXCodeGenerator::CreateLocalGenerator(
524   cmMakefile* mf)
525 {
526   std::unique_ptr<cmLocalGenerator> lg(
527     cm::make_unique<cmLocalXCodeGenerator>(this, mf));
528   if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
529     // For this build system variant we generate custom commands as
530     // shell scripts directly rather than inside Makefiles.
531     // FIXME: Rename or refactor this option for clarity.
532     lg->SetLinkScriptShell(true);
533   }
534   return lg;
535 }
536 
AddExtraIDETargets()537 void cmGlobalXCodeGenerator::AddExtraIDETargets()
538 {
539   // make sure extra targets are added before calling
540   // the parent generate which will call trace depends
541   for (auto keyVal : this->ProjectMap) {
542     cmLocalGenerator* root = keyVal.second[0];
543     this->SetGenerationRoot(root);
544     // add ALL_BUILD, INSTALL, etc
545     this->AddExtraTargets(root, keyVal.second);
546   }
547 }
548 
Generate()549 void cmGlobalXCodeGenerator::Generate()
550 {
551   this->cmGlobalGenerator::Generate();
552   if (cmSystemTools::GetErrorOccuredFlag()) {
553     return;
554   }
555 
556   for (auto keyVal : this->ProjectMap) {
557     cmLocalGenerator* root = keyVal.second[0];
558 
559     bool generateTopLevelProjectOnly =
560       root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
561 
562     if (generateTopLevelProjectOnly) {
563       cmStateSnapshot snp = root->GetStateSnapshot();
564       if (snp.GetBuildsystemDirectoryParent().IsValid()) {
565         continue;
566       }
567     }
568 
569     // cache the enabled languages for source file type queries
570     this->GetEnabledLanguages(this->EnabledLangs);
571 
572     this->SetGenerationRoot(root);
573     // now create the project
574     this->OutputXCodeProject(root, keyVal.second);
575   }
576 }
577 
SetGenerationRoot(cmLocalGenerator * root)578 void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root)
579 {
580   this->CurrentProject = root->GetProjectName();
581   this->SetCurrentLocalGenerator(root);
582   this->CurrentRootGenerator = root;
583   this->CurrentXCodeHackMakefile =
584     cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
585   cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile);
586   this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
587 }
588 
PostBuildMakeTarget(std::string const & tName,std::string const & configName)589 std::string cmGlobalXCodeGenerator::PostBuildMakeTarget(
590   std::string const& tName, std::string const& configName)
591 {
592   std::string target = tName;
593   std::replace(target.begin(), target.end(), ' ', '_');
594   std::string out = cmStrCat("PostBuild.", target, '.', configName);
595   return out;
596 }
597 
598 #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"
599 #define OBJECT_LIBRARY_ARTIFACT_DIR std::string()
600 
AddExtraTargets(cmLocalGenerator * root,std::vector<cmLocalGenerator * > & gens)601 void cmGlobalXCodeGenerator::AddExtraTargets(
602   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens)
603 {
604   const char* no_working_directory = nullptr;
605   std::vector<std::string> no_byproducts;
606   std::vector<std::string> no_depends;
607 
608   // Add ALL_BUILD
609   cmTarget* allbuild = root->AddUtilityCommand(
610     "ALL_BUILD", true, no_working_directory, no_byproducts, no_depends,
611     cmMakeSingleCommandLine({ "echo", "Build all projects" }),
612     cmPolicies::NEW);
613 
614   root->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(allbuild, root));
615 
616   // Add XCODE depend helper
617   std::string legacyDependHelperDir = root->GetCurrentBinaryDirectory();
618   cmCustomCommandLines legacyDependHelperCommandLines;
619   if (this->XcodeBuildSystem == BuildSystem::One) {
620     legacyDependHelperCommandLines = cmMakeSingleCommandLine(
621       { "make", "-C", legacyDependHelperDir, "-f",
622         this->CurrentXCodeHackMakefile, "OBJDIR=$(OBJDIR)",
623         /* placeholder, see below */ "" });
624   }
625 
626   // Add ZERO_CHECK
627   bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
628   bool generateTopLevelProjectOnly =
629     root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
630   bool isTopLevel =
631     !root->GetStateSnapshot().GetBuildsystemDirectoryParent().IsValid();
632   bool isGenerateProject = isTopLevel || !generateTopLevelProjectOnly;
633   if (regenerate && isGenerateProject) {
634     this->CreateReRunCMakeFile(root, gens);
635     std::string file =
636       this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
637     cmSystemTools::ReplaceString(file, "\\ ", " ");
638     cmTarget* check = root->AddUtilityCommand(
639       CMAKE_CHECK_BUILD_SYSTEM_TARGET, true, no_working_directory,
640       no_byproducts, no_depends,
641       cmMakeSingleCommandLine({ "make", "-f", file }), cmPolicies::NEW);
642 
643     root->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(check, root));
644   }
645 
646   // now make the allbuild depend on all the non-utility targets
647   // in the project
648   for (auto& gen : gens) {
649     for (const auto& target : gen->GetGeneratorTargets()) {
650       if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
651         continue;
652       }
653 
654       if (regenerate &&
655           (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
656         target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false);
657       }
658 
659       // make all exe, shared libs and modules
660       // run the depend check makefile as a post build rule
661       // this will make sure that when the next target is built
662       // things are up-to-date
663       if (this->XcodeBuildSystem == BuildSystem::One && isGenerateProject &&
664           target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
665         legacyDependHelperCommandLines.front().back() = // fill placeholder
666           this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
667         gen->AddCustomCommandToTarget(
668           target->GetName(), no_byproducts, no_depends,
669           legacyDependHelperCommandLines, cmCustomCommandType::POST_BUILD,
670           "Depend check for xcode", legacyDependHelperDir.c_str(),
671           cmPolicies::NEW, true, false, "", "", false,
672           cmObjectLibraryCommands::Accept);
673       }
674 
675       if (!this->IsExcluded(gens[0], target.get())) {
676         allbuild->AddUtility(target->GetName(), false);
677       }
678     }
679   }
680 }
681 
CreateReRunCMakeFile(cmLocalGenerator * root,std::vector<cmLocalGenerator * > const & gens)682 void cmGlobalXCodeGenerator::CreateReRunCMakeFile(
683   cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
684 {
685   std::vector<std::string> lfiles;
686   for (auto gen : gens) {
687     cm::append(lfiles, gen->GetMakefile()->GetListFiles());
688   }
689 
690   // sort the array
691   std::sort(lfiles.begin(), lfiles.end());
692   lfiles.erase(std::unique(lfiles.begin(), lfiles.end()), lfiles.end());
693 
694   cmake* cm = this->GetCMakeInstance();
695   if (cm->DoWriteGlobVerifyTarget()) {
696     lfiles.emplace_back(cm->GetGlobVerifyStamp());
697   }
698 
699   this->CurrentReRunCMakeMakefile =
700     cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
701   cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile);
702   this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
703   cmGeneratedFileStream makefileStream(this->CurrentReRunCMakeMakefile);
704   makefileStream.SetCopyIfDifferent(true);
705   makefileStream << "# Generated by CMake, DO NOT EDIT\n\n";
706 
707   makefileStream << "TARGETS:= \n";
708   makefileStream << "empty:= \n";
709   makefileStream << "space:= $(empty) $(empty)\n";
710   makefileStream << "spaceplus:= $(empty)\\ $(empty)\n\n";
711 
712   for (const auto& lfile : lfiles) {
713     makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
714                    << this->ConvertToRelativeForMake(lfile) << "))\n";
715   }
716   makefileStream << "\n";
717 
718   std::string checkCache =
719     cmStrCat(root->GetBinaryDirectory(), "/CMakeFiles/cmake.check_cache");
720 
721   if (cm->DoWriteGlobVerifyTarget()) {
722     makefileStream << ".NOTPARALLEL:\n\n";
723     makefileStream << ".PHONY: all VERIFY_GLOBS\n\n";
724     makefileStream << "all: VERIFY_GLOBS "
725                    << this->ConvertToRelativeForMake(checkCache) << "\n\n";
726     makefileStream << "VERIFY_GLOBS:\n";
727     makefileStream << "\t"
728                    << this->ConvertToRelativeForMake(
729                         cmSystemTools::GetCMakeCommand())
730                    << " -P "
731                    << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript())
732                    << "\n\n";
733   }
734 
735   makefileStream << this->ConvertToRelativeForMake(checkCache)
736                  << ": $(TARGETS)\n";
737   makefileStream << "\t"
738                  << this->ConvertToRelativeForMake(
739                       cmSystemTools::GetCMakeCommand())
740                  << " -H"
741                  << this->ConvertToRelativeForMake(root->GetSourceDirectory())
742                  << " -B"
743                  << this->ConvertToRelativeForMake(root->GetBinaryDirectory())
744                  << "\n";
745 }
746 
objectIdLessThan(const std::unique_ptr<cmXCodeObject> & l,const std::unique_ptr<cmXCodeObject> & r)747 static bool objectIdLessThan(const std::unique_ptr<cmXCodeObject>& l,
748                              const std::unique_ptr<cmXCodeObject>& r)
749 {
750   return l->GetId() < r->GetId();
751 }
752 
SortXCodeObjects()753 void cmGlobalXCodeGenerator::SortXCodeObjects()
754 {
755   std::sort(this->XCodeObjects.begin(), this->XCodeObjects.end(),
756             objectIdLessThan);
757 }
758 
ClearXCodeObjects()759 void cmGlobalXCodeGenerator::ClearXCodeObjects()
760 {
761   this->TargetDoneSet.clear();
762   this->XCodeObjects.clear();
763   this->XCodeObjectIDs.clear();
764   this->XCodeObjectMap.clear();
765   this->GroupMap.clear();
766   this->GroupNameMap.clear();
767   this->TargetGroup.clear();
768   this->FileRefs.clear();
769   this->ExternalLibRefs.clear();
770   this->EmbeddedLibRefs.clear();
771   this->FileRefToBuildFileMap.clear();
772   this->FileRefToEmbedBuildFileMap.clear();
773   this->CommandsVisited.clear();
774 }
775 
addObject(std::unique_ptr<cmXCodeObject> obj)776 void cmGlobalXCodeGenerator::addObject(std::unique_ptr<cmXCodeObject> obj)
777 {
778   if (obj->GetType() == cmXCodeObject::OBJECT) {
779     const std::string& id = obj->GetId();
780 
781     // If this is a duplicate id, it's an error:
782     //
783     if (this->XCodeObjectIDs.count(id)) {
784       cmSystemTools::Error(
785         "Xcode generator: duplicate object ids not allowed");
786     }
787 
788     this->XCodeObjectIDs.insert(id);
789   }
790 
791   this->XCodeObjects.push_back(std::move(obj));
792 }
793 
CreateObject(cmXCodeObject::PBXType ptype,cm::string_view key)794 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(
795   cmXCodeObject::PBXType ptype, cm::string_view key)
796 {
797   auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT,
798                                               this->GetObjectId(ptype, key));
799   auto ptr = obj.get();
800   this->addObject(std::move(obj));
801   return ptr;
802 }
803 
CreateObject(cmXCodeObject::Type type)804 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
805 {
806   auto obj = cm::make_unique<cmXCodeObject>(
807     cmXCodeObject::None, type,
808     "Temporary cmake object, should not be referred to in Xcode file");
809   auto ptr = obj.get();
810   this->addObject(std::move(obj));
811   return ptr;
812 }
813 
CreateString(const std::string & s)814 cmXCodeObject* cmGlobalXCodeGenerator::CreateString(const std::string& s)
815 {
816   cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING);
817   obj->SetString(s);
818   return obj;
819 }
820 
CreateObjectReference(cmXCodeObject * ref)821 cmXCodeObject* cmGlobalXCodeGenerator::CreateObjectReference(
822   cmXCodeObject* ref)
823 {
824   cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF);
825   obj->SetObject(ref);
826   return obj;
827 }
828 
CreateFlatClone(cmXCodeObject * orig)829 cmXCodeObject* cmGlobalXCodeGenerator::CreateFlatClone(cmXCodeObject* orig)
830 {
831   cmXCodeObject* obj = this->CreateObject(orig->GetType());
832   obj->CopyAttributes(orig);
833   return obj;
834 }
835 
GetGroupMapKeyFromPath(cmGeneratorTarget * target,const std::string & fullpath)836 std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target,
837                                    const std::string& fullpath)
838 {
839   std::string key(target->GetName());
840   key += "-";
841   key += fullpath;
842   return key;
843 }
844 
CreateXCodeBuildFileFromPath(const std::string & fullpath,cmGeneratorTarget * target,const std::string & lang,cmSourceFile * sf)845 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeBuildFileFromPath(
846   const std::string& fullpath, cmGeneratorTarget* target,
847   const std::string& lang, cmSourceFile* sf)
848 {
849   // Using a map and the full path guarantees that we will always get the same
850   // fileRef object for any given full path. Same goes for the buildFile
851   // object.
852   cmXCodeObject* fileRef =
853     this->CreateXCodeFileReferenceFromPath(fullpath, target, lang, sf);
854   if (fileRef) {
855     auto it = this->FileRefToBuildFileMap.find(fileRef);
856     if (it == this->FileRefToBuildFileMap.end()) {
857       cmXCodeObject* buildFile =
858         this->CreateObject(cmXCodeObject::PBXBuildFile);
859       buildFile->SetComment(fileRef->GetComment());
860       buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef));
861       this->FileRefToBuildFileMap[fileRef] = buildFile;
862       return buildFile;
863     }
864     return it->second;
865   }
866   return nullptr;
867 }
868 
869 class XCodeGeneratorExpressionInterpreter
870   : public cmGeneratorExpressionInterpreter
871 {
872 public:
XCodeGeneratorExpressionInterpreter(cmSourceFile * sourceFile,cmLocalGenerator * localGenerator,cmGeneratorTarget * headTarget,const std::string & lang)873   XCodeGeneratorExpressionInterpreter(cmSourceFile* sourceFile,
874                                       cmLocalGenerator* localGenerator,
875                                       cmGeneratorTarget* headTarget,
876                                       const std::string& lang)
877     : cmGeneratorExpressionInterpreter(
878         localGenerator, "NO-PER-CONFIG-SUPPORT-IN-XCODE", headTarget, lang)
879     , SourceFile(sourceFile)
880   {
881   }
882 
883   XCodeGeneratorExpressionInterpreter(
884     XCodeGeneratorExpressionInterpreter const&) = delete;
885   XCodeGeneratorExpressionInterpreter& operator=(
886     XCodeGeneratorExpressionInterpreter const&) = delete;
887 
Evaluate(const char * expression,const std::string & property)888   const std::string& Evaluate(const char* expression,
889                               const std::string& property)
890   {
891     return this->Evaluate(std::string(expression ? expression : ""), property);
892   }
893 
Evaluate(const std::string & expression,const std::string & property)894   const std::string& Evaluate(const std::string& expression,
895                               const std::string& property)
896   {
897     const std::string& processed =
898       this->cmGeneratorExpressionInterpreter::Evaluate(expression, property);
899     if (this->CompiledGeneratorExpression->GetHadContextSensitiveCondition()) {
900       std::ostringstream e;
901       /* clang-format off */
902       e <<
903           "Xcode does not support per-config per-source " << property << ":\n"
904           "  " << expression << "\n"
905           "specified for source:\n"
906           "  " << this->SourceFile->ResolveFullPath() << "\n";
907       /* clang-format on */
908       this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
909     }
910 
911     return processed;
912   }
913 
914 private:
915   cmSourceFile* SourceFile = nullptr;
916 };
917 
CreateXCodeSourceFile(cmLocalGenerator * lg,cmSourceFile * sf,cmGeneratorTarget * gtgt)918 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
919   cmLocalGenerator* lg, cmSourceFile* sf, cmGeneratorTarget* gtgt)
920 {
921   std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
922 
923   XCodeGeneratorExpressionInterpreter genexInterpreter(sf, lg, gtgt, lang);
924 
925   // Add flags from target and source file properties.
926   std::string flags;
927   std::string const& srcfmt = sf->GetSafeProperty("Fortran_FORMAT");
928   switch (cmOutputConverter::GetFortranFormat(srcfmt)) {
929     case cmOutputConverter::FortranFormatFixed:
930       flags = "-fixed " + flags;
931       break;
932     case cmOutputConverter::FortranFormatFree:
933       flags = "-free " + flags;
934       break;
935     default:
936       break;
937   }
938 
939   // Explicitly add the explicit language flag before any other flag
940   // so user flags can override it.
941   gtgt->AddExplicitLanguageFlags(flags, *sf);
942 
943   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
944   if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) {
945     lg->AppendFlags(flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
946   }
947   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
948   if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) {
949     lg->AppendCompileOptions(
950       flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
951   }
952 
953   // Add per-source definitions.
954   BuildObjectListOrString flagsBuild(this, false);
955   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
956   if (cmValue compile_defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
957     this->AppendDefines(
958       flagsBuild,
959       genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS).c_str(),
960       true);
961   }
962 
963   if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
964     this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
965   }
966 
967   if (!flagsBuild.IsEmpty()) {
968     if (!flags.empty()) {
969       flags += ' ';
970     }
971     flags += flagsBuild.GetString();
972   }
973 
974   // Add per-source include directories.
975   std::vector<std::string> includes;
976   const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
977   if (cmValue cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
978     lg->AppendIncludeDirectories(
979       includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
980       *sf);
981   }
982   lg->AppendFlags(flags,
983                   lg->GetIncludeFlags(includes, gtgt, lang, std::string()));
984 
985   cmXCodeObject* buildFile =
986     this->CreateXCodeBuildFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf);
987 
988   cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
989   settings->AddAttributeIfNotEmpty("COMPILER_FLAGS",
990                                    this->CreateString(flags));
991 
992   cmGeneratorTarget::SourceFileFlags tsFlags =
993     gtgt->GetTargetSourceFileFlags(sf);
994 
995   cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
996 
997   // Is this a "private" or "public" framework header file?
998   // Set the ATTRIBUTES attribute appropriately...
999   //
1000   if (gtgt->IsFrameworkOnApple()) {
1001     if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePrivateHeader) {
1002       attrs->AddObject(this->CreateString("Private"));
1003     } else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePublicHeader) {
1004       attrs->AddObject(this->CreateString("Public"));
1005     }
1006   }
1007 
1008   // Add user-specified file attributes.
1009   cmValue extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
1010   if (extraFileAttributes) {
1011     // Expand the list of attributes.
1012     std::vector<std::string> attributes = cmExpandedList(*extraFileAttributes);
1013 
1014     // Store the attributes.
1015     for (const auto& attribute : attributes) {
1016       attrs->AddObject(this->CreateString(attribute));
1017     }
1018   }
1019 
1020   settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
1021 
1022   if (buildFile) {
1023     buildFile->AddAttributeIfNotEmpty("settings", settings);
1024   }
1025   return buildFile;
1026 }
1027 
AddXCodeProjBuildRule(cmGeneratorTarget * target,std::vector<cmSourceFile * > & sources) const1028 void cmGlobalXCodeGenerator::AddXCodeProjBuildRule(
1029   cmGeneratorTarget* target, std::vector<cmSourceFile*>& sources) const
1030 {
1031   std::string listfile =
1032     cmStrCat(target->GetLocalGenerator()->GetCurrentSourceDirectory(),
1033              "/CMakeLists.txt");
1034   cmSourceFile* srcCMakeLists = target->Makefile->GetOrCreateSource(
1035     listfile, false, cmSourceFileLocationKind::Known);
1036   if (!cm::contains(sources, srcCMakeLists)) {
1037     sources.push_back(srcCMakeLists);
1038   }
1039 }
1040 
1041 namespace {
1042 
IsLinkPhaseLibraryExtension(const std::string & fileExt)1043 bool IsLinkPhaseLibraryExtension(const std::string& fileExt)
1044 {
1045   // Empty file extension is a special case for paths to framework's
1046   // internal binary which could be MyFw.framework/Versions/*/MyFw
1047   return (fileExt == ".framework" || fileExt == ".a" || fileExt == ".o" ||
1048           fileExt == ".dylib" || fileExt == ".tbd" || fileExt.empty());
1049 }
IsLibraryType(const std::string & fileType)1050 bool IsLibraryType(const std::string& fileType)
1051 {
1052   return (fileType == "wrapper.framework" || fileType == "archive.ar" ||
1053           fileType == "compiled.mach-o.objfile" ||
1054           fileType == "compiled.mach-o.dylib" ||
1055           fileType == "compiled.mach-o.executable" ||
1056           fileType == "sourcecode.text-based-dylib-definition");
1057 }
1058 
GetDirectoryValueFromFileExtension(const std::string & dirExt)1059 std::string GetDirectoryValueFromFileExtension(const std::string& dirExt)
1060 {
1061   std::string ext = cmSystemTools::LowerCase(dirExt);
1062   if (ext == "framework") {
1063     return "wrapper.framework";
1064   }
1065   if (ext == "xcassets") {
1066     return "folder.assetcatalog";
1067   }
1068   return "folder";
1069 }
1070 
GetSourcecodeValueFromFileExtension(const std::string & _ext,const std::string & lang,bool & keepLastKnownFileType,const std::vector<std::string> & enabled_langs)1071 std::string GetSourcecodeValueFromFileExtension(
1072   const std::string& _ext, const std::string& lang,
1073   bool& keepLastKnownFileType, const std::vector<std::string>& enabled_langs)
1074 {
1075   std::string ext = cmSystemTools::LowerCase(_ext);
1076   std::string sourcecode = "sourcecode";
1077 
1078   if (ext == "o") {
1079     keepLastKnownFileType = true;
1080     sourcecode = "compiled.mach-o.objfile";
1081   } else if (ext == "xctest") {
1082     sourcecode = "wrapper.cfbundle";
1083   } else if (ext == "xib") {
1084     keepLastKnownFileType = true;
1085     sourcecode = "file.xib";
1086   } else if (ext == "storyboard") {
1087     keepLastKnownFileType = true;
1088     sourcecode = "file.storyboard";
1089   } else if (ext == "mm" && !cm::contains(enabled_langs, "OBJCXX")) {
1090     sourcecode += ".cpp.objcpp";
1091   } else if (ext == "m" && !cm::contains(enabled_langs, "OBJC")) {
1092     sourcecode += ".c.objc";
1093   } else if (ext == "swift") {
1094     sourcecode += ".swift";
1095   } else if (ext == "plist") {
1096     sourcecode += ".text.plist";
1097   } else if (ext == "h") {
1098     sourcecode += ".c.h";
1099   } else if (ext == "hxx" || ext == "hpp" || ext == "txx" || ext == "pch" ||
1100              ext == "hh" || ext == "inl") {
1101     sourcecode += ".cpp.h";
1102   } else if (ext == "png" || ext == "gif" || ext == "jpg") {
1103     keepLastKnownFileType = true;
1104     sourcecode = "image";
1105   } else if (ext == "txt") {
1106     sourcecode += ".text";
1107   } else if (lang == "CXX") {
1108     sourcecode += ".cpp.cpp";
1109   } else if (lang == "C") {
1110     sourcecode += ".c.c";
1111   } else if (lang == "OBJCXX") {
1112     sourcecode += ".cpp.objcpp";
1113   } else if (lang == "OBJC") {
1114     sourcecode += ".c.objc";
1115   } else if (lang == "Fortran") {
1116     sourcecode += ".fortran.f90";
1117   } else if (lang == "ASM") {
1118     sourcecode += ".asm";
1119   } else if (ext == "metal") {
1120     sourcecode += ".metal";
1121   } else if (ext == "mig") {
1122     sourcecode += ".mig";
1123   } else if (ext == "tbd") {
1124     sourcecode += ".text-based-dylib-definition";
1125   } else if (ext == "a") {
1126     keepLastKnownFileType = true;
1127     sourcecode = "archive.ar";
1128   } else if (ext == "dylib") {
1129     keepLastKnownFileType = true;
1130     sourcecode = "compiled.mach-o.dylib";
1131   } else if (ext == "framework") {
1132     keepLastKnownFileType = true;
1133     sourcecode = "wrapper.framework";
1134   } else if (ext == "xcassets") {
1135     keepLastKnownFileType = true;
1136     sourcecode = "folder.assetcatalog";
1137   }
1138   // else
1139   //  {
1140   //  // Already specialized above or we leave sourcecode == "sourcecode"
1141   //  // which is probably the most correct choice. Extensionless headers,
1142   //  // for example... Or file types unknown to Xcode that do not map to a
1143   //  // valid explicitFileType value.
1144   //  }
1145 
1146   return sourcecode;
1147 }
1148 
1149 // If the file has no extension it's either a raw executable or might
1150 // be a direct reference to a binary within a framework (bad practice!).
1151 // This is where we change the path to point to the framework directory.
1152 // .tbd files also can be located in SDK frameworks (they are
1153 // placeholders for actual libraries shipped with the OS)
GetLibraryOrFrameworkPath(const std::string & path)1154 std::string GetLibraryOrFrameworkPath(const std::string& path)
1155 {
1156   auto ext = cmSystemTools::GetFilenameLastExtension(path);
1157   if (ext.empty() || ext == ".tbd") {
1158     auto name = cmSystemTools::GetFilenameWithoutExtension(path);
1159     // Check for iOS framework structure:
1160     //    FwName.framework/FwName (and also on macOS where FwName lib is a
1161     //    symlink)
1162     auto parentDir = cmSystemTools::GetParentDirectory(path);
1163     auto parentName = cmSystemTools::GetFilenameWithoutExtension(parentDir);
1164     ext = cmSystemTools::GetFilenameLastExtension(parentDir);
1165     if (ext == ".framework" && name == parentName) {
1166       return parentDir;
1167     }
1168     // Check for macOS framework structure:
1169     //    FwName.framework/Versions/*/FwName
1170     std::vector<std::string> components;
1171     cmSystemTools::SplitPath(path, components);
1172     if (components.size() > 3 &&
1173         components[components.size() - 3] == "Versions") {
1174       ext = cmSystemTools::GetFilenameLastExtension(
1175         components[components.size() - 4]);
1176       parentName = cmSystemTools::GetFilenameWithoutExtension(
1177         components[components.size() - 4]);
1178       if (ext == ".framework" && name == parentName) {
1179         components.erase(components.begin() + components.size() - 3,
1180                          components.end());
1181         return cmSystemTools::JoinPath(components);
1182       }
1183     }
1184   }
1185   return path;
1186 }
1187 
1188 } // anonymous
1189 
CreateXCodeFileReferenceFromPath(const std::string & fullpath,cmGeneratorTarget * target,const std::string & lang,cmSourceFile * sf)1190 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
1191   const std::string& fullpath, cmGeneratorTarget* target,
1192   const std::string& lang, cmSourceFile* sf)
1193 {
1194   bool useLastKnownFileType = false;
1195   std::string fileType;
1196   if (sf) {
1197     if (cmValue e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
1198       fileType = *e;
1199     } else if (cmValue l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE")) {
1200       useLastKnownFileType = true;
1201       fileType = *l;
1202     }
1203   }
1204   // Make a copy so that we can override it later
1205   std::string path = cmSystemTools::CollapseFullPath(fullpath);
1206   // Compute the extension without leading '.'.
1207   std::string ext = cmSystemTools::GetFilenameLastExtension(path);
1208   if (!ext.empty()) {
1209     ext = ext.substr(1);
1210   }
1211   if (fileType.empty()) {
1212     path = GetLibraryOrFrameworkPath(path);
1213     ext = cmSystemTools::GetFilenameLastExtension(path);
1214     if (!ext.empty()) {
1215       ext = ext.substr(1);
1216     }
1217     // If fullpath references a directory, then we need to specify
1218     // lastKnownFileType as folder in order for Xcode to be able to
1219     // open the contents of the folder.
1220     // (Xcode 4.6 does not like explicitFileType=folder).
1221     if (cmSystemTools::FileIsDirectory(path)) {
1222       fileType = GetDirectoryValueFromFileExtension(ext);
1223       useLastKnownFileType = true;
1224     } else {
1225       if (ext.empty() && !sf) {
1226         // Special case for executable or library without extension
1227         // that is not a source file. We can't tell which without reading
1228         // its Mach-O header, but the file might not exist yet, so we
1229         // have to pick one here.
1230         useLastKnownFileType = true;
1231         fileType = "compiled.mach-o.executable";
1232       } else {
1233         fileType = GetSourcecodeValueFromFileExtension(
1234           ext, lang, useLastKnownFileType, this->EnabledLangs);
1235       }
1236     }
1237   }
1238 
1239   std::string key = GetGroupMapKeyFromPath(target, path);
1240   cmXCodeObject* fileRef = this->FileRefs[key];
1241   if (!fileRef) {
1242     fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
1243     fileRef->SetComment(path);
1244     this->FileRefs[key] = fileRef;
1245   }
1246   fileRef->AddAttribute("fileEncoding", this->CreateString("4"));
1247   fileRef->AddAttribute(useLastKnownFileType ? "lastKnownFileType"
1248                                              : "explicitFileType",
1249                         this->CreateString(fileType));
1250   // Store the file path relative to the top of the source tree.
1251   if (!IsLibraryType(fileType)) {
1252     path = this->RelativeToSource(path);
1253   }
1254   std::string name = cmSystemTools::GetFilenameName(path);
1255   const char* sourceTree =
1256     cmSystemTools::FileIsFullPath(path) ? "<absolute>" : "SOURCE_ROOT";
1257   fileRef->AddAttribute("name", this->CreateString(name));
1258   fileRef->AddAttribute("path", this->CreateString(path));
1259   fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree));
1260 
1261   cmXCodeObject* group = this->GroupMap[key];
1262   if (!group && IsLibraryType(fileType)) {
1263     group = this->FrameworkGroup;
1264     this->GroupMap[key] = group;
1265   }
1266   if (!group) {
1267     cmSystemTools::Error("Could not find a PBX group for " + key);
1268     return nullptr;
1269   }
1270   cmXCodeObject* children = group->GetAttribute("children");
1271   if (!children->HasObject(fileRef)) {
1272     children->AddObject(fileRef);
1273   }
1274   return fileRef;
1275 }
1276 
CreateXCodeFileReference(cmSourceFile * sf,cmGeneratorTarget * target)1277 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReference(
1278   cmSourceFile* sf, cmGeneratorTarget* target)
1279 {
1280   std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
1281 
1282   return this->CreateXCodeFileReferenceFromPath(sf->ResolveFullPath(), target,
1283                                                 lang, sf);
1284 }
1285 
SpecialTargetEmitted(std::string const & tname)1286 bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname)
1287 {
1288   if (tname == "ALL_BUILD" || tname == "install" || tname == "package" ||
1289       tname == "RUN_TESTS" || tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
1290     if (this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) {
1291       return true;
1292     }
1293     this->TargetDoneSet.insert(tname);
1294     return false;
1295   }
1296   return false;
1297 }
1298 
SetCurrentLocalGenerator(cmLocalGenerator * gen)1299 void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen)
1300 {
1301   this->CurrentLocalGenerator = gen;
1302   this->CurrentMakefile = gen->GetMakefile();
1303 
1304   // Select the current set of configuration types.
1305   this->CurrentConfigurationTypes =
1306     this->CurrentMakefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
1307 }
1308 
1309 struct cmSourceFilePathCompare
1310 {
operator ()cmSourceFilePathCompare1311   bool operator()(cmSourceFile* l, cmSourceFile* r)
1312   {
1313     return l->ResolveFullPath() < r->ResolveFullPath();
1314   }
1315 };
1316 
1317 struct cmCompareTargets
1318 {
operator ()cmCompareTargets1319   bool operator()(cmXCodeObject* l, cmXCodeObject* r) const
1320   {
1321     std::string const& a = l->GetTarget()->GetName();
1322     std::string const& b = r->GetTarget()->GetName();
1323     if (a == "ALL_BUILD") {
1324       return true;
1325     }
1326     if (b == "ALL_BUILD") {
1327       return false;
1328     }
1329     return a < b;
1330   }
1331 };
1332 
CreateXCodeTargets(cmLocalGenerator * gen,std::vector<cmXCodeObject * > & targets)1333 bool cmGlobalXCodeGenerator::CreateXCodeTargets(
1334   cmLocalGenerator* gen, std::vector<cmXCodeObject*>& targets)
1335 {
1336   this->SetCurrentLocalGenerator(gen);
1337   std::vector<cmGeneratorTarget*> gts =
1338     this->GetLocalGeneratorTargetsInOrder(gen);
1339   for (auto gtgt : gts) {
1340     if (!this->CreateXCodeTarget(gtgt, targets)) {
1341       return false;
1342     }
1343   }
1344   std::sort(targets.begin(), targets.end(), cmCompareTargets());
1345   return true;
1346 }
1347 
CreateXCodeTarget(cmGeneratorTarget * gtgt,std::vector<cmXCodeObject * > & targets)1348 bool cmGlobalXCodeGenerator::CreateXCodeTarget(
1349   cmGeneratorTarget* gtgt, std::vector<cmXCodeObject*>& targets)
1350 {
1351   std::string targetName = gtgt->GetName();
1352 
1353   // make sure ALL_BUILD, INSTALL, etc are only done once
1354   if (this->SpecialTargetEmitted(targetName)) {
1355     return true;
1356   }
1357 
1358   if (!gtgt->IsInBuildSystem()) {
1359     return true;
1360   }
1361 
1362   auto& gtgt_visited = this->CommandsVisited[gtgt];
1363   auto& deps = this->GetTargetDirectDepends(gtgt);
1364   for (auto& d : deps) {
1365     // Take the union of visited source files of custom commands so far.
1366     // ComputeTargetOrder ensures our dependencies already visited their
1367     // custom commands and updated CommandsVisited.
1368     auto& dep_visited = this->CommandsVisited[d];
1369     gtgt_visited.insert(dep_visited.begin(), dep_visited.end());
1370   }
1371 
1372   if (gtgt->GetType() == cmStateEnums::UTILITY ||
1373       gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
1374       gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) {
1375     cmXCodeObject* t = this->CreateUtilityTarget(gtgt);
1376     if (!t) {
1377       return false;
1378     }
1379     targets.push_back(t);
1380     return true;
1381   }
1382 
1383   // organize the sources
1384   std::vector<cmSourceFile*> commonSourceFiles;
1385   if (!gtgt->GetConfigCommonSourceFilesForXcode(commonSourceFiles)) {
1386     return false;
1387   }
1388 
1389   // Add CMakeLists.txt file for user convenience.
1390   this->AddXCodeProjBuildRule(gtgt, commonSourceFiles);
1391 
1392   // Add the Info.plist we are about to generate for an App Bundle.
1393   if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
1394     std::string plist = this->ComputeInfoPListLocation(gtgt);
1395     cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
1396       plist, true, cmSourceFileLocationKind::Known);
1397     commonSourceFiles.push_back(sf);
1398   }
1399 
1400   std::sort(commonSourceFiles.begin(), commonSourceFiles.end(),
1401             cmSourceFilePathCompare());
1402 
1403   gtgt->ComputeObjectMapping();
1404 
1405   std::vector<cmXCodeObject*> externalObjFiles;
1406   std::vector<cmXCodeObject*> headerFiles;
1407   std::vector<cmXCodeObject*> resourceFiles;
1408   std::vector<cmXCodeObject*> sourceFiles;
1409   for (auto sourceFile : commonSourceFiles) {
1410     cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1411       this->CurrentLocalGenerator, sourceFile, gtgt);
1412     cmXCodeObject* fr = xsf->GetAttribute("fileRef");
1413     cmXCodeObject* filetype =
1414       fr->GetObject()->GetAttribute("explicitFileType");
1415     if (!filetype) {
1416       filetype = fr->GetObject()->GetAttribute("lastKnownFileType");
1417     }
1418 
1419     cmGeneratorTarget::SourceFileFlags tsFlags =
1420       gtgt->GetTargetSourceFileFlags(sourceFile);
1421 
1422     if (filetype && filetype->GetString() == "compiled.mach-o.objfile") {
1423       if (sourceFile->GetObjectLibrary().empty()) {
1424         externalObjFiles.push_back(xsf);
1425       }
1426     } else if (this->IsHeaderFile(sourceFile) ||
1427                (tsFlags.Type ==
1428                 cmGeneratorTarget::SourceFileTypePrivateHeader) ||
1429                (tsFlags.Type ==
1430                 cmGeneratorTarget::SourceFileTypePublicHeader)) {
1431       headerFiles.push_back(xsf);
1432     } else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeResource) {
1433       resourceFiles.push_back(xsf);
1434     } else if (!sourceFile->GetPropertyAsBool("HEADER_FILE_ONLY") &&
1435                !gtgt->IsSourceFilePartOfUnityBatch(
1436                  sourceFile->ResolveFullPath())) {
1437       // Include this file in the build if it has a known language
1438       // and has not been listed as an ignored extension for this
1439       // generator.
1440       if (!this->CurrentLocalGenerator->GetSourceFileLanguage(*sourceFile)
1441              .empty() &&
1442           !this->IgnoreFile(sourceFile->GetExtension().c_str())) {
1443         sourceFiles.push_back(xsf);
1444       }
1445     }
1446   }
1447 
1448   // some build phases only apply to bundles and/or frameworks
1449   bool isFrameworkTarget = gtgt->IsFrameworkOnApple();
1450   bool isBundleTarget = gtgt->GetPropertyAsBool("MACOSX_BUNDLE");
1451   bool isCFBundleTarget = gtgt->IsCFBundleOnApple();
1452 
1453   cmXCodeObject* buildFiles = nullptr;
1454 
1455   // create source build phase
1456   cmXCodeObject* sourceBuildPhase = nullptr;
1457   if (!sourceFiles.empty()) {
1458     sourceBuildPhase = this->CreateObject(cmXCodeObject::PBXSourcesBuildPhase);
1459     sourceBuildPhase->SetComment("Sources");
1460     sourceBuildPhase->AddAttribute("buildActionMask",
1461                                    this->CreateString("2147483647"));
1462     buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1463     for (auto& sourceFile : sourceFiles) {
1464       buildFiles->AddObject(sourceFile);
1465     }
1466     sourceBuildPhase->AddAttribute("files", buildFiles);
1467     sourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1468                                    this->CreateString("0"));
1469   }
1470 
1471   // create header build phase - only for framework targets
1472   cmXCodeObject* headerBuildPhase = nullptr;
1473   if (!headerFiles.empty() && isFrameworkTarget) {
1474     headerBuildPhase = this->CreateObject(cmXCodeObject::PBXHeadersBuildPhase);
1475     headerBuildPhase->SetComment("Headers");
1476     headerBuildPhase->AddAttribute("buildActionMask",
1477                                    this->CreateString("2147483647"));
1478     buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1479     for (auto& headerFile : headerFiles) {
1480       buildFiles->AddObject(headerFile);
1481     }
1482     headerBuildPhase->AddAttribute("files", buildFiles);
1483     headerBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1484                                    this->CreateString("0"));
1485   }
1486 
1487   // create resource build phase - only for framework or bundle targets
1488   cmXCodeObject* resourceBuildPhase = nullptr;
1489   if (!resourceFiles.empty() &&
1490       (isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
1491     resourceBuildPhase =
1492       this->CreateObject(cmXCodeObject::PBXResourcesBuildPhase);
1493     resourceBuildPhase->SetComment("Resources");
1494     resourceBuildPhase->AddAttribute("buildActionMask",
1495                                      this->CreateString("2147483647"));
1496     buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1497     for (auto& resourceFile : resourceFiles) {
1498       buildFiles->AddObject(resourceFile);
1499     }
1500     resourceBuildPhase->AddAttribute("files", buildFiles);
1501     resourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1502                                      this->CreateString("0"));
1503   }
1504 
1505   // create vector of "non-resource content file" build phases - only for
1506   // framework or bundle targets
1507   std::vector<cmXCodeObject*> contentBuildPhases;
1508   if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
1509     using mapOfVectorOfSourceFiles =
1510       std::map<std::string, std::vector<cmSourceFile*>>;
1511     mapOfVectorOfSourceFiles bundleFiles;
1512     for (auto sourceFile : commonSourceFiles) {
1513       cmGeneratorTarget::SourceFileFlags tsFlags =
1514         gtgt->GetTargetSourceFileFlags(sourceFile);
1515       if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeMacContent) {
1516         bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
1517       }
1518     }
1519     for (auto const& keySources : bundleFiles) {
1520       cmXCodeObject* copyFilesBuildPhase =
1521         this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
1522       copyFilesBuildPhase->SetComment("Copy files");
1523       copyFilesBuildPhase->AddAttribute("buildActionMask",
1524                                         this->CreateString("2147483647"));
1525       copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
1526                                         this->CreateString("6"));
1527       std::ostringstream ostr;
1528       if (gtgt->IsFrameworkOnApple()) {
1529         // dstPath in frameworks is relative to Versions/<version>
1530         ostr << keySources.first;
1531       } else if (keySources.first != "MacOS") {
1532         if (gtgt->Target->GetMakefile()->PlatformIsAppleEmbedded()) {
1533           ostr << keySources.first;
1534         } else {
1535           // dstPath in bundles is relative to Contents/MacOS
1536           ostr << "../" << keySources.first;
1537         }
1538       }
1539       copyFilesBuildPhase->AddAttribute("dstPath",
1540                                         this->CreateString(ostr.str()));
1541       copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1542                                         this->CreateString("0"));
1543       buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1544       copyFilesBuildPhase->AddAttribute("files", buildFiles);
1545       for (auto sourceFile : keySources.second) {
1546         cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1547           this->CurrentLocalGenerator, sourceFile, gtgt);
1548         buildFiles->AddObject(xsf);
1549       }
1550       contentBuildPhases.push_back(copyFilesBuildPhase);
1551     }
1552   }
1553 
1554   // create vector of "resource content file" build phases - only for
1555   // framework or bundle targets
1556   if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
1557     using mapOfVectorOfSourceFiles =
1558       std::map<std::string, std::vector<cmSourceFile*>>;
1559     mapOfVectorOfSourceFiles bundleFiles;
1560     for (auto sourceFile : commonSourceFiles) {
1561       cmGeneratorTarget::SourceFileFlags tsFlags =
1562         gtgt->GetTargetSourceFileFlags(sourceFile);
1563       if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeDeepResource) {
1564         bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
1565       }
1566     }
1567     for (auto const& keySources : bundleFiles) {
1568       cmXCodeObject* copyFilesBuildPhase =
1569         this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
1570       copyFilesBuildPhase->SetComment("Copy files");
1571       copyFilesBuildPhase->AddAttribute("buildActionMask",
1572                                         this->CreateString("2147483647"));
1573       copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
1574                                         this->CreateString("7"));
1575       copyFilesBuildPhase->AddAttribute("dstPath",
1576                                         this->CreateString(keySources.first));
1577       copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1578                                         this->CreateString("0"));
1579       buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1580       copyFilesBuildPhase->AddAttribute("files", buildFiles);
1581       for (auto sourceFile : keySources.second) {
1582         cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1583           this->CurrentLocalGenerator, sourceFile, gtgt);
1584         buildFiles->AddObject(xsf);
1585       }
1586       contentBuildPhases.push_back(copyFilesBuildPhase);
1587     }
1588   }
1589 
1590   // Always create Link Binary With Libraries build phase
1591   cmXCodeObject* frameworkBuildPhase = nullptr;
1592   frameworkBuildPhase =
1593     this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase);
1594   frameworkBuildPhase->SetComment("Frameworks");
1595   frameworkBuildPhase->AddAttribute("buildActionMask",
1596                                     this->CreateString("2147483647"));
1597   buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1598   frameworkBuildPhase->AddAttribute("files", buildFiles);
1599   // Add all collected .o files to this build phase
1600   for (auto& externalObjFile : externalObjFiles) {
1601     buildFiles->AddObject(externalObjFile);
1602   }
1603   frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1604                                     this->CreateString("0"));
1605 
1606   // create list of build phases and create the Xcode target
1607   cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1608 
1609   this->CreateCustomCommands(buildPhases, sourceBuildPhase, headerBuildPhase,
1610                              resourceBuildPhase, contentBuildPhases,
1611                              frameworkBuildPhase, gtgt);
1612 
1613   targets.push_back(this->CreateXCodeTarget(gtgt, buildPhases));
1614   return true;
1615 }
1616 
ForceLinkerLanguages()1617 void cmGlobalXCodeGenerator::ForceLinkerLanguages()
1618 {
1619   for (const auto& localGenerator : this->LocalGenerators) {
1620     // All targets depend on the build-system check target.
1621     for (const auto& tgt : localGenerator->GetGeneratorTargets()) {
1622       // This makes sure all targets link using the proper language.
1623       this->ForceLinkerLanguage(tgt.get());
1624     }
1625   }
1626 }
1627 
ForceLinkerLanguage(cmGeneratorTarget * gtgt)1628 void cmGlobalXCodeGenerator::ForceLinkerLanguage(cmGeneratorTarget* gtgt)
1629 {
1630   // This matters only for targets that link.
1631   if (gtgt->GetType() != cmStateEnums::EXECUTABLE &&
1632       gtgt->GetType() != cmStateEnums::SHARED_LIBRARY &&
1633       gtgt->GetType() != cmStateEnums::MODULE_LIBRARY) {
1634     return;
1635   }
1636 
1637   std::string llang = gtgt->GetLinkerLanguage("NOCONFIG");
1638   if (llang.empty()) {
1639     return;
1640   }
1641 
1642   // If the language is compiled as a source trust Xcode to link with it.
1643   for (auto const& Language :
1644        gtgt->GetLinkImplementation("NOCONFIG")->Languages) {
1645     if (Language == llang) {
1646       return;
1647     }
1648   }
1649 
1650   // Allow empty source file list for iOS Sticker packs
1651   if (const char* productType = GetTargetProductType(gtgt)) {
1652     if (strcmp(productType,
1653                "com.apple.product-type.app-extension.messages-sticker-pack") ==
1654         0)
1655       return;
1656   }
1657 
1658   // Add an empty source file to the target that compiles with the
1659   // linker language.  This should convince Xcode to choose the proper
1660   // language.
1661   cmMakefile* mf = gtgt->Target->GetMakefile();
1662   std::string fname = cmStrCat(
1663     gtgt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/",
1664     gtgt->GetName(), "-CMakeForceLinker.", cmSystemTools::LowerCase(llang));
1665   {
1666     cmGeneratedFileStream fout(fname);
1667     fout << "\n";
1668   }
1669   if (cmSourceFile* sf = mf->GetOrCreateSource(fname)) {
1670     sf->SetProperty("LANGUAGE", llang);
1671     gtgt->AddSource(fname);
1672   }
1673 }
1674 
IsHeaderFile(cmSourceFile * sf)1675 bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf)
1676 {
1677   return cm::contains(this->CMakeInstance->GetHeaderExtensions(),
1678                       sf->GetExtension());
1679 }
1680 
CreateLegacyRunScriptBuildPhase(const char * name,const char * name2,cmGeneratorTarget * target,const std::vector<cmCustomCommand> & commands)1681 cmXCodeObject* cmGlobalXCodeGenerator::CreateLegacyRunScriptBuildPhase(
1682   const char* name, const char* name2, cmGeneratorTarget* target,
1683   const std::vector<cmCustomCommand>& commands)
1684 {
1685   if (commands.empty() && strcmp(name, "CMake ReRun") != 0) {
1686     return nullptr;
1687   }
1688   cmXCodeObject* buildPhase =
1689     this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
1690   buildPhase->AddAttribute("buildActionMask",
1691                            this->CreateString("2147483647"));
1692   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1693   buildPhase->AddAttribute("files", buildFiles);
1694   buildPhase->AddAttribute("name", this->CreateString(name));
1695   buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1696                            this->CreateString("0"));
1697   buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
1698   this->AddCommandsToBuildPhase(buildPhase, target, commands, name2);
1699   return buildPhase;
1700 }
1701 
CreateCustomCommands(cmXCodeObject * buildPhases,cmXCodeObject * sourceBuildPhase,cmXCodeObject * headerBuildPhase,cmXCodeObject * resourceBuildPhase,std::vector<cmXCodeObject * > const & contentBuildPhases,cmXCodeObject * frameworkBuildPhase,cmGeneratorTarget * gtgt)1702 void cmGlobalXCodeGenerator::CreateCustomCommands(
1703   cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
1704   cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
1705   std::vector<cmXCodeObject*> const& contentBuildPhases,
1706   cmXCodeObject* frameworkBuildPhase, cmGeneratorTarget* gtgt)
1707 {
1708   std::vector<cmCustomCommand> const& prebuild = gtgt->GetPreBuildCommands();
1709   std::vector<cmCustomCommand> const& prelink = gtgt->GetPreLinkCommands();
1710   std::vector<cmCustomCommand> postbuild = gtgt->GetPostBuildCommands();
1711 
1712   if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
1713       !gtgt->IsFrameworkOnApple()) {
1714     std::string str_file = cmStrCat("$<TARGET_FILE:", gtgt->GetName(), '>');
1715     std::string str_so_file =
1716       cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
1717     std::string str_link_file =
1718       cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
1719     bool stdPipesUTF8 = true;
1720     cmCustomCommandLines cmd = cmMakeSingleCommandLine(
1721       { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
1722         str_file, str_so_file, str_link_file });
1723 
1724     cmCustomCommand command(
1725       std::vector<std::string>(), std::vector<std::string>(),
1726       std::vector<std::string>(), cmd, this->CurrentMakefile->GetBacktrace(),
1727       "Creating symlinks", "", stdPipesUTF8);
1728 
1729     postbuild.push_back(std::move(command));
1730   }
1731 
1732   cmXCodeObject* legacyCustomCommandsBuildPhase = nullptr;
1733   cmXCodeObject* preBuildPhase = nullptr;
1734   cmXCodeObject* preLinkPhase = nullptr;
1735   cmXCodeObject* postBuildPhase = nullptr;
1736 
1737   if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
1738     // create prebuild phase
1739     preBuildPhase =
1740       this->CreateRunScriptBuildPhase("CMake PreBuild Rules", gtgt, prebuild);
1741     // create prelink phase
1742     preLinkPhase =
1743       this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink);
1744     // create postbuild phase
1745     postBuildPhase = this->CreateRunScriptBuildPhase("CMake PostBuild Rules",
1746                                                      gtgt, postbuild);
1747   } else {
1748     std::vector<cmSourceFile*> classes;
1749     if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) {
1750       return;
1751     }
1752     // add all the sources
1753     std::vector<cmCustomCommand> commands;
1754     auto& visited = this->CommandsVisited[gtgt];
1755     for (auto sourceFile : classes) {
1756       if (sourceFile->GetCustomCommand() &&
1757           visited.insert(sourceFile).second) {
1758         commands.push_back(*sourceFile->GetCustomCommand());
1759       }
1760     }
1761     // create custom commands phase
1762     legacyCustomCommandsBuildPhase = this->CreateLegacyRunScriptBuildPhase(
1763       "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands);
1764     // create prebuild phase
1765     preBuildPhase = this->CreateLegacyRunScriptBuildPhase(
1766       "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild);
1767     // create prelink phase
1768     preLinkPhase = this->CreateLegacyRunScriptBuildPhase(
1769       "CMake PreLink Rules", "preLinkCommands", gtgt, prelink);
1770     // create postbuild phase
1771     postBuildPhase = this->CreateLegacyRunScriptBuildPhase(
1772       "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild);
1773   }
1774 
1775   // The order here is the order they will be built in.
1776   // The order "headers, resources, sources" mimics a native project generated
1777   // from an xcode template...
1778   //
1779   if (preBuildPhase) {
1780     buildPhases->AddObject(preBuildPhase);
1781   }
1782   if (legacyCustomCommandsBuildPhase) {
1783     buildPhases->AddObject(legacyCustomCommandsBuildPhase);
1784   }
1785   if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
1786     this->CreateRunScriptBuildPhases(buildPhases, gtgt);
1787   }
1788   if (headerBuildPhase) {
1789     buildPhases->AddObject(headerBuildPhase);
1790   }
1791   if (resourceBuildPhase) {
1792     buildPhases->AddObject(resourceBuildPhase);
1793   }
1794   for (auto obj : contentBuildPhases) {
1795     buildPhases->AddObject(obj);
1796   }
1797   if (sourceBuildPhase) {
1798     buildPhases->AddObject(sourceBuildPhase);
1799   }
1800   if (preLinkPhase) {
1801     buildPhases->AddObject(preLinkPhase);
1802   }
1803   if (frameworkBuildPhase) {
1804     buildPhases->AddObject(frameworkBuildPhase);
1805   }
1806 
1807   // When this build phase is present, it must be last. More build phases may
1808   // be added later for embedding things and they will insert themselves just
1809   // before this last build phase.
1810   if (postBuildPhase) {
1811     buildPhases->AddObject(postBuildPhase);
1812   }
1813 }
1814 
CreateRunScriptBuildPhases(cmXCodeObject * buildPhases,cmGeneratorTarget const * gt)1815 void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases(
1816   cmXCodeObject* buildPhases, cmGeneratorTarget const* gt)
1817 {
1818   std::vector<cmSourceFile*> sources;
1819   if (!gt->GetConfigCommonSourceFilesForXcode(sources)) {
1820     return;
1821   }
1822   auto& visited = this->CommandsVisited[gt];
1823   for (auto sf : sources) {
1824     this->CreateRunScriptBuildPhases(buildPhases, sf, gt, visited);
1825   }
1826 }
1827 
CreateRunScriptBuildPhases(cmXCodeObject * buildPhases,cmSourceFile const * sf,cmGeneratorTarget const * gt,std::set<cmSourceFile const * > & visited)1828 void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases(
1829   cmXCodeObject* buildPhases, cmSourceFile const* sf,
1830   cmGeneratorTarget const* gt, std::set<cmSourceFile const*>& visited)
1831 {
1832   cmCustomCommand const* cc = sf->GetCustomCommand();
1833   if (cc && visited.insert(sf).second) {
1834     this->CustomCommandRoots[sf].insert(gt);
1835     if (std::vector<cmSourceFile*> const* depends = gt->GetSourceDepends(sf)) {
1836       for (cmSourceFile const* di : *depends) {
1837         this->CreateRunScriptBuildPhases(buildPhases, di, gt, visited);
1838       }
1839     }
1840     cmXCodeObject* buildPhase = this->CreateRunScriptBuildPhase(sf, gt, *cc);
1841     buildPhases->AddObject(buildPhase);
1842   }
1843 }
1844 
CreateRunScriptBuildPhase(cmSourceFile const * sf,cmGeneratorTarget const * gt,cmCustomCommand const & cc)1845 cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
1846   cmSourceFile const* sf, cmGeneratorTarget const* gt,
1847   cmCustomCommand const& cc)
1848 {
1849   std::set<std::string> allConfigInputs;
1850   std::set<std::string> allConfigOutputs;
1851 
1852   cmXCodeObject* buildPhase =
1853     this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
1854                        cmStrCat(gt->GetName(), ':', sf->GetFullPath()));
1855 
1856   auto depfilesDirectory = cmStrCat(
1857     gt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/d/");
1858   auto depfilesPrefix = cmStrCat(depfilesDirectory, buildPhase->GetId(), ".");
1859 
1860   std::string shellScript = "set -e\n";
1861   for (std::string const& configName : this->CurrentConfigurationTypes) {
1862     cmCustomCommandGenerator ccg(
1863       cc, configName, this->CurrentLocalGenerator, true, {},
1864       [&depfilesPrefix](const std::string& config, const std::string&)
1865         -> std::string { return cmStrCat(depfilesPrefix, config, ".d"); });
1866     std::vector<std::string> realDepends;
1867     realDepends.reserve(ccg.GetDepends().size());
1868     for (auto const& d : ccg.GetDepends()) {
1869       std::string dep;
1870       if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) {
1871         realDepends.emplace_back(std::move(dep));
1872       }
1873     }
1874 
1875     allConfigInputs.insert(realDepends.begin(), realDepends.end());
1876     allConfigOutputs.insert(ccg.GetByproducts().begin(),
1877                             ccg.GetByproducts().end());
1878     allConfigOutputs.insert(ccg.GetOutputs().begin(), ccg.GetOutputs().end());
1879 
1880     shellScript =
1881       cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")", configName,
1882                "\"; then :\n", this->ConstructScript(ccg), "fi\n");
1883   }
1884 
1885   if (!cc.GetDepfile().empty()) {
1886     buildPhase->AddAttribute(
1887       "dependencyFile",
1888       this->CreateString(cmStrCat(depfilesDirectory, buildPhase->GetId(),
1889                                   ".$(CONFIGURATION).d")));
1890     // to avoid spurious errors during first build,  create empty dependency
1891     // files
1892     cmSystemTools::MakeDirectory(depfilesDirectory);
1893     for (std::string const& configName : this->CurrentConfigurationTypes) {
1894       auto file = cmStrCat(depfilesPrefix, configName, ".d");
1895       if (!cmSystemTools::FileExists(file)) {
1896         cmSystemTools::Touch(file, true);
1897       }
1898     }
1899   }
1900 
1901   buildPhase->AddAttribute("buildActionMask",
1902                            this->CreateString("2147483647"));
1903   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1904   buildPhase->AddAttribute("files", buildFiles);
1905   {
1906     std::string name;
1907     if (!allConfigOutputs.empty()) {
1908       name = cmStrCat("Generate ",
1909                       this->RelativeToBinary(*allConfigOutputs.begin()));
1910     } else {
1911       name = sf->GetLocation().GetName();
1912     }
1913     buildPhase->AddAttribute("name", this->CreateString(name));
1914   }
1915   buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1916                            this->CreateString("0"));
1917   buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
1918   buildPhase->AddAttribute("shellScript", this->CreateString(shellScript));
1919   buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
1920 
1921   bool symbolic = false;
1922   {
1923     cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1924     for (std::string const& i : allConfigInputs) {
1925       inputPaths->AddUniqueObject(this->CreateString(i));
1926       if (!symbolic) {
1927         if (cmSourceFile* isf =
1928               gt->GetLocalGenerator()->GetMakefile()->GetSource(
1929                 i, cmSourceFileLocationKind::Known)) {
1930           symbolic = isf->GetPropertyAsBool("SYMBOLIC");
1931         }
1932       }
1933     }
1934     buildPhase->AddAttribute("inputPaths", inputPaths);
1935   }
1936   {
1937     cmXCodeObject* outputPaths =
1938       this->CreateObject(cmXCodeObject::OBJECT_LIST);
1939     for (std::string const& o : allConfigOutputs) {
1940       outputPaths->AddUniqueObject(this->CreateString(o));
1941       if (!symbolic) {
1942         if (cmSourceFile* osf =
1943               gt->GetLocalGenerator()->GetMakefile()->GetSource(
1944                 o, cmSourceFileLocationKind::Known)) {
1945           symbolic = osf->GetPropertyAsBool("SYMBOLIC");
1946         }
1947       }
1948     }
1949     buildPhase->AddAttribute("outputPaths", outputPaths);
1950   }
1951   if (symbolic) {
1952     buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1"));
1953   }
1954 
1955   return buildPhase;
1956 }
1957 
CreateRunScriptBuildPhase(std::string const & name,cmGeneratorTarget const * gt,std::vector<cmCustomCommand> const & commands)1958 cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase(
1959   std::string const& name, cmGeneratorTarget const* gt,
1960   std::vector<cmCustomCommand> const& commands)
1961 {
1962   if (commands.empty()) {
1963     return nullptr;
1964   }
1965 
1966   std::set<std::string> allConfigOutputs;
1967 
1968   std::string shellScript = "set -e\n";
1969   for (std::string const& configName : this->CurrentConfigurationTypes) {
1970     shellScript = cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")",
1971                            configName, "\"; then :\n");
1972     for (cmCustomCommand const& cc : commands) {
1973       cmCustomCommandGenerator ccg(cc, configName,
1974                                    this->CurrentLocalGenerator);
1975       shellScript = cmStrCat(shellScript, this->ConstructScript(ccg));
1976       allConfigOutputs.insert(ccg.GetByproducts().begin(),
1977                               ccg.GetByproducts().end());
1978     }
1979     shellScript = cmStrCat(shellScript, "fi\n");
1980   }
1981 
1982   cmXCodeObject* buildPhase =
1983     this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase,
1984                        cmStrCat(gt->GetName(), ':', name));
1985   buildPhase->AddAttribute("buildActionMask",
1986                            this->CreateString("2147483647"));
1987   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1988   buildPhase->AddAttribute("files", buildFiles);
1989   buildPhase->AddAttribute("name", this->CreateString(name));
1990   buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1991                            this->CreateString("0"));
1992   buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
1993   buildPhase->AddAttribute("shellScript", this->CreateString(shellScript));
1994   buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
1995   {
1996     cmXCodeObject* outputPaths =
1997       this->CreateObject(cmXCodeObject::OBJECT_LIST);
1998     for (std::string const& o : allConfigOutputs) {
1999       outputPaths->AddUniqueObject(this->CreateString(o));
2000     }
2001     buildPhase->AddAttribute("outputPaths", outputPaths);
2002   }
2003   buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1"));
2004 
2005   return buildPhase;
2006 }
2007 
2008 namespace {
ReplaceScriptVars(std::string & cmd)2009 void ReplaceScriptVars(std::string& cmd)
2010 {
2011   cmSystemTools::ReplaceString(cmd, "$(CONFIGURATION)", "$CONFIGURATION");
2012   cmSystemTools::ReplaceString(cmd, "$(EFFECTIVE_PLATFORM_NAME)",
2013                                "$EFFECTIVE_PLATFORM_NAME");
2014 }
2015 }
2016 
ConstructScript(cmCustomCommandGenerator const & ccg)2017 std::string cmGlobalXCodeGenerator::ConstructScript(
2018   cmCustomCommandGenerator const& ccg)
2019 {
2020   std::string script;
2021   cmLocalGenerator* lg = this->CurrentLocalGenerator;
2022   std::string wd = ccg.GetWorkingDirectory();
2023   if (wd.empty()) {
2024     wd = lg->GetCurrentBinaryDirectory();
2025   }
2026   wd = lg->ConvertToOutputFormat(wd, cmOutputConverter::SHELL);
2027   ReplaceScriptVars(wd);
2028   script = cmStrCat(script, "  cd ", wd, "\n");
2029   for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
2030     std::string cmd = ccg.GetCommand(c);
2031     if (cmd.empty()) {
2032       continue;
2033     }
2034     cmSystemTools::ReplaceString(cmd, "/./", "/");
2035     cmd = lg->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL);
2036     ccg.AppendArguments(c, cmd);
2037     ReplaceScriptVars(cmd);
2038     script = cmStrCat(script, "  ", cmd, '\n');
2039   }
2040   return script;
2041 }
2042 
2043 // This function removes each occurrence of the flag and returns the last one
2044 // (i.e., the dominant flag in GCC)
ExtractFlag(const char * flag,std::string & flags)2045 std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag,
2046                                                 std::string& flags)
2047 {
2048   std::string retFlag;
2049   std::string::size_type lastOccurancePos = flags.rfind(flag);
2050   bool saved = false;
2051   while (lastOccurancePos != std::string::npos) {
2052     // increment pos, we use lastOccurancePos to reduce search space on next
2053     // inc
2054     std::string::size_type pos = lastOccurancePos;
2055     if (pos == 0 || flags[pos - 1] == ' ') {
2056       while (pos < flags.size() && flags[pos] != ' ') {
2057         if (!saved) {
2058           retFlag += flags[pos];
2059         }
2060         flags[pos] = ' ';
2061         pos++;
2062       }
2063       saved = true;
2064     }
2065     // decrement lastOccurancePos while making sure we don't loop around
2066     // and become a very large positive number since size_type is unsigned
2067     lastOccurancePos = lastOccurancePos == 0 ? 0 : lastOccurancePos - 1;
2068     lastOccurancePos = flags.rfind(flag, lastOccurancePos);
2069   }
2070   return retFlag;
2071 }
2072 
2073 // This function removes each matching occurrence of the expression and
2074 // returns the last one (i.e., the dominant flag in GCC)
ExtractFlagRegex(const char * exp,int matchIndex,std::string & flags)2075 std::string cmGlobalXCodeGenerator::ExtractFlagRegex(const char* exp,
2076                                                      int matchIndex,
2077                                                      std::string& flags)
2078 {
2079   std::string retFlag;
2080 
2081   cmsys::RegularExpression regex(exp);
2082   assert(regex.is_valid());
2083   if (!regex.is_valid()) {
2084     return retFlag;
2085   }
2086 
2087   std::string::size_type offset = 0;
2088 
2089   while (regex.find(&flags[offset])) {
2090     const std::string::size_type startPos = offset + regex.start(matchIndex);
2091     const std::string::size_type endPos = offset + regex.end(matchIndex);
2092     const std::string::size_type size = endPos - startPos;
2093 
2094     offset = startPos + 1;
2095 
2096     retFlag.assign(flags, startPos, size);
2097     flags.replace(startPos, size, size, ' ');
2098   }
2099 
2100   return retFlag;
2101 }
2102 
2103 //----------------------------------------------------------------------------
2104 // This function strips off Xcode attributes that do not target the current
2105 // configuration
FilterConfigurationAttribute(std::string const & configName,std::string & attribute)2106 void cmGlobalXCodeGenerator::FilterConfigurationAttribute(
2107   std::string const& configName, std::string& attribute)
2108 {
2109   // Handle [variant=<config>] condition explicitly here.
2110   std::string::size_type beginVariant = attribute.find("[variant=");
2111   if (beginVariant == std::string::npos) {
2112     // There is no variant in this attribute.
2113     return;
2114   }
2115 
2116   std::string::size_type endVariant = attribute.find(']', beginVariant + 9);
2117   if (endVariant == std::string::npos) {
2118     // There is no terminating bracket.
2119     return;
2120   }
2121 
2122   // Compare the variant to the configuration.
2123   std::string variant =
2124     attribute.substr(beginVariant + 9, endVariant - beginVariant - 9);
2125   if (variant == configName) {
2126     // The variant matches the configuration so use this
2127     // attribute but drop the [variant=<config>] condition.
2128     attribute.erase(beginVariant, endVariant - beginVariant + 1);
2129   } else {
2130     // The variant does not match the configuration so
2131     // do not use this attribute.
2132     attribute.clear();
2133   }
2134 }
2135 
AddCommandsToBuildPhase(cmXCodeObject * buildphase,cmGeneratorTarget * target,std::vector<cmCustomCommand> const & commands,const char * name)2136 void cmGlobalXCodeGenerator::AddCommandsToBuildPhase(
2137   cmXCodeObject* buildphase, cmGeneratorTarget* target,
2138   std::vector<cmCustomCommand> const& commands, const char* name)
2139 {
2140   std::string dir = cmStrCat(
2141     this->CurrentLocalGenerator->GetCurrentBinaryDirectory(), "/CMakeScripts");
2142   cmSystemTools::MakeDirectory(dir);
2143   std::string makefile =
2144     cmStrCat(dir, '/', target->GetName(), '_', name, ".make");
2145 
2146   for (const auto& currentConfig : this->CurrentConfigurationTypes) {
2147     this->CreateCustomRulesMakefile(makefile.c_str(), target, commands,
2148                                     currentConfig);
2149   }
2150 
2151   std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
2152   cdir = this->ConvertToRelativeForMake(cdir);
2153   std::string makecmd =
2154     cmStrCat("make -C ", cdir, " -f ",
2155              this->ConvertToRelativeForMake((makefile + "$CONFIGURATION")),
2156              " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
2157   buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
2158   buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
2159 }
2160 
CreateCustomRulesMakefile(const char * makefileBasename,cmGeneratorTarget * target,std::vector<cmCustomCommand> const & commands,const std::string & configName)2161 void cmGlobalXCodeGenerator::CreateCustomRulesMakefile(
2162   const char* makefileBasename, cmGeneratorTarget* target,
2163   std::vector<cmCustomCommand> const& commands, const std::string& configName)
2164 {
2165   std::string makefileName = cmStrCat(makefileBasename, configName);
2166   cmGeneratedFileStream makefileStream(makefileName);
2167   if (!makefileStream) {
2168     return;
2169   }
2170   makefileStream.SetCopyIfDifferent(true);
2171   makefileStream << "# Generated by CMake, DO NOT EDIT\n";
2172   makefileStream << "# Custom rules for " << target->GetName() << "\n";
2173 
2174   // disable the implicit rules
2175   makefileStream << ".SUFFIXES: "
2176                  << "\n";
2177 
2178   // have all depend on all outputs
2179   makefileStream << "all: ";
2180   std::map<const cmCustomCommand*, std::string> tname;
2181   int count = 0;
2182   for (auto const& command : commands) {
2183     cmCustomCommandGenerator ccg(command, configName,
2184                                  this->CurrentLocalGenerator);
2185     if (ccg.GetNumberOfCommands() > 0) {
2186       const std::vector<std::string>& outputs = ccg.GetOutputs();
2187       if (!outputs.empty()) {
2188         for (auto const& output : outputs) {
2189           makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(output);
2190         }
2191       } else {
2192         std::ostringstream str;
2193         str << "_buildpart_" << count++;
2194         tname[&ccg.GetCC()] = target->GetName() + str.str();
2195         makefileStream << "\\\n\t" << tname[&ccg.GetCC()];
2196       }
2197     }
2198   }
2199   makefileStream << "\n\n";
2200 
2201   auto depfilesDirectory =
2202     cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
2203              "/CMakeFiles/d/");
2204 
2205   for (auto const& command : commands) {
2206     cmCustomCommandGenerator ccg(
2207       command, configName, this->CurrentLocalGenerator, true, {},
2208       [this, &depfilesDirectory](const std::string& config,
2209                                  const std::string& file) -> std::string {
2210         return cmStrCat(
2211           depfilesDirectory,
2212           this->GetObjectId(cmXCodeObject::PBXShellScriptBuildPhase, file),
2213           ".", config, ".d");
2214       });
2215 
2216     auto depfile = ccg.GetInternalDepfile();
2217     if (!depfile.empty()) {
2218       makefileStream << "include "
2219                      << cmSystemTools::ConvertToOutputPath(depfile) << "\n\n";
2220 
2221       cmSystemTools::MakeDirectory(depfilesDirectory);
2222       if (!cmSystemTools::FileExists(depfile)) {
2223         cmSystemTools::Touch(depfile, true);
2224       }
2225     }
2226 
2227     std::vector<std::string> realDepends;
2228     realDepends.reserve(ccg.GetDepends().size());
2229     for (auto const& d : ccg.GetDepends()) {
2230       std::string dep;
2231       if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) {
2232         realDepends.emplace_back(std::move(dep));
2233       }
2234     }
2235 
2236     if (ccg.GetNumberOfCommands() > 0) {
2237       makefileStream << "\n";
2238       const std::vector<std::string>& outputs = ccg.GetOutputs();
2239       if (!outputs.empty()) {
2240         // There is at least one output, start the rule for it
2241         const char* sep = "";
2242         for (auto const& output : outputs) {
2243           makefileStream << sep << this->ConvertToRelativeForMake(output);
2244           sep = " ";
2245         }
2246         makefileStream << ": ";
2247       } else {
2248         // There are no outputs.  Use the generated force rule name.
2249         makefileStream << tname[&ccg.GetCC()] << ": ";
2250       }
2251       for (auto const& dep : realDepends) {
2252         makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep);
2253       }
2254       makefileStream << "\n";
2255 
2256       if (const char* comment = ccg.GetComment()) {
2257         std::string echo_cmd =
2258           cmStrCat("echo ",
2259                    (this->CurrentLocalGenerator->EscapeForShell(
2260                      comment, ccg.GetCC().GetEscapeAllowMakeVars())));
2261         makefileStream << "\t" << echo_cmd << "\n";
2262       }
2263 
2264       // Add each command line to the set of commands.
2265       for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
2266         // Build the command line in a single string.
2267         std::string cmd2 = ccg.GetCommand(c);
2268         cmSystemTools::ReplaceString(cmd2, "/./", "/");
2269         cmd2 = this->ConvertToRelativeForMake(cmd2);
2270         std::string cmd;
2271         std::string wd = ccg.GetWorkingDirectory();
2272         if (!wd.empty()) {
2273           cmd += "cd ";
2274           cmd += this->ConvertToRelativeForMake(wd);
2275           cmd += " && ";
2276         }
2277         cmd += cmd2;
2278         ccg.AppendArguments(c, cmd);
2279         makefileStream << "\t" << cmd << "\n";
2280       }
2281 
2282       // Symbolic inputs are not expected to exist, so add dummy rules.
2283       for (auto const& dep : realDepends) {
2284         if (cmSourceFile* dsf =
2285               target->GetLocalGenerator()->GetMakefile()->GetSource(
2286                 dep, cmSourceFileLocationKind::Known)) {
2287           if (dsf->GetPropertyAsBool("SYMBOLIC")) {
2288             makefileStream << this->ConvertToRelativeForMake(dep) << ":\n";
2289           }
2290         }
2291       }
2292     }
2293   }
2294 }
2295 
AddPositionIndependentLinkAttribute(cmGeneratorTarget * target,cmXCodeObject * buildSettings,const std::string & configName)2296 void cmGlobalXCodeGenerator::AddPositionIndependentLinkAttribute(
2297   cmGeneratorTarget* target, cmXCodeObject* buildSettings,
2298   const std::string& configName)
2299 {
2300   // For now, only EXECUTABLE is concerned
2301   if (target->GetType() != cmStateEnums::EXECUTABLE) {
2302     return;
2303   }
2304 
2305   const char* PICValue = target->GetLinkPIEProperty(configName);
2306   if (PICValue == nullptr) {
2307     // POSITION_INDEPENDENT_CODE is not set
2308     return;
2309   }
2310 
2311   buildSettings->AddAttribute(
2312     "LD_NO_PIE", this->CreateString(cmIsOn(PICValue) ? "NO" : "YES"));
2313 }
2314 
CreateBuildSettings(cmGeneratorTarget * gtgt,cmXCodeObject * buildSettings,const std::string & configName)2315 void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
2316                                                  cmXCodeObject* buildSettings,
2317                                                  const std::string& configName)
2318 {
2319   if (!gtgt->IsInBuildSystem()) {
2320     return;
2321   }
2322 
2323   std::string defFlags;
2324   bool shared = ((gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) ||
2325                  (gtgt->GetType() == cmStateEnums::MODULE_LIBRARY));
2326   bool binary = ((gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) ||
2327                  (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) ||
2328                  (gtgt->GetType() == cmStateEnums::EXECUTABLE) || shared);
2329 
2330   // Compute the compilation flags for each language.
2331   std::set<std::string> languages;
2332   gtgt->GetLanguages(languages, configName);
2333   std::map<std::string, std::string> cflags;
2334   for (auto const& lang : languages) {
2335     std::string& flags = cflags[lang];
2336 
2337     // Add language-specific flags.
2338     this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang,
2339                                                   configName);
2340 
2341     if (gtgt->IsIPOEnabled(lang, configName)) {
2342       this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
2343     }
2344 
2345     // Add shared-library flags if needed.
2346     this->CurrentLocalGenerator->AddCMP0018Flags(flags, gtgt, lang,
2347                                                  configName);
2348 
2349     this->CurrentLocalGenerator->AddVisibilityPresetFlags(flags, gtgt, lang);
2350 
2351     this->CurrentLocalGenerator->AddCompileOptions(flags, gtgt, lang,
2352                                                    configName);
2353   }
2354 
2355   std::string llang = gtgt->GetLinkerLanguage(configName);
2356   if (binary && llang.empty()) {
2357     cmSystemTools::Error(
2358       "CMake can not determine linker language for target: " +
2359       gtgt->GetName());
2360     return;
2361   }
2362   std::string const& langForPreprocessor = llang;
2363 
2364   if (gtgt->IsIPOEnabled(llang, configName)) {
2365     const char* ltoValue =
2366       this->CurrentMakefile->IsOn("_CMAKE_LTO_THIN") ? "YES_THIN" : "YES";
2367     buildSettings->AddAttribute("LLVM_LTO", this->CreateString(ltoValue));
2368   }
2369 
2370   // Handle PIE linker configuration
2371   this->AddPositionIndependentLinkAttribute(gtgt, buildSettings, configName);
2372 
2373   // Add define flags
2374   this->CurrentLocalGenerator->AppendFlags(
2375     defFlags, this->CurrentMakefile->GetDefineFlags());
2376 
2377   // Add preprocessor definitions for this target and configuration.
2378   BuildObjectListOrString ppDefs(this, true);
2379   this->AppendDefines(
2380     ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"");
2381   if (const std::string* exportMacro = gtgt->GetExportMacro()) {
2382     // Add the export symbol definition for shared library objects.
2383     this->AppendDefines(ppDefs, exportMacro->c_str());
2384   }
2385   std::vector<std::string> targetDefines;
2386   if (!langForPreprocessor.empty()) {
2387     gtgt->GetCompileDefinitions(targetDefines, configName,
2388                                 langForPreprocessor);
2389   }
2390   this->AppendDefines(ppDefs, targetDefines);
2391   buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS",
2392                               ppDefs.CreateList());
2393 
2394   std::string extraLinkOptionsVar;
2395   std::string extraLinkOptions;
2396   if (gtgt->GetType() == cmStateEnums::EXECUTABLE) {
2397     extraLinkOptionsVar = "CMAKE_EXE_LINKER_FLAGS";
2398   } else if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
2399     extraLinkOptionsVar = "CMAKE_SHARED_LINKER_FLAGS";
2400   } else if (gtgt->GetType() == cmStateEnums::MODULE_LIBRARY) {
2401     extraLinkOptionsVar = "CMAKE_MODULE_LINKER_FLAGS";
2402   }
2403   if (!extraLinkOptionsVar.empty()) {
2404     this->CurrentLocalGenerator->AddConfigVariableFlags(
2405       extraLinkOptions, extraLinkOptionsVar, configName);
2406   }
2407 
2408   if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
2409       gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) {
2410     this->CurrentLocalGenerator->GetStaticLibraryFlags(
2411       extraLinkOptions, configName, llang, gtgt);
2412   } else {
2413     cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
2414     if (targetLinkFlags) {
2415       this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
2416                                                *targetLinkFlags);
2417     }
2418     if (!configName.empty()) {
2419       std::string linkFlagsVar =
2420         cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(configName));
2421       if (cmValue linkFlags = gtgt->GetProperty(linkFlagsVar)) {
2422         this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, *linkFlags);
2423       }
2424     }
2425     std::vector<std::string> opts;
2426     gtgt->GetLinkOptions(opts, configName, llang);
2427     // LINK_OPTIONS are escaped.
2428     this->CurrentLocalGenerator->AppendCompileOptions(extraLinkOptions, opts);
2429   }
2430 
2431   // Set target-specific architectures.
2432   std::vector<std::string> archs;
2433   gtgt->GetAppleArchs(configName, archs);
2434 
2435   if (!archs.empty()) {
2436     // Enable ARCHS attribute.
2437     buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("NO"));
2438 
2439     // Store ARCHS value.
2440     if (archs.size() == 1) {
2441       buildSettings->AddAttribute("ARCHS", this->CreateString(archs[0]));
2442     } else {
2443       cmXCodeObject* archObjects =
2444         this->CreateObject(cmXCodeObject::OBJECT_LIST);
2445       for (auto& arch : archs) {
2446         archObjects->AddObject(this->CreateString(arch));
2447       }
2448       buildSettings->AddAttribute("ARCHS", archObjects);
2449     }
2450   }
2451 
2452   // Get the product name components.
2453   std::string pnprefix;
2454   std::string pnbase;
2455   std::string pnsuffix;
2456   gtgt->GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName);
2457 
2458   cmValue version = gtgt->GetProperty("VERSION");
2459   cmValue soversion = gtgt->GetProperty("SOVERSION");
2460   if (!gtgt->HasSOName(configName) || gtgt->IsFrameworkOnApple()) {
2461     version = nullptr;
2462     soversion = nullptr;
2463   }
2464   if (version && !soversion) {
2465     soversion = version;
2466   }
2467   if (!version && soversion) {
2468     version = soversion;
2469   }
2470 
2471   std::string realName = pnbase;
2472   std::string soName = pnbase;
2473   if (version && soversion) {
2474     realName += ".";
2475     realName += *version;
2476     soName += ".";
2477     soName += *soversion;
2478   }
2479 
2480   // Set attributes to specify the proper name for the target.
2481   std::string pndir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
2482   if (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY ||
2483       gtgt->GetType() == cmStateEnums::SHARED_LIBRARY ||
2484       gtgt->GetType() == cmStateEnums::MODULE_LIBRARY ||
2485       gtgt->GetType() == cmStateEnums::EXECUTABLE) {
2486     if (!gtgt->UsesDefaultOutputDir(configName,
2487                                     cmStateEnums::RuntimeBinaryArtifact)) {
2488       std::string pncdir = gtgt->GetDirectory(configName);
2489       buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
2490                                   this->CreateString(pncdir));
2491     }
2492 
2493     if (gtgt->IsFrameworkOnApple() || gtgt->IsCFBundleOnApple()) {
2494       pnprefix = "";
2495     }
2496 
2497     buildSettings->AddAttribute("EXECUTABLE_PREFIX",
2498                                 this->CreateString(pnprefix));
2499     buildSettings->AddAttribute("EXECUTABLE_SUFFIX",
2500                                 this->CreateString(pnsuffix));
2501   } else if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2502     pnprefix = "lib";
2503     pnbase = gtgt->GetName();
2504     pnsuffix = ".a";
2505 
2506     std::string pncdir = this->GetObjectsDirectory(
2507       this->CurrentProject, configName, gtgt, OBJECT_LIBRARY_ARTIFACT_DIR);
2508     buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
2509                                 this->CreateString(pncdir));
2510   }
2511 
2512   // Store the product name for all target types.
2513   buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(realName));
2514   buildSettings->AddAttribute("SYMROOT", this->CreateString(pndir));
2515 
2516   // Handle settings for each target type.
2517   switch (gtgt->GetType()) {
2518     case cmStateEnums::STATIC_LIBRARY:
2519       if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
2520         std::string fw_version = gtgt->GetFrameworkVersion();
2521         buildSettings->AddAttribute("FRAMEWORK_VERSION",
2522                                     this->CreateString(fw_version));
2523         cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2524         if (ext) {
2525           buildSettings->AddAttribute("WRAPPER_EXTENSION",
2526                                       this->CreateString(*ext));
2527         }
2528 
2529         std::string plist = this->ComputeInfoPListLocation(gtgt);
2530         // Xcode will create the final version of Info.plist at build time,
2531         // so let it replace the framework name. This avoids creating
2532         // a per-configuration Info.plist file.
2533         this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
2534           gtgt, "$(EXECUTABLE_NAME)", plist);
2535         buildSettings->AddAttribute("INFOPLIST_FILE",
2536                                     this->CreateString(plist));
2537         buildSettings->AddAttribute("MACH_O_TYPE",
2538                                     this->CreateString("staticlib"));
2539       } else {
2540         buildSettings->AddAttribute("LIBRARY_STYLE",
2541                                     this->CreateString("STATIC"));
2542       }
2543       break;
2544 
2545     case cmStateEnums::OBJECT_LIBRARY: {
2546       buildSettings->AddAttribute("LIBRARY_STYLE",
2547                                   this->CreateString("STATIC"));
2548       break;
2549     }
2550 
2551     case cmStateEnums::MODULE_LIBRARY: {
2552       buildSettings->AddAttribute("LIBRARY_STYLE",
2553                                   this->CreateString("BUNDLE"));
2554       if (gtgt->IsCFBundleOnApple()) {
2555         // It turns out that a BUNDLE is basically the same
2556         // in many ways as an application bundle, as far as
2557         // link flags go
2558         std::string createFlags = this->LookupFlags(
2559           "CMAKE_SHARED_MODULE_CREATE_", llang, "_FLAGS", "-bundle");
2560         if (!createFlags.empty()) {
2561           extraLinkOptions += " ";
2562           extraLinkOptions += createFlags;
2563         }
2564         cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2565         if (ext) {
2566           buildSettings->AddAttribute("WRAPPER_EXTENSION",
2567                                       this->CreateString(*ext));
2568         }
2569         std::string plist = this->ComputeInfoPListLocation(gtgt);
2570         // Xcode will create the final version of Info.plist at build time,
2571         // so let it replace the cfbundle name. This avoids creating
2572         // a per-configuration Info.plist file. The cfbundle plist
2573         // is very similar to the application bundle plist
2574         this->CurrentLocalGenerator->GenerateAppleInfoPList(
2575           gtgt, "$(EXECUTABLE_NAME)", plist);
2576         buildSettings->AddAttribute("INFOPLIST_FILE",
2577                                     this->CreateString(plist));
2578       } else {
2579         buildSettings->AddAttribute("MACH_O_TYPE",
2580                                     this->CreateString("mh_bundle"));
2581         buildSettings->AddAttribute("GCC_DYNAMIC_NO_PIC",
2582                                     this->CreateString("NO"));
2583         // Add the flags to create an executable.
2584         std::string createFlags =
2585           this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
2586         if (!createFlags.empty()) {
2587           extraLinkOptions += " ";
2588           extraLinkOptions += createFlags;
2589         }
2590       }
2591       break;
2592     }
2593     case cmStateEnums::SHARED_LIBRARY: {
2594       if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
2595         std::string fw_version = gtgt->GetFrameworkVersion();
2596         buildSettings->AddAttribute("FRAMEWORK_VERSION",
2597                                     this->CreateString(fw_version));
2598         cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2599         if (ext) {
2600           buildSettings->AddAttribute("WRAPPER_EXTENSION",
2601                                       this->CreateString(*ext));
2602         }
2603 
2604         std::string plist = this->ComputeInfoPListLocation(gtgt);
2605         // Xcode will create the final version of Info.plist at build time,
2606         // so let it replace the framework name. This avoids creating
2607         // a per-configuration Info.plist file.
2608         this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
2609           gtgt, "$(EXECUTABLE_NAME)", plist);
2610         buildSettings->AddAttribute("INFOPLIST_FILE",
2611                                     this->CreateString(plist));
2612       } else {
2613         // Add the flags to create a shared library.
2614         std::string createFlags = this->LookupFlags(
2615           "CMAKE_SHARED_LIBRARY_CREATE_", llang, "_FLAGS", "-dynamiclib");
2616         if (!createFlags.empty()) {
2617           extraLinkOptions += " ";
2618           extraLinkOptions += createFlags;
2619         }
2620       }
2621 
2622       buildSettings->AddAttribute("LIBRARY_STYLE",
2623                                   this->CreateString("DYNAMIC"));
2624       break;
2625     }
2626     case cmStateEnums::EXECUTABLE: {
2627       // Add the flags to create an executable.
2628       std::string createFlags =
2629         this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
2630       if (!createFlags.empty()) {
2631         extraLinkOptions += " ";
2632         extraLinkOptions += createFlags;
2633       }
2634 
2635       // Handle bundles and normal executables separately.
2636       if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
2637         cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2638         if (ext) {
2639           buildSettings->AddAttribute("WRAPPER_EXTENSION",
2640                                       this->CreateString(*ext));
2641         }
2642         std::string plist = this->ComputeInfoPListLocation(gtgt);
2643         // Xcode will create the final version of Info.plist at build time,
2644         // so let it replace the executable name.  This avoids creating
2645         // a per-configuration Info.plist file.
2646         this->CurrentLocalGenerator->GenerateAppleInfoPList(
2647           gtgt, "$(EXECUTABLE_NAME)", plist);
2648         buildSettings->AddAttribute("INFOPLIST_FILE",
2649                                     this->CreateString(plist));
2650       }
2651     } break;
2652     default:
2653       break;
2654   }
2655 
2656   BuildObjectListOrString dirs(this, true);
2657   BuildObjectListOrString fdirs(this, true);
2658   BuildObjectListOrString sysdirs(this, true);
2659   BuildObjectListOrString sysfdirs(this, true);
2660   const bool emitSystemIncludes = this->XcodeVersion >= 83;
2661 
2662   std::vector<std::string> includes;
2663   if (!langForPreprocessor.empty()) {
2664     this->CurrentLocalGenerator->GetIncludeDirectories(
2665       includes, gtgt, langForPreprocessor, configName);
2666   }
2667   std::set<std::string> emitted;
2668   emitted.insert("/System/Library/Frameworks");
2669 
2670   for (auto& include : includes) {
2671     if (this->NameResolvesToFramework(include)) {
2672       std::string frameworkDir = cmStrCat(include, "/../");
2673       frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
2674       if (emitted.insert(frameworkDir).second) {
2675         std::string incpath = this->XCodeEscapePath(frameworkDir);
2676         if (emitSystemIncludes &&
2677             gtgt->IsSystemIncludeDirectory(frameworkDir, configName,
2678                                            langForPreprocessor)) {
2679           sysfdirs.Add(incpath);
2680         } else {
2681           fdirs.Add(incpath);
2682         }
2683       }
2684     } else {
2685       std::string incpath = this->XCodeEscapePath(include);
2686       if (emitSystemIncludes &&
2687           gtgt->IsSystemIncludeDirectory(include, configName,
2688                                          langForPreprocessor)) {
2689         sysdirs.Add(incpath);
2690       } else {
2691         dirs.Add(incpath);
2692       }
2693     }
2694   }
2695   // Add framework search paths needed for linking.
2696   if (cmComputeLinkInformation* cli = gtgt->GetLinkInformation(configName)) {
2697     for (auto const& fwDir : cli->GetFrameworkPaths()) {
2698       if (emitted.insert(fwDir).second) {
2699         std::string incpath = this->XCodeEscapePath(fwDir);
2700         if (emitSystemIncludes &&
2701             gtgt->IsSystemIncludeDirectory(fwDir, configName,
2702                                            langForPreprocessor)) {
2703           sysfdirs.Add(incpath);
2704         } else {
2705           fdirs.Add(incpath);
2706         }
2707       }
2708     }
2709   }
2710   if (!fdirs.IsEmpty()) {
2711     buildSettings->AddAttribute("FRAMEWORK_SEARCH_PATHS", fdirs.CreateList());
2712   }
2713   if (!dirs.IsEmpty()) {
2714     buildSettings->AddAttribute("HEADER_SEARCH_PATHS", dirs.CreateList());
2715   }
2716   if (!sysfdirs.IsEmpty()) {
2717     buildSettings->AddAttribute("SYSTEM_FRAMEWORK_SEARCH_PATHS",
2718                                 sysfdirs.CreateList());
2719   }
2720   if (!sysdirs.IsEmpty()) {
2721     buildSettings->AddAttribute("SYSTEM_HEADER_SEARCH_PATHS",
2722                                 sysdirs.CreateList());
2723   }
2724 
2725   if (this->XcodeVersion >= 60 && !emitSystemIncludes) {
2726     // Add those per-language flags in addition to HEADER_SEARCH_PATHS to gain
2727     // system include directory awareness. We need to also keep on setting
2728     // HEADER_SEARCH_PATHS to work around a missing compile options flag for
2729     // GNU assembly files (#16449)
2730     for (auto const& language : languages) {
2731       std::string includeFlags = this->CurrentLocalGenerator->GetIncludeFlags(
2732         includes, gtgt, language, configName);
2733 
2734       if (!includeFlags.empty()) {
2735         cflags[language] += " " + includeFlags;
2736       }
2737     }
2738   }
2739 
2740   bool same_gflags = true;
2741   std::map<std::string, std::string> gflags;
2742   std::string const* last_gflag = nullptr;
2743   std::string optLevel = "0";
2744 
2745   // Minimal map of flags to build settings.
2746   for (auto const& language : languages) {
2747     std::string& flags = cflags[language];
2748     std::string& gflag = gflags[language];
2749     std::string oflag =
2750       this->ExtractFlagRegex("(^| )(-Ofast|-Os|-O[0-9]*)( |$)", 2, flags);
2751     if (oflag.size() == 2) {
2752       optLevel = "1";
2753     } else if (oflag.size() > 2) {
2754       optLevel = oflag.substr(2);
2755     }
2756     gflag = this->ExtractFlag("-g", flags);
2757     // put back gdwarf-2 if used since there is no way
2758     // to represent it in the gui, but we still want debug yes
2759     if (gflag == "-gdwarf-2") {
2760       flags += " ";
2761       flags += gflag;
2762     }
2763     if (last_gflag && *last_gflag != gflag) {
2764       same_gflags = false;
2765     }
2766     last_gflag = &gflag;
2767   }
2768 
2769   const char* debugStr = "YES";
2770   if (!same_gflags) {
2771     // We can't set the Xcode flag differently depending on the language,
2772     // so put them back in this case.
2773     for (auto const& language : languages) {
2774       cflags[language] += " ";
2775       cflags[language] += gflags[language];
2776     }
2777     debugStr = "NO";
2778   } else if (last_gflag && (last_gflag->empty() || *last_gflag == "-g0")) {
2779     debugStr = "NO";
2780   }
2781 
2782   // extract C++ stdlib
2783   for (auto const& language : languages) {
2784     if (language != "CXX" && language != "OBJCXX") {
2785       continue;
2786     }
2787     std::string& flags = cflags[language];
2788 
2789     auto stdlib =
2790       this->ExtractFlagRegex("(^| )(-stdlib=[^ ]+)( |$)", 2, flags);
2791     if (stdlib.size() > 8) {
2792       const auto cxxLibrary = stdlib.substr(8);
2793       if (language == "CXX" ||
2794           !buildSettings->GetAttribute("CLANG_CXX_LIBRARY")) {
2795         buildSettings->AddAttribute("CLANG_CXX_LIBRARY",
2796                                     this->CreateString(cxxLibrary));
2797       }
2798     }
2799   }
2800 
2801   buildSettings->AddAttribute("COMBINE_HIDPI_IMAGES",
2802                               this->CreateString("YES"));
2803   buildSettings->AddAttribute("GCC_GENERATE_DEBUGGING_SYMBOLS",
2804                               this->CreateString(debugStr));
2805   buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL",
2806                               this->CreateString(optLevel));
2807   buildSettings->AddAttribute("GCC_SYMBOLS_PRIVATE_EXTERN",
2808                               this->CreateString("NO"));
2809   buildSettings->AddAttribute("GCC_INLINES_ARE_PRIVATE_EXTERN",
2810                               this->CreateString("NO"));
2811 
2812   for (auto const& language : languages) {
2813     std::string flags = cflags[language] + " " + defFlags;
2814     if (language == "CXX" || language == "OBJCXX") {
2815       if (language == "CXX" ||
2816           !buildSettings->GetAttribute("OTHER_CPLUSPLUSFLAGS")) {
2817         buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS",
2818                                     this->CreateString(flags));
2819       }
2820     } else if (language == "Fortran") {
2821       buildSettings->AddAttribute("IFORT_OTHER_FLAGS",
2822                                   this->CreateString(flags));
2823     } else if (language == "C" || language == "OBJC") {
2824       if (language == "C" || !buildSettings->GetAttribute("OTHER_CFLAGS")) {
2825         buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags));
2826       }
2827     } else if (language == "Swift") {
2828       buildSettings->AddAttribute("OTHER_SWIFT_FLAGS",
2829                                   this->CreateString(flags));
2830     }
2831   }
2832 
2833   // Add Fortran source format attribute if property is set.
2834   const char* format = nullptr;
2835   std::string const& tgtfmt = gtgt->GetSafeProperty("Fortran_FORMAT");
2836   switch (cmOutputConverter::GetFortranFormat(tgtfmt)) {
2837     case cmOutputConverter::FortranFormatFixed:
2838       format = "fixed";
2839       break;
2840     case cmOutputConverter::FortranFormatFree:
2841       format = "free";
2842       break;
2843     default:
2844       break;
2845   }
2846   if (format) {
2847     buildSettings->AddAttribute("IFORT_LANG_SRCFMT",
2848                                 this->CreateString(format));
2849   }
2850 
2851   // Create the INSTALL_PATH attribute.
2852   std::string install_name_dir;
2853   if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
2854     // Get the install_name directory for the build tree.
2855     install_name_dir = gtgt->GetInstallNameDirForBuildTree(configName);
2856     // Xcode doesn't create the correct install_name in some cases.
2857     // That is, if the INSTALL_PATH is empty, or if we have versioning
2858     // of dylib libraries, we want to specify the install_name.
2859     // This is done by adding a link flag to create an install_name
2860     // with just the library soname.
2861     std::string install_name;
2862     if (!install_name_dir.empty()) {
2863       // Convert to a path for the native build tool.
2864       cmSystemTools::ConvertToUnixSlashes(install_name_dir);
2865       install_name += install_name_dir;
2866       install_name += "/";
2867     }
2868     install_name += gtgt->GetSOName(configName);
2869 
2870     if ((realName != soName) || install_name_dir.empty()) {
2871       install_name_dir = "";
2872       extraLinkOptions += " -install_name ";
2873       extraLinkOptions += XCodeEscapePath(install_name);
2874     }
2875   }
2876   buildSettings->AddAttribute("INSTALL_PATH",
2877                               this->CreateString(install_name_dir));
2878 
2879   // Create the LD_RUNPATH_SEARCH_PATHS
2880   cmComputeLinkInformation* pcli = gtgt->GetLinkInformation(configName);
2881   if (pcli) {
2882     std::string search_paths;
2883     std::vector<std::string> runtimeDirs;
2884     pcli->GetRPath(runtimeDirs, false);
2885     // runpath dirs needs to be unique to prevent corruption
2886     std::set<std::string> unique_dirs;
2887 
2888     for (auto runpath : runtimeDirs) {
2889       runpath = this->ExpandCFGIntDir(runpath, configName);
2890 
2891       if (unique_dirs.find(runpath) == unique_dirs.end()) {
2892         unique_dirs.insert(runpath);
2893         if (!search_paths.empty()) {
2894           search_paths += " ";
2895         }
2896         search_paths += this->XCodeEscapePath(runpath);
2897       }
2898     }
2899     if (!search_paths.empty()) {
2900       buildSettings->AddAttribute("LD_RUNPATH_SEARCH_PATHS",
2901                                   this->CreateString(search_paths));
2902     }
2903   }
2904 
2905   buildSettings->AddAttribute(this->GetTargetLinkFlagsVar(gtgt),
2906                               this->CreateString(extraLinkOptions));
2907   buildSettings->AddAttribute("OTHER_REZFLAGS", this->CreateString(""));
2908   buildSettings->AddAttribute("SECTORDER_FLAGS", this->CreateString(""));
2909   buildSettings->AddAttribute("USE_HEADERMAP", this->CreateString("NO"));
2910   cmXCodeObject* group = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2911   group->AddObject(this->CreateString("$(inherited)"));
2912   buildSettings->AddAttribute("WARNING_CFLAGS", group);
2913 
2914   // Runtime version information.
2915   if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
2916     int major;
2917     int minor;
2918     int patch;
2919 
2920     // MACHO_CURRENT_VERSION or VERSION -> current_version
2921     gtgt->GetTargetVersionFallback("MACHO_CURRENT_VERSION", "VERSION", major,
2922                                    minor, patch);
2923     std::ostringstream v;
2924 
2925     // Xcode always wants at least 1.0.0 or nothing
2926     if (!(major == 0 && minor == 0 && patch == 0)) {
2927       v << major << "." << minor << "." << patch;
2928     }
2929     buildSettings->AddAttribute("DYLIB_CURRENT_VERSION",
2930                                 this->CreateString(v.str()));
2931 
2932     // MACHO_COMPATIBILITY_VERSION or SOVERSION -> compatibility_version
2933     gtgt->GetTargetVersionFallback("MACHO_COMPATIBILITY_VERSION", "SOVERSION",
2934                                    major, minor, patch);
2935     std::ostringstream vso;
2936 
2937     // Xcode always wants at least 1.0.0 or nothing
2938     if (!(major == 0 && minor == 0 && patch == 0)) {
2939       vso << major << "." << minor << "." << patch;
2940     }
2941     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
2942                                 this->CreateString(vso.str()));
2943   }
2944 
2945   // Precompile Headers
2946   std::string pchHeader = gtgt->GetPchHeader(configName, llang);
2947   if (!pchHeader.empty()) {
2948     buildSettings->AddAttribute("GCC_PREFIX_HEADER",
2949                                 this->CreateString(pchHeader));
2950     buildSettings->AddAttribute("GCC_PRECOMPILE_PREFIX_HEADER",
2951                                 this->CreateString("YES"));
2952   }
2953 
2954   // put this last so it can override existing settings
2955   // Convert "XCODE_ATTRIBUTE_*" properties directly.
2956   {
2957     for (auto const& prop : gtgt->GetPropertyKeys()) {
2958       if (cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) {
2959         std::string attribute = prop.substr(16);
2960         this->FilterConfigurationAttribute(configName, attribute);
2961         if (!attribute.empty()) {
2962           std::string const& pr = gtgt->GetSafeProperty(prop);
2963           std::string processed = cmGeneratorExpression::Evaluate(
2964             pr, this->CurrentLocalGenerator, configName);
2965           buildSettings->AddAttribute(attribute,
2966                                       this->CreateString(processed));
2967         }
2968       }
2969     }
2970   }
2971 }
2972 
CreateUtilityTarget(cmGeneratorTarget * gtgt)2973 cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(
2974   cmGeneratorTarget* gtgt)
2975 {
2976   cmXCodeObject* shellBuildPhase = this->CreateObject(
2977     cmXCodeObject::PBXShellScriptBuildPhase, gtgt->GetName());
2978   shellBuildPhase->AddAttribute("buildActionMask",
2979                                 this->CreateString("2147483647"));
2980   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2981   shellBuildPhase->AddAttribute("files", buildFiles);
2982   cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2983   shellBuildPhase->AddAttribute("inputPaths", inputPaths);
2984   cmXCodeObject* outputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2985   shellBuildPhase->AddAttribute("outputPaths", outputPaths);
2986   shellBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
2987                                 this->CreateString("0"));
2988   shellBuildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
2989   shellBuildPhase->AddAttribute(
2990     "shellScript", this->CreateString("# shell script goes here\nexit 0"));
2991   shellBuildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
2992 
2993   cmXCodeObject* target =
2994     this->CreateObject(cmXCodeObject::PBXAggregateTarget);
2995   target->SetComment(gtgt->GetName());
2996   cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2997   std::vector<cmXCodeObject*> emptyContentVector;
2998   this->CreateCustomCommands(buildPhases, nullptr, nullptr, nullptr,
2999                              emptyContentVector, nullptr, gtgt);
3000   target->AddAttribute("buildPhases", buildPhases);
3001   this->AddConfigurations(target, gtgt);
3002   cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3003   target->AddAttribute("dependencies", dependencies);
3004   target->AddAttribute("name", this->CreateString(gtgt->GetName()));
3005   target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
3006   target->SetTarget(gtgt);
3007   this->XCodeObjectMap[gtgt] = target;
3008 
3009   // Add source files without build rules for editing convenience.
3010   if (gtgt->GetType() != cmStateEnums::GLOBAL_TARGET &&
3011       gtgt->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
3012     std::vector<cmSourceFile*> sources;
3013     if (!gtgt->GetConfigCommonSourceFilesForXcode(sources)) {
3014       return nullptr;
3015     }
3016 
3017     // Add CMakeLists.txt file for user convenience.
3018     this->AddXCodeProjBuildRule(gtgt, sources);
3019 
3020     for (auto sourceFile : sources) {
3021       if (!sourceFile->GetIsGenerated()) {
3022         this->CreateXCodeFileReference(sourceFile, gtgt);
3023       }
3024     }
3025   }
3026 
3027   target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
3028 
3029   return target;
3030 }
3031 
AddConfigurations(cmXCodeObject * target,cmGeneratorTarget * gtgt)3032 std::string cmGlobalXCodeGenerator::AddConfigurations(cmXCodeObject* target,
3033                                                       cmGeneratorTarget* gtgt)
3034 {
3035   std::vector<std::string> const configVector = cmExpandedList(
3036     this->CurrentMakefile->GetRequiredDefinition("CMAKE_CONFIGURATION_TYPES"));
3037   cmXCodeObject* configlist =
3038     this->CreateObject(cmXCodeObject::XCConfigurationList);
3039   cmXCodeObject* buildConfigurations =
3040     this->CreateObject(cmXCodeObject::OBJECT_LIST);
3041   configlist->AddAttribute("buildConfigurations", buildConfigurations);
3042   std::string comment = cmStrCat("Build configuration list for ",
3043                                  cmXCodeObject::PBXTypeNames[target->GetIsA()],
3044                                  " \"", gtgt->GetName(), '"');
3045   configlist->SetComment(comment);
3046   target->AddAttribute("buildConfigurationList",
3047                        this->CreateObjectReference(configlist));
3048   for (auto const& i : configVector) {
3049     cmXCodeObject* config =
3050       this->CreateObject(cmXCodeObject::XCBuildConfiguration);
3051     buildConfigurations->AddObject(config);
3052     cmXCodeObject* buildSettings =
3053       this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3054     this->CreateBuildSettings(gtgt, buildSettings, i);
3055     config->AddAttribute("name", this->CreateString(i));
3056     config->SetComment(i);
3057     config->AddAttribute("buildSettings", buildSettings);
3058   }
3059   if (!configVector.empty()) {
3060     configlist->AddAttribute("defaultConfigurationName",
3061                              this->CreateString(configVector[0]));
3062     configlist->AddAttribute("defaultConfigurationIsVisible",
3063                              this->CreateString("0"));
3064     return configVector[0];
3065   }
3066   return "";
3067 }
3068 
GetTargetLinkFlagsVar(cmGeneratorTarget const * target) const3069 const char* cmGlobalXCodeGenerator::GetTargetLinkFlagsVar(
3070   cmGeneratorTarget const* target) const
3071 {
3072   if (this->XcodeVersion >= 60 &&
3073       (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
3074        target->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
3075     return "OTHER_LIBTOOLFLAGS";
3076   }
3077   return "OTHER_LDFLAGS";
3078 }
3079 
GetTargetFileType(cmGeneratorTarget * target)3080 const char* cmGlobalXCodeGenerator::GetTargetFileType(
3081   cmGeneratorTarget* target)
3082 {
3083   if (cmValue e = target->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
3084     return e->c_str();
3085   }
3086 
3087   switch (target->GetType()) {
3088     case cmStateEnums::OBJECT_LIBRARY:
3089       return "archive.ar";
3090     case cmStateEnums::STATIC_LIBRARY:
3091       return (target->GetPropertyAsBool("FRAMEWORK") ? "wrapper.framework"
3092                                                      : "archive.ar");
3093     case cmStateEnums::MODULE_LIBRARY:
3094       if (target->IsXCTestOnApple()) {
3095         return "wrapper.cfbundle";
3096       }
3097       if (target->IsCFBundleOnApple()) {
3098         return "wrapper.plug-in";
3099       }
3100       return "compiled.mach-o.executable";
3101     case cmStateEnums::SHARED_LIBRARY:
3102       return (target->GetPropertyAsBool("FRAMEWORK")
3103                 ? "wrapper.framework"
3104                 : "compiled.mach-o.dylib");
3105     case cmStateEnums::EXECUTABLE:
3106       return "compiled.mach-o.executable";
3107     default:
3108       break;
3109   }
3110   return nullptr;
3111 }
3112 
GetTargetProductType(cmGeneratorTarget * target)3113 const char* cmGlobalXCodeGenerator::GetTargetProductType(
3114   cmGeneratorTarget* target)
3115 {
3116   if (cmValue e = target->GetProperty("XCODE_PRODUCT_TYPE")) {
3117     return e->c_str();
3118   }
3119 
3120   switch (target->GetType()) {
3121     case cmStateEnums::OBJECT_LIBRARY:
3122       return "com.apple.product-type.library.static";
3123     case cmStateEnums::STATIC_LIBRARY:
3124       return (target->GetPropertyAsBool("FRAMEWORK")
3125                 ? "com.apple.product-type.framework"
3126                 : "com.apple.product-type.library.static");
3127     case cmStateEnums::MODULE_LIBRARY:
3128       if (target->IsXCTestOnApple()) {
3129         return "com.apple.product-type.bundle.unit-test";
3130       } else if (target->IsCFBundleOnApple()) {
3131         return "com.apple.product-type.bundle";
3132       } else {
3133         return "com.apple.product-type.tool";
3134       }
3135     case cmStateEnums::SHARED_LIBRARY:
3136       return (target->GetPropertyAsBool("FRAMEWORK")
3137                 ? "com.apple.product-type.framework"
3138                 : "com.apple.product-type.library.dynamic");
3139     case cmStateEnums::EXECUTABLE:
3140       return (target->GetPropertyAsBool("MACOSX_BUNDLE")
3141                 ? "com.apple.product-type.application"
3142                 : "com.apple.product-type.tool");
3143     default:
3144       break;
3145   }
3146   return nullptr;
3147 }
3148 
CreateXCodeTarget(cmGeneratorTarget * gtgt,cmXCodeObject * buildPhases)3149 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget(
3150   cmGeneratorTarget* gtgt, cmXCodeObject* buildPhases)
3151 {
3152   if (!gtgt->IsInBuildSystem()) {
3153     return nullptr;
3154   }
3155   cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget);
3156   target->AddAttribute("buildPhases", buildPhases);
3157   cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3158   target->AddAttribute("buildRules", buildRules);
3159   std::string defConfig;
3160   defConfig = this->AddConfigurations(target, gtgt);
3161   cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3162   target->AddAttribute("dependencies", dependencies);
3163   target->AddAttribute("name", this->CreateString(gtgt->GetName()));
3164   target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
3165 
3166   cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
3167   if (const char* fileType = this->GetTargetFileType(gtgt)) {
3168     fileRef->AddAttribute("explicitFileType", this->CreateString(fileType));
3169   }
3170   std::string fullName;
3171   if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
3172     fullName = cmStrCat("lib", gtgt->GetName(), ".a");
3173   } else {
3174     fullName = gtgt->GetFullName(defConfig);
3175   }
3176   fileRef->AddAttribute("path", this->CreateString(fullName));
3177   fileRef->AddAttribute("sourceTree",
3178                         this->CreateString("BUILT_PRODUCTS_DIR"));
3179   fileRef->SetComment(gtgt->GetName());
3180   target->AddAttribute("productReference",
3181                        this->CreateObjectReference(fileRef));
3182   if (const char* productType = this->GetTargetProductType(gtgt)) {
3183     target->AddAttribute("productType", this->CreateString(productType));
3184   }
3185   target->SetTarget(gtgt);
3186   this->XCodeObjectMap[gtgt] = target;
3187   target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
3188   return target;
3189 }
3190 
FindXCodeTarget(cmGeneratorTarget const * t)3191 cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget(
3192   cmGeneratorTarget const* t)
3193 {
3194   if (!t) {
3195     return nullptr;
3196   }
3197 
3198   auto const i = this->XCodeObjectMap.find(t);
3199   if (i == this->XCodeObjectMap.end()) {
3200     return nullptr;
3201   }
3202   return i->second;
3203 }
3204 
GetObjectId(cmXCodeObject::PBXType ptype,cm::string_view key)3205 std::string cmGlobalXCodeGenerator::GetObjectId(cmXCodeObject::PBXType ptype,
3206                                                 cm::string_view key)
3207 {
3208   std::string objectId;
3209   if (!key.empty()) {
3210     cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
3211     hash.Initialize();
3212     hash.Append(&ptype, sizeof(ptype));
3213     hash.Append(key);
3214     objectId = cmSystemTools::UpperCase(hash.FinalizeHex().substr(0, 24));
3215   } else {
3216     char cUuid[40] = { 0 };
3217     CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
3218     CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid);
3219     CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8);
3220     objectId = cUuid;
3221     CFRelease(s);
3222     CFRelease(uuid);
3223     cmSystemTools::ReplaceString(objectId, "-", "");
3224     if (objectId.size() > 24) {
3225       objectId = objectId.substr(0, 24);
3226     }
3227   }
3228   return objectId;
3229 }
3230 
GetOrCreateId(const std::string & name,const std::string & id)3231 std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name,
3232                                                   const std::string& id)
3233 {
3234   std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
3235   cmValue storedGUID = this->CMakeInstance->GetCacheDefinition(guidStoreName);
3236 
3237   if (storedGUID) {
3238     return *storedGUID;
3239   }
3240 
3241   this->CMakeInstance->AddCacheEntry(
3242     guidStoreName, id, "Stored Xcode object GUID", cmStateEnums::INTERNAL);
3243 
3244   return id;
3245 }
3246 
AddDependTarget(cmXCodeObject * target,cmXCodeObject * dependTarget)3247 void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target,
3248                                              cmXCodeObject* dependTarget)
3249 {
3250   // This is called once for every edge in the target dependency graph.
3251   cmXCodeObject* container =
3252     this->CreateObject(cmXCodeObject::PBXContainerItemProxy);
3253   container->SetComment("PBXContainerItemProxy");
3254   container->AddAttribute("containerPortal",
3255                           this->CreateObjectReference(this->RootObject));
3256   container->AddAttribute("proxyType", this->CreateString("1"));
3257   container->AddAttribute("remoteGlobalIDString",
3258                           this->CreateObjectReference(dependTarget));
3259   container->AddAttribute(
3260     "remoteInfo", this->CreateString(dependTarget->GetTarget()->GetName()));
3261   cmXCodeObject* targetdep =
3262     this->CreateObject(cmXCodeObject::PBXTargetDependency);
3263   targetdep->SetComment("PBXTargetDependency");
3264   targetdep->AddAttribute("target", this->CreateObjectReference(dependTarget));
3265   targetdep->AddAttribute("targetProxy",
3266                           this->CreateObjectReference(container));
3267 
3268   cmXCodeObject* depends = target->GetAttribute("dependencies");
3269   if (!depends) {
3270     cmSystemTools::Error(
3271       "target does not have dependencies attribute error..");
3272 
3273   } else {
3274     depends->AddUniqueObject(targetdep);
3275   }
3276 }
3277 
AppendOrAddBuildSetting(cmXCodeObject * settings,const char * attribute,cmXCodeObject * value)3278 void cmGlobalXCodeGenerator::AppendOrAddBuildSetting(cmXCodeObject* settings,
3279                                                      const char* attribute,
3280                                                      cmXCodeObject* value)
3281 {
3282   if (settings) {
3283     cmXCodeObject* attr = settings->GetAttribute(attribute);
3284     if (!attr) {
3285       settings->AddAttribute(attribute, value);
3286     } else {
3287       this->AppendBuildSettingAttribute(settings, attribute, attr, value);
3288     }
3289   }
3290 }
3291 
AppendBuildSettingAttribute(cmXCodeObject * settings,const char * attribute,cmXCodeObject * attr,cmXCodeObject * value)3292 void cmGlobalXCodeGenerator::AppendBuildSettingAttribute(
3293   cmXCodeObject* settings, const char* attribute, cmXCodeObject* attr,
3294   cmXCodeObject* value)
3295 {
3296   if (value->GetType() != cmXCodeObject::OBJECT_LIST &&
3297       value->GetType() != cmXCodeObject::STRING) {
3298     cmSystemTools::Error("Unsupported value type for appending: " +
3299                          std::string(attribute));
3300     return;
3301   }
3302   if (attr->GetType() == cmXCodeObject::OBJECT_LIST) {
3303     if (value->GetType() == cmXCodeObject::OBJECT_LIST) {
3304       for (auto* obj : value->GetObjectList()) {
3305         attr->AddObject(obj);
3306       }
3307     } else {
3308       attr->AddObject(value);
3309     }
3310   } else if (attr->GetType() == cmXCodeObject::STRING) {
3311     if (value->GetType() == cmXCodeObject::OBJECT_LIST) {
3312       // Add old value as a list item to new object list
3313       // and replace the attribute with the new list
3314       value->PrependObject(attr);
3315       settings->AddAttribute(attribute, value);
3316     } else {
3317       std::string newValue =
3318         cmStrCat(attr->GetString(), ' ', value->GetString());
3319       attr->SetString(newValue);
3320     }
3321   } else {
3322     cmSystemTools::Error("Unsupported attribute type for appending: " +
3323                          std::string(attribute));
3324   }
3325 }
3326 
AppendBuildSettingAttribute(cmXCodeObject * target,const char * attribute,cmXCodeObject * value,const std::string & configName)3327 void cmGlobalXCodeGenerator::AppendBuildSettingAttribute(
3328   cmXCodeObject* target, const char* attribute, cmXCodeObject* value,
3329   const std::string& configName)
3330 {
3331   // There are multiple configurations.  Add the setting to the
3332   // buildSettings of the configuration name given.
3333   cmXCodeObject* configurationList =
3334     target->GetAttribute("buildConfigurationList")->GetObject();
3335   cmXCodeObject* buildConfigs =
3336     configurationList->GetAttribute("buildConfigurations");
3337   for (auto obj : buildConfigs->GetObjectList()) {
3338     if (configName.empty() ||
3339         obj->GetAttribute("name")->GetString() == configName) {
3340       cmXCodeObject* settings = obj->GetAttribute("buildSettings");
3341       this->AppendOrAddBuildSetting(settings, attribute, value);
3342     }
3343   }
3344 }
3345 
InheritBuildSettingAttribute(cmXCodeObject * target,const char * attribute)3346 void cmGlobalXCodeGenerator::InheritBuildSettingAttribute(
3347   cmXCodeObject* target, const char* attribute)
3348 {
3349   cmXCodeObject* configurationList =
3350     target->GetAttribute("buildConfigurationList")->GetObject();
3351   cmXCodeObject* buildConfigs =
3352     configurationList->GetAttribute("buildConfigurations");
3353   for (auto obj : buildConfigs->GetObjectList()) {
3354     cmXCodeObject* settings = obj->GetAttribute("buildSettings");
3355     if (cmXCodeObject* attr = settings->GetAttribute(attribute)) {
3356       BuildObjectListOrString inherited(this, true);
3357       inherited.Add("$(inherited)");
3358       this->AppendBuildSettingAttribute(settings, attribute, attr,
3359                                         inherited.CreateList());
3360     }
3361   }
3362 }
3363 
AddDependAndLinkInformation(cmXCodeObject * target)3364 void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
3365 {
3366   cmGeneratorTarget* gt = target->GetTarget();
3367   if (!gt) {
3368     cmSystemTools::Error("Error no target on xobject\n");
3369     return;
3370   }
3371   if (!gt->IsInBuildSystem()) {
3372     return;
3373   }
3374 
3375   // Add dependencies on other CMake targets.
3376   for (const auto& dep : this->GetTargetDirectDepends(gt)) {
3377     if (cmXCodeObject* dptarget = this->FindXCodeTarget(dep)) {
3378       this->AddDependTarget(target, dptarget);
3379     }
3380   }
3381 
3382   // Separate libraries into ones that can be linked using "Link Binary With
3383   // Libraries" build phase and the ones that can't. Only targets that build
3384   // Apple bundles (.app, .framework, .bundle), executables and dylibs can use
3385   // this feature and only targets that represent actual libraries (object,
3386   // static, dynamic or bundle, excluding executables) will be used. These are
3387   // limitations imposed by CMake use-cases - otherwise a lot of things break.
3388   // The rest will be linked using linker flags (OTHER_LDFLAGS setting in Xcode
3389   // project).
3390   std::map<std::string, std::vector<cmComputeLinkInformation::Item const*>>
3391     configItemMap;
3392   auto addToLinkerArguments =
3393     [&configItemMap](const std::string& configName,
3394                      cmComputeLinkInformation::Item const* libItemPtr) {
3395       auto& linkVector = configItemMap[configName];
3396       if (std::find_if(linkVector.begin(), linkVector.end(),
3397                        [libItemPtr](cmComputeLinkInformation::Item const* p) {
3398                          return p == libItemPtr;
3399                        }) == linkVector.end()) {
3400         linkVector.push_back(libItemPtr);
3401       }
3402     };
3403   std::vector<cmComputeLinkInformation::Item const*> linkPhaseTargetVector;
3404   std::map<std::string, std::vector<std::string>> targetConfigMap;
3405   using ConfigItemPair =
3406     std::pair<std::string, cmComputeLinkInformation::Item const*>;
3407   std::map<std::string, std::vector<ConfigItemPair>> targetItemMap;
3408   std::map<std::string, std::vector<std::string>> targetProductNameMap;
3409   bool useLinkPhase = false;
3410   bool forceLinkPhase = false;
3411   cmValue prop =
3412     target->GetTarget()->GetProperty("XCODE_LINK_BUILD_PHASE_MODE");
3413   if (prop) {
3414     if (*prop == "BUILT_ONLY") {
3415       useLinkPhase = true;
3416     } else if (*prop == "KNOWN_LOCATION") {
3417       useLinkPhase = true;
3418       forceLinkPhase = true;
3419     } else if (*prop != "NONE") {
3420       cmSystemTools::Error("Invalid value for XCODE_LINK_BUILD_PHASE_MODE: " +
3421                            *prop);
3422       return;
3423     }
3424   }
3425   for (auto const& configName : this->CurrentConfigurationTypes) {
3426     cmComputeLinkInformation* cli = gt->GetLinkInformation(configName);
3427     if (!cli) {
3428       continue;
3429     }
3430     for (auto const& libItem : cli->GetItems()) {
3431       // We want to put only static libraries, dynamic libraries, frameworks
3432       // and bundles that are built from targets that are not imported in "Link
3433       // Binary With Libraries" build phase. Except if the target property
3434       // XCODE_LINK_BUILD_PHASE_MODE is KNOWN_LOCATION then all imported and
3435       // non-target libraries will be added as well.
3436       if (useLinkPhase &&
3437           (gt->GetType() == cmStateEnums::EXECUTABLE ||
3438            gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
3439            gt->GetType() == cmStateEnums::MODULE_LIBRARY) &&
3440           ((libItem.Target &&
3441             (!libItem.Target->IsImported() || forceLinkPhase) &&
3442             (libItem.Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
3443              libItem.Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
3444              libItem.Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
3445              libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY)) ||
3446            (!libItem.Target &&
3447             libItem.IsPath == cmComputeLinkInformation::ItemIsPath::Yes &&
3448             forceLinkPhase))) {
3449         std::string libName;
3450         bool canUseLinkPhase = true;
3451         if (libItem.Target) {
3452           if (libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) {
3453             canUseLinkPhase = canUseLinkPhase && forceLinkPhase;
3454           } else {
3455             // If a library target uses custom build output directory Xcode
3456             // won't pick it up so we have to resort back to linker flags, but
3457             // that's OK as long as the custom output dir is absolute path.
3458             for (auto const& libConfigName : this->CurrentConfigurationTypes) {
3459               canUseLinkPhase = canUseLinkPhase &&
3460                 libItem.Target->UsesDefaultOutputDir(
3461                   libConfigName, cmStateEnums::RuntimeBinaryArtifact);
3462             }
3463           }
3464           libName = libItem.Target->GetName();
3465         } else {
3466           libName = cmSystemTools::GetFilenameName(libItem.Value.Value);
3467           // We don't want all the possible files here, just standard libraries
3468           const auto libExt = cmSystemTools::GetFilenameExtension(libName);
3469           if (!IsLinkPhaseLibraryExtension(libExt)) {
3470             canUseLinkPhase = false;
3471           }
3472         }
3473         if (canUseLinkPhase) {
3474           // Add unique configuration name to target-config map for later
3475           // checks
3476           auto& configVector = targetConfigMap[libName];
3477           if (std::find(configVector.begin(), configVector.end(),
3478                         configName) == configVector.end()) {
3479             configVector.push_back(configName);
3480           }
3481           // Add a pair of config and item to target-item map
3482           auto& itemVector = targetItemMap[libName];
3483           itemVector.emplace_back(ConfigItemPair(configName, &libItem));
3484           // Add product file-name to a lib-product map
3485           auto productName =
3486             cmSystemTools::GetFilenameName(libItem.Value.Value);
3487           auto& productVector = targetProductNameMap[libName];
3488           if (std::find(productVector.begin(), productVector.end(),
3489                         productName) == productVector.end()) {
3490             productVector.push_back(productName);
3491           }
3492           continue;
3493         }
3494       }
3495       // Add this library item to a regular linker flag list
3496       addToLinkerArguments(configName, &libItem);
3497     }
3498   }
3499 
3500   // Go through target library map and separate libraries that are linked
3501   // in all configurations and produce only single product, from the rest.
3502   // Only these will be linked through "Link Binary With Libraries" build
3503   // phase.
3504   for (auto const& targetLibConfigs : targetConfigMap) {
3505     // Add this library to "Link Binary With Libraries" build phase if it's
3506     // linked in all configurations and it has only one product name
3507     auto& itemVector = targetItemMap[targetLibConfigs.first];
3508     auto& productVector = targetProductNameMap[targetLibConfigs.first];
3509     if (targetLibConfigs.second == this->CurrentConfigurationTypes &&
3510         productVector.size() == 1) {
3511       // Add this library to "Link Binary With Libraries" list
3512       linkPhaseTargetVector.push_back(itemVector[0].second);
3513     } else {
3514       for (auto const& libItem : targetItemMap[targetLibConfigs.first]) {
3515         // Add this library item to a regular linker flag list
3516         addToLinkerArguments(libItem.first, libItem.second);
3517       }
3518     }
3519   }
3520 
3521   // Add libraries to "Link Binary With Libraries" build phase and collect
3522   // their search paths. Xcode does not support per-configuration linking
3523   // in this build phase so we don't have to do this for each configuration
3524   // separately.
3525   std::vector<std::string> linkSearchPaths;
3526   std::vector<std::string> frameworkSearchPaths;
3527   for (auto const& libItem : linkPhaseTargetVector) {
3528     // Add target output directory as a library search path
3529     std::string linkDir;
3530     if (libItem->Target) {
3531       linkDir = libItem->Target->GetLocationForBuild();
3532     } else {
3533       linkDir = libItem->Value.Value;
3534     }
3535     linkDir = GetLibraryOrFrameworkPath(linkDir);
3536     bool isFramework = cmSystemTools::IsPathToFramework(linkDir);
3537     linkDir = cmSystemTools::GetParentDirectory(linkDir);
3538     if (isFramework) {
3539       if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(),
3540                     linkDir) == frameworkSearchPaths.end()) {
3541         frameworkSearchPaths.push_back(linkDir);
3542       }
3543     } else {
3544       if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) ==
3545           linkSearchPaths.end()) {
3546         linkSearchPaths.push_back(linkDir);
3547       }
3548     }
3549     // Add target dependency
3550     if (libItem->Target && !libItem->Target->IsImported()) {
3551       for (auto const& configName : this->CurrentConfigurationTypes) {
3552         target->AddDependTarget(configName, libItem->Target->GetName());
3553       }
3554     }
3555     // Get the library target
3556     auto* libTarget = FindXCodeTarget(libItem->Target);
3557     cmXCodeObject* buildFile;
3558     if (!libTarget) {
3559       if (libItem->IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
3560         // Get or create a direct file ref in the root project
3561         auto cleanPath = libItem->Value.Value;
3562         if (cmSystemTools::FileIsFullPath(cleanPath)) {
3563           // Some arguments are reported as paths, but they are actually not,
3564           // so we can't collapse them, and neither can we collapse relative
3565           // paths
3566           cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
3567         }
3568         auto it = this->ExternalLibRefs.find(cleanPath);
3569         if (it == this->ExternalLibRefs.end()) {
3570           buildFile = CreateXCodeBuildFileFromPath(cleanPath, gt, "", nullptr);
3571           if (!buildFile) {
3572             // Add this library item back to a regular linker flag list
3573             for (const auto& conf : configItemMap) {
3574               addToLinkerArguments(conf.first, libItem);
3575             }
3576             continue;
3577           }
3578           this->ExternalLibRefs.emplace(cleanPath, buildFile);
3579         } else {
3580           buildFile = it->second;
3581         }
3582       } else {
3583         // Add this library item back to a regular linker flag list
3584         for (const auto& conf : configItemMap) {
3585           addToLinkerArguments(conf.first, libItem);
3586         }
3587         continue;
3588       }
3589     } else {
3590       // Add the target output file as a build reference for other targets
3591       // to link against
3592       auto* fileRefObject = libTarget->GetAttribute("productReference");
3593       if (!fileRefObject) {
3594         // Add this library item back to a regular linker flag list
3595         for (const auto& conf : configItemMap) {
3596           addToLinkerArguments(conf.first, libItem);
3597         }
3598         continue;
3599       }
3600       auto it = FileRefToBuildFileMap.find(fileRefObject);
3601       if (it == FileRefToBuildFileMap.end()) {
3602         buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
3603         buildFile->AddAttribute("fileRef", fileRefObject);
3604         FileRefToBuildFileMap[fileRefObject] = buildFile;
3605       } else {
3606         buildFile = it->second;
3607       }
3608     }
3609     // Add this reference to current target
3610     auto* buildPhases = target->GetAttribute("buildPhases");
3611     if (!buildPhases) {
3612       cmSystemTools::Error("Missing buildPhase of target");
3613       continue;
3614     }
3615     auto* frameworkBuildPhase =
3616       buildPhases->GetObject(cmXCodeObject::PBXFrameworksBuildPhase);
3617     if (!frameworkBuildPhase) {
3618       cmSystemTools::Error("Missing PBXFrameworksBuildPhase of buildPhase");
3619       continue;
3620     }
3621     auto* buildFiles = frameworkBuildPhase->GetAttribute("files");
3622     if (!buildFiles) {
3623       cmSystemTools::Error("Missing files of PBXFrameworksBuildPhase");
3624       continue;
3625     }
3626     if (buildFile && !buildFiles->HasObject(buildFile)) {
3627       buildFiles->AddObject(buildFile);
3628     }
3629   }
3630 
3631   // Loop over configuration types and set per-configuration info.
3632   for (auto const& configName : this->CurrentConfigurationTypes) {
3633     {
3634       // Add object library contents as link flags.
3635       BuildObjectListOrString libSearchPaths(this, true);
3636       std::vector<cmSourceFile const*> objs;
3637       gt->GetExternalObjects(objs, configName);
3638       for (auto sourceFile : objs) {
3639         if (sourceFile->GetObjectLibrary().empty()) {
3640           continue;
3641         }
3642         libSearchPaths.Add(this->XCodeEscapePath(sourceFile->GetFullPath()));
3643       }
3644       this->AppendBuildSettingAttribute(
3645         target, this->GetTargetLinkFlagsVar(gt), libSearchPaths.CreateList(),
3646         configName);
3647     }
3648 
3649     // Skip link information for object libraries.
3650     if (gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
3651         gt->GetType() == cmStateEnums::STATIC_LIBRARY) {
3652       continue;
3653     }
3654 
3655     // Compute the link library and directory information.
3656     cmComputeLinkInformation* cli = gt->GetLinkInformation(configName);
3657     if (!cli) {
3658       continue;
3659     }
3660 
3661     // Add dependencies directly on library files.
3662     for (auto const& libDep : cli->GetDepends()) {
3663       target->AddDependLibrary(configName, libDep);
3664     }
3665 
3666     // add the library search paths
3667     {
3668       BuildObjectListOrString libSearchPaths(this, true);
3669       std::string linkDirs;
3670       for (auto const& libDir : cli->GetDirectories()) {
3671         if (!libDir.empty() && libDir != "/usr/lib") {
3672           libSearchPaths.Add(this->XCodeEscapePath(
3673             libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"));
3674           libSearchPaths.Add(this->XCodeEscapePath(libDir));
3675         }
3676       }
3677       // Add previously collected paths where to look for libraries
3678       // that were added to "Link Binary With Libraries"
3679       for (auto& libDir : linkSearchPaths) {
3680         libSearchPaths.Add(this->XCodeEscapePath(libDir));
3681       }
3682       if (!libSearchPaths.IsEmpty()) {
3683         this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS",
3684                                           libSearchPaths.CreateList(),
3685                                           configName);
3686       }
3687     }
3688 
3689     // add framework search paths
3690     {
3691       BuildObjectListOrString fwSearchPaths(this, true);
3692       // Add previously collected paths where to look for frameworks
3693       // that were added to "Link Binary With Libraries"
3694       for (auto& fwDir : frameworkSearchPaths) {
3695         fwSearchPaths.Add(this->XCodeEscapePath(fwDir));
3696       }
3697       if (!fwSearchPaths.IsEmpty()) {
3698         this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS",
3699                                           fwSearchPaths.CreateList(),
3700                                           configName);
3701       }
3702     }
3703 
3704     // now add the left-over link libraries
3705     {
3706       // Keep track of framework search paths we've already added or that are
3707       // part of the set of implicit search paths. We don't want to repeat
3708       // them and we also need to avoid hard-coding any SDK-specific paths.
3709       // This is essential for getting device-and-simulator builds to work,
3710       // otherwise we end up hard-coding a path to the wrong SDK for
3711       // SDK-provided frameworks that are added by their full path.
3712       std::set<std::string> emitted(cli->GetFrameworkPathsEmitted());
3713       const auto& fwPaths = cli->GetFrameworkPaths();
3714       emitted.insert(fwPaths.begin(), fwPaths.end());
3715       BuildObjectListOrString libPaths(this, true);
3716       for (auto const& libItem : configItemMap[configName]) {
3717         auto const& libName = *libItem;
3718         if (libName.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
3719           auto cleanPath = libName.Value.Value;
3720           if (cmSystemTools::FileIsFullPath(cleanPath)) {
3721             cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
3722           }
3723           const auto libPath = GetLibraryOrFrameworkPath(cleanPath);
3724           if (cmSystemTools::StringEndsWith(libPath.c_str(), ".framework")) {
3725             const auto fwName =
3726               cmSystemTools::GetFilenameWithoutExtension(libPath);
3727             const auto fwDir = cmSystemTools::GetParentDirectory(libPath);
3728             if (emitted.insert(fwDir).second) {
3729               // This is a search path we had not added before and it isn't an
3730               // implicit search path, so we need it
3731               libPaths.Add("-F " + this->XCodeEscapePath(fwDir));
3732             }
3733             libPaths.Add("-framework " + this->XCodeEscapePath(fwName));
3734           } else {
3735             libPaths.Add(this->XCodeEscapePath(cleanPath));
3736           }
3737           if ((!libName.Target || libName.Target->IsImported()) &&
3738               IsLinkPhaseLibraryExtension(libPath)) {
3739             // Create file reference for embedding
3740             auto it = this->ExternalLibRefs.find(cleanPath);
3741             if (it == this->ExternalLibRefs.end()) {
3742               auto* buildFile =
3743                 this->CreateXCodeBuildFileFromPath(cleanPath, gt, "", nullptr);
3744               if (buildFile) {
3745                 this->ExternalLibRefs.emplace(cleanPath, buildFile);
3746               }
3747             }
3748           }
3749         } else if (!libName.Target ||
3750                    libName.Target->GetType() !=
3751                      cmStateEnums::INTERFACE_LIBRARY) {
3752           libPaths.Add(libName.Value.Value);
3753         }
3754         if (libName.Target && !libName.Target->IsImported()) {
3755           target->AddDependTarget(configName, libName.Target->GetName());
3756         }
3757       }
3758       this->AppendBuildSettingAttribute(target,
3759                                         this->GetTargetLinkFlagsVar(gt),
3760                                         libPaths.CreateList(), configName);
3761     }
3762   }
3763 }
3764 
AddEmbeddedObjects(cmXCodeObject * target,const std::string & copyFilesBuildPhaseName,const std::string & embedPropertyName,const std::string & dstSubfolderSpec,int actionsOnByDefault)3765 void cmGlobalXCodeGenerator::AddEmbeddedObjects(
3766   cmXCodeObject* target, const std::string& copyFilesBuildPhaseName,
3767   const std::string& embedPropertyName, const std::string& dstSubfolderSpec,
3768   int actionsOnByDefault)
3769 {
3770   cmGeneratorTarget* gt = target->GetTarget();
3771   if (!gt) {
3772     cmSystemTools::Error("Error no target on xobject\n");
3773     return;
3774   }
3775   if (!gt->IsInBuildSystem()) {
3776     return;
3777   }
3778   bool isFrameworkTarget = gt->IsFrameworkOnApple();
3779   bool isBundleTarget = gt->GetPropertyAsBool("MACOSX_BUNDLE");
3780   bool isCFBundleTarget = gt->IsCFBundleOnApple();
3781   if (!(isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
3782     return;
3783   }
3784   cmValue files = gt->GetProperty(embedPropertyName);
3785   if (!files) {
3786     return;
3787   }
3788 
3789   // Create an "Embedded Frameworks" build phase
3790   auto* copyFilesBuildPhase =
3791     this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
3792   copyFilesBuildPhase->SetComment(copyFilesBuildPhaseName);
3793   copyFilesBuildPhase->AddAttribute("buildActionMask",
3794                                     this->CreateString("2147483647"));
3795   copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
3796                                     this->CreateString(dstSubfolderSpec));
3797   copyFilesBuildPhase->AddAttribute(
3798     "name", this->CreateString(copyFilesBuildPhaseName));
3799   if (cmValue fwEmbedPath =
3800         gt->GetProperty(cmStrCat(embedPropertyName, "_PATH"))) {
3801     copyFilesBuildPhase->AddAttribute("dstPath",
3802                                       this->CreateString(*fwEmbedPath));
3803   } else {
3804     copyFilesBuildPhase->AddAttribute("dstPath", this->CreateString(""));
3805   }
3806   copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
3807                                     this->CreateString("0"));
3808   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3809   // Collect all embedded frameworks and dylibs and add them to build phase
3810   std::vector<std::string> relFiles = cmExpandedList(*files);
3811   for (std::string const& relFile : relFiles) {
3812     cmXCodeObject* buildFile{ nullptr };
3813     std::string filePath = relFile;
3814     auto* genTarget = this->FindGeneratorTarget(relFile);
3815     if (genTarget) {
3816       // This is a target - get it's product path reference
3817       auto* xcTarget = this->FindXCodeTarget(genTarget);
3818       if (!xcTarget) {
3819         cmSystemTools::Error("Can not find a target for " +
3820                              genTarget->GetName());
3821         continue;
3822       }
3823       // Add the target output file as a build reference for other targets
3824       // to link against
3825       auto* fileRefObject = xcTarget->GetAttribute("productReference");
3826       if (!fileRefObject) {
3827         cmSystemTools::Error("Target " + genTarget->GetName() +
3828                              " is missing product reference");
3829         continue;
3830       }
3831       auto it = this->FileRefToEmbedBuildFileMap.find(fileRefObject);
3832       if (it == this->FileRefToEmbedBuildFileMap.end()) {
3833         buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
3834         buildFile->AddAttribute("fileRef", fileRefObject);
3835         this->FileRefToEmbedBuildFileMap[fileRefObject] = buildFile;
3836       } else {
3837         buildFile = it->second;
3838       }
3839     } else if (cmSystemTools::IsPathToFramework(relFile) ||
3840                cmSystemTools::IsPathToMacOSSharedLibrary(relFile)) {
3841       // This is a regular string path - create file reference
3842       auto it = this->EmbeddedLibRefs.find(relFile);
3843       if (it == this->EmbeddedLibRefs.end()) {
3844         cmXCodeObject* fileRef =
3845           this->CreateXCodeFileReferenceFromPath(relFile, gt, "", nullptr);
3846         if (fileRef) {
3847           buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
3848           buildFile->SetComment(fileRef->GetComment());
3849           buildFile->AddAttribute("fileRef",
3850                                   this->CreateObjectReference(fileRef));
3851         }
3852         if (!buildFile) {
3853           cmSystemTools::Error("Can't create build file for " + relFile);
3854           continue;
3855         }
3856         this->EmbeddedLibRefs.emplace(filePath, buildFile);
3857       } else {
3858         buildFile = it->second;
3859       }
3860     }
3861     if (!buildFile) {
3862       cmSystemTools::Error("Can't find a build file for " + relFile);
3863       continue;
3864     }
3865     // Set build file configuration
3866     cmXCodeObject* settings =
3867       this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3868     cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3869 
3870     bool removeHeaders = actionsOnByDefault & RemoveHeadersOnCopyByDefault;
3871     if (auto prop = gt->GetProperty(
3872           cmStrCat(embedPropertyName, "_REMOVE_HEADERS_ON_COPY"))) {
3873       removeHeaders = cmIsOn(*prop);
3874     }
3875     if (removeHeaders) {
3876       attrs->AddObject(this->CreateString("RemoveHeadersOnCopy"));
3877     }
3878 
3879     bool codeSign = actionsOnByDefault & CodeSignOnCopyByDefault;
3880     if (auto prop =
3881           gt->GetProperty(cmStrCat(embedPropertyName, "_CODE_SIGN_ON_COPY"))) {
3882       codeSign = cmIsOn(*prop);
3883     }
3884     if (codeSign) {
3885       attrs->AddObject(this->CreateString("CodeSignOnCopy"));
3886     }
3887 
3888     settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
3889     buildFile->AddAttributeIfNotEmpty("settings", settings);
3890     if (!buildFiles->HasObject(buildFile)) {
3891       buildFiles->AddObject(buildFile);
3892     }
3893   }
3894   copyFilesBuildPhase->AddAttribute("files", buildFiles);
3895   auto* buildPhases = target->GetAttribute("buildPhases");
3896   // Embed-something build phases must be inserted before the post-build
3897   // command because that command is expected to be last
3898   buildPhases->InsertObject(buildPhases->GetObjectCount() - 1,
3899                             copyFilesBuildPhase);
3900 }
3901 
AddEmbeddedFrameworks(cmXCodeObject * target)3902 void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
3903 {
3904   static const auto dstSubfolderSpec = "10";
3905 
3906   // Despite the name, by default Xcode uses "Embed Frameworks" build phase for
3907   // both frameworks and dynamic libraries
3908   this->AddEmbeddedObjects(target, "Embed Frameworks",
3909                            "XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec,
3910                            NoActionOnCopyByDefault);
3911 }
3912 
AddEmbeddedAppExtensions(cmXCodeObject * target)3913 void cmGlobalXCodeGenerator::AddEmbeddedAppExtensions(cmXCodeObject* target)
3914 {
3915   static const auto dstSubfolderSpec = "13";
3916 
3917   this->AddEmbeddedObjects(target, "Embed App Extensions",
3918                            "XCODE_EMBED_APP_EXTENSIONS", dstSubfolderSpec,
3919                            RemoveHeadersOnCopyByDefault);
3920 }
3921 
CreateGroups(std::vector<cmLocalGenerator * > & generators)3922 bool cmGlobalXCodeGenerator::CreateGroups(
3923   std::vector<cmLocalGenerator*>& generators)
3924 {
3925   for (auto& generator : generators) {
3926     cmMakefile* mf = generator->GetMakefile();
3927     std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
3928     for (const auto& gtgt : generator->GetGeneratorTargets()) {
3929       // Same skipping logic here as in CreateXCodeTargets so that we do not
3930       // end up with (empty anyhow) ZERO_CHECK, install, or test source
3931       // groups:
3932       //
3933       if (!gtgt->IsInBuildSystem() ||
3934           gtgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
3935           gtgt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
3936         continue;
3937       }
3938 
3939       auto addSourceToGroup = [this, mf, &gtgt,
3940                                &sourceGroups](std::string const& source) {
3941         cmSourceGroup* sourceGroup = mf->FindSourceGroup(source, sourceGroups);
3942         cmXCodeObject* pbxgroup =
3943           this->CreateOrGetPBXGroup(gtgt.get(), sourceGroup);
3944         std::string key = GetGroupMapKeyFromPath(gtgt.get(), source);
3945         this->GroupMap[key] = pbxgroup;
3946       };
3947 
3948       // Put cmSourceFile instances in proper groups:
3949       for (auto const& si : gtgt->GetAllConfigSources()) {
3950         cmSourceFile const* sf = si.Source;
3951         if (!sf->GetObjectLibrary().empty()) {
3952           // Object library files go on the link line instead.
3953           continue;
3954         }
3955         addSourceToGroup(sf->GetFullPath());
3956       }
3957 
3958       // Add CMakeLists.txt file for user convenience.
3959       {
3960         std::string listfile =
3961           cmStrCat(gtgt->GetLocalGenerator()->GetCurrentSourceDirectory(),
3962                    "/CMakeLists.txt");
3963         cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
3964           listfile, false, cmSourceFileLocationKind::Known);
3965         addSourceToGroup(sf->ResolveFullPath());
3966       }
3967 
3968       // Add the Info.plist we are about to generate for an App Bundle.
3969       if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
3970         std::string plist = this->ComputeInfoPListLocation(gtgt.get());
3971         cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
3972           plist, true, cmSourceFileLocationKind::Known);
3973         addSourceToGroup(sf->ResolveFullPath());
3974       }
3975     }
3976   }
3977   return true;
3978 }
3979 
CreatePBXGroup(cmXCodeObject * parent,const std::string & name)3980 cmXCodeObject* cmGlobalXCodeGenerator::CreatePBXGroup(cmXCodeObject* parent,
3981                                                       const std::string& name)
3982 {
3983   cmXCodeObject* parentChildren = nullptr;
3984   if (parent) {
3985     parentChildren = parent->GetAttribute("children");
3986   }
3987   cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup);
3988   cmXCodeObject* groupChildren =
3989     this->CreateObject(cmXCodeObject::OBJECT_LIST);
3990   group->AddAttribute("name", this->CreateString(name));
3991   group->AddAttribute("children", groupChildren);
3992   group->AddAttribute("sourceTree", this->CreateString("<group>"));
3993   if (parentChildren) {
3994     parentChildren->AddObject(group);
3995   }
3996   return group;
3997 }
3998 
CreateOrGetPBXGroup(cmGeneratorTarget * gtgt,cmSourceGroup * sg)3999 cmXCodeObject* cmGlobalXCodeGenerator::CreateOrGetPBXGroup(
4000   cmGeneratorTarget* gtgt, cmSourceGroup* sg)
4001 {
4002   std::string s;
4003   std::string target;
4004   const std::string targetFolder = gtgt->GetEffectiveFolderName();
4005   if (!targetFolder.empty()) {
4006     target = cmStrCat(targetFolder, '/');
4007   }
4008   target += gtgt->GetName();
4009   s = cmStrCat(target, '/', sg->GetFullName());
4010   auto it = this->GroupNameMap.find(s);
4011   if (it != this->GroupNameMap.end()) {
4012     return it->second;
4013   }
4014 
4015   it = this->TargetGroup.find(target);
4016   cmXCodeObject* tgroup = nullptr;
4017   if (it != this->TargetGroup.end()) {
4018     tgroup = it->second;
4019   } else {
4020     std::vector<std::string> tgt_folders = cmTokenize(target, "/");
4021     std::string curr_tgt_folder;
4022     for (std::vector<std::string>::size_type i = 0; i < tgt_folders.size();
4023          i++) {
4024       if (i != 0) {
4025         curr_tgt_folder += "/";
4026       }
4027       curr_tgt_folder += tgt_folders[i];
4028       it = this->TargetGroup.find(curr_tgt_folder);
4029       if (it != this->TargetGroup.end()) {
4030         tgroup = it->second;
4031         continue;
4032       }
4033       tgroup = this->CreatePBXGroup(tgroup, tgt_folders[i]);
4034       this->TargetGroup[curr_tgt_folder] = tgroup;
4035       if (i == 0) {
4036         this->MainGroupChildren->AddObject(tgroup);
4037       }
4038     }
4039   }
4040   this->TargetGroup[target] = tgroup;
4041 
4042   // If it's the default source group (empty name) then put the source file
4043   // directly in the tgroup...
4044   //
4045   if (sg->GetFullName().empty()) {
4046     this->GroupNameMap[s] = tgroup;
4047     return tgroup;
4048   }
4049 
4050   // It's a recursive folder structure, let's find the real parent group
4051   if (sg->GetFullName() != sg->GetName()) {
4052     std::string curr_folder = cmStrCat(target, '/');
4053     for (auto const& folder : cmTokenize(sg->GetFullName(), "\\")) {
4054       curr_folder += folder;
4055       auto const i_folder = this->GroupNameMap.find(curr_folder);
4056       // Create new folder
4057       if (i_folder == this->GroupNameMap.end()) {
4058         cmXCodeObject* group = this->CreatePBXGroup(tgroup, folder);
4059         this->GroupNameMap[curr_folder] = group;
4060         tgroup = group;
4061       } else {
4062         tgroup = i_folder->second;
4063       }
4064       curr_folder += "\\";
4065     }
4066     return tgroup;
4067   }
4068   cmXCodeObject* group = this->CreatePBXGroup(tgroup, sg->GetName());
4069   this->GroupNameMap[s] = group;
4070   return group;
4071 }
4072 
CreateXCodeObjects(cmLocalGenerator * root,std::vector<cmLocalGenerator * > & generators)4073 bool cmGlobalXCodeGenerator::CreateXCodeObjects(
4074   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
4075 {
4076   this->ClearXCodeObjects();
4077   this->RootObject = nullptr;
4078   this->MainGroupChildren = nullptr;
4079   this->FrameworkGroup = nullptr;
4080   cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
4081   group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
4082   cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
4083   for (const std::string& CurrentConfigurationType :
4084        this->CurrentConfigurationTypes) {
4085     cmXCodeObject* buildStyle =
4086       this->CreateObject(cmXCodeObject::PBXBuildStyle);
4087     const std::string& name = CurrentConfigurationType;
4088     buildStyle->AddAttribute("name", this->CreateString(name));
4089     buildStyle->SetComment(name);
4090     cmXCodeObject* sgroup = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
4091     sgroup->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
4092     buildStyle->AddAttribute("buildSettings", sgroup);
4093     listObjs->AddObject(buildStyle);
4094   }
4095 
4096   cmXCodeObject* mainGroup = this->CreateObject(cmXCodeObject::PBXGroup);
4097   this->MainGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST);
4098   mainGroup->AddAttribute("children", this->MainGroupChildren);
4099   mainGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
4100 
4101   // now create the cmake groups
4102   if (!this->CreateGroups(generators)) {
4103     return false;
4104   }
4105 
4106   cmXCodeObject* productGroup = this->CreateObject(cmXCodeObject::PBXGroup);
4107   productGroup->AddAttribute("name", this->CreateString("Products"));
4108   productGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
4109   cmXCodeObject* productGroupChildren =
4110     this->CreateObject(cmXCodeObject::OBJECT_LIST);
4111   productGroup->AddAttribute("children", productGroupChildren);
4112   this->MainGroupChildren->AddObject(productGroup);
4113 
4114   this->FrameworkGroup = this->CreateObject(cmXCodeObject::PBXGroup);
4115   this->FrameworkGroup->AddAttribute("name", this->CreateString("Frameworks"));
4116   this->FrameworkGroup->AddAttribute("sourceTree",
4117                                      this->CreateString("<group>"));
4118   cmXCodeObject* frameworkGroupChildren =
4119     this->CreateObject(cmXCodeObject::OBJECT_LIST);
4120   this->FrameworkGroup->AddAttribute("children", frameworkGroupChildren);
4121   this->MainGroupChildren->AddObject(this->FrameworkGroup);
4122 
4123   this->RootObject = this->CreateObject(cmXCodeObject::PBXProject);
4124   this->RootObject->SetComment("Project object");
4125 
4126   std::string project_id = cmStrCat("PROJECT_", root->GetProjectName());
4127   this->RootObject->SetId(
4128     this->GetOrCreateId(project_id, this->RootObject->GetId()));
4129 
4130   group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
4131   this->RootObject->AddAttribute("mainGroup",
4132                                  this->CreateObjectReference(mainGroup));
4133   this->RootObject->AddAttribute("buildSettings", group);
4134   this->RootObject->AddAttribute("buildStyles", listObjs);
4135   this->RootObject->AddAttribute("hasScannedForEncodings",
4136                                  this->CreateString("0"));
4137   group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
4138   group->AddAttribute("BuildIndependentTargetsInParallel",
4139                       this->CreateString("YES"));
4140   std::ostringstream v;
4141   v << std::setfill('0') << std::setw(4) << XcodeVersion * 10;
4142   group->AddAttribute("LastUpgradeCheck", this->CreateString(v.str()));
4143   this->RootObject->AddAttribute("attributes", group);
4144   this->RootObject->AddAttribute("compatibilityVersion",
4145                                  this->CreateString("Xcode 3.2"));
4146   // Point Xcode at the top of the source tree.
4147   {
4148     std::string pdir =
4149       this->RelativeToBinary(root->GetCurrentSourceDirectory());
4150     this->RootObject->AddAttribute("projectDirPath", this->CreateString(pdir));
4151     this->RootObject->AddAttribute("projectRoot", this->CreateString(""));
4152   }
4153   cmXCodeObject* configlist =
4154     this->CreateObject(cmXCodeObject::XCConfigurationList);
4155   cmXCodeObject* buildConfigurations =
4156     this->CreateObject(cmXCodeObject::OBJECT_LIST);
4157   using Configs = std::vector<std::pair<std::string, cmXCodeObject*>>;
4158   Configs configs;
4159   std::string defaultConfigName;
4160   for (const auto& name : this->CurrentConfigurationTypes) {
4161     if (defaultConfigName.empty()) {
4162       defaultConfigName = name;
4163     }
4164     cmXCodeObject* config =
4165       this->CreateObject(cmXCodeObject::XCBuildConfiguration);
4166     config->AddAttribute("name", this->CreateString(name));
4167     configs.push_back(std::make_pair(name, config));
4168   }
4169   if (defaultConfigName.empty()) {
4170     defaultConfigName = "Debug";
4171   }
4172   for (auto& config : configs) {
4173     buildConfigurations->AddObject(config.second);
4174   }
4175   configlist->AddAttribute("buildConfigurations", buildConfigurations);
4176 
4177   std::string comment = cmStrCat("Build configuration list for PBXProject \"",
4178                                  this->CurrentProject, '"');
4179   configlist->SetComment(comment);
4180   configlist->AddAttribute("defaultConfigurationIsVisible",
4181                            this->CreateString("0"));
4182   configlist->AddAttribute("defaultConfigurationName",
4183                            this->CreateString(defaultConfigName));
4184   cmXCodeObject* buildSettings =
4185     this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
4186   cmValue sysroot = this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT");
4187   cmValue deploymentTarget =
4188     this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
4189   if (sysroot) {
4190     buildSettings->AddAttribute("SDKROOT", this->CreateString(*sysroot));
4191   }
4192   // recompute this as it may have been changed since enable language
4193   this->ComputeArchitectures(this->CurrentMakefile);
4194   std::string const archs = cmJoin(this->Architectures, " ");
4195   if (archs.empty()) {
4196     // Tell Xcode to use NATIVE_ARCH instead of ARCHS.
4197     buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("YES"));
4198     // When targeting macOS, use only the host architecture.
4199     if (this->SystemName == "Darwin"_s &&
4200         (!cmNonempty(sysroot) ||
4201          cmSystemTools::LowerCase(*sysroot).find("macos") !=
4202            std::string::npos)) {
4203       buildSettings->AddAttribute("ARCHS",
4204                                   this->CreateString("$(NATIVE_ARCH_ACTUAL)"));
4205     }
4206   } else {
4207     // Tell Xcode to use ARCHS (ONLY_ACTIVE_ARCH defaults to NO).
4208     buildSettings->AddAttribute("ARCHS", this->CreateString(archs));
4209   }
4210   if (cmNonempty(deploymentTarget)) {
4211     buildSettings->AddAttribute(GetDeploymentPlatform(root->GetMakefile()),
4212                                 this->CreateString(*deploymentTarget));
4213   }
4214   if (!this->GeneratorToolset.empty()) {
4215     buildSettings->AddAttribute("GCC_VERSION",
4216                                 this->CreateString(this->GeneratorToolset));
4217   }
4218   if (this->GetLanguageEnabled("Swift")) {
4219     std::string swiftVersion;
4220     if (cmValue vers = this->CurrentMakefile->GetDefinition(
4221           "CMAKE_Swift_LANGUAGE_VERSION")) {
4222       swiftVersion = *vers;
4223     } else if (this->XcodeVersion >= 102) {
4224       swiftVersion = "4.0";
4225     } else if (this->XcodeVersion >= 83) {
4226       swiftVersion = "3.0";
4227     } else {
4228       swiftVersion = "2.3";
4229     }
4230     buildSettings->AddAttribute("SWIFT_VERSION",
4231                                 this->CreateString(swiftVersion));
4232   }
4233 
4234   std::string symroot = cmStrCat(root->GetCurrentBinaryDirectory(), "/build");
4235   buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot));
4236 
4237   // Inside a try_compile project, do not require signing on any platform.
4238   if (this->CMakeInstance->GetIsInTryCompile()) {
4239     buildSettings->AddAttribute("CODE_SIGNING_ALLOWED",
4240                                 this->CreateString("NO"));
4241   }
4242 
4243   for (auto& config : configs) {
4244     cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
4245 
4246     // Put this last so it can override existing settings
4247     // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
4248     for (const auto& var : this->CurrentMakefile->GetDefinitions()) {
4249       if (cmHasLiteralPrefix(var, "CMAKE_XCODE_ATTRIBUTE_")) {
4250         std::string attribute = var.substr(22);
4251         this->FilterConfigurationAttribute(config.first, attribute);
4252         if (!attribute.empty()) {
4253           std::string processed = cmGeneratorExpression::Evaluate(
4254             this->CurrentMakefile->GetSafeDefinition(var),
4255             this->CurrentLocalGenerator, config.first);
4256           buildSettingsForCfg->AddAttribute(attribute,
4257                                             this->CreateString(processed));
4258         }
4259       }
4260     }
4261     // store per-config buildSettings into configuration object
4262     config.second->AddAttribute("buildSettings", buildSettingsForCfg);
4263   }
4264 
4265   this->RootObject->AddAttribute("buildConfigurationList",
4266                                  this->CreateObjectReference(configlist));
4267 
4268   std::vector<cmXCodeObject*> targets;
4269   for (auto& generator : generators) {
4270     if (!this->CreateXCodeTargets(generator, targets)) {
4271       return false;
4272     }
4273     for (auto const& ccRoot : this->CustomCommandRoots) {
4274       if (ccRoot.second.size() > 1) {
4275         std::string e = "The custom command ";
4276         std::vector<std::string> const& outputs =
4277           ccRoot.first->GetCustomCommand()->GetOutputs();
4278         if (!outputs.empty()) {
4279           e = cmStrCat(e, "generating\n  ", outputs[0]);
4280         } else {
4281           e = cmStrCat(e, "driven by\n  ", ccRoot.first->GetFullPath());
4282         }
4283         e = cmStrCat(e, "\nis attached to multiple targets:");
4284         for (cmGeneratorTarget const* gt : ccRoot.second) {
4285           e = cmStrCat(e, "\n  ", gt->GetName());
4286         }
4287         e = cmStrCat(
4288           e,
4289           "\nbut none of these is a common dependency of the other(s).  "
4290           "This is not allowed by the Xcode \"new build system\".");
4291         generator->IssueMessage(MessageType::FATAL_ERROR, e);
4292         return false;
4293       }
4294     }
4295     this->CustomCommandRoots.clear();
4296   }
4297   // loop over all targets and add link and depend info
4298   for (auto t : targets) {
4299     this->AddDependAndLinkInformation(t);
4300     this->AddEmbeddedFrameworks(t);
4301     this->AddEmbeddedAppExtensions(t);
4302     // Inherit project-wide values for any target-specific search paths.
4303     this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS");
4304     this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS");
4305     this->InheritBuildSettingAttribute(t, "FRAMEWORK_SEARCH_PATHS");
4306     this->InheritBuildSettingAttribute(t, "SYSTEM_FRAMEWORK_SEARCH_PATHS");
4307     this->InheritBuildSettingAttribute(t, "LIBRARY_SEARCH_PATHS");
4308     this->InheritBuildSettingAttribute(t, "LD_RUNPATH_SEARCH_PATHS");
4309     this->InheritBuildSettingAttribute(t, "GCC_PREPROCESSOR_DEFINITIONS");
4310     this->InheritBuildSettingAttribute(t, "OTHER_CFLAGS");
4311     this->InheritBuildSettingAttribute(t, "OTHER_LDFLAGS");
4312   }
4313 
4314   if (this->XcodeBuildSystem == BuildSystem::One) {
4315     this->CreateXCodeDependHackMakefile(targets);
4316   }
4317   // now add all targets to the root object
4318   cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST);
4319   for (auto t : targets) {
4320     allTargets->AddObject(t);
4321     cmXCodeObject* productRef = t->GetAttribute("productReference");
4322     if (productRef) {
4323       productGroupChildren->AddObject(productRef->GetObject());
4324     }
4325   }
4326   this->RootObject->AddAttribute("targets", allTargets);
4327   return true;
4328 }
4329 
GetObjectsDirectory(const std::string & projName,const std::string & configName,const cmGeneratorTarget * t,const std::string & variant) const4330 std::string cmGlobalXCodeGenerator::GetObjectsDirectory(
4331   const std::string& projName, const std::string& configName,
4332   const cmGeneratorTarget* t, const std::string& variant) const
4333 {
4334   std::string dir = cmStrCat(
4335     t->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', projName,
4336     ".build/", configName, '/', t->GetName(), ".build/", variant);
4337   return dir;
4338 }
4339 
ComputeArchitectures(cmMakefile * mf)4340 void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf)
4341 {
4342   this->Architectures.clear();
4343   cmValue sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
4344   if (sysroot) {
4345     mf->GetDefExpandList("CMAKE_OSX_ARCHITECTURES", this->Architectures);
4346   }
4347 
4348   if (this->Architectures.empty()) {
4349     mf->GetDefExpandList("_CMAKE_APPLE_ARCHS_DEFAULT", this->Architectures);
4350   }
4351 
4352   if (this->Architectures.empty()) {
4353     // With no ARCHS we use ONLY_ACTIVE_ARCH and possibly a
4354     // platform-specific default ARCHS placeholder value.
4355     // Look up the arch that Xcode chooses in this case.
4356     if (cmValue arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) {
4357       this->ObjectDirArchDefault = *arch;
4358       // We expect only one arch but choose the first just in case.
4359       std::string::size_type pos = this->ObjectDirArchDefault.find(';');
4360       if (pos != std::string::npos) {
4361         this->ObjectDirArchDefault = this->ObjectDirArchDefault.substr(0, pos);
4362       }
4363     }
4364   }
4365 
4366   this->ComputeObjectDirArch(mf);
4367 }
4368 
ComputeObjectDirArch(cmMakefile * mf)4369 void cmGlobalXCodeGenerator::ComputeObjectDirArch(cmMakefile* mf)
4370 {
4371   if (this->Architectures.size() > 1 || this->UseEffectivePlatformName(mf)) {
4372     this->ObjectDirArch = "$(CURRENT_ARCH)";
4373   } else if (!this->Architectures.empty()) {
4374     this->ObjectDirArch = this->Architectures[0];
4375   } else {
4376     this->ObjectDirArch = this->ObjectDirArchDefault;
4377   }
4378 }
4379 
CreateXCodeDependHackMakefile(std::vector<cmXCodeObject * > & targets)4380 void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile(
4381   std::vector<cmXCodeObject*>& targets)
4382 {
4383   cmGeneratedFileStream makefileStream(this->CurrentXCodeHackMakefile);
4384   if (!makefileStream) {
4385     cmSystemTools::Error("Could not create " + this->CurrentXCodeHackMakefile);
4386     return;
4387   }
4388   makefileStream.SetCopyIfDifferent(true);
4389   // one more pass for external depend information not handled
4390   // correctly by xcode
4391   /* clang-format off */
4392   makefileStream << "# DO NOT EDIT\n";
4393   makefileStream << "# This makefile makes sure all linkable targets are\n";
4394   makefileStream << "# up-to-date with anything they link to\n"
4395     "default:\n"
4396     "\techo \"Do not invoke directly\"\n"
4397     "\n";
4398   /* clang-format on */
4399 
4400   std::set<std::string> dummyRules;
4401 
4402   // Write rules to help Xcode relink things at the right time.
4403   /* clang-format off */
4404   makefileStream <<
4405     "# Rules to remove targets that are older than anything to which they\n"
4406     "# link.  This forces Xcode to relink the targets from scratch.  It\n"
4407     "# does not seem to check these dependencies itself.\n";
4408   /* clang-format on */
4409   for (const auto& configName : this->CurrentConfigurationTypes) {
4410     for (auto target : targets) {
4411       cmGeneratorTarget* gt = target->GetTarget();
4412 
4413       if (gt->GetType() == cmStateEnums::EXECUTABLE ||
4414           gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
4415           gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
4416           gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
4417           gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
4418         // Declare an entry point for the target post-build phase.
4419         makefileStream << this->PostBuildMakeTarget(gt->GetName(), configName)
4420                        << ":\n";
4421       }
4422 
4423       if (gt->GetType() == cmStateEnums::EXECUTABLE ||
4424           gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
4425           gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
4426           gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
4427         std::string tfull = gt->GetFullPath(configName);
4428         std::string trel = this->ConvertToRelativeForMake(tfull);
4429 
4430         // Add this target to the post-build phases of its dependencies.
4431         auto const y = target->GetDependTargets().find(configName);
4432         if (y != target->GetDependTargets().end()) {
4433           for (auto const& deptgt : y->second) {
4434             makefileStream << this->PostBuildMakeTarget(deptgt, configName)
4435                            << ": " << trel << "\n";
4436           }
4437         }
4438 
4439         std::vector<cmGeneratorTarget*> objlibs;
4440         gt->GetObjectLibrariesCMP0026(objlibs);
4441         for (auto objLib : objlibs) {
4442           makefileStream << this->PostBuildMakeTarget(objLib->GetName(),
4443                                                       configName)
4444                          << ": " << trel << "\n";
4445         }
4446 
4447         // Create a rule for this target.
4448         makefileStream << trel << ":";
4449 
4450         // List dependencies if any exist.
4451         auto const x = target->GetDependLibraries().find(configName);
4452         if (x != target->GetDependLibraries().end()) {
4453           for (auto const& deplib : x->second) {
4454             std::string file = this->ConvertToRelativeForMake(deplib);
4455             makefileStream << "\\\n\t" << file;
4456             dummyRules.insert(file);
4457           }
4458         }
4459 
4460         for (auto objLib : objlibs) {
4461 
4462           const std::string objLibName = objLib->GetName();
4463           std::string d = cmStrCat(
4464             this->GetObjectsDirectory(this->CurrentProject, configName, objLib,
4465                                       OBJECT_LIBRARY_ARTIFACT_DIR),
4466             "lib", objLibName, ".a");
4467 
4468           std::string dependency = this->ConvertToRelativeForMake(d);
4469           makefileStream << "\\\n\t" << dependency;
4470           dummyRules.insert(dependency);
4471         }
4472 
4473         // Write the action to remove the target if it is out of date.
4474         makefileStream << "\n";
4475         makefileStream << "\t/bin/rm -f "
4476                        << this->ConvertToRelativeForMake(tfull) << "\n";
4477         // if building for more than one architecture
4478         // then remove those executables as well
4479         if (this->Architectures.size() > 1) {
4480           std::string universal = this->GetObjectsDirectory(
4481             this->CurrentProject, configName, gt, "$(OBJDIR)/");
4482           for (const auto& architecture : this->Architectures) {
4483             std::string universalFile = cmStrCat(universal, architecture, '/',
4484                                                  gt->GetFullName(configName));
4485             makefileStream << "\t/bin/rm -f "
4486                            << this->ConvertToRelativeForMake(universalFile)
4487                            << "\n";
4488           }
4489         }
4490         makefileStream << "\n\n";
4491       }
4492     }
4493   }
4494 
4495   makefileStream << "\n\n"
4496                  << "# For each target create a dummy rule"
4497                  << "so the target does not have to exist\n";
4498   for (auto const& dummyRule : dummyRules) {
4499     makefileStream << dummyRule << ":\n";
4500   }
4501 }
4502 
OutputXCodeProject(cmLocalGenerator * root,std::vector<cmLocalGenerator * > & generators)4503 void cmGlobalXCodeGenerator::OutputXCodeProject(
4504   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
4505 {
4506   if (generators.empty()) {
4507     return;
4508   }
4509   if (!this->CreateXCodeObjects(root, generators)) {
4510     return;
4511   }
4512   std::string xcodeDir = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
4513                                   root->GetProjectName(), ".xcodeproj");
4514   cmSystemTools::MakeDirectory(xcodeDir);
4515   std::string xcodeProjFile = xcodeDir + "/project.pbxproj";
4516   cmGeneratedFileStream fout(xcodeProjFile);
4517   fout.SetCopyIfDifferent(true);
4518   if (!fout) {
4519     return;
4520   }
4521   this->WriteXCodePBXProj(fout, root, generators);
4522 
4523   bool hasGeneratedSchemes = this->OutputXCodeSharedSchemes(xcodeDir, root);
4524   this->OutputXCodeWorkspaceSettings(xcodeDir, hasGeneratedSchemes);
4525 
4526   this->ClearXCodeObjects();
4527 
4528   // Since this call may have created new cache entries, save the cache:
4529   //
4530   root->GetMakefile()->GetCMakeInstance()->SaveCache(
4531     root->GetBinaryDirectory());
4532 }
4533 
OutputXCodeSharedSchemes(const std::string & xcProjDir,cmLocalGenerator * root)4534 bool cmGlobalXCodeGenerator::OutputXCodeSharedSchemes(
4535   const std::string& xcProjDir, cmLocalGenerator* root)
4536 {
4537   // collect all tests for the targets
4538   std::map<std::string, cmXCodeScheme::TestObjects> testables;
4539 
4540   for (const auto& obj : this->XCodeObjects) {
4541     if (obj->GetType() != cmXCodeObject::OBJECT ||
4542         obj->GetIsA() != cmXCodeObject::PBXNativeTarget) {
4543       continue;
4544     }
4545 
4546     if (!obj->GetTarget()->IsXCTestOnApple()) {
4547       continue;
4548     }
4549 
4550     cmValue testee = obj->GetTarget()->GetProperty("XCTEST_TESTEE");
4551     if (!testee) {
4552       continue;
4553     }
4554 
4555     testables[*testee].push_back(obj.get());
4556   }
4557 
4558   // generate scheme
4559   bool ret = false;
4560 
4561   // Since the lowest available Xcode version for testing was 6.4,
4562   // I'm setting this as a limit then
4563   if (this->XcodeVersion >= 64) {
4564     for (const auto& obj : this->XCodeObjects) {
4565       if (obj->GetType() == cmXCodeObject::OBJECT &&
4566           (obj->GetIsA() == cmXCodeObject::PBXNativeTarget ||
4567            obj->GetIsA() == cmXCodeObject::PBXAggregateTarget) &&
4568           (root->GetMakefile()->GetCMakeInstance()->GetIsInTryCompile() ||
4569            obj->GetTarget()->GetPropertyAsBool("XCODE_GENERATE_SCHEME"))) {
4570         const std::string& targetName = obj->GetTarget()->GetName();
4571         cmXCodeScheme schm(root, obj.get(), testables[targetName],
4572                            this->CurrentConfigurationTypes,
4573                            this->XcodeVersion);
4574         schm.WriteXCodeSharedScheme(xcProjDir,
4575                                     this->RelativeToSource(xcProjDir));
4576         ret = true;
4577       }
4578     }
4579   }
4580 
4581   return ret;
4582 }
4583 
OutputXCodeWorkspaceSettings(const std::string & xcProjDir,bool hasGeneratedSchemes)4584 void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings(
4585   const std::string& xcProjDir, bool hasGeneratedSchemes)
4586 {
4587   std::string xcodeSharedDataDir =
4588     cmStrCat(xcProjDir, "/project.xcworkspace/xcshareddata");
4589   cmSystemTools::MakeDirectory(xcodeSharedDataDir);
4590 
4591   std::string workspaceSettingsFile =
4592     cmStrCat(xcodeSharedDataDir, "/WorkspaceSettings.xcsettings");
4593 
4594   cmGeneratedFileStream fout(workspaceSettingsFile);
4595   fout.SetCopyIfDifferent(true);
4596   if (!fout) {
4597     return;
4598   }
4599 
4600   cmXMLWriter xout(fout);
4601   xout.StartDocument();
4602   xout.Doctype("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\""
4603                "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
4604   xout.StartElement("plist");
4605   xout.Attribute("version", "1.0");
4606   xout.StartElement("dict");
4607   if (this->XcodeVersion >= 100) {
4608     xout.Element("key", "BuildSystemType");
4609     switch (this->XcodeBuildSystem) {
4610       case BuildSystem::One:
4611         xout.Element("string", "Original");
4612         if (this->XcodeVersion >= 130) {
4613           xout.Element("key", "DisableBuildSystemDeprecationDiagnostic");
4614         } else {
4615           xout.Element("key", "DisableBuildSystemDeprecationWarning");
4616         }
4617         xout.Element("true");
4618         break;
4619       case BuildSystem::Twelve:
4620         xout.Element("string", "Latest");
4621         break;
4622     }
4623   }
4624   if (hasGeneratedSchemes) {
4625     xout.Element("key",
4626                  "IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded");
4627     xout.Element("false");
4628   }
4629   xout.EndElement(); // dict
4630   xout.EndElement(); // plist
4631   xout.EndDocument();
4632 }
4633 
WriteXCodePBXProj(std::ostream & fout,cmLocalGenerator *,std::vector<cmLocalGenerator * > &)4634 void cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout,
4635                                                cmLocalGenerator*,
4636                                                std::vector<cmLocalGenerator*>&)
4637 {
4638   SortXCodeObjects();
4639 
4640   fout << "// !$*UTF8*$!\n";
4641   fout << "{\n";
4642   cmXCodeObject::Indent(1, fout);
4643   fout << "archiveVersion = 1;\n";
4644   cmXCodeObject::Indent(1, fout);
4645   fout << "classes = {\n";
4646   cmXCodeObject::Indent(1, fout);
4647   fout << "};\n";
4648   cmXCodeObject::Indent(1, fout);
4649   fout << "objectVersion = 46;\n";
4650   cmXCode21Object::PrintList(this->XCodeObjects, fout);
4651   cmXCodeObject::Indent(1, fout);
4652   fout << "rootObject = " << this->RootObject->GetId()
4653        << " /* Project object */;\n";
4654   fout << "}\n";
4655 }
4656 
GetCMakeCFGIntDir() const4657 const char* cmGlobalXCodeGenerator::GetCMakeCFGIntDir() const
4658 {
4659   return "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
4660 }
4661 
ExpandCFGIntDir(const std::string & str,const std::string & config) const4662 std::string cmGlobalXCodeGenerator::ExpandCFGIntDir(
4663   const std::string& str, const std::string& config) const
4664 {
4665   std::string replace1 = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
4666   std::string replace2 = "$(CONFIGURATION)";
4667 
4668   std::string tmp = str;
4669   for (std::string::size_type i = tmp.find(replace1); i != std::string::npos;
4670        i = tmp.find(replace1, i)) {
4671     tmp.replace(i, replace1.size(), config);
4672     i += config.size();
4673   }
4674   for (std::string::size_type i = tmp.find(replace2); i != std::string::npos;
4675        i = tmp.find(replace2, i)) {
4676     tmp.replace(i, replace2.size(), config);
4677     i += config.size();
4678   }
4679   return tmp;
4680 }
4681 
GetDocumentation(cmDocumentationEntry & entry)4682 void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry)
4683 {
4684   entry.Name = cmGlobalXCodeGenerator::GetActualName();
4685   entry.Brief = "Generate Xcode project files.";
4686 }
4687 
ConvertToRelativeForMake(std::string const & p)4688 std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(
4689   std::string const& p)
4690 {
4691   return cmSystemTools::ConvertToOutputPath(p);
4692 }
4693 
RelativeToSource(const std::string & p)4694 std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p)
4695 {
4696   // We force conversion because Xcode breakpoints do not work unless
4697   // they are in a file named relative to the source tree.
4698   return cmSystemTools::ForceToRelativePath(
4699     this->CurrentRootGenerator->GetCurrentSourceDirectory(), p);
4700 }
4701 
RelativeToBinary(const std::string & p)4702 std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p)
4703 {
4704   return this->CurrentRootGenerator->MaybeRelativeToCurBinDir(p);
4705 }
4706 
XCodeEscapePath(const std::string & p)4707 std::string cmGlobalXCodeGenerator::XCodeEscapePath(const std::string& p)
4708 {
4709   if (p.find_first_of(" []") != std::string::npos) {
4710     std::string t = cmStrCat('"', p, '"');
4711     return t;
4712   }
4713   return p;
4714 }
4715 
AppendDirectoryForConfig(const std::string & prefix,const std::string & config,const std::string & suffix,std::string & dir)4716 void cmGlobalXCodeGenerator::AppendDirectoryForConfig(
4717   const std::string& prefix, const std::string& config,
4718   const std::string& suffix, std::string& dir)
4719 {
4720   if (!config.empty()) {
4721     dir += prefix;
4722     dir += config;
4723     dir += suffix;
4724   }
4725 }
4726 
LookupFlags(const std::string & varNamePrefix,const std::string & varNameLang,const std::string & varNameSuffix,const std::string & default_flags)4727 std::string cmGlobalXCodeGenerator::LookupFlags(
4728   const std::string& varNamePrefix, const std::string& varNameLang,
4729   const std::string& varNameSuffix, const std::string& default_flags)
4730 {
4731   if (!varNameLang.empty()) {
4732     std::string varName = cmStrCat(varNamePrefix, varNameLang, varNameSuffix);
4733     if (cmValue varValue = this->CurrentMakefile->GetDefinition(varName)) {
4734       if (!varValue->empty()) {
4735         return *varValue;
4736       }
4737     }
4738   }
4739   return default_flags;
4740 }
4741 
AppendDefines(BuildObjectListOrString & defs,const char * defines_list,bool dflag)4742 void cmGlobalXCodeGenerator::AppendDefines(BuildObjectListOrString& defs,
4743                                            const char* defines_list,
4744                                            bool dflag)
4745 {
4746   // Skip this if there are no definitions.
4747   if (!defines_list) {
4748     return;
4749   }
4750 
4751   // Expand the list of definitions.
4752   std::vector<std::string> defines = cmExpandedList(defines_list);
4753 
4754   // Store the definitions in the string.
4755   this->AppendDefines(defs, defines, dflag);
4756 }
4757 
AppendDefines(BuildObjectListOrString & defs,std::vector<std::string> const & defines,bool dflag)4758 void cmGlobalXCodeGenerator::AppendDefines(
4759   BuildObjectListOrString& defs, std::vector<std::string> const& defines,
4760   bool dflag)
4761 {
4762   // GCC_PREPROCESSOR_DEFINITIONS is a space-separated list of definitions.
4763   std::string def;
4764   for (auto const& define : defines) {
4765     // Start with -D if requested.
4766     def = cmStrCat(dflag ? "-D" : "", define);
4767 
4768     // Append the flag with needed escapes.
4769     std::string tmp;
4770     this->AppendFlag(tmp, def);
4771     defs.Add(tmp);
4772   }
4773 }
4774 
AppendFlag(std::string & flags,std::string const & flag) const4775 void cmGlobalXCodeGenerator::AppendFlag(std::string& flags,
4776                                         std::string const& flag) const
4777 {
4778   // Short-circuit for an empty flag.
4779   if (flag.empty()) {
4780     return;
4781   }
4782 
4783   // Separate from previous flags.
4784   if (!flags.empty()) {
4785     flags += " ";
4786   }
4787 
4788   // Check if the flag needs quoting.
4789   bool quoteFlag =
4790     flag.find_first_of("`~!@#$%^&*()+={}[]|:;\"'<>,.? ") != std::string::npos;
4791 
4792   // We escape a flag as follows:
4793   //   - Place each flag in single quotes ''
4794   //   - Escape a single quote as \'
4795   //   - Escape a backslash as \\ since it itself is an escape
4796   // Note that in the code below we need one more level of escapes for
4797   // C string syntax in this source file.
4798   //
4799   // The final level of escaping is done when the string is stored
4800   // into the project file by cmXCodeObject::PrintString.
4801 
4802   if (quoteFlag) {
4803     // Open single quote.
4804     flags += "'";
4805   }
4806 
4807   // Flag value with escaped quotes and backslashes.
4808   for (auto c : flag) {
4809     if (c == '\'') {
4810       flags += "'\\''";
4811     } else if (c == '\\') {
4812       flags += "\\\\";
4813     } else {
4814       flags += c;
4815     }
4816   }
4817 
4818   if (quoteFlag) {
4819     // Close single quote.
4820     flags += "'";
4821   }
4822 }
4823 
ComputeInfoPListLocation(cmGeneratorTarget * target)4824 std::string cmGlobalXCodeGenerator::ComputeInfoPListLocation(
4825   cmGeneratorTarget* target)
4826 {
4827   std::string plist =
4828     cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
4829              "/CMakeFiles/", target->GetName(), ".dir/Info.plist");
4830   return plist;
4831 }
4832 
4833 // Return true if the generated build tree may contain multiple builds.
4834 // i.e. "Can I build Debug and Release in the same tree?"
IsMultiConfig() const4835 bool cmGlobalXCodeGenerator::IsMultiConfig() const
4836 {
4837   // Newer Xcode versions are multi config:
4838   return true;
4839 }
4840 
HasKnownObjectFileLocation(std::string * reason) const4841 bool cmGlobalXCodeGenerator::HasKnownObjectFileLocation(
4842   std::string* reason) const
4843 {
4844   if (this->ObjectDirArch.find('$') != std::string::npos) {
4845     if (reason != nullptr) {
4846       *reason = " under Xcode with multiple architectures";
4847     }
4848     return false;
4849   }
4850   return true;
4851 }
4852 
UseEffectivePlatformName(cmMakefile * mf) const4853 bool cmGlobalXCodeGenerator::UseEffectivePlatformName(cmMakefile* mf) const
4854 {
4855   cmValue epnValue = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
4856     "XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
4857 
4858   if (!epnValue) {
4859     return mf->PlatformIsAppleEmbedded();
4860   }
4861 
4862   return cmIsOn(*epnValue);
4863 }
4864 
ShouldStripResourcePath(cmMakefile *) const4865 bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
4866 {
4867   // Xcode determines Resource location itself
4868   return true;
4869 }
4870 
ComputeTargetObjectDirectory(cmGeneratorTarget * gt) const4871 void cmGlobalXCodeGenerator::ComputeTargetObjectDirectory(
4872   cmGeneratorTarget* gt) const
4873 {
4874   std::string configName = this->GetCMakeCFGIntDir();
4875   std::string dir =
4876     cmStrCat(this->GetObjectsDirectory("$(PROJECT_NAME)", configName, gt,
4877                                        "$(OBJECT_FILE_DIR_normal:base)/"),
4878              this->ObjectDirArch, '/');
4879   gt->ObjectDirectory = dir;
4880 }
4881 
GetDeploymentPlatform(const cmMakefile * mf)4882 std::string cmGlobalXCodeGenerator::GetDeploymentPlatform(const cmMakefile* mf)
4883 {
4884   switch (mf->GetAppleSDKType()) {
4885     case cmMakefile::AppleSDK::AppleTVOS:
4886     case cmMakefile::AppleSDK::AppleTVSimulator:
4887       return "TVOS_DEPLOYMENT_TARGET";
4888 
4889     case cmMakefile::AppleSDK::IPhoneOS:
4890     case cmMakefile::AppleSDK::IPhoneSimulator:
4891       return "IPHONEOS_DEPLOYMENT_TARGET";
4892 
4893     case cmMakefile::AppleSDK::WatchOS:
4894     case cmMakefile::AppleSDK::WatchSimulator:
4895       return "WATCHOS_DEPLOYMENT_TARGET";
4896 
4897     case cmMakefile::AppleSDK::MacOS:
4898     default:
4899       return "MACOSX_DEPLOYMENT_TARGET";
4900   }
4901 }
4902