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, >gt,
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