// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Xml; using System.Xml.XPath; using System.Xml.Linq; using System.Linq; using System.Text; using Tools.DotNETCommon; namespace UnrealBuildTool { /// /// Info needed to make a file a member of specific group /// class XcodeSourceFile : ProjectFile.SourceFile { /// /// Constructor /// public XcodeSourceFile(FileReference InitFilePath, DirectoryReference InitRelativeBaseFolder) : base(InitFilePath, InitRelativeBaseFolder) { FileGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); FileRefGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); } /// /// File Guid for use in Xcode project /// public string FileGuid { get; private set; } public void ReplaceGuids(string NewFileGuid, string NewFileRefGuid) { FileGuid = NewFileGuid; FileRefGuid = NewFileRefGuid; } /// /// File reference Guid for use in Xcode project /// public string FileRefGuid { get; private set; } } /// /// Represents a group of files shown in Xcode's project navigator as a folder /// class XcodeFileGroup { public XcodeFileGroup(string InName, string InPath, bool InIsReference = false) { GroupName = InName; GroupPath = InPath; GroupGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); bIsReference = InIsReference; } public string GroupGuid; public string GroupName; public string GroupPath; public Dictionary Children = new Dictionary(); public List Files = new List(); public bool bIsReference; } class XcodeProjectFile : ProjectFile { FileReference OnlyGameProject; Dictionary Groups = new Dictionary(); /// /// Constructs a new project file object /// /// The path to the project file on disk /// /// True for distribution builds /// Override option for bundle identifier public XcodeProjectFile(FileReference InitFilePath, FileReference InOnlyGameProject, bool IsForDistribution, string BundleID) : base(InitFilePath) { OnlyGameProject = InOnlyGameProject; bForDistribution = IsForDistribution; BundleIdentifier = BundleID; } public override string ToString() { return ProjectFilePath.GetFileNameWithoutExtension(); } /// /// Used to mark the project for distribution (some platforms require this) /// bool bForDistribution = false; /// /// Override for bundle identifier /// string BundleIdentifier = ""; /// /// Gets Xcode file category based on its extension /// private string GetFileCategory(string Extension) { // @todo Mac: Handle more categories switch (Extension) { case ".framework": return "Frameworks"; default: return "Sources"; } } /// /// Gets Xcode file type based on its extension /// private string GetFileType(string Extension) { // @todo Mac: Handle more file types switch (Extension) { case ".c": case ".m": return "sourcecode.c.objc"; case ".cc": case ".cpp": case ".mm": return "sourcecode.cpp.objcpp"; case ".h": case ".inl": case ".pch": return "sourcecode.c.h"; case ".framework": return "wrapper.framework"; case ".plist": return "text.plist.xml"; case ".png": return "image.png"; case ".icns": return "image.icns"; default: return "file.text"; } } /// /// Returns true if Extension is a known extension for files containing source code /// private bool IsSourceCode(string Extension) { return Extension == ".c" || Extension == ".cc" || Extension == ".cpp" || Extension == ".m" || Extension == ".mm"; } private bool ShouldIncludeFileInBuildPhaseSection(XcodeSourceFile SourceFile) { string FileExtension = SourceFile.Reference.GetExtension(); if (IsSourceCode(FileExtension)) { foreach (string PlatformName in Enum.GetNames(typeof(UnrealTargetPlatform))) { string AltName = PlatformName == "Win32" || PlatformName == "Win64" ? "windows" : PlatformName.ToLower(); if ((SourceFile.Reference.FullName.ToLower().Contains("/" + PlatformName.ToLower() + "/") || SourceFile.Reference.FullName.ToLower().Contains("/" + AltName + "/")) && PlatformName != "Mac" && PlatformName != "IOS" && PlatformName != "TVOS") { // Build phase is used for indexing only and indexing currently works only with files that can be compiled for Mac, so skip files for other platforms return false; } } return true; } return false; } /// /// Returns a project navigator group to which the file should belong based on its path. /// Creates a group tree if it doesn't exist yet. /// public XcodeFileGroup FindGroupByAbsolutePath(ref Dictionary Groups, string AbsolutePath) { string[] Parts = AbsolutePath.Split(Path.DirectorySeparatorChar); string CurrentPath = "/"; Dictionary CurrentSubGroups = Groups; for (int Index = 1; Index < Parts.Count(); ++Index) { string Part = Parts[Index]; if (CurrentPath.Length > 1) { CurrentPath += Path.DirectorySeparatorChar; } CurrentPath += Part; XcodeFileGroup CurrentGroup; if (!CurrentSubGroups.ContainsKey(CurrentPath)) { CurrentGroup = new XcodeFileGroup(Path.GetFileName(CurrentPath), CurrentPath); CurrentSubGroups.Add(CurrentPath, CurrentGroup); } else { CurrentGroup = CurrentSubGroups[CurrentPath]; } if (CurrentPath == AbsolutePath) { return CurrentGroup; } CurrentSubGroups = CurrentGroup.Children; } return null; } /// /// Convert all paths to Apple/Unix format (with forward slashes) /// /// The path to convert /// The normalized path private static string ConvertPath(string InPath) { return InPath.Replace("\\", "/"); } /// /// Allocates a generator-specific source file object /// /// Path to the source file on disk /// Optional sub-folder to put the file in. If empty, this will be determined automatically from the file's path relative to the project file /// The newly allocated source file object public override SourceFile AllocSourceFile(FileReference InitFilePath, DirectoryReference InitProjectSubFolder) { if (InitFilePath.GetFileName().StartsWith(".")) { return null; } return new XcodeSourceFile(InitFilePath, InitProjectSubFolder); } /// /// Generates bodies of all sections that contain a list of source files plus a dictionary of project navigator groups. /// private void GenerateSectionsWithSourceFiles(StringBuilder PBXBuildFileSection, StringBuilder PBXFileReferenceSection, StringBuilder PBXSourcesBuildPhaseSection, string TargetAppGuid, string TargetName) { SourceFiles.Sort((x, y) => { return x.Reference.FullName.CompareTo(y.Reference.FullName); }); foreach (SourceFile CurSourceFile in SourceFiles) { XcodeSourceFile SourceFile = CurSourceFile as XcodeSourceFile; string FileName = SourceFile.Reference.GetFileName(); string FileExtension = Path.GetExtension(FileName); string FilePath = SourceFile.Reference.MakeRelativeTo(ProjectFilePath.Directory); string FilePathMac = Utils.CleanDirectorySeparators(FilePath, '/'); if (IsGeneratedProject) { PBXBuildFileSection.Append(string.Format("\t\t{0} /* {1} in {2} */ = {{isa = PBXBuildFile; fileRef = {3} /* {1} */; }};" + ProjectFileGenerator.NewLine, SourceFile.FileGuid, FileName, GetFileCategory(FileExtension), SourceFile.FileRefGuid)); } PBXFileReferenceSection.Append(string.Format("\t\t{0} /* {1} */ = {{isa = PBXFileReference; explicitFileType = {2}; name = \"{1}\"; path = \"{3}\"; sourceTree = SOURCE_ROOT; }};" + ProjectFileGenerator.NewLine, SourceFile.FileRefGuid, FileName, GetFileType(FileExtension), FilePathMac)); if (ShouldIncludeFileInBuildPhaseSection(SourceFile)) { PBXSourcesBuildPhaseSection.Append("\t\t\t\t" + SourceFile.FileGuid + " /* " + FileName + " in Sources */," + ProjectFileGenerator.NewLine); } XcodeFileGroup Group = FindGroupByAbsolutePath(ref Groups, CurSourceFile.Reference.Directory.FullName); if (Group != null) { Group.Files.Add(SourceFile); } } PBXFileReferenceSection.Append(string.Format("\t\t{0} /* {1} */ = {{isa = PBXFileReference; explicitFileType = wrapper.application; path = {1}; sourceTree = BUILT_PRODUCTS_DIR; }};" + ProjectFileGenerator.NewLine, TargetAppGuid, TargetName)); } private void AppendGroup(XcodeFileGroup Group, StringBuilder Content) { if (!Group.bIsReference) { Content.Append(string.Format("\t\t{0} = {{{1}", Group.GroupGuid, ProjectFileGenerator.NewLine)); Content.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); foreach (XcodeFileGroup ChildGroup in Group.Children.Values) { Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", ChildGroup.GroupGuid, ChildGroup.GroupName, ProjectFileGenerator.NewLine)); } foreach (XcodeSourceFile File in Group.Files) { Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", File.FileRefGuid, File.Reference.GetFileName(), ProjectFileGenerator.NewLine)); } Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + Group.GroupName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tpath = \"" + Group.GroupPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); foreach (XcodeFileGroup ChildGroup in Group.Children.Values) { AppendGroup(ChildGroup, Content); } } } private void AppendBuildFileSection(StringBuilder Content, StringBuilder SectionContent) { Content.Append("/* Begin PBXBuildFile section */" + ProjectFileGenerator.NewLine); Content.Append(SectionContent); Content.Append("/* End PBXBuildFile section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendFileReferenceSection(StringBuilder Content, StringBuilder SectionContent) { Content.Append("/* Begin PBXFileReference section */" + ProjectFileGenerator.NewLine); Content.Append(SectionContent); Content.Append("/* End PBXFileReference section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendSourcesBuildPhaseSection(StringBuilder Content, StringBuilder SectionContent, string SourcesBuildPhaseGuid) { Content.Append("/* Begin PBXSourcesBuildPhase section */" + ProjectFileGenerator.NewLine); Content.Append(string.Format("\t\t{0} = {{{1}", SourcesBuildPhaseGuid, ProjectFileGenerator.NewLine)); Content.Append("\t\t\tisa = PBXSourcesBuildPhase;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildActionMask = 2147483647;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tfiles = (" + ProjectFileGenerator.NewLine); Content.Append(SectionContent); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\trunOnlyForDeploymentPostprocessing = 0;" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXSourcesBuildPhase section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private XcodeFileGroup FindRootFileGroup(Dictionary GroupsDict) { foreach (XcodeFileGroup Group in GroupsDict.Values) { if (Group.Children.Count > 1 || Group.Files.Count > 0) { return Group; } else { XcodeFileGroup Found = FindRootFileGroup(Group.Children); if (Found != null) { return Found; } } } return null; } private void AppendGroupSection(StringBuilder Content, string MainGroupGuid, string ProductRefGroupGuid, string TargetAppGuid, string TargetName) { XcodeFileGroup RootGroup = FindRootFileGroup(Groups); if (RootGroup == null) { return; } Content.Append("/* Begin PBXGroup section */" + ProjectFileGenerator.NewLine); // Main group Content.Append(string.Format("\t\t{0} = {{{1}", MainGroupGuid, ProjectFileGenerator.NewLine)); Content.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); foreach (XcodeFileGroup Group in RootGroup.Children.Values) { Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", Group.GroupGuid, Group.GroupName, ProjectFileGenerator.NewLine)); } foreach (XcodeSourceFile File in RootGroup.Files) { Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", File.FileRefGuid, File.Reference.GetFileName(), ProjectFileGenerator.NewLine)); } Content.Append(string.Format("\t\t\t\t{0} /* Products */,{1}", ProductRefGroupGuid, ProjectFileGenerator.NewLine)); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); // Sources groups foreach (XcodeFileGroup Group in RootGroup.Children.Values) { AppendGroup(Group, Content); } // Products group Content.Append(string.Format("\t\t{0} /* Products */ = {{{1}", ProductRefGroupGuid, ProjectFileGenerator.NewLine)); Content.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", TargetAppGuid, TargetName, ProjectFileGenerator.NewLine)); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = Products;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXGroup section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendLegacyTargetSection(StringBuilder Content, string TargetName, string TargetGuid, string TargetBuildConfigGuid, FileReference UProjectPath, bool bHasEditorConfiguration) { string UE4Dir = ConvertPath(Path.GetFullPath(Directory.GetCurrentDirectory() + "../../..")); string BuildToolPath = UE4Dir + "/Engine/Build/BatchFiles/Mac/XcodeBuild.sh"; Content.Append("/* Begin PBXLegacyTarget section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetGuid + " /* " + TargetName + " */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXLegacyTarget;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildArgumentsString = \"$(ACTION) $(UE_BUILD_TARGET_NAME) $(PLATFORM_NAME) $(UE_BUILD_TARGET_CONFIG)" + (UProjectPath == null ? "" : " \\\"" + UProjectPath.FullName + "\\\"") + (bHasEditorConfiguration ? " -buildscw" : "") + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildConfigurationList = " + TargetBuildConfigGuid + " /* Build configuration list for PBXLegacyTarget \"" + TargetName + "\" */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildPhases = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildToolPath = \"" + BuildToolPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildWorkingDirectory = \"" + UE4Dir + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdependencies = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tpassBuildSettingsInEnvironment = 1;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductName = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXLegacyTarget section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendRunTargetSection(StringBuilder Content, string TargetName, string TargetGuid, string TargetBuildConfigGuid, string TargetDependencyGuid, string TargetAppGuid) { Content.Append("/* Begin PBXNativeTarget section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetGuid + " /* " + TargetName + " */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXNativeTarget;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildConfigurationList = " + TargetBuildConfigGuid + " /* Build configuration list for PBXNativeTarget \"" + TargetName + "\" */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildPhases = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdependencies = (" + ProjectFileGenerator.NewLine); if (!XcodeProjectFileGenerator.bGeneratingRunIOSProject && !XcodeProjectFileGenerator.bGeneratingRunTVOSProject) { Content.Append("\t\t\t\t" + TargetDependencyGuid + " /* PBXTargetDependency */," + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tpassBuildSettingsInEnvironment = 1;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductName = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductReference = \"" + TargetAppGuid + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductType = \"com.apple.product-type.application\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXNativeTarget section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendIndexTargetSection(StringBuilder Content, string TargetName, string TargetGuid, string TargetBuildConfigGuid, string SourcesBuildPhaseGuid) { Content.Append("/* Begin PBXNativeTarget section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetGuid + " /* " + TargetName + " */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXNativeTarget;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildConfigurationList = " + TargetBuildConfigGuid + " /* Build configuration list for PBXNativeTarget \"" + TargetName + "\" */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildPhases = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t" + SourcesBuildPhaseGuid + " /* Sources */," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdependencies = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tpassBuildSettingsInEnvironment = 1;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductName = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductType = \"com.apple.product-type.library.static\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXNativeTarget section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendProjectSection(StringBuilder Content, string TargetName, string TargetGuid, string BuildTargetName, string BuildTargetGuid, string IndexTargetName, string IndexTargetGuid, string MainGroupGuid, string ProductRefGroupGuid, string ProjectGuid, string ProjectBuildConfigGuid, FileReference ProjectFile) { Content.Append("/* Begin PBXProject section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectGuid + " /* Project object */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXProject;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tattributes = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tLastUpgradeCheck = 2000;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tORGANIZATIONNAME = \"Epic Games, Inc.\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tTargetAttributes = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\t" + TargetGuid + " = {" + ProjectFileGenerator.NewLine); bool bAutomaticSigning = false; if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.IOS, EProjectType.Code)) { IOSPlatform IOSPlatform = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)); IOSProjectSettings ProjectSettings = IOSPlatform.ReadProjectSettings(ProjectFile); bAutomaticSigning = ProjectSettings.bAutomaticSigning; } if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.TVOS, EProjectType.Code)) { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); bAutomaticSigning = ProjectSettings.bAutomaticSigning; } if (bAutomaticSigning) { Content.Append("\t\t\t\t\t\tProvisioningStyle = Automatic;" + ProjectFileGenerator.NewLine); } else { Content.Append("\t\t\t\t\t\tProvisioningStyle = Manual;" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildConfigurationList = " + ProjectBuildConfigGuid + " /* Build configuration list for PBXProject \"" + TargetName + "\" */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tcompatibilityVersion = \"Xcode 8.0\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdevelopmentRegion = English;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\thasScannedForEncodings = 0;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tknownRegions = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\ten" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tmainGroup = " + MainGroupGuid + ";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproductRefGroup = " + ProductRefGroupGuid + ";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tprojectDirPath = \"\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tprojectRoot = \"\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\ttargets = (" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + TargetGuid + " /* " + TargetName + " */," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + BuildTargetGuid + " /* " + BuildTargetName + " */," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + IndexTargetGuid + " /* " + IndexTargetName + " */," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXProject section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendContainerItemProxySection(StringBuilder Content, string TargetName, string TargetGuid, string TargetProxyGuid, string ProjectGuid) { Content.Append("/* Begin PBXContainerItemProxy section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetProxyGuid + " /* PBXContainerItemProxy */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXContainerItemProxy;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tcontainerPortal = " + ProjectGuid + " /* Project object */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tproxyType = 1;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tremoteGlobalIDString = " + TargetGuid + ";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tremoteInfo = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXContainerItemProxy section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendTargetDependencySection(StringBuilder Content, string TargetName, string TargetGuid, string TargetDependencyGuid, string TargetProxyGuid) { Content.Append("/* Begin PBXTargetDependency section */" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetDependencyGuid + " /* PBXTargetDependency */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = PBXTargetDependency;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\ttarget = " + TargetGuid + " /* " + TargetName + " */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\ttargetProxy = " + TargetProxyGuid + " /* PBXContainerItemProxy */;" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); Content.Append("/* End PBXTargetDependency section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendProjectBuildConfiguration(StringBuilder Content, string ConfigName, string ConfigGuid) { Content.Append("\t\t" + ConfigGuid + " /* \"" + ConfigName + "\" */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (" + ProjectFileGenerator.NewLine); foreach (string Definition in IntelliSensePreprocessorDefinitions) { Content.Append("\t\t\t\t\t\"" + Definition.Replace("\"", "").Replace("\\", "") + "\"," + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t\t\"__INTELLISENSE__\"," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\t\"MONOLITHIC_BUILD=1\"," + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tHEADER_SEARCH_PATHS = (" + ProjectFileGenerator.NewLine); foreach (string SearchPath in IntelliSenseSystemIncludeSearchPaths) { string Path = SearchPath.Contains(" ") ? "\\\"" + SearchPath + "\\\"" : SearchPath; Content.Append("\t\t\t\t\t\"" + Path + "\"," + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tUSER_HEADER_SEARCH_PATHS = (" + ProjectFileGenerator.NewLine); foreach (string SearchPath in IntelliSenseIncludeSearchPaths) { string Path = SearchPath.Contains(" ") ? "\\\"" + SearchPath + "\\\"" : SearchPath; Content.Append("\t\t\t\t\t\"" + Path + "\"," + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t);" + ProjectFileGenerator.NewLine); if (ConfigName == "Debug") { Content.Append("\t\t\t\tONLY_ACTIVE_ARCH = YES;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tENABLE_TESTABILITY = YES;" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"c++14\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tGCC_ENABLE_CPP_RTTI = NO;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tGCC_WARN_CHECK_SWITCH_STATEMENTS = NO;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tUSE_HEADERMAP = NO;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + ConfigName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); } private void AppendNativeTargetBuildConfiguration(StringBuilder Content, XcodeBuildConfig Config, string ConfigGuid, bool bIsAGame, FileReference ProjectFile) { bool bMacOnly = true; if (Config.ProjectTarget.TargetRules != null && XcodeProjectFileGenerator.ProjectFilePlatform.HasFlag(XcodeProjectFileGenerator.XcodeProjectFilePlatform.iOS)) { if (Config.ProjectTarget.SupportedPlatforms.Contains(UnrealTargetPlatform.IOS)) { bMacOnly = false; } } Content.Append("\t\t" + ConfigGuid + " /* \"" + Config.DisplayName + "\" */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine); string UE4Dir = ConvertPath(Path.GetFullPath(Directory.GetCurrentDirectory() + "../../..")); string MacExecutableDir = ConvertPath(Config.MacExecutablePath.Directory.FullName); string MacExecutableFileName = Config.MacExecutablePath.GetFileName(); if (bMacOnly) { Content.Append("\t\t\t\tVALID_ARCHS = \"x86_64\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tSUPPORTED_PLATFORMS = \"macosx\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tPRODUCT_NAME = \"" + MacExecutableFileName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + MacExecutableDir + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;" + ProjectFileGenerator.NewLine); } else { string IOSRunTimeVersion = null; string IOSRunTimeDevices = null; string TVOSRunTimeVersion = null; string TVOSRunTimeDevices = null; string ValidArchs = "x86_64"; string SupportedPlatforms = "macosx"; bool bAutomaticSigning = false; string UUID_IOS = ""; string UUID_TVOS = ""; string TEAM_IOS = ""; string TEAM_TVOS = ""; string IOS_CERT = "iPhone Developer"; string TVOS_CERT = "iPhone Developer"; if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.IOS, EProjectType.Code)) { IOSPlatform IOSPlatform = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)); IOSProjectSettings ProjectSettings = IOSPlatform.ReadProjectSettings(ProjectFile); IOSProvisioningData ProvisioningData = IOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); IOSRunTimeVersion = ProjectSettings.RuntimeVersion; IOSRunTimeDevices = ProjectSettings.RuntimeDevices; ValidArchs += " arm64 armv7 armv7s"; SupportedPlatforms += " iphoneos"; bAutomaticSigning = ProjectSettings.bAutomaticSigning; if (!bAutomaticSigning) { UUID_IOS = ProvisioningData.MobileProvisionUUID; IOS_CERT = ProvisioningData.SigningCertificate; } TEAM_IOS = ProvisioningData.TeamUUID; } if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.TVOS, EProjectType.Code)) { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings, bForDistribution); TVOSRunTimeVersion = ProjectSettings.RuntimeVersion; TVOSRunTimeDevices = ProjectSettings.RuntimeDevices; if (ValidArchs == "x86_64") { ValidArchs += " arm64 armv7 armv7s"; } SupportedPlatforms += " appletvos"; if (!bAutomaticSigning) { UUID_TVOS = ProvisioningData.MobileProvisionUUID; TVOS_CERT = ProvisioningData.SigningCertificate; } TEAM_TVOS = ProvisioningData.TeamUUID; } Content.Append("\t\t\t\tVALID_ARCHS = \"" + ValidArchs + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tSUPPORTED_PLATFORMS = \"" + SupportedPlatforms + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"PRODUCT_NAME[sdk=macosx*]\" = \"" + MacExecutableFileName + "\";" + ProjectFileGenerator.NewLine); if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = " + IOSRunTimeVersion + ";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"PRODUCT_NAME[sdk=iphoneos*]\" = \"" + Config.BuildTarget + "\";" + ProjectFileGenerator.NewLine); // @todo: change to Path.GetFileName(Config.IOSExecutablePath) when we stop using payload Content.Append("\t\t\t\t\"TARGETED_DEVICE_FAMILY[sdk=iphoneos*]\" = \"" + IOSRunTimeDevices + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"SDKROOT[sdk=iphoneos]\" = iphoneos;" + ProjectFileGenerator.NewLine); if (!string.IsNullOrEmpty(TEAM_IOS)) { Content.Append("\t\t\t\t\"DEVELOPMENT_TEAM[sdk=iphoneos*]\" = " + TEAM_IOS + ";" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"" + IOS_CERT + "\";" + ProjectFileGenerator.NewLine); if (!bAutomaticSigning && !string.IsNullOrEmpty(UUID_IOS)) { Content.Append("\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]\" = \"" + UUID_IOS + "\";" + ProjectFileGenerator.NewLine); } } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\tTVOS_DEPLOYMENT_TARGET = " + TVOSRunTimeVersion + ";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"PRODUCT_NAME[sdk=appletvos*]\" = \"" + Config.BuildTarget + "\";" + ProjectFileGenerator.NewLine); // @todo: change to Path.GetFileName(Config.TVOSExecutablePath) when we stop using payload Content.Append("\t\t\t\t\"TARGETED_DEVICE_FAMILY[sdk=appletvos*]\" = \"" + TVOSRunTimeDevices + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"SDKROOT[sdk=appletvos]\" = appletvos;" + ProjectFileGenerator.NewLine); if (!string.IsNullOrEmpty(TEAM_TVOS)) { Content.Append("\t\t\t\t\"DEVELOPMENT_TEAM[sdk=appletvos*]\" = " + TEAM_TVOS + ";" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=appletvos*]\" = \"" + TVOS_CERT + "\";" + ProjectFileGenerator.NewLine); if (!bAutomaticSigning && !string.IsNullOrEmpty(UUID_TVOS)) { Content.Append("\t\t\t\t\"PROVISIONING_PROFILE_SPECIFIER[sdk=appletvos*]\" = \"" + UUID_TVOS + "\";" + ProjectFileGenerator.NewLine); } } Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=macosx*]\" = \"" + MacExecutableDir + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"SDKROOT[sdk=macosx]\" = macosx;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tINFOPLIST_OUTPUT_FORMAT = xml;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;" + ProjectFileGenerator.NewLine); bool bIsUE4Game = Config.BuildTarget.Equals("UE4Game", StringComparison.InvariantCultureIgnoreCase); bool bIsUE4Client = Config.BuildTarget.Equals("UE4Client", StringComparison.InvariantCultureIgnoreCase); DirectoryReference GameDir = ProjectFile != null ? ProjectFile.Directory : null; string GamePath = GameDir != null ? ConvertPath(GameDir.FullName) : null; string IOSInfoPlistPath = null; string TVOSInfoPlistPath = null; string MacInfoPlistPath = null; string IOSEntitlementPath = null; string TVOSEntitlementPath = null; if (bIsUE4Game) { IOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/IOS/" + Config.BuildTarget + "-Info.plist"; TVOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/TVOS/" + Config.BuildTarget + "-Info.plist"; MacInfoPlistPath = UE4Dir + "/Engine/Intermediate/Mac/" + MacExecutableFileName + "-Info.plist"; IOSEntitlementPath = ""; TVOSEntitlementPath = ""; if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=iphoneos*]\" = \"" + UE4Dir + "/Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=appletvos*]\" = \"" + UE4Dir + "/Engine/Binaries/TVOS/Payload\";" + ProjectFileGenerator.NewLine); } } else if (bIsUE4Client) { IOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/IOS/UE4Game-Info.plist"; TVOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/TVOS/UE4Game-Info.plist"; MacInfoPlistPath = UE4Dir + "/Engine/Intermediate/Mac/" + MacExecutableFileName + "-Info.plist"; IOSEntitlementPath = ""; TVOSEntitlementPath = ""; if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=iphoneos*]\" = \"" + UE4Dir + "/Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=appletvos*]\" = \"" + UE4Dir + "/Engine/Binaries/TVOS/Payload\";" + ProjectFileGenerator.NewLine); } } else if (bIsAGame) { IOSInfoPlistPath = GamePath + "/Intermediate/IOS/" + Config.BuildTarget + "-Info.plist"; TVOSInfoPlistPath = GamePath + "/Intermediate/TVOS/" + Config.BuildTarget + "-Info.plist"; MacInfoPlistPath = GamePath + "/Intermediate/Mac/" + MacExecutableFileName + "-Info.plist"; IOSEntitlementPath = GamePath + "/Intermediate/IOS/" + Config.BuildTarget + ".entitlements"; TVOSEntitlementPath = GamePath + "/Intermediate/TVOS/" + Config.BuildTarget + ".entitlements"; if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=iphoneos*]\" = \"" + GamePath + "/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=appletvos*]\" = \"" + GamePath + "/Binaries/TVOS/Payload\";" + ProjectFileGenerator.NewLine); } } else { if (GamePath == null) { IOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/IOS/" + Config.BuildTarget + "-Info.plist"; TVOSInfoPlistPath = UE4Dir + "/Engine/Intermediate/TVOS/" + Config.BuildTarget + "-Info.plist"; MacInfoPlistPath = UE4Dir + "/Engine/Intermediate/Mac/" + MacExecutableFileName + "-Info.plist"; } else { IOSInfoPlistPath = GamePath + "/Intermediate/IOS/" + Config.BuildTarget + "-Info.plist"; TVOSInfoPlistPath = GamePath + "/Intermediate/TVOS/" + Config.BuildTarget + "-Info.plist"; MacInfoPlistPath = GamePath + "/Intermediate/Mac/" + MacExecutableFileName + "-Info.plist"; } if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=iphoneos*]\" = \"" + UE4Dir + "/Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"CONFIGURATION_BUILD_DIR[sdk=appletvos*]\" = \"" + UE4Dir + "/Engine/Binaries/TVOS/Payload\";" + ProjectFileGenerator.NewLine); } } if (XcodeProjectFileGenerator.bGeneratingRunIOSProject) { Content.Append("\t\t\t\tINFOPLIST_FILE = \"" + IOSInfoPlistPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCODE_SIGN_ENTITLEMENTS = \"" + IOSEntitlementPath + "\";" + ProjectFileGenerator.NewLine); } else if (XcodeProjectFileGenerator.bGeneratingRunTVOSProject) { Content.Append("\t\t\t\tINFOPLIST_FILE = \"" + TVOSInfoPlistPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tCODE_SIGN_ENTITLEMENTS = \"" + TVOSEntitlementPath + "\";" + ProjectFileGenerator.NewLine); } else { Content.Append("\t\t\t\t\"INFOPLIST_FILE[sdk=macosx*]\" = \"" + MacInfoPlistPath + "\";" + ProjectFileGenerator.NewLine); if (IOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"INFOPLIST_FILE[sdk=iphoneos*]\" = \"" + IOSInfoPlistPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]\" = \"" + IOSEntitlementPath + "\";" + ProjectFileGenerator.NewLine); } if (TVOSRunTimeVersion != null) { Content.Append("\t\t\t\t\"INFOPLIST_FILE[sdk=appletvos*]\" = \"" + TVOSInfoPlistPath + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\t\"CODE_SIGN_ENTITLEMENTS[sdk=appletvos*]\" = \"" + TVOSEntitlementPath + "\";" + ProjectFileGenerator.NewLine); } } // Prepare a temp Info.plist file so Xcode has some basic info about the target immediately after opening the project. // This is needed for the target to pass the settings validation before code signing. UBT will overwrite this plist file later, with proper contents. if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { bool bCreateMacInfoPlist = !File.Exists(MacInfoPlistPath); bool bCreateIOSInfoPlist = !File.Exists(IOSInfoPlistPath) && IOSRunTimeVersion != null; bool bCreateTVOSInfoPlist = !File.Exists(TVOSInfoPlistPath) && TVOSRunTimeVersion != null; if (bCreateMacInfoPlist || bCreateIOSInfoPlist || bCreateTVOSInfoPlist) { DirectoryReference ProjectPath = GameDir; DirectoryReference EngineDir = DirectoryReference.Combine(new DirectoryReference(UE4Dir), "Engine"); string GameName = Config.BuildTarget; if (ProjectPath == null) { ProjectPath = EngineDir; } if (bIsUE4Game) { ProjectPath = EngineDir; GameName = "UE4Game"; } if (bCreateMacInfoPlist) { Directory.CreateDirectory(Path.GetDirectoryName(MacInfoPlistPath)); UEDeployMac.GeneratePList(ProjectPath.FullName, bIsUE4Game, GameName, Config.BuildTarget, EngineDir.FullName, MacExecutableFileName); } if (bCreateIOSInfoPlist) { // get the receipt FileReference ReceiptFilename; if (bIsUE4Game) { ReceiptFilename = TargetReceipt.GetDefaultPath(UnrealBuildTool.EngineDirectory, "UE4Game", UnrealTargetPlatform.IOS, Config.BuildConfig, ""); } else { ReceiptFilename = TargetReceipt.GetDefaultPath(ProjectPath, GameName, UnrealTargetPlatform.IOS, Config.BuildConfig, ""); } Directory.CreateDirectory(Path.GetDirectoryName(IOSInfoPlistPath)); bool bSupportPortrait, bSupportLandscape, bSkipIcons; TargetReceipt Receipt; TargetReceipt.TryRead(ReceiptFilename, out Receipt); VersionNumber SdkVersion = UEDeployIOS.GetSdkVersion(Receipt); UEDeployIOS.GenerateIOSPList(ProjectFile, Config.BuildConfig, ProjectPath.FullName, bIsUE4Game, GameName, Config.BuildTarget, EngineDir.FullName, ProjectPath + "/Binaries/IOS/Payload", SdkVersion, null, BundleIdentifier, out bSupportPortrait, out bSupportLandscape, out bSkipIcons); } if (bCreateTVOSInfoPlist) { Directory.CreateDirectory(Path.GetDirectoryName(TVOSInfoPlistPath)); UEDeployTVOS.GenerateTVOSPList(ProjectPath.FullName, bIsUE4Game, GameName, Config.BuildTarget, EngineDir.FullName, ProjectPath + "/Binaries/TVOS/Payload", null, BundleIdentifier); } } } } Content.Append("\t\t\t\tMACOSX_DEPLOYMENT_TARGET = " + MacToolChain.Settings.MacOSVersion + ";" + ProjectFileGenerator.NewLine); //#jira UE-50382 Xcode Address Sanitizer feature does not work on iOS // address sanitizer dylib loader depends on the SDKROOT parameter. For macosx or default (missing, translated as macosx), the path is incorrect for iphone/appletv if (XcodeProjectFileGenerator.bGeneratingRunIOSProject) { Content.Append("\t\t\t\tSDKROOT = iphoneos;" + ProjectFileGenerator.NewLine); } else if (XcodeProjectFileGenerator.bGeneratingRunTVOSProject) { Content.Append("\t\t\t\tSDKROOT = appletvos;" + ProjectFileGenerator.NewLine); } else { Content.Append("\t\t\t\tSDKROOT = macosx;" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tGCC_PREFIX_HEADER = \"" + UE4Dir + "/Engine/Source/Editor/UnrealEd/Public/UnrealEd.h\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + Config.DisplayName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); } private void AppendLegacyTargetBuildConfiguration(StringBuilder Content, XcodeBuildConfig Config, string ConfigGuid) { bool bMacOnly = true; if (Config.ProjectTarget.TargetRules != null && XcodeProjectFileGenerator.ProjectFilePlatform.HasFlag(XcodeProjectFileGenerator.XcodeProjectFilePlatform.iOS)) { if (Config.ProjectTarget.SupportedPlatforms.Contains(UnrealTargetPlatform.IOS)) { bMacOnly = false; } } Content.Append("\t\t" + ConfigGuid + " /* \"" + Config.DisplayName + "\" */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine); if (bMacOnly) { Content.Append("\t\t\t\tVALID_ARCHS = \"x86_64\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tSUPPORTED_PLATFORMS = \"macosx\";" + ProjectFileGenerator.NewLine); } else { string ValidArchs = "x86_64"; string SupportedPlatforms = "macosx"; if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.IOS, EProjectType.Code)) { ValidArchs += " arm64 armv7 armv7s"; SupportedPlatforms += " iphoneos"; } if (InstalledPlatformInfo.IsValidPlatform(UnrealTargetPlatform.TVOS, EProjectType.Code)) { if (ValidArchs == "x86_64") { ValidArchs += " arm64 armv7 armv7s"; } SupportedPlatforms += " appletvos"; } Content.Append("\t\t\t\tVALID_ARCHS = \"" + ValidArchs + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tSUPPORTED_PLATFORMS = \"" + SupportedPlatforms + "\";" + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t\tUE_BUILD_TARGET_NAME = \"" + Config.BuildTarget + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t\tUE_BUILD_TARGET_CONFIG = \"" + Config.BuildConfig + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t};" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tname = \"" + Config.DisplayName + "\";" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); } private void AppendXCBuildConfigurationSection(StringBuilder Content, Dictionary ProjectBuildConfigs, Dictionary TargetBuildConfigs, Dictionary BuildTargetBuildConfigs, Dictionary IndexTargetBuildConfigs, bool bIsAGame, FileReference GameProjectPath) { Content.Append("/* Begin XCBuildConfiguration section */" + ProjectFileGenerator.NewLine); foreach (KeyValuePair Config in ProjectBuildConfigs) { AppendProjectBuildConfiguration(Content, Config.Value.DisplayName, Config.Key); } foreach (KeyValuePair Config in TargetBuildConfigs) { AppendNativeTargetBuildConfiguration(Content, Config.Value, Config.Key, bIsAGame, GameProjectPath); } foreach (KeyValuePair Config in BuildTargetBuildConfigs) { AppendLegacyTargetBuildConfiguration(Content, Config.Value, Config.Key); } foreach (KeyValuePair Config in IndexTargetBuildConfigs) { AppendNativeTargetBuildConfiguration(Content, Config.Value, Config.Key, bIsAGame, GameProjectPath); } Content.Append("/* End XCBuildConfiguration section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private void AppendXCConfigurationList(StringBuilder Content, string TypeName, string TargetName, string ConfigListGuid, Dictionary BuildConfigs) { Content.Append("\t\t" + ConfigListGuid + " /* Build configuration list for " + TypeName + " \"" + TargetName + "\" */ = {" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tisa = XCConfigurationList;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tbuildConfigurations = (" + ProjectFileGenerator.NewLine); foreach (KeyValuePair Config in BuildConfigs) { Content.Append("\t\t\t\t" + Config.Key + " /* \"" + Config.Value.DisplayName + "\" */," + ProjectFileGenerator.NewLine); } Content.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdefaultConfigurationIsVisible = 0;" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tdefaultConfigurationName = Development;" + ProjectFileGenerator.NewLine); Content.Append("\t\t};" + ProjectFileGenerator.NewLine); } private void AppendXCConfigurationListSection(StringBuilder Content, string TargetName, string BuildTargetName, string IndexTargetName, string ProjectConfigListGuid, Dictionary ProjectBuildConfigs, string TargetConfigListGuid, Dictionary TargetBuildConfigs, string BuildTargetConfigListGuid, Dictionary BuildTargetBuildConfigs, string IndexTargetConfigListGuid, Dictionary IndexTargetBuildConfigs) { Content.Append("/* Begin XCConfigurationList section */" + ProjectFileGenerator.NewLine); AppendXCConfigurationList(Content, "PBXProject", TargetName, ProjectConfigListGuid, ProjectBuildConfigs); AppendXCConfigurationList(Content, "PBXLegacyTarget", BuildTargetName, BuildTargetConfigListGuid, BuildTargetBuildConfigs); AppendXCConfigurationList(Content, "PBXNativeTarget", TargetName, TargetConfigListGuid, TargetBuildConfigs); AppendXCConfigurationList(Content, "PBXNativeTarget", IndexTargetName, IndexTargetConfigListGuid, IndexTargetBuildConfigs); Content.Append("/* End XCConfigurationList section */" + ProjectFileGenerator.NewLine); } public struct XcodeBuildConfig { public XcodeBuildConfig(string InDisplayName, string InBuildTarget, FileReference InMacExecutablePath, FileReference InIOSExecutablePath, FileReference InTVOSExecutablePath, ProjectTarget InProjectTarget, UnrealTargetConfiguration InBuildConfig) { DisplayName = InDisplayName; MacExecutablePath = InMacExecutablePath; IOSExecutablePath = InIOSExecutablePath; TVOSExecutablePath = InTVOSExecutablePath; BuildTarget = InBuildTarget; ProjectTarget = InProjectTarget; BuildConfig = InBuildConfig; } public string DisplayName; public FileReference MacExecutablePath; public FileReference IOSExecutablePath; public FileReference TVOSExecutablePath; public string BuildTarget; public ProjectTarget ProjectTarget; public UnrealTargetConfiguration BuildConfig; }; private List GetSupportedBuildConfigs(List Platforms, List Configurations, PlatformProjectGeneratorCollection PlatformProjectGenerators) { List BuildConfigs = new List(); string ProjectName = ProjectFilePath.GetFileNameWithoutExtension(); foreach (UnrealTargetConfiguration Configuration in Configurations) { if (InstalledPlatformInfo.IsValidConfiguration(Configuration, EProjectType.Code)) { foreach (UnrealTargetPlatform Platform in Platforms) { if (InstalledPlatformInfo.IsValidPlatform(Platform, EProjectType.Code) && (Platform == UnrealTargetPlatform.Mac || Platform == UnrealTargetPlatform.IOS || Platform == UnrealTargetPlatform.TVOS)) // @todo support other platforms { UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform, true); if ((BuildPlatform != null) && (BuildPlatform.HasRequiredSDKsInstalled() == SDKStatus.Valid)) { // Now go through all of the target types for this project if (ProjectTargets.Count == 0) { throw new BuildException("Expecting at least one ProjectTarget to be associated with project '{0}' in the TargetProjects list ", ProjectFilePath); } foreach (ProjectTarget ProjectTarget in ProjectTargets) { if (MSBuildProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, Platform, Configuration, PlatformProjectGenerators)) { // Figure out if this is a monolithic build bool bShouldCompileMonolithic = BuildPlatform.ShouldCompileMonolithicBinary(Platform); bShouldCompileMonolithic |= (ProjectTarget.CreateRulesDelegate(Platform, Configuration).LinkType == TargetLinkType.Monolithic); string ConfigName = Configuration.ToString(); if (ProjectTarget.TargetRules.Type != TargetType.Game && ProjectTarget.TargetRules.Type != TargetType.Program) { ConfigName += " " + ProjectTarget.TargetRules.Type.ToString(); } if (BuildConfigs.Where(Config => Config.DisplayName == ConfigName).ToList().Count == 0) { string TargetName = ProjectTarget.TargetFilePath.GetFileNameWithoutAnyExtensions(); // Get the output directory DirectoryReference RootDirectory = UnrealBuildTool.EngineDirectory; if ((ProjectTarget.TargetRules.Type == TargetType.Game || ProjectTarget.TargetRules.Type == TargetType.Client || ProjectTarget.TargetRules.Type == TargetType.Server) && bShouldCompileMonolithic) { if(ProjectTarget.UnrealProjectFilePath != null) { RootDirectory = ProjectTarget.UnrealProjectFilePath.Directory; } } if(ProjectTarget.TargetRules.Type == TargetType.Program && ProjectTarget.UnrealProjectFilePath != null) { RootDirectory = ProjectTarget.UnrealProjectFilePath.Directory; } // Get the output directory DirectoryReference OutputDirectory = DirectoryReference.Combine(RootDirectory, "Binaries"); string ExeName = TargetName; if (!bShouldCompileMonolithic && ProjectTarget.TargetRules.Type != TargetType.Program) { // Figure out what the compiled binary will be called so that we can point the IDE to the correct file if (ProjectTarget.TargetRules.Type != TargetType.Game) { ExeName = "UE4" + ProjectTarget.TargetRules.Type.ToString(); } } if (BuildPlatform.Platform == UnrealTargetPlatform.Mac) { string MacExecutableName = MakeExecutableFileName(ExeName, UnrealTargetPlatform.Mac, Configuration, ProjectTarget.TargetRules.Architecture, ProjectTarget.TargetRules.UndecoratedConfiguration); string IOSExecutableName = MacExecutableName.Replace("-Mac-", "-IOS-"); string TVOSExecutableName = MacExecutableName.Replace("-Mac-", "-TVOS-"); BuildConfigs.Add(new XcodeBuildConfig(ConfigName, TargetName, FileReference.Combine(OutputDirectory, "Mac", MacExecutableName), FileReference.Combine(OutputDirectory, "IOS", IOSExecutableName), FileReference.Combine(OutputDirectory, "TVOS", TVOSExecutableName), ProjectTarget, Configuration)); } else if (BuildPlatform.Platform == UnrealTargetPlatform.IOS || BuildPlatform.Platform == UnrealTargetPlatform.TVOS) { string IOSExecutableName = MakeExecutableFileName(ExeName, UnrealTargetPlatform.IOS, Configuration, ProjectTarget.TargetRules.Architecture, ProjectTarget.TargetRules.UndecoratedConfiguration); string TVOSExecutableName = IOSExecutableName.Replace("-IOS-", "-TVOS-"); string MacExecutableName = IOSExecutableName.Replace("-IOS-", "-Mac-"); BuildConfigs.Add(new XcodeBuildConfig(ConfigName, TargetName, FileReference.Combine(OutputDirectory, "Mac", IOSExecutableName), FileReference.Combine(OutputDirectory, "IOS", IOSExecutableName), FileReference.Combine(OutputDirectory, "TVOS", TVOSExecutableName), ProjectTarget, Configuration)); } } } } } } } } } return BuildConfigs; } private static string MakeExecutableFileName(string BinaryName, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string Architecture, UnrealTargetConfiguration UndecoratedConfiguration) { StringBuilder Result = new StringBuilder(); Result.Append(BinaryName); if (Configuration != UndecoratedConfiguration) { Result.AppendFormat("-{0}-{1}", Platform.ToString(), Configuration.ToString()); } UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform); if(BuildPlatform.RequiresArchitectureSuffix()) { Result.Append(Architecture); } return Result.ToString(); } private void WriteSchemeFile(string TargetName, string TargetGuid, string BuildTargetGuid, string IndexTargetGuid, bool bHasEditorConfiguration, string GameProjectPath) { DirectoryReference SchemesDir = new DirectoryReference(ProjectFilePath.FullName + "/xcshareddata/xcschemes"); if (!DirectoryReference.Exists(SchemesDir)) { DirectoryReference.CreateDirectory(SchemesDir); } string SchemeFilePath = SchemesDir + "/" + TargetName + ".xcscheme"; string OldCommandLineArguments = null; if (File.Exists(SchemeFilePath)) { string OldContents = File.ReadAllText(SchemeFilePath); int OldCommandLineArgumentsStart = OldContents.IndexOf("") + "".Length; int OldCommandLineArgumentsEnd = OldContents.IndexOf(""); if (OldCommandLineArgumentsStart != -1 && OldCommandLineArgumentsEnd != -1) { OldCommandLineArguments = OldContents.Substring(OldCommandLineArgumentsStart, OldCommandLineArgumentsEnd - OldCommandLineArgumentsStart); } } string DefaultConfiguration = bHasEditorConfiguration && !XcodeProjectFileGenerator.bGeneratingRunIOSProject && !XcodeProjectFileGenerator.bGeneratingRunTVOSProject ? "Development Editor" : "Development"; StringBuilder Content = new StringBuilder(); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); if (string.IsNullOrEmpty(OldCommandLineArguments)) { if (bHasEditorConfiguration && TargetName != "UE4") { Content.Append(" " + ProjectFileGenerator.NewLine); if (IsForeignProject) { Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); } else { Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); } // Always add a configuration argument Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); } } else { Content.Append(" " + OldCommandLineArguments + ""); } Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append(" " + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); File.WriteAllText(SchemeFilePath, Content.ToString(), new UTF8Encoding()); Content.Clear(); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("\tSchemeUserState" + ProjectFileGenerator.NewLine); Content.Append("\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetName + ".xcscheme_^#shared#^_" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t\torderHint" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t1" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t" + ProjectFileGenerator.NewLine); Content.Append("\tSuppressBuildableAutocreation" + ProjectFileGenerator.NewLine); Content.Append("\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + TargetGuid + "" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tprimary" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + BuildTargetGuid + "" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tprimary" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + IndexTargetGuid + "" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t\tprimary" + ProjectFileGenerator.NewLine); Content.Append("\t\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t\t" + ProjectFileGenerator.NewLine); Content.Append("\t" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); Content.Append("" + ProjectFileGenerator.NewLine); DirectoryReference ManagementFileDir = new DirectoryReference(ProjectFilePath.FullName + "/xcuserdata/" + Environment.UserName + ".xcuserdatad/xcschemes"); if (!DirectoryReference.Exists(ManagementFileDir)) { DirectoryReference.CreateDirectory(ManagementFileDir); } string ManagementFilePath = ManagementFileDir + "/xcschememanagement.plist"; File.WriteAllText(ManagementFilePath, Content.ToString(), new UTF8Encoding()); } /// Implements Project interface public override bool WriteProjectFile(List InPlatforms, List InConfigurations, PlatformProjectGeneratorCollection PlatformProjectGenerators) { bool bSuccess = true; string TargetName = ProjectFilePath.GetFileNameWithoutExtension(); string TargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string TargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string TargetDependencyGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string TargetProxyGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string TargetAppGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string BuildTargetName = TargetName + "_Build"; string BuildTargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string BuildTargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string IndexTargetName = TargetName + "_Index"; string IndexTargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string IndexTargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string ProjectGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string ProjectConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string MainGroupGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string ProductRefGroupGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); string SourcesBuildPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); // Figure out all the desired configurations List BuildConfigs = GetSupportedBuildConfigs(InPlatforms, InConfigurations, PlatformProjectGenerators); if (BuildConfigs.Count == 0) { return true; } bool bIsAGame = false; FileReference GameProjectPath = null; foreach(ProjectTarget Target in ProjectTargets) { if(Target.UnrealProjectFilePath != null) { bIsAGame = true; GameProjectPath = Target.UnrealProjectFilePath; break; } } bool bHasEditorConfiguration = false; Dictionary ProjectBuildConfigs = new Dictionary(); Dictionary TargetBuildConfigs = new Dictionary(); Dictionary BuildTargetBuildConfigs = new Dictionary(); Dictionary IndexTargetBuildConfigs = new Dictionary(); foreach (XcodeBuildConfig Config in BuildConfigs) { ProjectBuildConfigs[XcodeProjectFileGenerator.MakeXcodeGuid()] = Config; TargetBuildConfigs[XcodeProjectFileGenerator.MakeXcodeGuid()] = Config; BuildTargetBuildConfigs[XcodeProjectFileGenerator.MakeXcodeGuid()] = Config; IndexTargetBuildConfigs[XcodeProjectFileGenerator.MakeXcodeGuid()] = Config; if (Config.ProjectTarget.TargetRules.Type == TargetType.Editor) { bHasEditorConfiguration = true; } } StringBuilder PBXBuildFileSection = new StringBuilder(); StringBuilder PBXFileReferenceSection = new StringBuilder(); StringBuilder PBXSourcesBuildPhaseSection = new StringBuilder(); GenerateSectionsWithSourceFiles(PBXBuildFileSection, PBXFileReferenceSection, PBXSourcesBuildPhaseSection, TargetAppGuid, TargetName); StringBuilder ProjectFileContent = new StringBuilder(); ProjectFileContent.Append("// !$*UTF8*$!" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("{" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\tarchiveVersion = 1;" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\tclasses = {" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\t};" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\tobjectVersion = 46;" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\tobjects = {" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); AppendBuildFileSection(ProjectFileContent, PBXBuildFileSection); AppendFileReferenceSection(ProjectFileContent, PBXFileReferenceSection); AppendSourcesBuildPhaseSection(ProjectFileContent, PBXSourcesBuildPhaseSection, SourcesBuildPhaseGuid); AppendContainerItemProxySection(ProjectFileContent, BuildTargetName, BuildTargetGuid, TargetProxyGuid, ProjectGuid); if (!XcodeProjectFileGenerator.bGeneratingRunIOSProject) { AppendTargetDependencySection(ProjectFileContent, BuildTargetName, BuildTargetGuid, TargetDependencyGuid, TargetProxyGuid); } AppendGroupSection(ProjectFileContent, MainGroupGuid, ProductRefGroupGuid, TargetAppGuid, TargetName); AppendLegacyTargetSection(ProjectFileContent, BuildTargetName, BuildTargetGuid, BuildTargetConfigListGuid, GameProjectPath, bHasEditorConfiguration); AppendRunTargetSection(ProjectFileContent, TargetName, TargetGuid, TargetConfigListGuid, TargetDependencyGuid, TargetAppGuid); AppendIndexTargetSection(ProjectFileContent, IndexTargetName, IndexTargetGuid, IndexTargetConfigListGuid, SourcesBuildPhaseGuid); AppendProjectSection(ProjectFileContent, TargetName, TargetGuid, BuildTargetName, BuildTargetGuid, IndexTargetName, IndexTargetGuid, MainGroupGuid, ProductRefGroupGuid, ProjectGuid, ProjectConfigListGuid, GameProjectPath); AppendXCBuildConfigurationSection(ProjectFileContent, ProjectBuildConfigs, TargetBuildConfigs, BuildTargetBuildConfigs, IndexTargetBuildConfigs, bIsAGame, GameProjectPath); AppendXCConfigurationListSection(ProjectFileContent, TargetName, BuildTargetName, IndexTargetName, ProjectConfigListGuid, ProjectBuildConfigs, TargetConfigListGuid, TargetBuildConfigs, BuildTargetConfigListGuid, BuildTargetBuildConfigs, IndexTargetConfigListGuid, IndexTargetBuildConfigs); ProjectFileContent.Append("\t};" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("\trootObject = " + ProjectGuid + " /* Project object */;" + ProjectFileGenerator.NewLine); ProjectFileContent.Append("}" + ProjectFileGenerator.NewLine); if (bSuccess) { FileReference PBXProjFilePath = ProjectFilePath + "/project.pbxproj"; bSuccess = ProjectFileGenerator.WriteFileIfChanged(PBXProjFilePath.FullName, ProjectFileContent.ToString(), new UTF8Encoding()); } if (bSuccess) { WriteSchemeFile(TargetName, TargetGuid, BuildTargetGuid, IndexTargetGuid, bHasEditorConfiguration, GameProjectPath != null ? GameProjectPath.FullName : ""); } return bSuccess; } } }