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"
14 #include <cmext/algorithm>
15 #include <cmext/string_view>
17 #include "cmsys/RegularExpression.hxx"
19 #include "cmComputeLinkInformation.h"
20 #include "cmCustomCommand.h"
21 #include "cmCustomCommandGenerator.h"
22 #include "cmCustomCommandLines.h"
23 #include "cmDocumentationEntry.h"
24 #include "cmGeneratedFileStream.h"
25 #include "cmGeneratorExpression.h"
26 #include "cmGeneratorTarget.h"
27 #include "cmGlobalGeneratorFactory.h"
28 #include "cmLocalGenerator.h"
29 #include "cmLocalXCodeGenerator.h"
30 #include "cmMakefile.h"
31 #include "cmMessageType.h"
32 #include "cmOutputConverter.h"
33 #include "cmSourceFile.h"
34 #include "cmSourceGroup.h"
36 #include "cmStateTypes.h"
37 #include "cmStringAlgorithms.h"
38 #include "cmSystemTools.h"
40 #include "cmXCode21Object.h"
41 #include "cmXCodeObject.h"
42 #include "cmXCodeScheme.h"
45 struct cmLinkImplementation;
47 #if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
48 # define HAVE_APPLICATION_SERVICES
49 # include <ApplicationServices/ApplicationServices.h>
52 #if !defined(CMAKE_BOOTSTRAP)
53 # include "cmXMLParser.h"
55 // parse the xml file storing the installed version of Xcode on
57 class cmXcodeVersionParser : public cmXMLParser
60 cmXcodeVersionParser()
64 void StartElement(const std::string&, const char**) override
68 void EndElement(const std::string& name) override
71 this->Key = this->Data;
72 } else if (name == "string") {
73 if (this->Key == "CFBundleShortVersionString") {
74 this->Version = this->Data;
78 void CharacterDataHandler(const char* data, int length) override
80 this->Data.append(data, length);
88 // Builds either an object list or a space-separated string from the
90 class cmGlobalXCodeGenerator::BuildObjectListOrString
92 cmGlobalXCodeGenerator* Generator;
98 BuildObjectListOrString(cmGlobalXCodeGenerator* gen, bool buildObjectList)
103 if (buildObjectList) {
104 this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
108 bool IsEmpty() const { return this->Empty; }
110 void Add(const std::string& newString)
115 this->Group->AddObject(this->Generator->CreateString(newString));
117 this->String += newString;
122 const std::string& GetString() const { return this->String; }
124 cmXCodeObject* CreateList()
129 return this->Generator->CreateString(this->String);
133 class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory
136 std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
137 const std::string& name, cmake* cm) const override;
139 void GetDocumentation(cmDocumentationEntry& entry) const override
141 cmGlobalXCodeGenerator::GetDocumentation(entry);
144 std::vector<std::string> GetGeneratorNames() const override
146 std::vector<std::string> names;
147 names.push_back(cmGlobalXCodeGenerator::GetActualName());
151 std::vector<std::string> GetGeneratorNamesWithPlatform() const override
153 return std::vector<std::string>();
156 bool SupportsToolset() const override { return true; }
157 bool SupportsPlatform() const override { return false; }
159 std::vector<std::string> GetKnownPlatforms() const override
161 return std::vector<std::string>();
164 std::string GetDefaultPlatformName() const override { return std::string(); }
167 cmGlobalXCodeGenerator::cmGlobalXCodeGenerator(
168 cmake* cm, std::string const& version_string, unsigned int version_number)
169 : cmGlobalGenerator(cm)
171 this->VersionString = version_string;
172 this->XcodeVersion = version_number;
174 this->RootObject = nullptr;
175 this->MainGroupChildren = nullptr;
176 this->CurrentMakefile = nullptr;
177 this->CurrentLocalGenerator = nullptr;
178 this->XcodeBuildCommandInitialized = false;
180 this->ObjectDirArchDefault = "$(CURRENT_ARCH)";
181 this->ObjectDirArch = this->ObjectDirArchDefault;
183 cm->GetState()->SetIsGeneratorMultiConfig(true);
186 std::unique_ptr<cmGlobalGeneratorFactory> cmGlobalXCodeGenerator::NewFactory()
188 return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory);
191 std::unique_ptr<cmGlobalGenerator>
192 cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(const std::string& name,
195 if (name != GetActualName()) {
196 return std::unique_ptr<cmGlobalGenerator>();
198 #if !defined(CMAKE_BOOTSTRAP)
199 cmXcodeVersionParser parser;
200 std::string versionFile;
203 bool commandResult = cmSystemTools::RunSingleCommand(
204 "xcode-select --print-path", &out, nullptr, nullptr, nullptr,
205 cmSystemTools::OUTPUT_NONE);
207 std::string::size_type pos = out.find(".app/");
208 if (pos != std::string::npos) {
209 versionFile = out.substr(0, pos + 5) + "Contents/version.plist";
213 if (!versionFile.empty() && cmSystemTools::FileExists(versionFile)) {
214 parser.ParseFile(versionFile.c_str());
215 } else if (cmSystemTools::FileExists(
216 "/Applications/Xcode.app/Contents/version.plist")) {
217 parser.ParseFile("/Applications/Xcode.app/Contents/version.plist");
220 "/Developer/Applications/Xcode.app/Contents/version.plist");
222 std::string const& version_string = parser.Version;
224 // Compute an integer form of the version number.
225 unsigned int v[2] = { 0, 0 };
226 sscanf(version_string.c_str(), "%u.%u", &v[0], &v[1]);
227 unsigned int version_number = 10 * v[0] + v[1];
229 if (version_number < 50) {
230 cm->IssueMessage(MessageType::FATAL_ERROR,
231 "Xcode " + version_string + " not supported.");
232 return std::unique_ptr<cmGlobalGenerator>();
235 return std::unique_ptr<cmGlobalGenerator>(
236 cm::make_unique<cmGlobalXCodeGenerator>(cm, version_string,
239 std::cerr << "CMake should be built with cmake to use Xcode, "
240 "default to Xcode 1.5\n";
241 return std::unique_ptr<cmGlobalGenerator>(
242 cm::make_unique<cmGlobalXCodeGenerator>(cm));
246 bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf)
248 // The Xcode generator knows how to lookup its build tool
249 // directly instead of needing a helper module to do it, so we
250 // do not actually need to put CMAKE_MAKE_PROGRAM into the cache.
251 if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
252 mf->AddDefinition("CMAKE_MAKE_PROGRAM", this->GetXcodeBuildCommand());
257 std::string const& cmGlobalXCodeGenerator::GetXcodeBuildCommand()
259 if (!this->XcodeBuildCommandInitialized) {
260 this->XcodeBuildCommandInitialized = true;
261 this->XcodeBuildCommand = this->FindXcodeBuildCommand();
263 return this->XcodeBuildCommand;
266 std::string cmGlobalXCodeGenerator::FindXcodeBuildCommand()
268 std::string makeProgram = cmSystemTools::FindProgram("xcodebuild");
269 if (makeProgram.empty()) {
270 makeProgram = "xcodebuild";
275 bool cmGlobalXCodeGenerator::SetSystemName(std::string const& s,
278 this->SystemName = s;
279 return this->cmGlobalGenerator::SetSystemName(s, mf);
282 bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts,
283 bool build, cmMakefile* mf)
285 if (ts.find_first_of(",=") != std::string::npos) {
286 std::ostringstream e;
287 /* clang-format off */
290 " " << this->GetName() << "\n"
291 "does not recognize the toolset\n"
293 "that was specified.";
294 /* clang-format on */
295 mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
298 this->GeneratorToolset = ts;
302 if (!this->GeneratorToolset.empty()) {
303 mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset);
308 void cmGlobalXCodeGenerator::EnableLanguage(
309 std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
311 mf->AddDefinition("XCODE", "1");
312 mf->AddDefinition("XCODE_VERSION", this->VersionString);
313 if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
314 mf->AddCacheDefinition(
315 "CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
316 "Semicolon separated list of supported configuration types, "
317 "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
318 "anything else will be ignored.",
319 cmStateEnums::STRING);
321 mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
322 this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
323 this->ComputeArchitectures(mf);
326 bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
327 const std::string& projectName, bool dryRun)
331 #ifdef HAVE_APPLICATION_SERVICES
332 std::string url = bindir + "/" + projectName + ".xcodeproj";
335 return cmSystemTools::FileExists(url, false);
338 CFStringRef cfStr = CFStringCreateWithCString(
339 kCFAllocatorDefault, url.c_str(), kCFStringEncodingUTF8);
341 CFURLRef cfUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfStr,
342 kCFURLPOSIXPathStyle, true);
344 OSStatus err = LSOpenCFURLRef(cfUrl, nullptr);
355 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
356 cmGlobalXCodeGenerator::GenerateBuildCommand(
357 const std::string& makeProgram, const std::string& projectName,
358 const std::string& /*projectDir*/,
359 std::vector<std::string> const& targetNames, const std::string& config,
360 bool /*fast*/, int jobs, bool /*verbose*/,
361 std::vector<std::string> const& makeOptions)
363 GeneratedMakeCommand makeCommand;
364 // now build the test
366 this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()));
368 if (!projectName.empty()) {
369 makeCommand.Add("-project");
370 std::string projectArg = cmStrCat(projectName, ".xcodeproj");
371 makeCommand.Add(projectArg);
373 if (cm::contains(targetNames, "clean")) {
374 makeCommand.Add("clean");
375 makeCommand.Add("-target", "ALL_BUILD");
377 makeCommand.Add("build");
378 if (targetNames.empty() ||
379 ((targetNames.size() == 1) && targetNames.front().empty())) {
380 makeCommand.Add("-target", "ALL_BUILD");
382 for (const auto& tname : targetNames) {
383 if (!tname.empty()) {
384 makeCommand.Add("-target", tname);
390 makeCommand.Add("-configuration", (config.empty() ? "Debug" : config));
392 if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
393 makeCommand.Add("-jobs");
394 if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
395 makeCommand.Add(std::to_string(jobs));
399 if (this->XcodeVersion >= 70) {
400 makeCommand.Add("-hideShellScriptEnvironment");
402 makeCommand.Add(makeOptions.begin(), makeOptions.end());
403 return { std::move(makeCommand) };
406 //! Create a local generator appropriate to this Global Generator
407 std::unique_ptr<cmLocalGenerator> cmGlobalXCodeGenerator::CreateLocalGenerator(
410 return std::unique_ptr<cmLocalGenerator>(
411 cm::make_unique<cmLocalXCodeGenerator>(this, mf));
414 void cmGlobalXCodeGenerator::AddExtraIDETargets()
416 // make sure extra targets are added before calling
417 // the parent generate which will call trace depends
418 for (auto keyVal : this->ProjectMap) {
419 cmLocalGenerator* root = keyVal.second[0];
420 this->SetGenerationRoot(root);
421 // add ALL_BUILD, INSTALL, etc
422 this->AddExtraTargets(root, keyVal.second);
426 void cmGlobalXCodeGenerator::ComputeTargetOrder()
429 auto const& lgens = this->GetLocalGenerators();
430 for (auto const& lgen : lgens) {
431 const auto& targets = lgen->GetGeneratorTargets();
432 for (const auto& gt : targets) {
433 this->ComputeTargetOrder(gt.get(), index);
436 assert(index == this->TargetOrderIndex.size());
439 void cmGlobalXCodeGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
442 std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0);
443 auto insertion = this->TargetOrderIndex.insert(value);
444 if (!insertion.second) {
447 auto entry = insertion.first;
449 auto& deps = this->GetTargetDirectDepends(gt);
450 for (auto& d : deps) {
451 this->ComputeTargetOrder(d, index);
454 entry->second = index++;
457 void cmGlobalXCodeGenerator::Generate()
459 this->cmGlobalGenerator::Generate();
460 if (cmSystemTools::GetErrorOccuredFlag()) {
464 this->ComputeTargetOrder();
466 for (auto keyVal : this->ProjectMap) {
467 cmLocalGenerator* root = keyVal.second[0];
469 bool generateTopLevelProjectOnly =
470 root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
472 if (generateTopLevelProjectOnly) {
473 cmStateSnapshot snp = root->GetStateSnapshot();
474 if (snp.GetBuildsystemDirectoryParent().IsValid()) {
479 this->SetGenerationRoot(root);
480 // now create the project
481 this->OutputXCodeProject(root, keyVal.second);
485 void cmGlobalXCodeGenerator::SetGenerationRoot(cmLocalGenerator* root)
487 this->CurrentProject = root->GetProjectName();
488 this->SetCurrentLocalGenerator(root);
489 cmSystemTools::SplitPath(
490 this->CurrentLocalGenerator->GetCurrentSourceDirectory(),
491 this->ProjectSourceDirectoryComponents);
492 cmSystemTools::SplitPath(
493 this->CurrentLocalGenerator->GetCurrentBinaryDirectory(),
494 this->ProjectOutputDirectoryComponents);
496 this->CurrentXCodeHackMakefile =
497 cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
498 cmSystemTools::MakeDirectory(this->CurrentXCodeHackMakefile);
499 this->CurrentXCodeHackMakefile += "/XCODE_DEPEND_HELPER.make";
502 std::string cmGlobalXCodeGenerator::PostBuildMakeTarget(
503 std::string const& tName, std::string const& configName)
505 std::string target = tName;
506 std::replace(target.begin(), target.end(), ' ', '_');
507 std::string out = cmStrCat("PostBuild.", target, '.', configName);
511 #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"
512 #define OBJECT_LIBRARY_ARTIFACT_DIR std::string()
514 void cmGlobalXCodeGenerator::AddExtraTargets(
515 cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens)
517 const char* no_working_directory = nullptr;
518 std::vector<std::string> no_byproducts;
519 std::vector<std::string> no_depends;
522 cmTarget* allbuild = root->AddUtilityCommand(
523 "ALL_BUILD", true, no_working_directory, no_byproducts, no_depends,
524 cmMakeSingleCommandLine({ "echo", "Build all projects" }));
526 root->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(allbuild, root));
528 // Add XCODE depend helper
529 std::string dir = root->GetCurrentBinaryDirectory();
530 cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
531 { "make", "-C", dir, "-f", this->CurrentXCodeHackMakefile,
532 "OBJDIR=$(OBJDIR)", /* placeholder, see below */ "" });
535 bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
536 bool generateTopLevelProjectOnly =
537 root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY");
539 !root->GetStateSnapshot().GetBuildsystemDirectoryParent().IsValid();
540 bool isGenerateProject = isTopLevel || !generateTopLevelProjectOnly;
541 if (regenerate && isGenerateProject) {
542 this->CreateReRunCMakeFile(root, gens);
544 this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
545 cmSystemTools::ReplaceString(file, "\\ ", " ");
547 root->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, true,
548 no_working_directory, no_byproducts, no_depends,
549 cmMakeSingleCommandLine({ "make", "-f", file }));
551 root->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(check, root));
554 // now make the allbuild depend on all the non-utility targets
556 for (auto& gen : gens) {
557 for (const auto& target : gen->GetGeneratorTargets()) {
558 if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
563 (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
564 target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false);
567 // make all exe, shared libs and modules
568 // run the depend check makefile as a post build rule
569 // this will make sure that when the next target is built
570 // things are up-to-date
571 if (isGenerateProject &&
572 target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
573 commandLines.front().back() = // fill placeholder
574 this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
575 gen->AddCustomCommandToTarget(
576 target->GetName(), no_byproducts, no_depends, commandLines,
577 cmCustomCommandType::POST_BUILD, "Depend check for xcode",
578 dir.c_str(), true, false, "", "", false,
579 cmObjectLibraryCommands::Accept);
582 if (!this->IsExcluded(gens[0], target.get())) {
583 allbuild->AddUtility(target->GetName(), false);
589 void cmGlobalXCodeGenerator::CreateReRunCMakeFile(
590 cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
592 std::vector<std::string> lfiles;
593 for (auto gen : gens) {
594 cm::append(lfiles, gen->GetMakefile()->GetListFiles());
598 std::sort(lfiles.begin(), lfiles.end());
599 lfiles.erase(std::unique(lfiles.begin(), lfiles.end()), lfiles.end());
601 cmake* cm = this->GetCMakeInstance();
602 if (cm->DoWriteGlobVerifyTarget()) {
603 lfiles.emplace_back(cm->GetGlobVerifyStamp());
606 this->CurrentReRunCMakeMakefile =
607 cmStrCat(root->GetCurrentBinaryDirectory(), "/CMakeScripts");
608 cmSystemTools::MakeDirectory(this->CurrentReRunCMakeMakefile);
609 this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
610 cmGeneratedFileStream makefileStream(this->CurrentReRunCMakeMakefile);
611 makefileStream.SetCopyIfDifferent(true);
612 makefileStream << "# Generated by CMake, DO NOT EDIT\n\n";
614 makefileStream << "TARGETS:= \n";
615 makefileStream << "empty:= \n";
616 makefileStream << "space:= $(empty) $(empty)\n";
617 makefileStream << "spaceplus:= $(empty)\\ $(empty)\n\n";
619 for (const auto& lfile : lfiles) {
620 makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
621 << this->ConvertToRelativeForMake(lfile) << "))\n";
623 makefileStream << "\n";
625 std::string checkCache =
626 cmStrCat(root->GetBinaryDirectory(), "/CMakeFiles/cmake.check_cache");
628 if (cm->DoWriteGlobVerifyTarget()) {
629 makefileStream << ".NOTPARALLEL:\n\n";
630 makefileStream << ".PHONY: all VERIFY_GLOBS\n\n";
631 makefileStream << "all: VERIFY_GLOBS "
632 << this->ConvertToRelativeForMake(checkCache) << "\n\n";
633 makefileStream << "VERIFY_GLOBS:\n";
634 makefileStream << "\t"
635 << this->ConvertToRelativeForMake(
636 cmSystemTools::GetCMakeCommand())
638 << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript())
642 makefileStream << this->ConvertToRelativeForMake(checkCache)
644 makefileStream << "\t"
645 << this->ConvertToRelativeForMake(
646 cmSystemTools::GetCMakeCommand())
648 << this->ConvertToRelativeForMake(root->GetSourceDirectory())
650 << this->ConvertToRelativeForMake(root->GetBinaryDirectory())
654 static bool objectIdLessThan(const std::unique_ptr<cmXCodeObject>& l,
655 const std::unique_ptr<cmXCodeObject>& r)
657 return l->GetId() < r->GetId();
660 void cmGlobalXCodeGenerator::SortXCodeObjects()
662 std::sort(this->XCodeObjects.begin(), this->XCodeObjects.end(),
666 void cmGlobalXCodeGenerator::ClearXCodeObjects()
668 this->TargetDoneSet.clear();
669 this->XCodeObjects.clear();
670 this->XCodeObjectIDs.clear();
671 this->XCodeObjectMap.clear();
672 this->GroupMap.clear();
673 this->GroupNameMap.clear();
674 this->TargetGroup.clear();
675 this->FileRefs.clear();
678 void cmGlobalXCodeGenerator::addObject(std::unique_ptr<cmXCodeObject> obj)
680 if (obj->GetType() == cmXCodeObject::OBJECT) {
681 const std::string& id = obj->GetId();
683 // If this is a duplicate id, it's an error:
685 if (this->XCodeObjectIDs.count(id)) {
686 cmSystemTools::Error(
687 "Xcode generator: duplicate object ids not allowed");
690 this->XCodeObjectIDs.insert(id);
693 this->XCodeObjects.push_back(std::move(obj));
696 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(
697 cmXCodeObject::PBXType ptype)
699 auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT);
700 auto ptr = obj.get();
701 this->addObject(std::move(obj));
705 cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type)
707 auto obj = cm::make_unique<cmXCodeObject>(cmXCodeObject::None, type);
708 auto ptr = obj.get();
709 this->addObject(std::move(obj));
713 cmXCodeObject* cmGlobalXCodeGenerator::CreateString(const std::string& s)
715 cmXCodeObject* obj = this->CreateObject(cmXCodeObject::STRING);
720 cmXCodeObject* cmGlobalXCodeGenerator::CreateObjectReference(
723 cmXCodeObject* obj = this->CreateObject(cmXCodeObject::OBJECT_REF);
728 cmXCodeObject* cmGlobalXCodeGenerator::CreateFlatClone(cmXCodeObject* orig)
730 cmXCodeObject* obj = this->CreateObject(orig->GetType());
731 obj->CopyAttributes(orig);
735 std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target,
736 const std::string& fullpath)
738 std::string key(target->GetName());
744 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath(
745 const std::string& fullpath, cmGeneratorTarget* target,
746 const std::string& lang, cmSourceFile* sf)
748 // Using a map and the full path guarantees that we will always get the same
749 // fileRef object for any given full path.
751 cmXCodeObject* fileRef =
752 this->CreateXCodeFileReferenceFromPath(fullpath, target, lang, sf);
754 cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile);
755 buildFile->SetComment(fileRef->GetComment());
756 buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef));
761 class XCodeGeneratorExpressionInterpreter
762 : public cmGeneratorExpressionInterpreter
765 XCodeGeneratorExpressionInterpreter(cmSourceFile* sourceFile,
766 cmLocalGenerator* localGenerator,
767 cmGeneratorTarget* headTarget,
768 const std::string& lang)
769 : cmGeneratorExpressionInterpreter(
770 localGenerator, "NO-PER-CONFIG-SUPPORT-IN-XCODE", headTarget, lang)
771 , SourceFile(sourceFile)
775 XCodeGeneratorExpressionInterpreter(
776 XCodeGeneratorExpressionInterpreter const&) = delete;
777 XCodeGeneratorExpressionInterpreter& operator=(
778 XCodeGeneratorExpressionInterpreter const&) = delete;
780 const std::string& Evaluate(const char* expression,
781 const std::string& property)
783 return this->Evaluate(std::string(expression ? expression : ""), property);
786 const std::string& Evaluate(const std::string& expression,
787 const std::string& property)
789 const std::string& processed =
790 this->cmGeneratorExpressionInterpreter::Evaluate(expression, property);
791 if (this->CompiledGeneratorExpression->GetHadContextSensitiveCondition()) {
792 std::ostringstream e;
793 /* clang-format off */
795 "Xcode does not support per-config per-source " << property << ":\n"
796 " " << expression << "\n"
797 "specified for source:\n"
798 " " << this->SourceFile->ResolveFullPath() << "\n";
799 /* clang-format on */
800 this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
807 cmSourceFile* SourceFile = nullptr;
810 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile(
811 cmLocalGenerator* lg, cmSourceFile* sf, cmGeneratorTarget* gtgt)
813 std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
815 XCodeGeneratorExpressionInterpreter genexInterpreter(sf, lg, gtgt, lang);
817 // Add flags from target and source file properties.
819 std::string const& srcfmt = sf->GetSafeProperty("Fortran_FORMAT");
820 switch (cmOutputConverter::GetFortranFormat(srcfmt)) {
821 case cmOutputConverter::FortranFormatFixed:
822 flags = "-fixed " + flags;
824 case cmOutputConverter::FortranFormatFree:
825 flags = "-free " + flags;
830 const std::string COMPILE_FLAGS("COMPILE_FLAGS");
831 if (cmProp cflags = sf->GetProperty(COMPILE_FLAGS)) {
832 lg->AppendFlags(flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
834 const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
835 if (cmProp coptions = sf->GetProperty(COMPILE_OPTIONS)) {
836 lg->AppendCompileOptions(
837 flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
840 // Add per-source definitions.
841 BuildObjectListOrString flagsBuild(this, false);
842 const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
843 if (cmProp compile_defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
846 genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS).c_str(),
850 if (sf->GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS")) {
851 this->AppendDefines(flagsBuild, "CMAKE_SKIP_PRECOMPILE_HEADERS", true);
854 if (!flagsBuild.IsEmpty()) {
855 if (!flags.empty()) {
858 flags += flagsBuild.GetString();
861 // Add per-source include directories.
862 std::vector<std::string> includes;
863 const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
864 if (cmProp cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
865 lg->AppendIncludeDirectories(
866 includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
869 lg->AppendFlags(flags, lg->GetIncludeFlags(includes, gtgt, lang, true));
871 cmXCodeObject* buildFile =
872 this->CreateXCodeSourceFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf);
874 cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
875 settings->AddAttributeIfNotEmpty("COMPILER_FLAGS",
876 this->CreateString(flags));
878 cmGeneratorTarget::SourceFileFlags tsFlags =
879 gtgt->GetTargetSourceFileFlags(sf);
881 cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
883 // Is this a "private" or "public" framework header file?
884 // Set the ATTRIBUTES attribute appropriately...
886 if (gtgt->IsFrameworkOnApple()) {
887 if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePrivateHeader) {
888 attrs->AddObject(this->CreateString("Private"));
889 } else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypePublicHeader) {
890 attrs->AddObject(this->CreateString("Public"));
894 // Add user-specified file attributes.
895 cmProp extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
896 if (extraFileAttributes) {
897 // Expand the list of attributes.
898 std::vector<std::string> attributes = cmExpandedList(*extraFileAttributes);
900 // Store the attributes.
901 for (const auto& attribute : attributes) {
902 attrs->AddObject(this->CreateString(attribute));
906 settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs);
908 buildFile->AddAttributeIfNotEmpty("settings", settings);
912 void cmGlobalXCodeGenerator::AddXCodeProjBuildRule(
913 cmGeneratorTarget* target, std::vector<cmSourceFile*>& sources) const
915 std::string listfile =
916 cmStrCat(target->GetLocalGenerator()->GetCurrentSourceDirectory(),
918 cmSourceFile* srcCMakeLists = target->Makefile->GetOrCreateSource(
919 listfile, false, cmSourceFileLocationKind::Known);
920 if (!cm::contains(sources, srcCMakeLists)) {
921 sources.push_back(srcCMakeLists);
925 std::string GetSourcecodeValueFromFileExtension(const std::string& _ext,
926 const std::string& lang,
927 bool& keepLastKnownFileType)
929 std::string ext = cmSystemTools::LowerCase(_ext);
930 std::string sourcecode = "sourcecode";
933 sourcecode = "compiled.mach-o.objfile";
934 } else if (ext == "xctest") {
935 sourcecode = "wrapper.cfbundle";
936 } else if (ext == "xib") {
937 keepLastKnownFileType = true;
938 sourcecode = "file.xib";
939 } else if (ext == "storyboard") {
940 keepLastKnownFileType = true;
941 sourcecode = "file.storyboard";
942 } else if (ext == "mm") {
943 sourcecode += ".cpp.objcpp";
944 } else if (ext == "m") {
945 sourcecode += ".c.objc";
946 } else if (ext == "swift") {
947 sourcecode += ".swift";
948 } else if (ext == "plist") {
949 sourcecode += ".text.plist";
950 } else if (ext == "h") {
951 sourcecode += ".c.h";
952 } else if (ext == "hxx" || ext == "hpp" || ext == "txx" || ext == "pch" ||
954 sourcecode += ".cpp.h";
955 } else if (ext == "png" || ext == "gif" || ext == "jpg") {
956 keepLastKnownFileType = true;
957 sourcecode = "image";
958 } else if (ext == "txt") {
959 sourcecode += ".text";
960 } else if (lang == "CXX") {
961 sourcecode += ".cpp.cpp";
962 } else if (lang == "C") {
963 sourcecode += ".c.c";
964 } else if (lang == "OBJCXX") {
965 sourcecode += ".cpp.objcpp";
966 } else if (lang == "OBJC") {
967 sourcecode += ".c.objc";
968 } else if (lang == "Fortran") {
969 sourcecode += ".fortran.f90";
970 } else if (lang == "ASM") {
971 sourcecode += ".asm";
972 } else if (ext == "metal") {
973 sourcecode += ".metal";
974 } else if (ext == "mig") {
975 sourcecode += ".mig";
979 // // Already specialized above or we leave sourcecode == "sourcecode"
980 // // which is probably the most correct choice. Extensionless headers,
981 // // for example... Or file types unknown to Xcode that do not map to a
982 // // valid explicitFileType value.
988 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath(
989 const std::string& fullpath, cmGeneratorTarget* target,
990 const std::string& lang, cmSourceFile* sf)
992 std::string key = GetGroupMapKeyFromPath(target, fullpath);
993 cmXCodeObject* fileRef = this->FileRefs[key];
995 fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
996 fileRef->SetComment(fullpath);
997 this->FileRefs[key] = fileRef;
999 cmXCodeObject* group = this->GroupMap[key];
1000 cmXCodeObject* children = group->GetObject("children");
1001 if (!children->HasObject(fileRef)) {
1002 children->AddObject(fileRef);
1004 fileRef->AddAttribute("fileEncoding", this->CreateString("4"));
1006 bool useLastKnownFileType = false;
1007 std::string fileType;
1009 if (cmProp e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
1011 } else if (cmProp l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE")) {
1012 useLastKnownFileType = true;
1016 if (fileType.empty()) {
1017 // Compute the extension without leading '.'.
1018 std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath);
1020 ext = ext.substr(1);
1023 // If fullpath references a directory, then we need to specify
1024 // lastKnownFileType as folder in order for Xcode to be able to
1025 // open the contents of the folder.
1026 // (Xcode 4.6 does not like explicitFileType=folder).
1027 if (cmSystemTools::FileIsDirectory(fullpath)) {
1028 fileType = (ext == "xcassets" ? "folder.assetcatalog" : "folder");
1029 useLastKnownFileType = true;
1032 GetSourcecodeValueFromFileExtension(ext, lang, useLastKnownFileType);
1036 fileRef->AddAttribute(useLastKnownFileType ? "lastKnownFileType"
1037 : "explicitFileType",
1038 this->CreateString(fileType));
1040 // Store the file path relative to the top of the source tree.
1041 std::string path = this->RelativeToSource(fullpath);
1042 std::string name = cmSystemTools::GetFilenameName(path);
1043 const char* sourceTree =
1044 cmSystemTools::FileIsFullPath(path) ? "<absolute>" : "SOURCE_ROOT";
1045 fileRef->AddAttribute("name", this->CreateString(name));
1046 fileRef->AddAttribute("path", this->CreateString(path));
1047 fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree));
1051 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReference(
1052 cmSourceFile* sf, cmGeneratorTarget* target)
1054 std::string lang = this->CurrentLocalGenerator->GetSourceFileLanguage(*sf);
1056 return this->CreateXCodeFileReferenceFromPath(sf->ResolveFullPath(), target,
1060 bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname)
1062 if (tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" ||
1063 tname == "install" || tname == "package" || tname == "RUN_TESTS" ||
1064 tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
1065 if (this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) {
1068 this->TargetDoneSet.insert(tname);
1074 void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen)
1076 this->CurrentLocalGenerator = gen;
1077 this->CurrentMakefile = gen->GetMakefile();
1079 // Select the current set of configuration types.
1080 this->CurrentConfigurationTypes.clear();
1081 this->CurrentMakefile->GetConfigurations(this->CurrentConfigurationTypes);
1082 if (this->CurrentConfigurationTypes.empty()) {
1083 this->CurrentConfigurationTypes.emplace_back();
1087 struct cmSourceFilePathCompare
1089 bool operator()(cmSourceFile* l, cmSourceFile* r)
1091 return l->ResolveFullPath() < r->ResolveFullPath();
1095 struct cmCompareTargets
1097 bool operator()(cmXCodeObject* l, cmXCodeObject* r) const
1099 std::string const& a = l->GetTarget()->GetName();
1100 std::string const& b = r->GetTarget()->GetName();
1101 if (a == "ALL_BUILD") {
1104 if (b == "ALL_BUILD") {
1111 bool cmGlobalXCodeGenerator::CreateXCodeTargets(
1112 cmLocalGenerator* gen, std::vector<cmXCodeObject*>& targets)
1114 this->SetCurrentLocalGenerator(gen);
1115 std::vector<cmGeneratorTarget*> gts;
1116 cm::append(gts, this->CurrentLocalGenerator->GetGeneratorTargets());
1117 std::sort(gts.begin(), gts.end(),
1118 [this](cmGeneratorTarget const* l, cmGeneratorTarget const* r) {
1119 return this->TargetOrderIndex[l] < this->TargetOrderIndex[r];
1121 for (auto gtgt : gts) {
1122 if (!this->CreateXCodeTarget(gtgt, targets)) {
1126 std::sort(targets.begin(), targets.end(), cmCompareTargets());
1130 bool cmGlobalXCodeGenerator::CreateXCodeTarget(
1131 cmGeneratorTarget* gtgt, std::vector<cmXCodeObject*>& targets)
1133 std::string targetName = gtgt->GetName();
1135 // make sure ALL_BUILD, INSTALL, etc are only done once
1136 if (this->SpecialTargetEmitted(targetName)) {
1140 if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
1144 if (gtgt->GetType() == cmStateEnums::UTILITY ||
1145 gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) {
1146 cmXCodeObject* t = this->CreateUtilityTarget(gtgt);
1150 targets.push_back(t);
1154 // organize the sources
1155 std::vector<cmSourceFile*> classes;
1156 if (!gtgt->GetConfigCommonSourceFiles(classes)) {
1160 // Add CMakeLists.txt file for user convenience.
1161 this->AddXCodeProjBuildRule(gtgt, classes);
1163 // Add the Info.plist we are about to generate for an App Bundle.
1164 if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
1165 std::string plist = this->ComputeInfoPListLocation(gtgt);
1166 cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
1167 plist, true, cmSourceFileLocationKind::Known);
1168 classes.push_back(sf);
1171 std::sort(classes.begin(), classes.end(), cmSourceFilePathCompare());
1173 gtgt->ComputeObjectMapping();
1175 std::vector<cmXCodeObject*> externalObjFiles;
1176 std::vector<cmXCodeObject*> headerFiles;
1177 std::vector<cmXCodeObject*> resourceFiles;
1178 std::vector<cmXCodeObject*> sourceFiles;
1179 for (auto sourceFile : classes) {
1180 cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1181 this->CurrentLocalGenerator, sourceFile, gtgt);
1182 cmXCodeObject* fr = xsf->GetObject("fileRef");
1183 cmXCodeObject* filetype = fr->GetObject()->GetObject("explicitFileType");
1185 cmGeneratorTarget::SourceFileFlags tsFlags =
1186 gtgt->GetTargetSourceFileFlags(sourceFile);
1188 if (filetype && filetype->GetString() == "compiled.mach-o.objfile") {
1189 if (sourceFile->GetObjectLibrary().empty()) {
1190 externalObjFiles.push_back(xsf);
1192 } else if (this->IsHeaderFile(sourceFile) ||
1194 cmGeneratorTarget::SourceFileTypePrivateHeader) ||
1196 cmGeneratorTarget::SourceFileTypePublicHeader)) {
1197 headerFiles.push_back(xsf);
1198 } else if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeResource) {
1199 resourceFiles.push_back(xsf);
1200 } else if (!sourceFile->GetPropertyAsBool("HEADER_FILE_ONLY") &&
1201 !gtgt->IsSourceFilePartOfUnityBatch(
1202 sourceFile->ResolveFullPath())) {
1203 // Include this file in the build if it has a known language
1204 // and has not been listed as an ignored extension for this
1206 if (!this->CurrentLocalGenerator->GetSourceFileLanguage(*sourceFile)
1208 !this->IgnoreFile(sourceFile->GetExtension().c_str())) {
1209 sourceFiles.push_back(xsf);
1214 // some build phases only apply to bundles and/or frameworks
1215 bool isFrameworkTarget = gtgt->IsFrameworkOnApple();
1216 bool isBundleTarget = gtgt->GetPropertyAsBool("MACOSX_BUNDLE");
1217 bool isCFBundleTarget = gtgt->IsCFBundleOnApple();
1219 cmXCodeObject* buildFiles = nullptr;
1221 // create source build phase
1222 cmXCodeObject* sourceBuildPhase = nullptr;
1223 if (!sourceFiles.empty()) {
1224 sourceBuildPhase = this->CreateObject(cmXCodeObject::PBXSourcesBuildPhase);
1225 sourceBuildPhase->SetComment("Sources");
1226 sourceBuildPhase->AddAttribute("buildActionMask",
1227 this->CreateString("2147483647"));
1228 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1229 for (auto& sourceFile : sourceFiles) {
1230 buildFiles->AddObject(sourceFile);
1232 sourceBuildPhase->AddAttribute("files", buildFiles);
1233 sourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1234 this->CreateString("0"));
1237 // create header build phase - only for framework targets
1238 cmXCodeObject* headerBuildPhase = nullptr;
1239 if (!headerFiles.empty() && isFrameworkTarget) {
1240 headerBuildPhase = this->CreateObject(cmXCodeObject::PBXHeadersBuildPhase);
1241 headerBuildPhase->SetComment("Headers");
1242 headerBuildPhase->AddAttribute("buildActionMask",
1243 this->CreateString("2147483647"));
1244 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1245 for (auto& headerFile : headerFiles) {
1246 buildFiles->AddObject(headerFile);
1248 headerBuildPhase->AddAttribute("files", buildFiles);
1249 headerBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1250 this->CreateString("0"));
1253 // create resource build phase - only for framework or bundle targets
1254 cmXCodeObject* resourceBuildPhase = nullptr;
1255 if (!resourceFiles.empty() &&
1256 (isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
1257 resourceBuildPhase =
1258 this->CreateObject(cmXCodeObject::PBXResourcesBuildPhase);
1259 resourceBuildPhase->SetComment("Resources");
1260 resourceBuildPhase->AddAttribute("buildActionMask",
1261 this->CreateString("2147483647"));
1262 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1263 for (auto& resourceFile : resourceFiles) {
1264 buildFiles->AddObject(resourceFile);
1266 resourceBuildPhase->AddAttribute("files", buildFiles);
1267 resourceBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1268 this->CreateString("0"));
1271 // create vector of "non-resource content file" build phases - only for
1272 // framework or bundle targets
1273 std::vector<cmXCodeObject*> contentBuildPhases;
1274 if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
1275 using mapOfVectorOfSourceFiles =
1276 std::map<std::string, std::vector<cmSourceFile*>>;
1277 mapOfVectorOfSourceFiles bundleFiles;
1278 for (auto sourceFile : classes) {
1279 cmGeneratorTarget::SourceFileFlags tsFlags =
1280 gtgt->GetTargetSourceFileFlags(sourceFile);
1281 if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeMacContent) {
1282 bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
1285 for (auto const& keySources : bundleFiles) {
1286 cmXCodeObject* copyFilesBuildPhase =
1287 this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
1288 copyFilesBuildPhase->SetComment("Copy files");
1289 copyFilesBuildPhase->AddAttribute("buildActionMask",
1290 this->CreateString("2147483647"));
1291 copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
1292 this->CreateString("6"));
1293 std::ostringstream ostr;
1294 if (gtgt->IsFrameworkOnApple()) {
1295 // dstPath in frameworks is relative to Versions/<version>
1296 ostr << keySources.first;
1297 } else if (keySources.first != "MacOS") {
1298 if (gtgt->Target->GetMakefile()->PlatformIsAppleEmbedded()) {
1299 ostr << keySources.first;
1301 // dstPath in bundles is relative to Contents/MacOS
1302 ostr << "../" << keySources.first;
1305 copyFilesBuildPhase->AddAttribute("dstPath",
1306 this->CreateString(ostr.str()));
1307 copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1308 this->CreateString("0"));
1309 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1310 copyFilesBuildPhase->AddAttribute("files", buildFiles);
1311 for (auto sourceFile : keySources.second) {
1312 cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1313 this->CurrentLocalGenerator, sourceFile, gtgt);
1314 buildFiles->AddObject(xsf);
1316 contentBuildPhases.push_back(copyFilesBuildPhase);
1320 // create vector of "resource content file" build phases - only for
1321 // framework or bundle targets
1322 if (isFrameworkTarget || isBundleTarget || isCFBundleTarget) {
1323 using mapOfVectorOfSourceFiles =
1324 std::map<std::string, std::vector<cmSourceFile*>>;
1325 mapOfVectorOfSourceFiles bundleFiles;
1326 for (auto sourceFile : classes) {
1327 cmGeneratorTarget::SourceFileFlags tsFlags =
1328 gtgt->GetTargetSourceFileFlags(sourceFile);
1329 if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeDeepResource) {
1330 bundleFiles[tsFlags.MacFolder].push_back(sourceFile);
1333 for (auto const& keySources : bundleFiles) {
1334 cmXCodeObject* copyFilesBuildPhase =
1335 this->CreateObject(cmXCodeObject::PBXCopyFilesBuildPhase);
1336 copyFilesBuildPhase->SetComment("Copy files");
1337 copyFilesBuildPhase->AddAttribute("buildActionMask",
1338 this->CreateString("2147483647"));
1339 copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
1340 this->CreateString("7"));
1341 copyFilesBuildPhase->AddAttribute("dstPath",
1342 this->CreateString(keySources.first));
1343 copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1344 this->CreateString("0"));
1345 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1346 copyFilesBuildPhase->AddAttribute("files", buildFiles);
1347 for (auto sourceFile : keySources.second) {
1348 cmXCodeObject* xsf = this->CreateXCodeSourceFile(
1349 this->CurrentLocalGenerator, sourceFile, gtgt);
1350 buildFiles->AddObject(xsf);
1352 contentBuildPhases.push_back(copyFilesBuildPhase);
1356 // create framework build phase
1357 cmXCodeObject* frameworkBuildPhase = nullptr;
1358 if (!externalObjFiles.empty()) {
1359 frameworkBuildPhase =
1360 this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase);
1361 frameworkBuildPhase->SetComment("Frameworks");
1362 frameworkBuildPhase->AddAttribute("buildActionMask",
1363 this->CreateString("2147483647"));
1364 buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1365 frameworkBuildPhase->AddAttribute("files", buildFiles);
1366 for (auto& externalObjFile : externalObjFiles) {
1367 buildFiles->AddObject(externalObjFile);
1369 frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1370 this->CreateString("0"));
1373 // create list of build phases and create the Xcode target
1374 cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1376 this->CreateCustomCommands(buildPhases, sourceBuildPhase, headerBuildPhase,
1377 resourceBuildPhase, contentBuildPhases,
1378 frameworkBuildPhase, gtgt);
1380 targets.push_back(this->CreateXCodeTarget(gtgt, buildPhases));
1384 void cmGlobalXCodeGenerator::ForceLinkerLanguages()
1386 for (const auto& localGenerator : this->LocalGenerators) {
1387 // All targets depend on the build-system check target.
1388 for (const auto& tgt : localGenerator->GetGeneratorTargets()) {
1389 // This makes sure all targets link using the proper language.
1390 this->ForceLinkerLanguage(tgt.get());
1395 void cmGlobalXCodeGenerator::ForceLinkerLanguage(cmGeneratorTarget* gtgt)
1397 // This matters only for targets that link.
1398 if (gtgt->GetType() != cmStateEnums::EXECUTABLE &&
1399 gtgt->GetType() != cmStateEnums::SHARED_LIBRARY &&
1400 gtgt->GetType() != cmStateEnums::MODULE_LIBRARY) {
1404 std::string llang = gtgt->GetLinkerLanguage("NOCONFIG");
1405 if (llang.empty()) {
1409 // If the language is compiled as a source trust Xcode to link with it.
1410 for (auto const& Language :
1411 gtgt->GetLinkImplementation("NOCONFIG")->Languages) {
1412 if (Language == llang) {
1417 // Add an empty source file to the target that compiles with the
1418 // linker language. This should convince Xcode to choose the proper
1420 cmMakefile* mf = gtgt->Target->GetMakefile();
1421 std::string fname = cmStrCat(
1422 gtgt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/",
1423 gtgt->GetName(), "-CMakeForceLinker.", cmSystemTools::LowerCase(llang));
1425 cmGeneratedFileStream fout(fname);
1428 if (cmSourceFile* sf = mf->GetOrCreateSource(fname)) {
1429 sf->SetProperty("LANGUAGE", llang.c_str());
1430 gtgt->AddSource(fname);
1434 bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf)
1436 return cm::contains(this->CMakeInstance->GetHeaderExtensions(),
1437 sf->GetExtension());
1440 cmXCodeObject* cmGlobalXCodeGenerator::CreateBuildPhase(
1441 const char* name, const char* name2, cmGeneratorTarget* target,
1442 const std::vector<cmCustomCommand>& commands)
1444 if (commands.empty() && strcmp(name, "CMake ReRun") != 0) {
1447 cmXCodeObject* buildPhase =
1448 this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
1449 buildPhase->AddAttribute("buildActionMask",
1450 this->CreateString("2147483647"));
1451 cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
1452 buildPhase->AddAttribute("files", buildFiles);
1453 buildPhase->AddAttribute("name", this->CreateString(name));
1454 buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
1455 this->CreateString("0"));
1456 buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
1457 this->AddCommandsToBuildPhase(buildPhase, target, commands, name2);
1461 void cmGlobalXCodeGenerator::CreateCustomCommands(
1462 cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
1463 cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
1464 std::vector<cmXCodeObject*> const& contentBuildPhases,
1465 cmXCodeObject* frameworkBuildPhase, cmGeneratorTarget* gtgt)
1467 std::vector<cmCustomCommand> const& prebuild = gtgt->GetPreBuildCommands();
1468 std::vector<cmCustomCommand> const& prelink = gtgt->GetPreLinkCommands();
1469 std::vector<cmCustomCommand> postbuild = gtgt->GetPostBuildCommands();
1471 if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY &&
1472 !gtgt->IsFrameworkOnApple()) {
1473 std::string str_file = cmStrCat("$<TARGET_FILE:", gtgt->GetName(), '>');
1474 std::string str_so_file =
1475 cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
1476 std::string str_link_file =
1477 cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
1478 bool stdPipesUTF8 = true;
1479 cmCustomCommandLines cmd = cmMakeSingleCommandLine(
1480 { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
1481 str_file, str_so_file, str_link_file });
1483 cmCustomCommand command(
1484 std::vector<std::string>(), std::vector<std::string>(),
1485 std::vector<std::string>(), cmd, this->CurrentMakefile->GetBacktrace(),
1486 "Creating symlinks", "", stdPipesUTF8);
1488 postbuild.push_back(std::move(command));
1491 std::vector<cmSourceFile*> classes;
1492 if (!gtgt->GetConfigCommonSourceFiles(classes)) {
1495 // add all the sources
1496 std::vector<cmCustomCommand> commands;
1497 for (auto sourceFile : classes) {
1498 if (sourceFile->GetCustomCommand()) {
1499 commands.push_back(*sourceFile->GetCustomCommand());
1502 // create prebuild phase
1503 cmXCodeObject* cmakeRulesBuildPhase = this->CreateBuildPhase(
1504 "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands);
1505 // create prebuild phase
1506 cmXCodeObject* preBuildPhase = this->CreateBuildPhase(
1507 "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild);
1508 // create prelink phase
1509 cmXCodeObject* preLinkPhase = this->CreateBuildPhase(
1510 "CMake PreLink Rules", "preLinkCommands", gtgt, prelink);
1511 // create postbuild phase
1512 cmXCodeObject* postBuildPhase = this->CreateBuildPhase(
1513 "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild);
1515 // The order here is the order they will be built in.
1516 // The order "headers, resources, sources" mimics a native project generated
1517 // from an xcode template...
1519 if (preBuildPhase) {
1520 buildPhases->AddObject(preBuildPhase);
1522 if (cmakeRulesBuildPhase) {
1523 buildPhases->AddObject(cmakeRulesBuildPhase);
1525 if (headerBuildPhase) {
1526 buildPhases->AddObject(headerBuildPhase);
1528 if (resourceBuildPhase) {
1529 buildPhases->AddObject(resourceBuildPhase);
1531 for (auto obj : contentBuildPhases) {
1532 buildPhases->AddObject(obj);
1534 if (sourceBuildPhase) {
1535 buildPhases->AddObject(sourceBuildPhase);
1538 buildPhases->AddObject(preLinkPhase);
1540 if (frameworkBuildPhase) {
1541 buildPhases->AddObject(frameworkBuildPhase);
1543 if (postBuildPhase) {
1544 buildPhases->AddObject(postBuildPhase);
1548 // This function removes each occurrence of the flag and returns the last one
1549 // (i.e., the dominant flag in GCC)
1550 std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag,
1553 std::string retFlag;
1554 std::string::size_type lastOccurancePos = flags.rfind(flag);
1556 while (lastOccurancePos != std::string::npos) {
1557 // increment pos, we use lastOccurancePos to reduce search space on next
1559 std::string::size_type pos = lastOccurancePos;
1560 if (pos == 0 || flags[pos - 1] == ' ') {
1561 while (pos < flags.size() && flags[pos] != ' ') {
1563 retFlag += flags[pos];
1570 // decrement lastOccurancePos while making sure we don't loop around
1571 // and become a very large positive number since size_type is unsigned
1572 lastOccurancePos = lastOccurancePos == 0 ? 0 : lastOccurancePos - 1;
1573 lastOccurancePos = flags.rfind(flag, lastOccurancePos);
1578 // This function removes each matching occurrence of the expression and
1579 // returns the last one (i.e., the dominant flag in GCC)
1580 std::string cmGlobalXCodeGenerator::ExtractFlagRegex(const char* exp,
1584 std::string retFlag;
1586 cmsys::RegularExpression regex(exp);
1587 assert(regex.is_valid());
1588 if (!regex.is_valid()) {
1592 std::string::size_type offset = 0;
1594 while (regex.find(&flags[offset])) {
1595 const std::string::size_type startPos = offset + regex.start(matchIndex);
1596 const std::string::size_type endPos = offset + regex.end(matchIndex);
1597 const std::string::size_type size = endPos - startPos;
1599 offset = startPos + 1;
1601 retFlag.assign(flags, startPos, size);
1602 flags.replace(startPos, size, size, ' ');
1608 //----------------------------------------------------------------------------
1609 // This function strips off Xcode attributes that do not target the current
1611 void cmGlobalXCodeGenerator::FilterConfigurationAttribute(
1612 std::string const& configName, std::string& attribute)
1614 // Handle [variant=<config>] condition explicitly here.
1615 std::string::size_type beginVariant = attribute.find("[variant=");
1616 if (beginVariant == std::string::npos) {
1617 // There is no variant in this attribute.
1621 std::string::size_type endVariant = attribute.find(']', beginVariant + 9);
1622 if (endVariant == std::string::npos) {
1623 // There is no terminating bracket.
1627 // Compare the variant to the configuration.
1628 std::string variant =
1629 attribute.substr(beginVariant + 9, endVariant - beginVariant - 9);
1630 if (variant == configName) {
1631 // The variant matches the configuration so use this
1632 // attribute but drop the [variant=<config>] condition.
1633 attribute.erase(beginVariant, endVariant - beginVariant + 1);
1635 // The variant does not match the configuration so
1636 // do not use this attribute.
1641 void cmGlobalXCodeGenerator::AddCommandsToBuildPhase(
1642 cmXCodeObject* buildphase, cmGeneratorTarget* target,
1643 std::vector<cmCustomCommand> const& commands, const char* name)
1645 std::string dir = cmStrCat(
1646 this->CurrentLocalGenerator->GetCurrentBinaryDirectory(), "/CMakeScripts");
1647 cmSystemTools::MakeDirectory(dir);
1648 std::string makefile =
1649 cmStrCat(dir, '/', target->GetName(), '_', name, ".make");
1651 for (const auto& currentConfig : this->CurrentConfigurationTypes) {
1652 this->CreateCustomRulesMakefile(makefile.c_str(), target, commands,
1656 std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
1657 cdir = this->ConvertToRelativeForMake(cdir);
1658 std::string makecmd =
1659 cmStrCat("make -C ", cdir, " -f ",
1660 this->ConvertToRelativeForMake((makefile + "$CONFIGURATION")),
1661 " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
1662 buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
1663 buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
1666 void cmGlobalXCodeGenerator::CreateCustomRulesMakefile(
1667 const char* makefileBasename, cmGeneratorTarget* target,
1668 std::vector<cmCustomCommand> const& commands, const std::string& configName)
1670 std::string makefileName = cmStrCat(makefileBasename, configName);
1671 cmGeneratedFileStream makefileStream(makefileName);
1672 if (!makefileStream) {
1675 makefileStream.SetCopyIfDifferent(true);
1676 makefileStream << "# Generated by CMake, DO NOT EDIT\n";
1677 makefileStream << "# Custom rules for " << target->GetName() << "\n";
1679 // disable the implicit rules
1680 makefileStream << ".SUFFIXES: "
1683 // have all depend on all outputs
1684 makefileStream << "all: ";
1685 std::map<const cmCustomCommand*, std::string> tname;
1687 for (auto const& command : commands) {
1688 cmCustomCommandGenerator ccg(command, configName,
1689 this->CurrentLocalGenerator);
1690 if (ccg.GetNumberOfCommands() > 0) {
1691 const std::vector<std::string>& outputs = ccg.GetOutputs();
1692 if (!outputs.empty()) {
1693 for (auto const& output : outputs) {
1694 makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(output);
1697 std::ostringstream str;
1698 str << "_buildpart_" << count++;
1699 tname[&ccg.GetCC()] = target->GetName() + str.str();
1700 makefileStream << "\\\n\t" << tname[&ccg.GetCC()];
1704 makefileStream << "\n\n";
1705 for (auto const& command : commands) {
1706 cmCustomCommandGenerator ccg(command, configName,
1707 this->CurrentLocalGenerator);
1708 if (ccg.GetNumberOfCommands() > 0) {
1709 makefileStream << "\n";
1710 const std::vector<std::string>& outputs = ccg.GetOutputs();
1711 if (!outputs.empty()) {
1712 // There is at least one output, start the rule for it
1713 const char* sep = "";
1714 for (auto const& output : outputs) {
1715 makefileStream << sep << this->ConvertToRelativeForMake(output);
1718 makefileStream << ": ";
1720 // There are no outputs. Use the generated force rule name.
1721 makefileStream << tname[&ccg.GetCC()] << ": ";
1723 for (auto const& d : ccg.GetDepends()) {
1725 if (this->CurrentLocalGenerator->GetRealDependency(d, configName,
1727 makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep);
1730 makefileStream << "\n";
1732 if (const char* comment = ccg.GetComment()) {
1733 std::string echo_cmd =
1735 (this->CurrentLocalGenerator->EscapeForShell(
1736 comment, ccg.GetCC().GetEscapeAllowMakeVars())));
1737 makefileStream << "\t" << echo_cmd << "\n";
1740 // Add each command line to the set of commands.
1741 for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
1742 // Build the command line in a single string.
1743 std::string cmd2 = ccg.GetCommand(c);
1744 cmSystemTools::ReplaceString(cmd2, "/./", "/");
1745 cmd2 = this->ConvertToRelativeForMake(cmd2);
1747 std::string wd = ccg.GetWorkingDirectory();
1750 cmd += this->ConvertToRelativeForMake(wd);
1754 ccg.AppendArguments(c, cmd);
1755 makefileStream << "\t" << cmd << "\n";
1761 void cmGlobalXCodeGenerator::AddPositionIndependentLinkAttribute(
1762 cmGeneratorTarget* target, cmXCodeObject* buildSettings,
1763 const std::string& configName)
1765 // For now, only EXECUTABLE is concerned
1766 if (target->GetType() != cmStateEnums::EXECUTABLE) {
1770 const char* PICValue = target->GetLinkPIEProperty(configName);
1771 if (PICValue == nullptr) {
1772 // POSITION_INDEPENDENT_CODE is not set
1776 buildSettings->AddAttribute(
1777 "LD_NO_PIE", this->CreateString(cmIsOn(PICValue) ? "NO" : "YES"));
1780 void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
1781 cmXCodeObject* buildSettings,
1782 const std::string& configName)
1784 if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
1788 std::string defFlags;
1789 bool shared = ((gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) ||
1790 (gtgt->GetType() == cmStateEnums::MODULE_LIBRARY));
1791 bool binary = ((gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) ||
1792 (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) ||
1793 (gtgt->GetType() == cmStateEnums::EXECUTABLE) || shared);
1795 // Compute the compilation flags for each language.
1796 std::set<std::string> languages;
1797 gtgt->GetLanguages(languages, configName);
1798 std::map<std::string, std::string> cflags;
1799 for (auto const& lang : languages) {
1800 std::string& flags = cflags[lang];
1802 // Add language-specific flags.
1803 this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang,
1806 if (gtgt->IsIPOEnabled(lang, configName)) {
1807 this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
1810 // Add shared-library flags if needed.
1811 this->CurrentLocalGenerator->AddCMP0018Flags(flags, gtgt, lang,
1814 this->CurrentLocalGenerator->AddVisibilityPresetFlags(flags, gtgt, lang);
1816 this->CurrentLocalGenerator->AddCompileOptions(flags, gtgt, lang,
1820 std::string llang = gtgt->GetLinkerLanguage(configName);
1821 if (binary && llang.empty()) {
1822 cmSystemTools::Error(
1823 "CMake can not determine linker language for target: " +
1827 std::string const& langForPreprocessor = llang;
1829 if (gtgt->IsIPOEnabled(llang, configName)) {
1830 const char* ltoValue =
1831 this->CurrentMakefile->IsOn("_CMAKE_LTO_THIN") ? "YES_THIN" : "YES";
1832 buildSettings->AddAttribute("LLVM_LTO", this->CreateString(ltoValue));
1835 // Handle PIE linker configuration
1836 this->AddPositionIndependentLinkAttribute(gtgt, buildSettings, configName);
1839 this->CurrentLocalGenerator->AppendFlags(
1840 defFlags, this->CurrentMakefile->GetDefineFlags());
1842 // Add preprocessor definitions for this target and configuration.
1843 BuildObjectListOrString ppDefs(this, true);
1844 this->AppendDefines(
1845 ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"");
1846 if (const std::string* exportMacro = gtgt->GetExportMacro()) {
1847 // Add the export symbol definition for shared library objects.
1848 this->AppendDefines(ppDefs, exportMacro->c_str());
1850 std::vector<std::string> targetDefines;
1851 if (!langForPreprocessor.empty()) {
1852 gtgt->GetCompileDefinitions(targetDefines, configName,
1853 langForPreprocessor);
1855 this->AppendDefines(ppDefs, targetDefines);
1856 buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS",
1857 ppDefs.CreateList());
1859 std::string extraLinkOptionsVar;
1860 std::string extraLinkOptions;
1861 if (gtgt->GetType() == cmStateEnums::EXECUTABLE) {
1862 extraLinkOptionsVar = "CMAKE_EXE_LINKER_FLAGS";
1863 } else if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
1864 extraLinkOptionsVar = "CMAKE_SHARED_LINKER_FLAGS";
1865 } else if (gtgt->GetType() == cmStateEnums::MODULE_LIBRARY) {
1866 extraLinkOptionsVar = "CMAKE_MODULE_LINKER_FLAGS";
1868 if (!extraLinkOptionsVar.empty()) {
1869 this->CurrentLocalGenerator->AddConfigVariableFlags(
1870 extraLinkOptions, extraLinkOptionsVar, configName);
1873 if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
1874 gtgt->GetType() == cmStateEnums::STATIC_LIBRARY) {
1875 this->CurrentLocalGenerator->GetStaticLibraryFlags(
1876 extraLinkOptions, configName, llang, gtgt);
1878 cmProp targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
1879 if (targetLinkFlags) {
1880 this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
1883 if (!configName.empty()) {
1884 std::string linkFlagsVar =
1885 cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(configName));
1886 if (cmProp linkFlags = gtgt->GetProperty(linkFlagsVar)) {
1887 this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, *linkFlags);
1890 std::vector<std::string> opts;
1891 gtgt->GetLinkOptions(opts, configName, llang);
1892 // LINK_OPTIONS are escaped.
1893 this->CurrentLocalGenerator->AppendCompileOptions(extraLinkOptions, opts);
1896 // Set target-specific architectures.
1897 std::vector<std::string> archs;
1898 gtgt->GetAppleArchs(configName, archs);
1900 if (!archs.empty()) {
1901 // Enable ARCHS attribute.
1902 buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("NO"));
1904 // Store ARCHS value.
1905 if (archs.size() == 1) {
1906 buildSettings->AddAttribute("ARCHS", this->CreateString(archs[0]));
1908 cmXCodeObject* archObjects =
1909 this->CreateObject(cmXCodeObject::OBJECT_LIST);
1910 for (auto& arch : archs) {
1911 archObjects->AddObject(this->CreateString(arch));
1913 buildSettings->AddAttribute("ARCHS", archObjects);
1917 // Get the product name components.
1918 std::string pnprefix;
1920 std::string pnsuffix;
1921 gtgt->GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName);
1923 cmProp version = gtgt->GetProperty("VERSION");
1924 cmProp soversion = gtgt->GetProperty("SOVERSION");
1925 if (!gtgt->HasSOName(configName) || gtgt->IsFrameworkOnApple()) {
1927 soversion = nullptr;
1929 if (version && !soversion) {
1930 soversion = version;
1932 if (!version && soversion) {
1933 version = soversion;
1936 std::string realName = pnbase;
1937 std::string soName = pnbase;
1938 if (version && soversion) {
1940 realName += *version;
1942 soName += *soversion;
1945 // Set attributes to specify the proper name for the target.
1946 std::string pndir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
1947 if (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY ||
1948 gtgt->GetType() == cmStateEnums::SHARED_LIBRARY ||
1949 gtgt->GetType() == cmStateEnums::MODULE_LIBRARY ||
1950 gtgt->GetType() == cmStateEnums::EXECUTABLE) {
1951 if (!gtgt->UsesDefaultOutputDir(configName,
1952 cmStateEnums::RuntimeBinaryArtifact)) {
1953 std::string pncdir = gtgt->GetDirectory(configName);
1954 buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
1955 this->CreateString(pncdir));
1958 if (gtgt->IsFrameworkOnApple() || gtgt->IsCFBundleOnApple()) {
1962 buildSettings->AddAttribute("EXECUTABLE_PREFIX",
1963 this->CreateString(pnprefix));
1964 buildSettings->AddAttribute("EXECUTABLE_SUFFIX",
1965 this->CreateString(pnsuffix));
1966 } else if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
1968 pnbase = gtgt->GetName();
1971 std::string pncdir = this->GetObjectsDirectory(
1972 this->CurrentProject, configName, gtgt, OBJECT_LIBRARY_ARTIFACT_DIR);
1973 buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
1974 this->CreateString(pncdir));
1977 // Store the product name for all target types.
1978 buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(realName));
1979 buildSettings->AddAttribute("SYMROOT", this->CreateString(pndir));
1981 // Handle settings for each target type.
1982 switch (gtgt->GetType()) {
1983 case cmStateEnums::STATIC_LIBRARY:
1984 if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
1985 std::string fw_version = gtgt->GetFrameworkVersion();
1986 buildSettings->AddAttribute("FRAMEWORK_VERSION",
1987 this->CreateString(fw_version));
1988 cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
1990 buildSettings->AddAttribute("WRAPPER_EXTENSION",
1991 this->CreateString(*ext));
1994 std::string plist = this->ComputeInfoPListLocation(gtgt);
1995 // Xcode will create the final version of Info.plist at build time,
1996 // so let it replace the framework name. This avoids creating
1997 // a per-configuration Info.plist file.
1998 this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
1999 gtgt, "$(EXECUTABLE_NAME)", plist);
2000 buildSettings->AddAttribute("INFOPLIST_FILE",
2001 this->CreateString(plist));
2002 buildSettings->AddAttribute("MACH_O_TYPE",
2003 this->CreateString("staticlib"));
2005 buildSettings->AddAttribute("LIBRARY_STYLE",
2006 this->CreateString("STATIC"));
2010 case cmStateEnums::OBJECT_LIBRARY: {
2011 buildSettings->AddAttribute("LIBRARY_STYLE",
2012 this->CreateString("STATIC"));
2016 case cmStateEnums::MODULE_LIBRARY: {
2017 buildSettings->AddAttribute("LIBRARY_STYLE",
2018 this->CreateString("BUNDLE"));
2019 if (gtgt->IsCFBundleOnApple()) {
2020 // It turns out that a BUNDLE is basically the same
2021 // in many ways as an application bundle, as far as
2023 std::string createFlags = this->LookupFlags(
2024 "CMAKE_SHARED_MODULE_CREATE_", llang, "_FLAGS", "-bundle");
2025 if (!createFlags.empty()) {
2026 extraLinkOptions += " ";
2027 extraLinkOptions += createFlags;
2029 cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2031 buildSettings->AddAttribute("WRAPPER_EXTENSION",
2032 this->CreateString(*ext));
2034 std::string plist = this->ComputeInfoPListLocation(gtgt);
2035 // Xcode will create the final version of Info.plist at build time,
2036 // so let it replace the cfbundle name. This avoids creating
2037 // a per-configuration Info.plist file. The cfbundle plist
2038 // is very similar to the application bundle plist
2039 this->CurrentLocalGenerator->GenerateAppleInfoPList(
2040 gtgt, "$(EXECUTABLE_NAME)", plist);
2041 buildSettings->AddAttribute("INFOPLIST_FILE",
2042 this->CreateString(plist));
2044 buildSettings->AddAttribute("MACH_O_TYPE",
2045 this->CreateString("mh_bundle"));
2046 buildSettings->AddAttribute("GCC_DYNAMIC_NO_PIC",
2047 this->CreateString("NO"));
2048 // Add the flags to create an executable.
2049 std::string createFlags =
2050 this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
2051 if (!createFlags.empty()) {
2052 extraLinkOptions += " ";
2053 extraLinkOptions += createFlags;
2058 case cmStateEnums::SHARED_LIBRARY: {
2059 if (gtgt->GetPropertyAsBool("FRAMEWORK")) {
2060 std::string fw_version = gtgt->GetFrameworkVersion();
2061 buildSettings->AddAttribute("FRAMEWORK_VERSION",
2062 this->CreateString(fw_version));
2063 cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2065 buildSettings->AddAttribute("WRAPPER_EXTENSION",
2066 this->CreateString(*ext));
2069 std::string plist = this->ComputeInfoPListLocation(gtgt);
2070 // Xcode will create the final version of Info.plist at build time,
2071 // so let it replace the framework name. This avoids creating
2072 // a per-configuration Info.plist file.
2073 this->CurrentLocalGenerator->GenerateFrameworkInfoPList(
2074 gtgt, "$(EXECUTABLE_NAME)", plist);
2075 buildSettings->AddAttribute("INFOPLIST_FILE",
2076 this->CreateString(plist));
2078 // Add the flags to create a shared library.
2079 std::string createFlags = this->LookupFlags(
2080 "CMAKE_SHARED_LIBRARY_CREATE_", llang, "_FLAGS", "-dynamiclib");
2081 if (!createFlags.empty()) {
2082 extraLinkOptions += " ";
2083 extraLinkOptions += createFlags;
2087 buildSettings->AddAttribute("LIBRARY_STYLE",
2088 this->CreateString("DYNAMIC"));
2091 case cmStateEnums::EXECUTABLE: {
2092 // Add the flags to create an executable.
2093 std::string createFlags =
2094 this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
2095 if (!createFlags.empty()) {
2096 extraLinkOptions += " ";
2097 extraLinkOptions += createFlags;
2100 // Handle bundles and normal executables separately.
2101 if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
2102 cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
2104 buildSettings->AddAttribute("WRAPPER_EXTENSION",
2105 this->CreateString(*ext));
2107 std::string plist = this->ComputeInfoPListLocation(gtgt);
2108 // Xcode will create the final version of Info.plist at build time,
2109 // so let it replace the executable name. This avoids creating
2110 // a per-configuration Info.plist file.
2111 this->CurrentLocalGenerator->GenerateAppleInfoPList(
2112 gtgt, "$(EXECUTABLE_NAME)", plist);
2113 buildSettings->AddAttribute("INFOPLIST_FILE",
2114 this->CreateString(plist));
2121 BuildObjectListOrString dirs(this, true);
2122 BuildObjectListOrString fdirs(this, true);
2123 BuildObjectListOrString sysdirs(this, true);
2124 BuildObjectListOrString sysfdirs(this, true);
2125 const bool emitSystemIncludes = this->XcodeVersion >= 83;
2127 std::vector<std::string> includes;
2128 if (!langForPreprocessor.empty()) {
2129 this->CurrentLocalGenerator->GetIncludeDirectories(
2130 includes, gtgt, langForPreprocessor, configName);
2132 std::set<std::string> emitted;
2133 emitted.insert("/System/Library/Frameworks");
2135 for (auto& include : includes) {
2136 if (this->NameResolvesToFramework(include)) {
2137 std::string frameworkDir = cmStrCat(include, "/../");
2138 frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
2139 if (emitted.insert(frameworkDir).second) {
2140 std::string incpath = this->XCodeEscapePath(frameworkDir);
2141 if (emitSystemIncludes &&
2142 gtgt->IsSystemIncludeDirectory(frameworkDir, configName,
2143 langForPreprocessor)) {
2144 sysfdirs.Add(incpath);
2150 std::string incpath = this->XCodeEscapePath(include);
2151 if (emitSystemIncludes &&
2152 gtgt->IsSystemIncludeDirectory(include, configName,
2153 langForPreprocessor)) {
2154 sysdirs.Add(incpath);
2160 // Add framework search paths needed for linking.
2161 if (cmComputeLinkInformation* cli = gtgt->GetLinkInformation(configName)) {
2162 for (auto const& fwDir : cli->GetFrameworkPaths()) {
2163 if (emitted.insert(fwDir).second) {
2164 std::string incpath = this->XCodeEscapePath(fwDir);
2165 if (emitSystemIncludes &&
2166 gtgt->IsSystemIncludeDirectory(fwDir, configName,
2167 langForPreprocessor)) {
2168 sysfdirs.Add(incpath);
2175 if (!fdirs.IsEmpty()) {
2176 buildSettings->AddAttribute("FRAMEWORK_SEARCH_PATHS", fdirs.CreateList());
2178 if (!dirs.IsEmpty()) {
2179 buildSettings->AddAttribute("HEADER_SEARCH_PATHS", dirs.CreateList());
2181 if (!sysfdirs.IsEmpty()) {
2182 buildSettings->AddAttribute("SYSTEM_FRAMEWORK_SEARCH_PATHS",
2183 sysfdirs.CreateList());
2185 if (!sysdirs.IsEmpty()) {
2186 buildSettings->AddAttribute("SYSTEM_HEADER_SEARCH_PATHS",
2187 sysdirs.CreateList());
2190 if (this->XcodeVersion >= 60 && !emitSystemIncludes) {
2191 // Add those per-language flags in addition to HEADER_SEARCH_PATHS to gain
2192 // system include directory awareness. We need to also keep on setting
2193 // HEADER_SEARCH_PATHS to work around a missing compile options flag for
2194 // GNU assembly files (#16449)
2195 for (auto const& language : languages) {
2196 std::string includeFlags = this->CurrentLocalGenerator->GetIncludeFlags(
2197 includes, gtgt, language, true, false, configName);
2199 if (!includeFlags.empty()) {
2200 cflags[language] += " " + includeFlags;
2205 bool same_gflags = true;
2206 std::map<std::string, std::string> gflags;
2207 std::string const* last_gflag = nullptr;
2208 std::string optLevel = "0";
2210 // Minimal map of flags to build settings.
2211 for (auto const& language : languages) {
2212 std::string& flags = cflags[language];
2213 std::string& gflag = gflags[language];
2215 this->ExtractFlagRegex("(^| )(-Ofast|-Os|-O[0-9]*)( |$)", 2, flags);
2216 if (oflag.size() == 2) {
2218 } else if (oflag.size() > 2) {
2219 optLevel = oflag.substr(2);
2221 gflag = this->ExtractFlag("-g", flags);
2222 // put back gdwarf-2 if used since there is no way
2223 // to represent it in the gui, but we still want debug yes
2224 if (gflag == "-gdwarf-2") {
2228 if (last_gflag && *last_gflag != gflag) {
2229 same_gflags = false;
2231 last_gflag = &gflag;
2234 const char* debugStr = "YES";
2236 // We can't set the Xcode flag differently depending on the language,
2237 // so put them back in this case.
2238 for (auto const& language : languages) {
2239 cflags[language] += " ";
2240 cflags[language] += gflags[language];
2243 } else if (last_gflag && (last_gflag->empty() || *last_gflag == "-g0")) {
2247 // extract C++ stdlib
2248 for (auto const& language : languages) {
2249 if (language != "CXX" && language != "OBJCXX") {
2252 std::string& flags = cflags[language];
2255 this->ExtractFlagRegex("(^| )(-stdlib=[^ ]+)( |$)", 2, flags);
2256 if (stdlib.size() > 8) {
2257 const auto cxxLibrary = stdlib.substr(8);
2258 if (language == "CXX" ||
2259 !buildSettings->GetObject("CLANG_CXX_LIBRARY")) {
2260 buildSettings->AddAttribute("CLANG_CXX_LIBRARY",
2261 this->CreateString(cxxLibrary));
2266 buildSettings->AddAttribute("COMBINE_HIDPI_IMAGES",
2267 this->CreateString("YES"));
2268 buildSettings->AddAttribute("GCC_GENERATE_DEBUGGING_SYMBOLS",
2269 this->CreateString(debugStr));
2270 buildSettings->AddAttribute("GCC_OPTIMIZATION_LEVEL",
2271 this->CreateString(optLevel));
2272 buildSettings->AddAttribute("GCC_SYMBOLS_PRIVATE_EXTERN",
2273 this->CreateString("NO"));
2274 buildSettings->AddAttribute("GCC_INLINES_ARE_PRIVATE_EXTERN",
2275 this->CreateString("NO"));
2277 for (auto const& language : languages) {
2278 std::string flags = cflags[language] + " " + defFlags;
2279 if (language == "CXX" || language == "OBJCXX") {
2280 if (language == "CXX" ||
2281 !buildSettings->GetObject("OTHER_CPLUSPLUSFLAGS")) {
2282 buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS",
2283 this->CreateString(flags));
2285 } else if (language == "Fortran") {
2286 buildSettings->AddAttribute("IFORT_OTHER_FLAGS",
2287 this->CreateString(flags));
2288 } else if (language == "C" || language == "OBJC") {
2289 if (language == "C" || !buildSettings->GetObject("OTHER_CFLAGS")) {
2290 buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags));
2292 } else if (language == "Swift") {
2293 buildSettings->AddAttribute("OTHER_SWIFT_FLAGS",
2294 this->CreateString(flags));
2298 // Add Fortran source format attribute if property is set.
2299 const char* format = nullptr;
2300 std::string const& tgtfmt = gtgt->GetSafeProperty("Fortran_FORMAT");
2301 switch (cmOutputConverter::GetFortranFormat(tgtfmt)) {
2302 case cmOutputConverter::FortranFormatFixed:
2305 case cmOutputConverter::FortranFormatFree:
2312 buildSettings->AddAttribute("IFORT_LANG_SRCFMT",
2313 this->CreateString(format));
2316 // Create the INSTALL_PATH attribute.
2317 std::string install_name_dir;
2318 if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
2319 // Get the install_name directory for the build tree.
2320 install_name_dir = gtgt->GetInstallNameDirForBuildTree(configName);
2321 // Xcode doesn't create the correct install_name in some cases.
2322 // That is, if the INSTALL_PATH is empty, or if we have versioning
2323 // of dylib libraries, we want to specify the install_name.
2324 // This is done by adding a link flag to create an install_name
2325 // with just the library soname.
2326 std::string install_name;
2327 if (!install_name_dir.empty()) {
2328 // Convert to a path for the native build tool.
2329 cmSystemTools::ConvertToUnixSlashes(install_name_dir);
2330 install_name += install_name_dir;
2331 install_name += "/";
2333 install_name += gtgt->GetSOName(configName);
2335 if ((realName != soName) || install_name_dir.empty()) {
2336 install_name_dir = "";
2337 extraLinkOptions += " -install_name ";
2338 extraLinkOptions += XCodeEscapePath(install_name);
2341 buildSettings->AddAttribute("INSTALL_PATH",
2342 this->CreateString(install_name_dir));
2344 // Create the LD_RUNPATH_SEARCH_PATHS
2345 cmComputeLinkInformation* pcli = gtgt->GetLinkInformation(configName);
2347 std::string search_paths;
2348 std::vector<std::string> runtimeDirs;
2349 pcli->GetRPath(runtimeDirs, false);
2350 // runpath dirs needs to be unique to prevent corruption
2351 std::set<std::string> unique_dirs;
2353 for (auto runpath : runtimeDirs) {
2354 runpath = this->ExpandCFGIntDir(runpath, configName);
2356 if (unique_dirs.find(runpath) == unique_dirs.end()) {
2357 unique_dirs.insert(runpath);
2358 if (!search_paths.empty()) {
2359 search_paths += " ";
2361 search_paths += this->XCodeEscapePath(runpath);
2364 if (!search_paths.empty()) {
2365 buildSettings->AddAttribute("LD_RUNPATH_SEARCH_PATHS",
2366 this->CreateString(search_paths));
2370 buildSettings->AddAttribute(this->GetTargetLinkFlagsVar(gtgt),
2371 this->CreateString(extraLinkOptions));
2372 buildSettings->AddAttribute("OTHER_REZFLAGS", this->CreateString(""));
2373 buildSettings->AddAttribute("SECTORDER_FLAGS", this->CreateString(""));
2374 buildSettings->AddAttribute("USE_HEADERMAP", this->CreateString("NO"));
2375 cmXCodeObject* group = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2376 group->AddObject(this->CreateString("$(inherited)"));
2377 buildSettings->AddAttribute("WARNING_CFLAGS", group);
2379 // Runtime version information.
2380 if (gtgt->GetType() == cmStateEnums::SHARED_LIBRARY) {
2385 // MACHO_CURRENT_VERSION or VERSION -> current_version
2386 gtgt->GetTargetVersionFallback("MACHO_CURRENT_VERSION", "VERSION", major,
2388 std::ostringstream v;
2390 // Xcode always wants at least 1.0.0 or nothing
2391 if (!(major == 0 && minor == 0 && patch == 0)) {
2392 v << major << "." << minor << "." << patch;
2394 buildSettings->AddAttribute("DYLIB_CURRENT_VERSION",
2395 this->CreateString(v.str()));
2397 // MACHO_COMPATIBILITY_VERSION or SOVERSION -> compatibility_version
2398 gtgt->GetTargetVersionFallback("MACHO_COMPATIBILITY_VERSION", "SOVERSION",
2399 major, minor, patch);
2400 std::ostringstream vso;
2402 // Xcode always wants at least 1.0.0 or nothing
2403 if (!(major == 0 && minor == 0 && patch == 0)) {
2404 vso << major << "." << minor << "." << patch;
2406 buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
2407 this->CreateString(vso.str()));
2410 // Precompile Headers
2411 std::string pchHeader = gtgt->GetPchHeader(configName, llang);
2412 if (!pchHeader.empty()) {
2413 buildSettings->AddAttribute("GCC_PREFIX_HEADER",
2414 this->CreateString(pchHeader));
2415 buildSettings->AddAttribute("GCC_PRECOMPILE_PREFIX_HEADER",
2416 this->CreateString("YES"));
2419 // put this last so it can override existing settings
2420 // Convert "XCODE_ATTRIBUTE_*" properties directly.
2422 for (auto const& prop : gtgt->GetPropertyKeys()) {
2423 if (cmHasLiteralPrefix(prop, "XCODE_ATTRIBUTE_")) {
2424 std::string attribute = prop.substr(16);
2425 this->FilterConfigurationAttribute(configName, attribute);
2426 if (!attribute.empty()) {
2427 std::string const& pr = gtgt->GetSafeProperty(prop);
2428 std::string processed = cmGeneratorExpression::Evaluate(
2429 pr, this->CurrentLocalGenerator, configName);
2430 buildSettings->AddAttribute(attribute,
2431 this->CreateString(processed));
2438 cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(
2439 cmGeneratorTarget* gtgt)
2441 cmXCodeObject* shellBuildPhase =
2442 this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase);
2443 shellBuildPhase->AddAttribute("buildActionMask",
2444 this->CreateString("2147483647"));
2445 cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2446 shellBuildPhase->AddAttribute("files", buildFiles);
2447 cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2448 shellBuildPhase->AddAttribute("inputPaths", inputPaths);
2449 cmXCodeObject* outputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2450 shellBuildPhase->AddAttribute("outputPaths", outputPaths);
2451 shellBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
2452 this->CreateString("0"));
2453 shellBuildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh"));
2454 shellBuildPhase->AddAttribute(
2455 "shellScript", this->CreateString("# shell script goes here\nexit 0"));
2456 shellBuildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
2458 cmXCodeObject* target =
2459 this->CreateObject(cmXCodeObject::PBXAggregateTarget);
2460 target->SetComment(gtgt->GetName());
2461 cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2462 std::vector<cmXCodeObject*> emptyContentVector;
2463 this->CreateCustomCommands(buildPhases, nullptr, nullptr, nullptr,
2464 emptyContentVector, nullptr, gtgt);
2465 target->AddAttribute("buildPhases", buildPhases);
2466 this->AddConfigurations(target, gtgt);
2467 cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2468 target->AddAttribute("dependencies", dependencies);
2469 target->AddAttribute("name", this->CreateString(gtgt->GetName()));
2470 target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
2471 target->SetTarget(gtgt);
2472 this->XCodeObjectMap[gtgt] = target;
2474 // Add source files without build rules for editing convenience.
2475 if (gtgt->GetType() == cmStateEnums::UTILITY &&
2476 gtgt->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
2477 std::vector<cmSourceFile*> sources;
2478 if (!gtgt->GetConfigCommonSourceFiles(sources)) {
2482 // Add CMakeLists.txt file for user convenience.
2483 this->AddXCodeProjBuildRule(gtgt, sources);
2485 for (auto sourceFile : sources) {
2486 if (!sourceFile->GetIsGenerated()) {
2487 this->CreateXCodeFileReference(sourceFile, gtgt);
2492 target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
2497 std::string cmGlobalXCodeGenerator::AddConfigurations(cmXCodeObject* target,
2498 cmGeneratorTarget* gtgt)
2500 std::vector<std::string> const configVector = cmExpandedList(
2501 this->CurrentMakefile->GetRequiredDefinition("CMAKE_CONFIGURATION_TYPES"));
2502 cmXCodeObject* configlist =
2503 this->CreateObject(cmXCodeObject::XCConfigurationList);
2504 cmXCodeObject* buildConfigurations =
2505 this->CreateObject(cmXCodeObject::OBJECT_LIST);
2506 configlist->AddAttribute("buildConfigurations", buildConfigurations);
2507 std::string comment = cmStrCat("Build configuration list for ",
2508 cmXCodeObject::PBXTypeNames[target->GetIsA()],
2509 " \"", gtgt->GetName(), '"');
2510 configlist->SetComment(comment);
2511 target->AddAttribute("buildConfigurationList",
2512 this->CreateObjectReference(configlist));
2513 for (auto const& i : configVector) {
2514 cmXCodeObject* config =
2515 this->CreateObject(cmXCodeObject::XCBuildConfiguration);
2516 buildConfigurations->AddObject(config);
2517 cmXCodeObject* buildSettings =
2518 this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
2519 this->CreateBuildSettings(gtgt, buildSettings, i);
2520 config->AddAttribute("name", this->CreateString(i));
2521 config->SetComment(i);
2522 config->AddAttribute("buildSettings", buildSettings);
2524 if (!configVector.empty()) {
2525 configlist->AddAttribute("defaultConfigurationName",
2526 this->CreateString(configVector[0]));
2527 configlist->AddAttribute("defaultConfigurationIsVisible",
2528 this->CreateString("0"));
2529 return configVector[0];
2534 const char* cmGlobalXCodeGenerator::GetTargetLinkFlagsVar(
2535 cmGeneratorTarget const* target) const
2537 if (this->XcodeVersion >= 60 &&
2538 (target->GetType() == cmStateEnums::STATIC_LIBRARY ||
2539 target->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
2540 return "OTHER_LIBTOOLFLAGS";
2542 return "OTHER_LDFLAGS";
2545 const char* cmGlobalXCodeGenerator::GetTargetFileType(
2546 cmGeneratorTarget* target)
2548 if (cmProp e = target->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
2552 switch (target->GetType()) {
2553 case cmStateEnums::OBJECT_LIBRARY:
2554 return "archive.ar";
2555 case cmStateEnums::STATIC_LIBRARY:
2556 return (target->GetPropertyAsBool("FRAMEWORK") ? "wrapper.framework"
2558 case cmStateEnums::MODULE_LIBRARY:
2559 if (target->IsXCTestOnApple()) {
2560 return "wrapper.cfbundle";
2562 if (target->IsCFBundleOnApple()) {
2563 return "wrapper.plug-in";
2565 return "compiled.mach-o.executable";
2566 case cmStateEnums::SHARED_LIBRARY:
2567 return (target->GetPropertyAsBool("FRAMEWORK")
2568 ? "wrapper.framework"
2569 : "compiled.mach-o.dylib");
2570 case cmStateEnums::EXECUTABLE:
2571 return "compiled.mach-o.executable";
2578 const char* cmGlobalXCodeGenerator::GetTargetProductType(
2579 cmGeneratorTarget* target)
2581 if (cmProp e = target->GetProperty("XCODE_PRODUCT_TYPE")) {
2585 switch (target->GetType()) {
2586 case cmStateEnums::OBJECT_LIBRARY:
2587 return "com.apple.product-type.library.static";
2588 case cmStateEnums::STATIC_LIBRARY:
2589 return (target->GetPropertyAsBool("FRAMEWORK")
2590 ? "com.apple.product-type.framework"
2591 : "com.apple.product-type.library.static");
2592 case cmStateEnums::MODULE_LIBRARY:
2593 if (target->IsXCTestOnApple()) {
2594 return "com.apple.product-type.bundle.unit-test";
2595 } else if (target->IsCFBundleOnApple()) {
2596 return "com.apple.product-type.bundle";
2598 return "com.apple.product-type.tool";
2600 case cmStateEnums::SHARED_LIBRARY:
2601 return (target->GetPropertyAsBool("FRAMEWORK")
2602 ? "com.apple.product-type.framework"
2603 : "com.apple.product-type.library.dynamic");
2604 case cmStateEnums::EXECUTABLE:
2605 return (target->GetPropertyAsBool("MACOSX_BUNDLE")
2606 ? "com.apple.product-type.application"
2607 : "com.apple.product-type.tool");
2614 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget(
2615 cmGeneratorTarget* gtgt, cmXCodeObject* buildPhases)
2617 if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
2620 cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget);
2621 target->AddAttribute("buildPhases", buildPhases);
2622 cmXCodeObject* buildRules = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2623 target->AddAttribute("buildRules", buildRules);
2624 std::string defConfig;
2625 defConfig = this->AddConfigurations(target, gtgt);
2626 cmXCodeObject* dependencies = this->CreateObject(cmXCodeObject::OBJECT_LIST);
2627 target->AddAttribute("dependencies", dependencies);
2628 target->AddAttribute("name", this->CreateString(gtgt->GetName()));
2629 target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
2631 cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
2632 if (const char* fileType = this->GetTargetFileType(gtgt)) {
2633 fileRef->AddAttribute("explicitFileType", this->CreateString(fileType));
2635 std::string fullName;
2636 if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2637 fullName = cmStrCat("lib", gtgt->GetName(), ".a");
2639 fullName = gtgt->GetFullName(defConfig);
2641 fileRef->AddAttribute("path", this->CreateString(fullName));
2642 fileRef->AddAttribute("sourceTree",
2643 this->CreateString("BUILT_PRODUCTS_DIR"));
2644 fileRef->SetComment(gtgt->GetName());
2645 target->AddAttribute("productReference",
2646 this->CreateObjectReference(fileRef));
2647 if (const char* productType = this->GetTargetProductType(gtgt)) {
2648 target->AddAttribute("productType", this->CreateString(productType));
2650 target->SetTarget(gtgt);
2651 this->XCodeObjectMap[gtgt] = target;
2652 target->SetId(this->GetOrCreateId(gtgt->GetName(), target->GetId()));
2656 cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget(
2657 cmGeneratorTarget const* t)
2663 auto const i = this->XCodeObjectMap.find(t);
2664 if (i == this->XCodeObjectMap.end()) {
2670 std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name,
2671 const std::string& id)
2673 std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
2674 const char* storedGUID =
2675 this->CMakeInstance->GetCacheDefinition(guidStoreName);
2681 this->CMakeInstance->AddCacheEntry(guidStoreName, id.c_str(),
2682 "Stored Xcode object GUID",
2683 cmStateEnums::INTERNAL);
2688 void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target,
2689 cmXCodeObject* dependTarget)
2691 // This is called once for every edge in the target dependency graph.
2692 cmXCodeObject* container =
2693 this->CreateObject(cmXCodeObject::PBXContainerItemProxy);
2694 container->SetComment("PBXContainerItemProxy");
2695 container->AddAttribute("containerPortal",
2696 this->CreateObjectReference(this->RootObject));
2697 container->AddAttribute("proxyType", this->CreateString("1"));
2698 container->AddAttribute("remoteGlobalIDString",
2699 this->CreateObjectReference(dependTarget));
2700 container->AddAttribute(
2701 "remoteInfo", this->CreateString(dependTarget->GetTarget()->GetName()));
2702 cmXCodeObject* targetdep =
2703 this->CreateObject(cmXCodeObject::PBXTargetDependency);
2704 targetdep->SetComment("PBXTargetDependency");
2705 targetdep->AddAttribute("target", this->CreateObjectReference(dependTarget));
2706 targetdep->AddAttribute("targetProxy",
2707 this->CreateObjectReference(container));
2709 cmXCodeObject* depends = target->GetObject("dependencies");
2711 cmSystemTools::Error(
2712 "target does not have dependencies attribute error..");
2715 depends->AddUniqueObject(targetdep);
2719 void cmGlobalXCodeGenerator::AppendOrAddBuildSetting(cmXCodeObject* settings,
2720 const char* attribute,
2724 cmXCodeObject* attr = settings->GetObject(attribute);
2726 settings->AddAttribute(attribute, this->CreateString(value));
2728 std::string oldValue = cmStrCat(attr->GetString(), ' ', value);
2729 attr->SetString(oldValue);
2734 void cmGlobalXCodeGenerator::AppendBuildSettingAttribute(
2735 cmXCodeObject* target, const char* attribute, const char* value,
2736 const std::string& configName)
2738 // There are multiple configurations. Add the setting to the
2739 // buildSettings of the configuration name given.
2740 cmXCodeObject* configurationList =
2741 target->GetObject("buildConfigurationList")->GetObject();
2742 cmXCodeObject* buildConfigs =
2743 configurationList->GetObject("buildConfigurations");
2744 for (auto obj : buildConfigs->GetObjectList()) {
2745 if (configName.empty() ||
2746 obj->GetObject("name")->GetString() == configName) {
2747 cmXCodeObject* settings = obj->GetObject("buildSettings");
2748 this->AppendOrAddBuildSetting(settings, attribute, value);
2753 void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
2755 cmGeneratorTarget* gt = target->GetTarget();
2757 cmSystemTools::Error("Error no target on xobject\n");
2760 if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
2764 // Add dependencies on other CMake targets.
2765 for (const auto& dep : this->GetTargetDirectDepends(gt)) {
2766 if (cmXCodeObject* dptarget = this->FindXCodeTarget(dep)) {
2767 this->AddDependTarget(target, dptarget);
2771 // Loop over configuration types and set per-configuration info.
2772 for (auto const& configName : this->CurrentConfigurationTypes) {
2774 // Add object library contents as link flags.
2775 std::string linkObjs;
2776 const char* sep = "";
2777 std::vector<cmSourceFile const*> objs;
2778 gt->GetExternalObjects(objs, configName);
2779 for (auto sourceFile : objs) {
2780 if (sourceFile->GetObjectLibrary().empty()) {
2785 linkObjs += this->XCodeEscapePath(sourceFile->GetFullPath());
2787 this->AppendBuildSettingAttribute(
2788 target, this->GetTargetLinkFlagsVar(gt), linkObjs.c_str(), configName);
2791 // Skip link information for object libraries.
2792 if (gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
2793 gt->GetType() == cmStateEnums::STATIC_LIBRARY) {
2797 // Compute the link library and directory information.
2798 cmComputeLinkInformation* pcli = gt->GetLinkInformation(configName);
2802 cmComputeLinkInformation& cli = *pcli;
2804 // Add dependencies directly on library files.
2805 for (auto const& libDep : cli.GetDepends()) {
2806 target->AddDependLibrary(configName, libDep);
2809 // add the library search paths
2811 std::string linkDirs;
2812 for (auto const& libDir : cli.GetDirectories()) {
2813 if (!libDir.empty() && libDir != "/usr/lib") {
2814 // Now add the same one but append
2815 // $(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) to it:
2817 linkDirs += this->XCodeEscapePath(
2818 libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)");
2820 linkDirs += this->XCodeEscapePath(libDir);
2823 this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS",
2824 linkDirs.c_str(), configName);
2827 // now add the link libraries
2829 std::string linkLibs;
2830 const char* sep = "";
2831 for (auto const& libName : cli.GetItems()) {
2834 if (libName.IsPath) {
2835 linkLibs += this->XCodeEscapePath(libName.Value.Value);
2836 } else if (!libName.Target ||
2837 libName.Target->GetType() !=
2838 cmStateEnums::INTERFACE_LIBRARY) {
2839 linkLibs += libName.Value.Value;
2841 if (libName.Target && !libName.Target->IsImported()) {
2842 target->AddDependTarget(configName, libName.Target->GetName());
2845 this->AppendBuildSettingAttribute(
2846 target, this->GetTargetLinkFlagsVar(gt), linkLibs.c_str(), configName);
2851 bool cmGlobalXCodeGenerator::CreateGroups(
2852 std::vector<cmLocalGenerator*>& generators)
2854 for (auto& generator : generators) {
2855 cmMakefile* mf = generator->GetMakefile();
2856 std::vector<cmSourceGroup> sourceGroups = mf->GetSourceGroups();
2857 for (const auto& gtgt : generator->GetGeneratorTargets()) {
2858 // Same skipping logic here as in CreateXCodeTargets so that we do not
2859 // end up with (empty anyhow) ZERO_CHECK, install, or test source
2862 if (gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) {
2865 if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
2868 if (gtgt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
2872 auto addSourceToGroup = [this, mf, >gt,
2873 &sourceGroups](std::string const& source) {
2874 cmSourceGroup* sourceGroup = mf->FindSourceGroup(source, sourceGroups);
2875 cmXCodeObject* pbxgroup =
2876 this->CreateOrGetPBXGroup(gtgt.get(), sourceGroup);
2877 std::string key = GetGroupMapKeyFromPath(gtgt.get(), source);
2878 this->GroupMap[key] = pbxgroup;
2881 // Put cmSourceFile instances in proper groups:
2882 for (auto const& si : gtgt->GetAllConfigSources()) {
2883 cmSourceFile const* sf = si.Source;
2884 if (!sf->GetObjectLibrary().empty()) {
2885 // Object library files go on the link line instead.
2888 addSourceToGroup(sf->GetFullPath());
2891 // Add CMakeLists.txt file for user convenience.
2893 std::string listfile =
2894 cmStrCat(gtgt->GetLocalGenerator()->GetCurrentSourceDirectory(),
2896 cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
2897 listfile, false, cmSourceFileLocationKind::Known);
2898 addSourceToGroup(sf->ResolveFullPath());
2901 // Add the Info.plist we are about to generate for an App Bundle.
2902 if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
2903 std::string plist = this->ComputeInfoPListLocation(gtgt.get());
2904 cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource(
2905 plist, true, cmSourceFileLocationKind::Known);
2906 addSourceToGroup(sf->ResolveFullPath());
2913 cmXCodeObject* cmGlobalXCodeGenerator::CreatePBXGroup(cmXCodeObject* parent,
2914 const std::string& name)
2916 cmXCodeObject* parentChildren = nullptr;
2918 parentChildren = parent->GetObject("children");
2920 cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup);
2921 cmXCodeObject* groupChildren =
2922 this->CreateObject(cmXCodeObject::OBJECT_LIST);
2923 group->AddAttribute("name", this->CreateString(name));
2924 group->AddAttribute("children", groupChildren);
2925 group->AddAttribute("sourceTree", this->CreateString("<group>"));
2926 if (parentChildren) {
2927 parentChildren->AddObject(group);
2932 cmXCodeObject* cmGlobalXCodeGenerator::CreateOrGetPBXGroup(
2933 cmGeneratorTarget* gtgt, cmSourceGroup* sg)
2937 const std::string targetFolder = gtgt->GetEffectiveFolderName();
2938 if (!targetFolder.empty()) {
2939 target = cmStrCat(targetFolder, '/');
2941 target += gtgt->GetName();
2942 s = cmStrCat(target, '/', sg->GetFullName());
2943 auto it = this->GroupNameMap.find(s);
2944 if (it != this->GroupNameMap.end()) {
2948 it = this->TargetGroup.find(target);
2949 cmXCodeObject* tgroup = nullptr;
2950 if (it != this->TargetGroup.end()) {
2951 tgroup = it->second;
2953 std::vector<std::string> tgt_folders = cmTokenize(target, "/");
2954 std::string curr_tgt_folder;
2955 for (std::vector<std::string>::size_type i = 0; i < tgt_folders.size();
2958 curr_tgt_folder += "/";
2960 curr_tgt_folder += tgt_folders[i];
2961 it = this->TargetGroup.find(curr_tgt_folder);
2962 if (it != this->TargetGroup.end()) {
2963 tgroup = it->second;
2966 tgroup = this->CreatePBXGroup(tgroup, tgt_folders[i]);
2967 this->TargetGroup[curr_tgt_folder] = tgroup;
2969 this->MainGroupChildren->AddObject(tgroup);
2973 this->TargetGroup[target] = tgroup;
2975 // If it's the default source group (empty name) then put the source file
2976 // directly in the tgroup...
2978 if (sg->GetFullName().empty()) {
2979 this->GroupNameMap[s] = tgroup;
2983 // It's a recursive folder structure, let's find the real parent group
2984 if (sg->GetFullName() != sg->GetName()) {
2985 std::string curr_folder = cmStrCat(target, '/');
2986 for (auto const& folder : cmTokenize(sg->GetFullName(), "\\")) {
2987 curr_folder += folder;
2988 auto const i_folder = this->GroupNameMap.find(curr_folder);
2989 // Create new folder
2990 if (i_folder == this->GroupNameMap.end()) {
2991 cmXCodeObject* group = this->CreatePBXGroup(tgroup, folder);
2992 this->GroupNameMap[curr_folder] = group;
2995 tgroup = i_folder->second;
2997 curr_folder += "\\";
3001 cmXCodeObject* group = this->CreatePBXGroup(tgroup, sg->GetName());
3002 this->GroupNameMap[s] = group;
3006 bool cmGlobalXCodeGenerator::CreateXCodeObjects(
3007 cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
3009 this->ClearXCodeObjects();
3010 this->RootObject = nullptr;
3011 this->MainGroupChildren = nullptr;
3012 cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3013 group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
3014 cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3015 for (const std::string& CurrentConfigurationType :
3016 this->CurrentConfigurationTypes) {
3017 cmXCodeObject* buildStyle =
3018 this->CreateObject(cmXCodeObject::PBXBuildStyle);
3019 const std::string& name = CurrentConfigurationType;
3020 buildStyle->AddAttribute("name", this->CreateString(name));
3021 buildStyle->SetComment(name);
3022 cmXCodeObject* sgroup = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3023 sgroup->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO"));
3024 buildStyle->AddAttribute("buildSettings", sgroup);
3025 listObjs->AddObject(buildStyle);
3028 cmXCodeObject* mainGroup = this->CreateObject(cmXCodeObject::PBXGroup);
3029 this->MainGroupChildren = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3030 mainGroup->AddAttribute("children", this->MainGroupChildren);
3031 mainGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
3033 // now create the cmake groups
3034 if (!this->CreateGroups(generators)) {
3038 cmXCodeObject* productGroup = this->CreateObject(cmXCodeObject::PBXGroup);
3039 productGroup->AddAttribute("name", this->CreateString("Products"));
3040 productGroup->AddAttribute("sourceTree", this->CreateString("<group>"));
3041 cmXCodeObject* productGroupChildren =
3042 this->CreateObject(cmXCodeObject::OBJECT_LIST);
3043 productGroup->AddAttribute("children", productGroupChildren);
3044 this->MainGroupChildren->AddObject(productGroup);
3046 this->RootObject = this->CreateObject(cmXCodeObject::PBXProject);
3047 this->RootObject->SetComment("Project object");
3049 std::string project_id = cmStrCat("PROJECT_", root->GetProjectName());
3050 this->RootObject->SetId(
3051 this->GetOrCreateId(project_id, this->RootObject->GetId()));
3053 group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3054 this->RootObject->AddAttribute("mainGroup",
3055 this->CreateObjectReference(mainGroup));
3056 this->RootObject->AddAttribute("buildSettings", group);
3057 this->RootObject->AddAttribute("buildStyles", listObjs);
3058 this->RootObject->AddAttribute("hasScannedForEncodings",
3059 this->CreateString("0"));
3060 group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3061 group->AddAttribute("BuildIndependentTargetsInParallel",
3062 this->CreateString("YES"));
3063 std::ostringstream v;
3064 v << std::setfill('0') << std::setw(4) << XcodeVersion * 10;
3065 group->AddAttribute("LastUpgradeCheck", this->CreateString(v.str()));
3066 this->RootObject->AddAttribute("attributes", group);
3067 this->RootObject->AddAttribute("compatibilityVersion",
3068 this->CreateString("Xcode 3.2"));
3069 // Point Xcode at the top of the source tree.
3072 this->RelativeToBinary(root->GetCurrentSourceDirectory());
3073 this->RootObject->AddAttribute("projectDirPath", this->CreateString(pdir));
3074 this->RootObject->AddAttribute("projectRoot", this->CreateString(""));
3076 cmXCodeObject* configlist =
3077 this->CreateObject(cmXCodeObject::XCConfigurationList);
3078 cmXCodeObject* buildConfigurations =
3079 this->CreateObject(cmXCodeObject::OBJECT_LIST);
3080 using Configs = std::vector<std::pair<std::string, cmXCodeObject*>>;
3082 std::string defaultConfigName;
3083 for (const auto& name : this->CurrentConfigurationTypes) {
3084 if (defaultConfigName.empty()) {
3085 defaultConfigName = name;
3087 cmXCodeObject* config =
3088 this->CreateObject(cmXCodeObject::XCBuildConfiguration);
3089 config->AddAttribute("name", this->CreateString(name));
3090 configs.push_back(std::make_pair(name, config));
3092 if (defaultConfigName.empty()) {
3093 defaultConfigName = "Debug";
3095 for (auto& config : configs) {
3096 buildConfigurations->AddObject(config.second);
3098 configlist->AddAttribute("buildConfigurations", buildConfigurations);
3100 std::string comment = cmStrCat("Build configuration list for PBXProject \"",
3101 this->CurrentProject, '"');
3102 configlist->SetComment(comment);
3103 configlist->AddAttribute("defaultConfigurationIsVisible",
3104 this->CreateString("0"));
3105 configlist->AddAttribute("defaultConfigurationName",
3106 this->CreateString(defaultConfigName));
3107 cmXCodeObject* buildSettings =
3108 this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
3109 const char* sysroot =
3110 this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT");
3111 const char* deploymentTarget =
3112 this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
3114 buildSettings->AddAttribute("SDKROOT", this->CreateString(sysroot));
3116 // recompute this as it may have been changed since enable language
3117 this->ComputeArchitectures(this->CurrentMakefile);
3118 std::string const archs = cmJoin(this->Architectures, " ");
3119 if (archs.empty()) {
3120 // Tell Xcode to use NATIVE_ARCH instead of ARCHS.
3121 buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("YES"));
3122 // When targeting macOS, use only the host architecture.
3123 if (this->SystemName == "Darwin"_s &&
3124 (!sysroot || !*sysroot ||
3125 cmSystemTools::LowerCase(sysroot).find("macos") !=
3126 std::string::npos)) {
3127 buildSettings->AddAttribute("ARCHS",
3128 this->CreateString("$(NATIVE_ARCH_ACTUAL)"));
3131 // Tell Xcode to use ARCHS (ONLY_ACTIVE_ARCH defaults to NO).
3132 buildSettings->AddAttribute("ARCHS", this->CreateString(archs));
3134 if (deploymentTarget && *deploymentTarget) {
3135 buildSettings->AddAttribute(GetDeploymentPlatform(root->GetMakefile()),
3136 this->CreateString(deploymentTarget));
3138 if (!this->GeneratorToolset.empty()) {
3139 buildSettings->AddAttribute("GCC_VERSION",
3140 this->CreateString(this->GeneratorToolset));
3142 if (this->GetLanguageEnabled("Swift")) {
3143 std::string swiftVersion;
3144 if (const char* vers = this->CurrentMakefile->GetDefinition(
3145 "CMAKE_Swift_LANGUAGE_VERSION")) {
3146 swiftVersion = vers;
3147 } else if (this->XcodeVersion >= 102) {
3148 swiftVersion = "4.0";
3149 } else if (this->XcodeVersion >= 83) {
3150 swiftVersion = "3.0";
3152 swiftVersion = "2.3";
3154 buildSettings->AddAttribute("SWIFT_VERSION",
3155 this->CreateString(swiftVersion));
3158 std::string symroot = cmStrCat(root->GetCurrentBinaryDirectory(), "/build");
3159 buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot));
3161 for (auto& config : configs) {
3162 cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
3164 // Put this last so it can override existing settings
3165 // Convert "CMAKE_XCODE_ATTRIBUTE_*" variables directly.
3166 for (const auto& var : this->CurrentMakefile->GetDefinitions()) {
3167 if (cmHasLiteralPrefix(var, "CMAKE_XCODE_ATTRIBUTE_")) {
3168 std::string attribute = var.substr(22);
3169 this->FilterConfigurationAttribute(config.first, attribute);
3170 if (!attribute.empty()) {
3171 std::string processed = cmGeneratorExpression::Evaluate(
3172 this->CurrentMakefile->GetSafeDefinition(var),
3173 this->CurrentLocalGenerator, config.first);
3174 buildSettingsForCfg->AddAttribute(attribute,
3175 this->CreateString(processed));
3179 // store per-config buildSettings into configuration object
3180 config.second->AddAttribute("buildSettings", buildSettingsForCfg);
3183 this->RootObject->AddAttribute("buildConfigurationList",
3184 this->CreateObjectReference(configlist));
3186 std::vector<cmXCodeObject*> targets;
3187 for (auto& generator : generators) {
3188 if (!this->CreateXCodeTargets(generator, targets)) {
3192 // loop over all targets and add link and depend info
3193 for (auto t : targets) {
3194 this->AddDependAndLinkInformation(t);
3196 this->CreateXCodeDependHackTarget(targets);
3197 // now add all targets to the root object
3198 cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST);
3199 for (auto t : targets) {
3200 allTargets->AddObject(t);
3201 cmXCodeObject* productRef = t->GetObject("productReference");
3203 productGroupChildren->AddObject(productRef->GetObject());
3206 this->RootObject->AddAttribute("targets", allTargets);
3210 std::string cmGlobalXCodeGenerator::GetObjectsDirectory(
3211 const std::string& projName, const std::string& configName,
3212 const cmGeneratorTarget* t, const std::string& variant) const
3214 std::string dir = cmStrCat(
3215 t->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', projName,
3216 ".build/", configName, '/', t->GetName(), ".build/", variant);
3220 void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf)
3222 this->Architectures.clear();
3223 const char* sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
3225 mf->GetDefExpandList("CMAKE_OSX_ARCHITECTURES", this->Architectures);
3228 if (this->Architectures.empty()) {
3229 // With no ARCHS we use ONLY_ACTIVE_ARCH and possibly a
3230 // platform-specific default ARCHS placeholder value.
3231 // Look up the arch that Xcode chooses in this case.
3232 if (const char* arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) {
3233 this->ObjectDirArchDefault = arch;
3234 // We expect only one arch but choose the first just in case.
3235 std::string::size_type pos = this->ObjectDirArchDefault.find(';');
3236 if (pos != std::string::npos) {
3237 this->ObjectDirArchDefault = this->ObjectDirArchDefault.substr(0, pos);
3242 this->ComputeObjectDirArch(mf);
3245 void cmGlobalXCodeGenerator::ComputeObjectDirArch(cmMakefile* mf)
3247 if (this->Architectures.size() > 1 || this->UseEffectivePlatformName(mf)) {
3248 this->ObjectDirArch = "$(CURRENT_ARCH)";
3249 } else if (!this->Architectures.empty()) {
3250 this->ObjectDirArch = this->Architectures[0];
3252 this->ObjectDirArch = this->ObjectDirArchDefault;
3256 void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget(
3257 std::vector<cmXCodeObject*>& targets)
3259 cmGeneratedFileStream makefileStream(this->CurrentXCodeHackMakefile);
3260 if (!makefileStream) {
3261 cmSystemTools::Error("Could not create " + this->CurrentXCodeHackMakefile);
3264 makefileStream.SetCopyIfDifferent(true);
3265 // one more pass for external depend information not handled
3266 // correctly by xcode
3267 /* clang-format off */
3268 makefileStream << "# DO NOT EDIT\n";
3269 makefileStream << "# This makefile makes sure all linkable targets are\n";
3270 makefileStream << "# up-to-date with anything they link to\n"
3272 "\techo \"Do not invoke directly\"\n"
3274 /* clang-format on */
3276 std::set<std::string> dummyRules;
3278 // Write rules to help Xcode relink things at the right time.
3279 /* clang-format off */
3281 "# Rules to remove targets that are older than anything to which they\n"
3282 "# link. This forces Xcode to relink the targets from scratch. It\n"
3283 "# does not seem to check these dependencies itself.\n";
3284 /* clang-format on */
3285 for (const auto& configName : this->CurrentConfigurationTypes) {
3286 for (auto target : targets) {
3287 cmGeneratorTarget* gt = target->GetTarget();
3289 if (gt->GetType() == cmStateEnums::EXECUTABLE ||
3290 gt->GetType() == cmStateEnums::OBJECT_LIBRARY ||
3291 gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
3292 gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
3293 gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
3294 // Declare an entry point for the target post-build phase.
3295 makefileStream << this->PostBuildMakeTarget(gt->GetName(), configName)
3299 if (gt->GetType() == cmStateEnums::EXECUTABLE ||
3300 gt->GetType() == cmStateEnums::STATIC_LIBRARY ||
3301 gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
3302 gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
3303 std::string tfull = gt->GetFullPath(configName);
3304 std::string trel = this->ConvertToRelativeForMake(tfull);
3306 // Add this target to the post-build phases of its dependencies.
3307 auto const y = target->GetDependTargets().find(configName);
3308 if (y != target->GetDependTargets().end()) {
3309 for (auto const& deptgt : y->second) {
3310 makefileStream << this->PostBuildMakeTarget(deptgt, configName)
3311 << ": " << trel << "\n";
3315 std::vector<cmGeneratorTarget*> objlibs;
3316 gt->GetObjectLibrariesCMP0026(objlibs);
3317 for (auto objLib : objlibs) {
3318 makefileStream << this->PostBuildMakeTarget(objLib->GetName(),
3320 << ": " << trel << "\n";
3323 // Create a rule for this target.
3324 makefileStream << trel << ":";
3326 // List dependencies if any exist.
3327 auto const x = target->GetDependLibraries().find(configName);
3328 if (x != target->GetDependLibraries().end()) {
3329 for (auto const& deplib : x->second) {
3330 std::string file = this->ConvertToRelativeForMake(deplib);
3331 makefileStream << "\\\n\t" << file;
3332 dummyRules.insert(file);
3336 for (auto objLib : objlibs) {
3338 const std::string objLibName = objLib->GetName();
3339 std::string d = cmStrCat(
3340 this->GetObjectsDirectory(this->CurrentProject, configName, objLib,
3341 OBJECT_LIBRARY_ARTIFACT_DIR),
3342 "lib", objLibName, ".a");
3344 std::string dependency = this->ConvertToRelativeForMake(d);
3345 makefileStream << "\\\n\t" << dependency;
3346 dummyRules.insert(dependency);
3349 // Write the action to remove the target if it is out of date.
3350 makefileStream << "\n";
3351 makefileStream << "\t/bin/rm -f "
3352 << this->ConvertToRelativeForMake(tfull) << "\n";
3353 // if building for more than one architecture
3354 // then remove those executables as well
3355 if (this->Architectures.size() > 1) {
3356 std::string universal = this->GetObjectsDirectory(
3357 this->CurrentProject, configName, gt, "$(OBJDIR)/");
3358 for (const auto& architecture : this->Architectures) {
3359 std::string universalFile = cmStrCat(universal, architecture, '/',
3360 gt->GetFullName(configName));
3361 makefileStream << "\t/bin/rm -f "
3362 << this->ConvertToRelativeForMake(universalFile)
3366 makefileStream << "\n\n";
3371 makefileStream << "\n\n"
3372 << "# For each target create a dummy rule"
3373 << "so the target does not have to exist\n";
3374 for (auto const& dummyRule : dummyRules) {
3375 makefileStream << dummyRule << ":\n";
3379 void cmGlobalXCodeGenerator::OutputXCodeProject(
3380 cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
3382 if (generators.empty()) {
3385 if (!this->CreateXCodeObjects(root, generators)) {
3388 std::string xcodeDir = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
3389 root->GetProjectName(), ".xcodeproj");
3390 cmSystemTools::MakeDirectory(xcodeDir);
3391 std::string xcodeProjFile = xcodeDir + "/project.pbxproj";
3392 cmGeneratedFileStream fout(xcodeProjFile);
3393 fout.SetCopyIfDifferent(true);
3397 this->WriteXCodePBXProj(fout, root, generators);
3399 bool hasGeneratedSchemes = this->OutputXCodeSharedSchemes(xcodeDir, root);
3400 this->OutputXCodeWorkspaceSettings(xcodeDir, hasGeneratedSchemes);
3402 this->ClearXCodeObjects();
3404 // Since this call may have created new cache entries, save the cache:
3406 root->GetMakefile()->GetCMakeInstance()->SaveCache(
3407 root->GetBinaryDirectory());
3410 bool cmGlobalXCodeGenerator::OutputXCodeSharedSchemes(
3411 const std::string& xcProjDir, cmLocalGenerator* root)
3413 // collect all tests for the targets
3414 std::map<std::string, cmXCodeScheme::TestObjects> testables;
3416 for (const auto& obj : this->XCodeObjects) {
3417 if (obj->GetType() != cmXCodeObject::OBJECT ||
3418 obj->GetIsA() != cmXCodeObject::PBXNativeTarget) {
3422 if (!obj->GetTarget()->IsXCTestOnApple()) {
3426 cmProp testee = obj->GetTarget()->GetProperty("XCTEST_TESTEE");
3431 testables[*testee].push_back(obj.get());
3437 // Since the lowest available Xcode version for testing was 6.4,
3438 // I'm setting this as a limit then
3439 if (this->XcodeVersion >= 64) {
3440 for (const auto& obj : this->XCodeObjects) {
3441 if (obj->GetType() == cmXCodeObject::OBJECT &&
3442 (obj->GetIsA() == cmXCodeObject::PBXNativeTarget ||
3443 obj->GetIsA() == cmXCodeObject::PBXAggregateTarget) &&
3444 (root->GetMakefile()->GetCMakeInstance()->GetIsInTryCompile() ||
3445 obj->GetTarget()->GetPropertyAsBool("XCODE_GENERATE_SCHEME"))) {
3446 const std::string& targetName = obj->GetTarget()->GetName();
3447 cmXCodeScheme schm(root, obj.get(), testables[targetName],
3448 this->CurrentConfigurationTypes,
3449 this->XcodeVersion);
3450 schm.WriteXCodeSharedScheme(xcProjDir,
3451 this->RelativeToSource(xcProjDir));
3460 void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings(
3461 const std::string& xcProjDir, bool hasGeneratedSchemes)
3463 std::string xcodeSharedDataDir =
3464 cmStrCat(xcProjDir, "/project.xcworkspace/xcshareddata");
3465 cmSystemTools::MakeDirectory(xcodeSharedDataDir);
3467 std::string workspaceSettingsFile =
3468 cmStrCat(xcodeSharedDataDir, "/WorkspaceSettings.xcsettings");
3470 cmGeneratedFileStream fout(workspaceSettingsFile);
3471 fout.SetCopyIfDifferent(true);
3476 cmXMLWriter xout(fout);
3477 xout.StartDocument();
3478 xout.Doctype("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\""
3479 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
3480 xout.StartElement("plist");
3481 xout.Attribute("version", "1.0");
3482 xout.StartElement("dict");
3483 if (this->XcodeVersion >= 100) {
3484 xout.Element("key", "BuildSystemType");
3485 xout.Element("string", "Original");
3486 xout.Element("key", "DisableBuildSystemDeprecationWarning");
3487 xout.Element("true");
3489 if (hasGeneratedSchemes) {
3491 "IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded");
3492 xout.Element("false");
3494 xout.EndElement(); // dict
3495 xout.EndElement(); // plist
3499 void cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout,
3501 std::vector<cmLocalGenerator*>&)
3505 fout << "// !$*UTF8*$!\n";
3507 cmXCodeObject::Indent(1, fout);
3508 fout << "archiveVersion = 1;\n";
3509 cmXCodeObject::Indent(1, fout);
3510 fout << "classes = {\n";
3511 cmXCodeObject::Indent(1, fout);
3513 cmXCodeObject::Indent(1, fout);
3514 fout << "objectVersion = 46;\n";
3515 cmXCode21Object::PrintList(this->XCodeObjects, fout);
3516 cmXCodeObject::Indent(1, fout);
3517 fout << "rootObject = " << this->RootObject->GetId()
3518 << " /* Project object */;\n";
3522 const char* cmGlobalXCodeGenerator::GetCMakeCFGIntDir() const
3524 return "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
3527 std::string cmGlobalXCodeGenerator::ExpandCFGIntDir(
3528 const std::string& str, const std::string& config) const
3530 std::string replace1 = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)";
3531 std::string replace2 = "$(CONFIGURATION)";
3533 std::string tmp = str;
3534 for (std::string::size_type i = tmp.find(replace1); i != std::string::npos;
3535 i = tmp.find(replace1, i)) {
3536 tmp.replace(i, replace1.size(), config);
3539 for (std::string::size_type i = tmp.find(replace2); i != std::string::npos;
3540 i = tmp.find(replace2, i)) {
3541 tmp.replace(i, replace2.size(), config);
3547 void cmGlobalXCodeGenerator::GetDocumentation(cmDocumentationEntry& entry)
3549 entry.Name = cmGlobalXCodeGenerator::GetActualName();
3550 entry.Brief = "Generate Xcode project files.";
3553 std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(
3554 std::string const& p)
3556 return cmSystemTools::ConvertToOutputPath(p);
3559 std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p)
3561 // We force conversion because Xcode breakpoints do not work unless
3562 // they are in a file named relative to the source tree.
3563 return cmSystemTools::ForceToRelativePath(
3564 cmSystemTools::JoinPath(this->ProjectSourceDirectoryComponents), p);
3567 std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p)
3569 return this->CurrentLocalGenerator->MaybeConvertToRelativePath(
3570 cmSystemTools::JoinPath(this->ProjectOutputDirectoryComponents), p);
3573 std::string cmGlobalXCodeGenerator::XCodeEscapePath(const std::string& p)
3575 if (p.find_first_of(" []") != std::string::npos) {
3576 std::string t = cmStrCat('"', p, '"');
3582 void cmGlobalXCodeGenerator::AppendDirectoryForConfig(
3583 const std::string& prefix, const std::string& config,
3584 const std::string& suffix, std::string& dir)
3586 if (!config.empty()) {
3593 std::string cmGlobalXCodeGenerator::LookupFlags(
3594 const std::string& varNamePrefix, const std::string& varNameLang,
3595 const std::string& varNameSuffix, const std::string& default_flags)
3597 if (!varNameLang.empty()) {
3598 std::string varName = cmStrCat(varNamePrefix, varNameLang, varNameSuffix);
3599 if (const char* varValue = this->CurrentMakefile->GetDefinition(varName)) {
3605 return default_flags;
3608 void cmGlobalXCodeGenerator::AppendDefines(BuildObjectListOrString& defs,
3609 const char* defines_list,
3612 // Skip this if there are no definitions.
3613 if (!defines_list) {
3617 // Expand the list of definitions.
3618 std::vector<std::string> defines = cmExpandedList(defines_list);
3620 // Store the definitions in the string.
3621 this->AppendDefines(defs, defines, dflag);
3624 void cmGlobalXCodeGenerator::AppendDefines(
3625 BuildObjectListOrString& defs, std::vector<std::string> const& defines,
3628 // GCC_PREPROCESSOR_DEFINITIONS is a space-separated list of definitions.
3630 for (auto const& define : defines) {
3631 // Start with -D if requested.
3632 def = cmStrCat(dflag ? "-D" : "", define);
3634 // Append the flag with needed escapes.
3636 this->AppendFlag(tmp, def);
3641 void cmGlobalXCodeGenerator::AppendFlag(std::string& flags,
3642 std::string const& flag) const
3644 // Short-circuit for an empty flag.
3649 // Separate from previous flags.
3650 if (!flags.empty()) {
3654 // Check if the flag needs quoting.
3656 flag.find_first_of("`~!@#$%^&*()+={}[]|:;\"'<>,.? ") != std::string::npos;
3658 // We escape a flag as follows:
3659 // - Place each flag in single quotes ''
3660 // - Escape a single quote as \'
3661 // - Escape a backslash as \\ since it itself is an escape
3662 // Note that in the code below we need one more level of escapes for
3663 // C string syntax in this source file.
3665 // The final level of escaping is done when the string is stored
3666 // into the project file by cmXCodeObject::PrintString.
3669 // Open single quote.
3673 // Flag value with escaped quotes and backslashes.
3674 for (auto c : flag) {
3677 } else if (c == '\\') {
3685 // Close single quote.
3690 std::string cmGlobalXCodeGenerator::ComputeInfoPListLocation(
3691 cmGeneratorTarget* target)
3694 cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
3695 "/CMakeFiles/", target->GetName(), ".dir/Info.plist");
3699 // Return true if the generated build tree may contain multiple builds.
3700 // i.e. "Can I build Debug and Release in the same tree?"
3701 bool cmGlobalXCodeGenerator::IsMultiConfig() const
3703 // Newer Xcode versions are multi config:
3707 bool cmGlobalXCodeGenerator::HasKnownObjectFileLocation(
3708 std::string* reason) const
3710 if (this->ObjectDirArch.find('$') != std::string::npos) {
3711 if (reason != nullptr) {
3712 *reason = " under Xcode with multiple architectures";
3719 bool cmGlobalXCodeGenerator::UseEffectivePlatformName(cmMakefile* mf) const
3721 cmProp epnValue = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
3722 "XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
3725 return mf->PlatformIsAppleEmbedded();
3728 return cmIsOn(*epnValue);
3731 bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
3733 // Xcode determines Resource location itself
3737 void cmGlobalXCodeGenerator::ComputeTargetObjectDirectory(
3738 cmGeneratorTarget* gt) const
3740 std::string configName = this->GetCMakeCFGIntDir();
3742 cmStrCat(this->GetObjectsDirectory("$(PROJECT_NAME)", configName, gt,
3743 "$(OBJECT_FILE_DIR_normal:base)/"),
3744 this->ObjectDirArch, '/');
3745 gt->ObjectDirectory = dir;
3748 std::string cmGlobalXCodeGenerator::GetDeploymentPlatform(const cmMakefile* mf)
3750 switch (mf->GetAppleSDKType()) {
3751 case cmMakefile::AppleSDK::AppleTVOS:
3752 case cmMakefile::AppleSDK::AppleTVSimulator:
3753 return "TVOS_DEPLOYMENT_TARGET";
3755 case cmMakefile::AppleSDK::IPhoneOS:
3756 case cmMakefile::AppleSDK::IPhoneSimulator:
3757 return "IPHONEOS_DEPLOYMENT_TARGET";
3759 case cmMakefile::AppleSDK::WatchOS:
3760 case cmMakefile::AppleSDK::WatchSimulator:
3761 return "WATCHOS_DEPLOYMENT_TARGET";
3763 case cmMakefile::AppleSDK::MacOS:
3765 return "MACOSX_DEPLOYMENT_TARGET";