// Copyright 1998-2018 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 /// public XcodeProjectFile(FileReference InitFilePath, FileReference InOnlyGameProject) : base(InitFilePath) { OnlyGameProject = InOnlyGameProject; } public override string ToString() { return ProjectFilePath.GetFileNameWithoutExtension(); } /// /// 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 FindGroupByRelativePath(ref Dictionary Groups, string RelativePath) { string[] Parts = RelativePath.Split(Path.DirectorySeparatorChar); string CurrentPath = ""; Dictionary CurrentParent = Groups; foreach (string Part in Parts) { XcodeFileGroup CurrentGroup; if (CurrentPath != "") { CurrentPath += Path.DirectorySeparatorChar; } CurrentPath += Part; if (!CurrentParent.ContainsKey(CurrentPath)) { CurrentGroup = new XcodeFileGroup(Path.GetFileName(CurrentPath), CurrentPath); CurrentParent.Add(CurrentPath, CurrentGroup); } else { CurrentGroup = CurrentParent[CurrentPath]; } if (CurrentPath == RelativePath) { return CurrentGroup; } CurrentParent = 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) { if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac) { if (InPath[1] != ':') { throw new BuildException("Can only convert full paths ({0})", InPath); } string MacPath = string.Format("{0}/{1}/{2}/{3}", RemoteToolChain.UserDevRootMac, Environment.MachineName, InPath[0].ToString().ToUpper(), InPath.Substring(3)); // clean the path MacPath = MacPath.Replace("\\", "/"); return MacPath; } else { 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) { foreach (var 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); } var ProjectRelativeSourceFile = CurSourceFile.Reference.MakeRelativeTo(ProjectFilePath.Directory); string RelativeSourceDirectory = Path.GetDirectoryName(ProjectRelativeSourceFile); // Use the specified relative base folder if (CurSourceFile.BaseFolder != null) // NOTE: We are looking for null strings, not empty strings! { RelativeSourceDirectory = Path.GetDirectoryName(CurSourceFile.Reference.MakeRelativeTo(CurSourceFile.BaseFolder)); } XcodeFileGroup Group = FindGroupByRelativePath(ref Groups, RelativeSourceDirectory); 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, bool bFilesOnly) { if (!Group.bIsReference) { if (!bFilesOnly) { 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)); } if (!bFilesOnly) { 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, bFilesOnly: false); } } } } 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 void AppendGroupSection(StringBuilder Content, string MainGroupGuid, string ProductRefGroupGuid, string TargetAppGuid, string TargetName) { 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 Groups.Values) { if (!string.IsNullOrEmpty(Group.GroupName)) { Content.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", Group.GroupGuid, Group.GroupName, ProjectFileGenerator.NewLine)); } } if (Groups.ContainsKey("")) { AppendGroup(Groups[""], Content, bFilesOnly: true); } 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 Groups.Values) { if (Group.GroupName != "") { AppendGroup(Group, Content, bFilesOnly: false); } } // 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/Build.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 = 0900;" + 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 (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.IOS)) { IOSPlatform IOSPlatform = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)); IOSProjectSettings ProjectSettings = IOSPlatform.ReadProjectSettings(ProjectFile); bAutomaticSigning = ProjectSettings.bAutomaticSigning; } if (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.TVOS)) { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings); 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 (var Definition in IntelliSensePreprocessorDefinitions) { Content.Append("\t\t\t\t\t\"" + Definition.Replace("\"", "").Replace("\\", "") + "\"," + 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 (var 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 (var 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 (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.IOS)) { IOSPlatform IOSPlatform = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)); IOSProjectSettings ProjectSettings = IOSPlatform.ReadProjectSettings(ProjectFile); IOSProvisioningData ProvisioningData = IOSPlatform.ReadProvisioningData(ProjectSettings); 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 (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.TVOS)) { TVOSPlatform TVOSPlatform = ((TVOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.TVOS)); TVOSProjectSettings ProjectSettings = TVOSPlatform.ReadProjectSettings(ProjectFile); TVOSProvisioningData ProvisioningData = TVOSPlatform.ReadProvisioningData(ProjectSettings); 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; 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 = ""; 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 = ""; 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"; 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); } 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); } } // 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) { Directory.CreateDirectory(Path.GetDirectoryName(IOSInfoPlistPath)); bool bSupportPortrait, bSupportLandscape, bSkipIcons; UEDeployIOS.GenerateIOSPList(ProjectFile, Config.BuildConfig, ProjectPath.FullName, bIsUE4Game, GameName, Config.BuildTarget, EngineDir.FullName, ProjectPath + "/Binaries/IOS/Payload", 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"); } } } } 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 (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.IOS)) { ValidArchs += " arm64 armv7 armv7s"; SupportedPlatforms += " iphoneos"; } if (UnrealBuildTool.IsValidPlatform(UnrealTargetPlatform.TVOS)) { 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 (var Config in ProjectBuildConfigs) { AppendProjectBuildConfiguration(Content, Config.Value.DisplayName, Config.Key); } foreach (var Config in TargetBuildConfigs) { AppendNativeTargetBuildConfiguration(Content, Config.Value, Config.Key, bIsAGame, GameProjectPath); } foreach (var Config in BuildTargetBuildConfigs) { AppendLegacyTargetBuildConfiguration(Content, Config.Value, Config.Key); } foreach (var 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 (var 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) { var BuildConfigs = new List(); string ProjectName = ProjectFilePath.GetFileNameWithoutExtension(); foreach (var Configuration in Configurations) { if (UnrealBuildTool.IsValidConfiguration(Configuration)) { foreach (var Platform in Platforms) { if (UnrealBuildTool.IsValidPlatform(Platform) && (Platform == UnrealTargetPlatform.Mac || Platform == UnrealTargetPlatform.IOS)) // @todo support other platforms { var 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 (var ProjectTarget in ProjectTargets) { if (MSBuildProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, Platform, Configuration)) { // Figure out if this is a monolithic build bool bShouldCompileMonolithic = BuildPlatform.ShouldCompileMonolithicBinary(Platform); bShouldCompileMonolithic |= (ProjectTarget.CreateRulesDelegate(Platform, Configuration).LinkType == TargetLinkType.Monolithic); var 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 = UEBuildTarget.MakeBinaryFileName(ExeName, UnrealTargetPlatform.Mac, (ExeName == "UE4Editor" && Configuration == UnrealTargetConfiguration.DebugGame) ? UnrealTargetConfiguration.Development : Configuration, ProjectTarget.TargetRules.Architecture, ProjectTarget.TargetRules.UndecoratedConfiguration, UEBuildBinaryType.Executable, null); 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) { string IOSExecutableName = UEBuildTarget.MakeBinaryFileName(ExeName, UnrealTargetPlatform.IOS, (ExeName == "UE4Editor" && Configuration == UnrealTargetConfiguration.DebugGame) ? UnrealTargetConfiguration.Development : Configuration, ProjectTarget.TargetRules.Architecture, ProjectTarget.TargetRules.UndecoratedConfiguration, UEBuildBinaryType.Executable, null); 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 void WriteSchemeFile(string TargetName, string TargetGuid, string BuildTargetGuid, string IndexTargetGuid, bool bHasEditorConfiguration, string GameProjectPath) { string DefaultConfiguration = bHasEditorConfiguration && !XcodeProjectFileGenerator.bGeneratingRunIOSProject && !XcodeProjectFileGenerator.bGeneratingRunTVOSProject ? "Development Editor" : "Development"; var 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 (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); } 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); DirectoryReference SchemesDir = new DirectoryReference(ProjectFilePath.FullName + "/xcshareddata/xcschemes"); if (!DirectoryReference.Exists(SchemesDir)) { DirectoryReference.CreateDirectory(SchemesDir); } string SchemeFilePath = SchemesDir + "/" + TargetName + ".xcscheme"; 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) { bool bSuccess = true; var TargetName = ProjectFilePath.GetFileNameWithoutExtension(); var TargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var TargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var TargetDependencyGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var TargetProxyGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var TargetAppGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var BuildTargetName = TargetName + "_Build"; var BuildTargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var BuildTargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var IndexTargetName = TargetName + "_Index"; var IndexTargetGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var IndexTargetConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var ProjectGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var ProjectConfigListGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var MainGroupGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var ProductRefGroupGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); var SourcesBuildPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); // Figure out all the desired configurations var BuildConfigs = GetSupportedBuildConfigs(InPlatforms, InConfigurations); 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; var ProjectBuildConfigs = new Dictionary(); var TargetBuildConfigs = new Dictionary(); var BuildTargetBuildConfigs = new Dictionary(); var IndexTargetBuildConfigs = new Dictionary(); foreach (var 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; } } var PBXBuildFileSection = new StringBuilder(); var PBXFileReferenceSection = new StringBuilder(); var PBXSourcesBuildPhaseSection = new StringBuilder(); GenerateSectionsWithSourceFiles(PBXBuildFileSection, PBXFileReferenceSection, PBXSourcesBuildPhaseSection, TargetAppGuid, TargetName); var 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) { var 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; } } }