// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.IO; namespace UnrealBuildTool { /// /// Represents a folder within the master project (e.g. Visual Studio solution) /// class XcodeProjectFolder : MasterProjectFolder { /// /// Constructor /// public XcodeProjectFolder(ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName) : base(InitOwnerProjectFileGenerator, InitFolderName) { // Generate a unique GUID for this folder // NOTE: When saving generated project files, we ignore differences in GUIDs if every other part of the file // matches identically with the pre-existing file FolderGUID = Guid.NewGuid(); } /// GUID for this folder public Guid FolderGUID { get; private set; } } public enum XcodeTargetType { Project, Legacy, Native, XCTest, XcodeHelper } /// /// Represents a build target /// public class XcodeProjectTarget { /// /// Constructor /// /// Name of the target that may be built. /// Specifies the type of the targtet to generate. /// Specifies the name of the executable if it differs from the InName /// Name of the target that may be built. /// Name of the target that may be built. /// Name of the target that may be built. public XcodeProjectTarget(string InDisplayName, string InTargetName, XcodeTargetType InType, string InProductName = "", UnrealTargetPlatform InTargetPlatform = UnrealTargetPlatform.Mac, bool bInIsMacOnly = false, List InDependencies = null, bool bHasPlist = false, List InFrameworks = null) { DisplayName = InDisplayName; TargetName = InTargetName; Type = InType; ProductName = InProductName; TargetPlatform = InTargetPlatform; bIsMacOnly = bInIsMacOnly; Guid = XcodeProjectFileGenerator.MakeXcodeGuid(); BuildConfigGuild = XcodeProjectFileGenerator.MakeXcodeGuid(); SourcesPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); ResourcesPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); FrameworksPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); ShellScriptPhaseGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); ProductGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); DebugConfigGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); DevelopmentConfigGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); ShippingConfigGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); TestConfigGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); DebugGameConfigGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); Dependencies = InDependencies == null ? new List() : InDependencies; FrameworkRefs = InFrameworks == null ? new List() : InFrameworks; // Meant to prevent adding plists that don't belong to the target, like in the case of Mac builds. if (bHasPlist) { PlistGuid = XcodeProjectFileGenerator.MakeXcodeGuid(); } } public string DisplayName; // Name displayed in Xcode's UI. public string TargetName; // Actual name of the target. public XcodeTargetType Type; public UnrealTargetPlatform TargetPlatform; //Mac, IOS public bool bIsMacOnly; public string ProductName; public string Guid; public string BuildConfigGuild; public string SourcesPhaseGuid; public string ResourcesPhaseGuid; public string FrameworksPhaseGuid; public string ShellScriptPhaseGuid; public string ProductGuid; public string DebugConfigGuid; public string DevelopmentConfigGuid; public string ShippingConfigGuid; public string TestConfigGuid; public string DebugGameConfigGuid; public string PlistGuid; public List Dependencies; // Target dependencies used for chaining Xcode target builds. public List FrameworkRefs; // Frameworks included in this target. public bool HasUI; // true if generates an .app instead of a console exe public override string ToString () { return string.Format ("[XcodeProjectTarget: {0}, {1}]", DisplayName, Type); } } /// /// Represents a target section. /// public class XcodeTargetDependency { public XcodeTargetDependency(string InLegacyTargetName, string InLegacyTargetGuid, string InContainerItemProxyGuid) { Guid = XcodeProjectFileGenerator.MakeXcodeGuid(); LegacyTargetName = InLegacyTargetName; LegacyTargetGuid = InLegacyTargetGuid; ContainerItemProxyGuid = InContainerItemProxyGuid; } public string Guid; public string LegacyTargetName; public string LegacyTargetGuid; public string ContainerItemProxyGuid; } /// /// Represents an item proxy section. /// public class XcodeContainerItemProxy { public XcodeContainerItemProxy(string InProjectGuid, string InLegacyTargetGuid, string InTargetName) { Guid = XcodeProjectFileGenerator.MakeXcodeGuid(); ProjectGuid = InProjectGuid; LegacyTargetGuid = InLegacyTargetGuid; TargetName = InTargetName; } public string Guid; public string ProjectGuid; public string LegacyTargetGuid; public string TargetName; } /// /// Represents a Framework. /// public class XcodeFramework { public XcodeFramework(string InName, string InPath, string InSourceTree) { Guid = XcodeProjectFileGenerator.MakeXcodeGuid(); Name = InName; Path = InPath; SourceTree = InSourceTree; } public string Name; public string Path; public string SourceTree; public string Guid; } /// /// Represents a reference to a Framework. /// public class XcodeFrameworkRef { public XcodeFrameworkRef(XcodeFramework InFramework) { Guid = XcodeProjectFileGenerator.MakeXcodeGuid(); Framework = InFramework; } public XcodeFramework Framework; public string Guid; } class XcodeProjectFileGenerator : ProjectFileGenerator { // always seed the random number the same, so multiple runs of the generator will generate the same project static Random Rand = new Random(0); /** * Make a random Guid string usable by Xcode (24 characters exactly) */ public static string MakeXcodeGuid() { string Guid = ""; byte[] Randoms = new byte[12]; Rand.NextBytes(Randoms); for (int Index = 0; Index < 12; Index++) { Guid += Randoms[Index].ToString("X2"); } return Guid; } /// File extension for project files we'll be generating (e.g. ".vcxproj") override public string ProjectFileExtension { get { return ".xcodeproj"; } } public override void CleanProjectFiles(string InMasterProjectRelativePath, string InMasterProjectName, string InIntermediateProjectFilesPath) { //@todo Mac. Implement this function... } /// /// Allocates a generator-specific project file object /// /// Path to the project file /// The newly allocated project file object protected override ProjectFile AllocateProjectFile(string InitFilePath) { return new XcodeProjectFile(InitFilePath); } /// ProjectFileGenerator interface public override MasterProjectFolder AllocateMasterProjectFolder(ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName) { return new XcodeProjectFolder(InitOwnerProjectFileGenerator, InitFolderName); } protected override bool WriteMasterProjectFile(ProjectFile UBTProject) { bool bSuccess = true; return bSuccess; } /// /// Appends the groups section. /// /// StringBuilder object to append groups string to /// Dictionary of all project groups /// Frameworks referenced private void AppendGroups(ref StringBuilder Contents, ref Dictionary Groups, List Targets, List Frameworks) { Contents.Append("/* Begin PBXGroup section */" + ProjectFileGenerator.NewLine); // Append main group MainGroupGuid = MakeXcodeGuid(); Contents.Append(string.Format("\t\t{0} = {{{1}", MainGroupGuid, ProjectFileGenerator.NewLine)); Contents.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); foreach (XcodeFileGroup Group in Groups.Values) { if (!string.IsNullOrEmpty(Group.GroupName)) { Contents.Append(string.Format("\t\t\t\t{0} /* {1} */,{2}", Group.GroupGuid, Group.GroupName, ProjectFileGenerator.NewLine)); } } foreach (XcodeProjectTarget Target in Targets) { if (!string.IsNullOrEmpty(Target.PlistGuid) && File.Exists(Path.Combine(RootRelativePath, Target.TargetName + "/Build/IOS/" + Target.TargetName + "-Info.plist"))) { Contents.Append("\t\t\t\t" + Target.PlistGuid + " /* " + Target.TargetName + "-Info.plist */," + ProjectFileGenerator.NewLine); } } ProductRefGroupGuid = MakeXcodeGuid(); FrameworkGroupGuid = MakeXcodeGuid(); Contents.Append(string.Format("\t\t\t\t{0} /* Products */,{1}", ProductRefGroupGuid, ProjectFileGenerator.NewLine)); Contents.Append(string.Format("\t\t\t\t{0} /* Frameworks */,{1}", FrameworkGroupGuid, ProjectFileGenerator.NewLine)); if (Groups.ContainsKey("")) { Groups[""].Append(ref Contents, bFilesOnly: true); } Contents.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Contents.Append("\t\t};" + ProjectFileGenerator.NewLine); // Add products group Contents.Append(string.Format("\t\t{0} = {{{1}", ProductRefGroupGuid, ProjectFileGenerator.NewLine)); Contents.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); foreach (XcodeProjectTarget Target in Targets) { if (IsXcodeTargetTypeNative(Target.Type)) { Contents.Append("\t\t\t\t" + Target.ProductGuid + " /* " + Target.ProductName + " */," + ProjectFileGenerator.NewLine); } } Contents.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tname = Products;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Contents.Append("\t\t};" + ProjectFileGenerator.NewLine); // Add Frameworks group Contents.Append(string.Format("\t\t{0} = {{{1}", FrameworkGroupGuid, ProjectFileGenerator.NewLine)); Contents.Append("\t\t\tisa = PBXGroup;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tchildren = (" + ProjectFileGenerator.NewLine); foreach (XcodeFramework Framework in Frameworks) { Contents.Append("\t\t\t\t" + Framework.Guid + " /* " + Framework.Name + " */," + ProjectFileGenerator.NewLine); } Contents.Append("\t\t\t);" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tname = Frameworks;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tsourceTree = \"\";" + ProjectFileGenerator.NewLine); Contents.Append("\t\t};" + ProjectFileGenerator.NewLine); foreach (XcodeFileGroup Group in Groups.Values) { if (Group.GroupName != "") { Group.Append(ref Contents); } } Contents.Append("/* End PBXGroup section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); } private static bool IsXcodeTargetTypeNative(XcodeTargetType Type) { return Type == XcodeTargetType.Native || Type == XcodeTargetType.XcodeHelper || Type == XcodeTargetType.XCTest; } /// /// Appends a target to targets section. /// /// StringBuilder object to append target string to /// Target to append private void AppendTarget(ref StringBuilder Contents, XcodeProjectTarget Target) { string TargetType = IsXcodeTargetTypeNative(Target.Type) ? "Native" : Target.Type.ToString(); Contents.Append( "\t\t" + Target.Guid + " /* " + Target.DisplayName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBX" + TargetType + "Target;" + ProjectFileGenerator.NewLine); if (Target.Type == XcodeTargetType.Legacy) { string UProjectPath = ""; if (UnrealBuildTool.HasUProjectFile() && Target.TargetName.StartsWith(Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile()))) { if (MasterProjectRelativePath == UnrealBuildTool.GetUProjectPath()) { UProjectPath = " " + "\\\"$(PROJECT_DIR)/" + Path.GetFileName(UnrealBuildTool.GetUProjectFile()) + "\\\""; } else { UProjectPath = " " + "\\\"" + UnrealBuildTool.GetUProjectFile() + "\\\""; } } // Xcode provides $ACTION argument for determining if we are building or cleaning a project Contents.Append("\t\t\tbuildArgumentsString = \"$(ACTION) " + Target.TargetName + " $(PLATFORM_NAME) $(CONFIGURATION)" + UProjectPath + "\";" + ProjectFileGenerator.NewLine); } Contents.Append("\t\t\tbuildConfigurationList = " + Target.BuildConfigGuild + " /* Build configuration list for PBX" + TargetType + "Target \"" + Target.DisplayName + "\" */;" + ProjectFileGenerator.NewLine); Contents.Append("\t\t\tbuildPhases = (" + ProjectFileGenerator.NewLine); if (IsXcodeTargetTypeNative(Target.Type)) { Contents.Append("\t\t\t\t" + Target.SourcesPhaseGuid + " /* Sources */," + ProjectFileGenerator.NewLine); //Contents.Append("\t\t\t\t" + Target.ResourcesPhaseGuid + " /* Resources */," + ProjectFileGenerator.NewLine); Contents.Append("\t\t\t\t" + Target.FrameworksPhaseGuid + " /* Frameworks */," + ProjectFileGenerator.NewLine); Contents.Append("\t\t\t\t" + Target.ShellScriptPhaseGuid + " /* ShellScript */," + ProjectFileGenerator.NewLine); } Contents.Append("\t\t\t);" + ProjectFileGenerator.NewLine); if (Target.Type == XcodeTargetType.Legacy) { string UE4Dir = Path.GetFullPath(Directory.GetCurrentDirectory() + "../../.."); if (bGeneratingRocketProjectFiles) { Contents.Append("\t\t\tbuildToolPath = \"" + UE4Dir + "/Engine/Build/BatchFiles/Mac/RocketBuild.sh\";" + ProjectFileGenerator.NewLine); } else { Contents.Append("\t\t\tbuildToolPath = \"" + UE4Dir + "/Engine/Build/BatchFiles/Mac/Build.sh\";" + ProjectFileGenerator.NewLine); } Contents.Append("\t\t\tbuildWorkingDirectory = \"" + UE4Dir + "\";" + ProjectFileGenerator.NewLine); } // This binds the "Run" targets to the "Build" targets. Contents.Append("\t\t\tdependencies = (" + ProjectFileGenerator.NewLine); foreach (XcodeTargetDependency Dependency in Target.Dependencies) { Contents.Append("\t\t\t\t" + Dependency.Guid + " /* PBXTargetDependency */" + ProjectFileGenerator.NewLine); } Contents.Append( "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\tname = \"" + Target.DisplayName + "\";" + ProjectFileGenerator.NewLine); if (Target.Type == XcodeTargetType.Legacy) { Contents.Append("\t\t\tpassBuildSettingsInEnvironment = 1;" + ProjectFileGenerator.NewLine); } Contents.Append("\t\t\tproductName = \"" + Target.DisplayName + "\";" + ProjectFileGenerator.NewLine); if (Target.Type == XcodeTargetType.XcodeHelper) { Contents.Append( "\t\t\tproductReference = " + Target.ProductGuid + " /* " + Target.ProductName + " */;" + ProjectFileGenerator.NewLine + "\t\t\tproductType = \"com.apple.product-type.library.static\";" + ProjectFileGenerator.NewLine); } else if (Target.Type == XcodeTargetType.Native) { Contents.Append( "\t\t\tproductReference = " + Target.ProductGuid + " /* " + Target.ProductName + " */;" + ProjectFileGenerator.NewLine + "\t\t\tproductType = \"com.apple.product-type.application\";" + ProjectFileGenerator.NewLine); } else if (Target.Type == XcodeTargetType.XCTest) { Contents.Append( "\t\t\tproductReference = " + Target.ProductGuid + " /* " + Target.ProductName + " */;" + ProjectFileGenerator.NewLine + "\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";" + ProjectFileGenerator.NewLine); } Contents.Append("\t\t};" + ProjectFileGenerator.NewLine); } private void AppendProjectConfig(ref StringBuilder Contents, string ConfigName, string ConfigGuid, string PreprocessorDefinitions, string HeaderSearchPaths) { string EngineSubdir = (bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles) ? "" : "Engine/"; Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + PreprocessorDefinitions + HeaderSearchPaths + "\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"c++0x\";" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_ENABLE_CPP_RTTI = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_WARN_CHECK_SWITCH_STATEMENTS = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tSUPPORTED_PLATFORMS = \"macosx\";" + ProjectFileGenerator.NewLine + "\t\t\t\tONLY_ACTIVE_ARCH = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = macosx;" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = " + EngineSubdir + "Intermediate/Build;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } private void AppendMacBuildConfig(ref StringBuilder Contents, string ConfigName, string ConfigGuid, bool bIsMacOnly) { if (bIsMacOnly) { Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + "\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tARCHS = \"x86_64\";" + ProjectFileGenerator.NewLine + "\t\t\t\tVALID_ARCHS = \"x86_64\";" + ProjectFileGenerator.NewLine + "\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";" + ProjectFileGenerator.NewLine + "\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = macosx;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } else { Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + "\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tARCHS = \"x86_64 arm64 armv7 armv7s\";" + ProjectFileGenerator.NewLine + "\t\t\t\tVALID_ARCHS = \"x86_64 arm64 armv7 armv7s\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSUPPORTED_PLATFORMS = \"macosx iphoneos iphonesimulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=macosx*]\" = \"$(TARGET_NAME)\";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphoneos*]\" = \"$(TARGET_NAME)\";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphonesimulator*]\" = \"$(TARGET_NAME)-simulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.9;" + ProjectFileGenerator.NewLine + "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = " + IOSToolChain.IOSVersion + ";" + ProjectFileGenerator.NewLine + "\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = macosx;" + ProjectFileGenerator.NewLine + "\t\t\t\t\"SDKROOT[arch=x86_64]\" = macosx;" + ProjectFileGenerator.NewLine + "\t\t\t\t\"SDKROOT[arch=arm*]\" = iphoneos;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } } private void AppendIOSBuildConfig(ref StringBuilder Contents, string ConfigName, string ConfigGuid) { Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphoneos*]\" = \"$(TARGET_NAME)\";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphonesimulator*]\" = \"$(TARGET_NAME)-simulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = " + IOSToolChain.IOSVersion + ";" + ProjectFileGenerator.NewLine + "\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = iphoneos;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } private void AppendIOSRunConfig(ref StringBuilder Contents, string ConfigName, string ConfigGuid, string TargetName, string EngineRelative, string GamePath, bool bIsUE4Game, bool IsAGame, bool bIsUE4Client) { Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphoneos*]\" = \"" + TargetName + "\";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PRODUCT_NAME[sdk=iphonesimulator*]\" = \"" + TargetName + "-simulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = " + IOSToolChain.IOSVersion + ";" + ProjectFileGenerator.NewLine + "\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";" + ProjectFileGenerator.NewLine); if (bIsUE4Game) { Contents.Append( "\t\t\t\tCODE_SIGN_RESOURCE_RULES_PATH = \"" + EngineRelative + "Engine/Build/iOS/XcodeSupportFiles/CustomResourceRules.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tINFOPLIST_FILE = \"" + EngineRelative + "Engine/Intermediate/IOS/" + TargetName + "-Info.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tOBJROOT = \"" + EngineRelative + "Engine/Intermediate/IOS/build\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } else if (bIsUE4Client) { Contents.Append( "\t\t\t\tCODE_SIGN_RESOURCE_RULES_PATH = \"" + EngineRelative + "Engine/Build/iOS/XcodeSupportFiles/CustomResourceRules.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tINFOPLIST_FILE = \"" + EngineRelative + "Engine/Intermediate/IOS/UE4Game-Info.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tOBJROOT = \"" + EngineRelative + "Engine/Intermediate/IOS/build\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } else if (IsAGame) { Contents.Append( "\t\t\t\tCODE_SIGN_RESOURCE_RULES_PATH = \"" + EngineRelative + "Engine/Build/iOS/XcodeSupportFiles/CustomResourceRules.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tINFOPLIST_FILE = \"" + GamePath + "/Intermediate/IOS/" + TargetName + "-Info.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = \"" + GamePath + "/Binaries/IOS\";" + ProjectFileGenerator.NewLine + "\t\t\t\tOBJROOT = \"" + GamePath + "/Intermediate/IOS/build\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + GamePath + "/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } else { Contents.Append( "\t\t\t\tCODE_SIGN_RESOURCE_RULES_PATH = \"" + EngineRelative + "Engine/Build/iOS/XcodeSupportFiles/CustomResourceRules.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tINFOPLIST_FILE = \"" + EngineRelative + "Engine/Source/Programs/" + TargetName + "/Resources/IOS/" + TargetName + "-Info.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tOBJROOT = \"" + EngineRelative + "Engine/Intermediate/IOS/build\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + EngineRelative + "Engine/Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine); } Contents.Append( "\t\t\t\tINFOPLIST_OUTPUT_FORMAT = xml;" + ProjectFileGenerator.NewLine + "\t\t\t\t\"PROVISIONING_PROFILE[sdk=iphoneos*]\" = \"\";" + ProjectFileGenerator.NewLine + "\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = iphoneos;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } private void AppendIOSXCTestConfig(ref StringBuilder Contents, string ConfigName, string ConfigGuid, string TargetName, string EngineSubdir, string EngineRelative) { Contents.Append( "\t\t" + ConfigGuid + " /* " + ConfigName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCBuildConfiguration;" + ProjectFileGenerator.NewLine + "\t\t\tbuildSettings = {" + ProjectFileGenerator.NewLine + "\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tBUNDLE_LOADER = \"" + EngineSubdir + "Binaries/IOS/Payload/" + TargetName + ".app/" + TargetName + "\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCLANG_ENABLE_MODULES = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCODE_SIGN_RESOURCE_RULES_PATH = \"" + EngineRelative + "Engine/Build/iOS/XcodeSupportFiles/CustomResourceRules.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCOPY_PHASE_STRIP = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tFRAMEWORK_SEARCH_PATHS = (" + ProjectFileGenerator.NewLine + "\t\t\t\t\t\"$(SDKROOT)/Developer/Library/Frameworks\"," + ProjectFileGenerator.NewLine + "\t\t\t\t\t\"$(inherited)\"," + ProjectFileGenerator.NewLine + "\t\t\t\t\t\"$(DEVELOPER_FRAMEWORKS_DIR)\"," + ProjectFileGenerator.NewLine + "\t\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;" + ProjectFileGenerator.NewLine + "\t\t\t\tDYNAMIC_NO_PIC = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tOPTIMIZATION_LEVEL = 0;" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (" + ProjectFileGenerator.NewLine + "\t\t\t\t\t\"DEBUG=1\"," + ProjectFileGenerator.NewLine + "\t\t\t\t\t\"$(inherited)\"," + ProjectFileGenerator.NewLine + "\t\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\t\tGCC_SYMBOLS_PRIVATE_EXTERN = NO;" + ProjectFileGenerator.NewLine + "\t\t\t\tINFOPLIST_FILE = \"" + EngineRelative + "Engine/Build/IOS/UE4CmdLineRun/UE4CmdLineRun-Info.plist\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSYMROOT = \"" + EngineSubdir + "Binaries/IOS\";" + ProjectFileGenerator.NewLine + "\t\t\t\tOBJROOT = \"" + EngineSubdir + "Intermediate/IOS/build\";" + ProjectFileGenerator.NewLine + "\t\t\t\tCONFIGURATION_BUILD_DIR = \"" + EngineSubdir + "Binaries/IOS/Payload\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";" + ProjectFileGenerator.NewLine + "\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 7.0;" + ProjectFileGenerator.NewLine + "\t\t\t\tONLY_ACTIVE_ARCH = YES;" + ProjectFileGenerator.NewLine + "\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";" + ProjectFileGenerator.NewLine + "\t\t\t\tSDKROOT = iphoneos;" + ProjectFileGenerator.NewLine + "\t\t\t\tTEST_HOST = \"$(BUNDLE_LOADER)\";" + ProjectFileGenerator.NewLine + "\t\t\t\tWRAPPER_EXTENSION = xctest;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tname = " + ConfigName + ";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } private void AppendSingleConfig(ref StringBuilder Contents, XcodeProjectTarget Target, string ConfigName, string ConfigGuid, string PreprocessorDefinitions, string HeaderSearchPaths, string EngineRelative, string GamePath, bool bIsUE4Game, bool IsAGame, bool bIsUE4Client) { if (Target.Type == XcodeTargetType.Project) { AppendProjectConfig(ref Contents, ConfigName, ConfigGuid, PreprocessorDefinitions, HeaderSearchPaths); } else { if (Target.TargetPlatform == UnrealTargetPlatform.Mac) { AppendMacBuildConfig(ref Contents, ConfigName, ConfigGuid, Target.bIsMacOnly); } else { if (Target.Type == XcodeTargetType.Legacy) { AppendIOSBuildConfig(ref Contents, ConfigName, ConfigGuid); } else { if (Target.Type != XcodeTargetType.XCTest) { AppendIOSRunConfig(ref Contents, ConfigName, ConfigGuid, Target.TargetName, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); } else { string EngineSubdir = (bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles) ? "" : "Engine/"; AppendIOSXCTestConfig(ref Contents, ConfigName, ConfigGuid, Target.TargetName, EngineSubdir, EngineRelative); } } } } } /// /// Appends a build configuration section for specific target. /// /// StringBuilder object to append build configuration string to /// Target for which we generate the build configuration /// Optional string with header search paths section /// Optional string with preprocessor definitions section private void AppendBuildConfigs(ref StringBuilder Contents, XcodeProjectTarget Target, string HeaderSearchPaths = "", string PreprocessorDefinitions = "") { List GameFolders = UEBuildTarget.DiscoverAllGameFolders(); bool IsAGame = false; string GamePath = null; bool bIsUE4Game = Target.TargetName.Equals("UE4Game", StringComparison.InvariantCultureIgnoreCase); bool bIsUE4Client = Target.TargetName.Equals("UE4Client", StringComparison.InvariantCultureIgnoreCase); string EngineRelative = ""; foreach (string GameFolder in GameFolders ) { if (GameFolder.EndsWith(Target.TargetName)) { IsAGame = true; if (Target.TargetPlatform == UnrealTargetPlatform.IOS) { var Toolchain = UEToolChain.GetPlatformToolChain(CPPTargetPlatform.IOS); GamePath = Toolchain.ConvertPath(Path.GetFullPath(GameFolder)); } break; } } if (bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles) { EngineRelative = Path.GetFullPath(EngineRelativePath + "/../"); } else if (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) { EngineRelative = Path.GetFullPath(EngineRelativePath + "/../../../../"); if (EngineRelative.IndexOf(':') == 1) { EngineRelative = EngineRelative.Substring(2); } if (Target.TargetPlatform == UnrealTargetPlatform.IOS) { var Toolchain = UEToolChain.GetPlatformToolChain(CPPTargetPlatform.IOS); EngineRelative = Toolchain.ConvertPath(EngineRelative); } } if (!bGeneratingRocketProjectFiles) { AppendSingleConfig(ref Contents, Target, "Debug", Target.DebugConfigGuid, PreprocessorDefinitions, HeaderSearchPaths, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); AppendSingleConfig(ref Contents, Target, "Test", Target.TestConfigGuid, PreprocessorDefinitions, HeaderSearchPaths, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); } AppendSingleConfig(ref Contents, Target, "DebugGame", Target.DebugGameConfigGuid, PreprocessorDefinitions, HeaderSearchPaths, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); AppendSingleConfig(ref Contents, Target, "Development", Target.DevelopmentConfigGuid, PreprocessorDefinitions, HeaderSearchPaths, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); AppendSingleConfig(ref Contents, Target, "Shipping", Target.ShippingConfigGuid, PreprocessorDefinitions, HeaderSearchPaths, EngineRelative, GamePath, bIsUE4Game, IsAGame, bIsUE4Client); } /// /// Appends a build configuration list section for specific target. /// /// StringBuilder object to append build configuration list string to /// Target for which we generate the build configuration list private void AppendConfigList(ref StringBuilder Contents, XcodeProjectTarget Target) { string TargetType = IsXcodeTargetTypeNative(Target.Type) ? "Native" : Target.Type.ToString(); string TypeName = Target.Type == XcodeTargetType.Project ? "PBXProject" : "PBX" + TargetType + "Target"; if (!bGeneratingRocketProjectFiles) { Contents.Append( "\t\t" + Target.BuildConfigGuild + " /* Build configuration list for " + TypeName + " \"" + Target.DisplayName + "\" */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCConfigurationList;" + ProjectFileGenerator.NewLine + "\t\t\tbuildConfigurations = (" + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.DebugConfigGuid + " /* Debug */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.DebugGameConfigGuid + " /* DebugGame */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.DevelopmentConfigGuid + " /* Development */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.ShippingConfigGuid + " /* Shipping */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.TestConfigGuid + " /* Test */," + ProjectFileGenerator.NewLine + "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\tdefaultConfigurationIsVisible = 0;" + ProjectFileGenerator.NewLine + "\t\t\tdefaultConfigurationName = Development;" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } else { Contents.Append( "\t\t" + Target.BuildConfigGuild + " /* Build configuration list for " + TypeName + " \"" + Target.DisplayName + "\" */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = XCConfigurationList;" + ProjectFileGenerator.NewLine + "\t\t\tbuildConfigurations = (" + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.DebugGameConfigGuid + " /* DebugGame */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.DevelopmentConfigGuid + " /* Development */," + ProjectFileGenerator.NewLine + "\t\t\t\t" + Target.ShippingConfigGuid + " /* Shipping */," + ProjectFileGenerator.NewLine + "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\tdefaultConfigurationIsVisible = 0;" + ProjectFileGenerator.NewLine + "\t\t\tdefaultConfigurationName = Development;" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine); } } private void AppendBuildPhase(ref string Contents, string Guid, string BuildPhaseName, string Files, List FileRefs) { Contents += "\t\t" + Guid + " /* " + BuildPhaseName + " */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBX" + BuildPhaseName + "BuildPhase;" + ProjectFileGenerator.NewLine + "\t\t\tbuildActionMask = 2147483647;" + ProjectFileGenerator.NewLine + "\t\t\tfiles = (" + ProjectFileGenerator.NewLine; if (Files != null) { Contents += Files; } Contents += "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\trunOnlyForDeploymentPostprocessing = 0;" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine; } private void AppendBuildPhases(List ProjectTargets, ref string PBXBuildFileSection, ref string PBXFileReferenceSection, ref string PBXSourcesBuildPhaseSection, ref string PBXResourcesBuildPhaseSection, ref string PBXFrameworksBuildPhaseSection, ref string PBXShellScriptBuildPhaseSection, string TestFrameworkFiles, string UE4CmdLineRunMFileGuid) { foreach (XcodeProjectTarget Target in ProjectTargets) { if (IsXcodeTargetTypeNative(Target.Type)) { // Generate Build references and Framework references for each Framework. string FrameworkFiles = ""; foreach (XcodeFrameworkRef FrameworkRef in Target.FrameworkRefs) { XcodeFramework Framework = FrameworkRef.Framework; PBXBuildFileSection += "\t\t" + FrameworkRef.Guid + " /* " + Framework.Name + " in Frameworks */ = {" + "isa = PBXBuildFile; " + "fileRef = " + Framework.Guid + " /* " + Framework.Name + " */;" + " };" + ProjectFileGenerator.NewLine; FrameworkFiles += "\t\t\t\t" + FrameworkRef.Guid + " /* " + Framework.Name + " in Frameworks */," + ProjectFileGenerator.NewLine; } AppendBuildPhase(ref PBXResourcesBuildPhaseSection, Target.ResourcesPhaseGuid, "Resources", null, null); string Sources = null; if (Target.Type == XcodeTargetType.XCTest) { // Add the xctest framework. FrameworkFiles += TestFrameworkFiles; // Normally every project either gets all source files compiled into it or none. // We just want one file: UE4CmdLineRun.m Sources = "\t\t\t\t" + UE4CmdLineRunMFileGuid + " /* UE4CmdLineRun.m in Sources */," + ProjectFileGenerator.NewLine; } AppendBuildPhase(ref PBXSourcesBuildPhaseSection, Target.SourcesPhaseGuid, "Sources", Sources, null); AppendBuildPhase(ref PBXFrameworksBuildPhaseSection, Target.FrameworksPhaseGuid, "Frameworks", FrameworkFiles, null); string PayloadDir = "Engine"; if (Target.Type == XcodeTargetType.Native && Target.TargetName != "UE4Game") { PayloadDir = Target.TargetName; } // add a script for preventing errors during Archive builds // this copies the contents of the app generated by UBT into the archiving app PBXShellScriptBuildPhaseSection += "\t\t" + Target.ShellScriptPhaseGuid + " /* ShellScript */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBXShellScriptBuildPhase;" + ProjectFileGenerator.NewLine + "\t\t\tbuildActionMask = 2147483647;" + ProjectFileGenerator.NewLine + "\t\t\tfiles = (" + ProjectFileGenerator.NewLine + "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\trunOnlyForDeploymentPostprocessing = 0;" + ProjectFileGenerator.NewLine + "\t\t\tshellPath = /bin/sh;" + ProjectFileGenerator.NewLine + "\t\t\tshellScript = \"if [ $DEPLOYMENT_LOCATION = \\\"YES\\\" ]\\nthen\\ncp -R " + PayloadDir +"/Binaries/IOS/Payload/" + Target.ProductName + "/ $DSTROOT/$LOCAL_APPS_DIR/" + Target.ProductName + "\\nfi\";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine; } } } private void AddPreGeneratedProject(ref string PBXBuildFileSection, ref string PBXFileReferenceSection, ref string PBXSourcesBuildPhaseSection, ref Dictionary Groups, string ProjectPath) { var ProjectFileName = Utils.MakePathRelativeTo(ProjectPath, MasterProjectRelativePath); var XcodeProject = new XcodeProjectFile(ProjectFileName); string ProjectDirectory = Path.GetDirectoryName(ProjectPath); XcodeProject.AddFilesToProject(SourceFileSearch.FindFiles(new List() { ProjectDirectory }, ExcludeNoRedistFiles: bExcludeNoRedistFiles, SubdirectoryNamesToExclude:new List() { "obj" }, SearchSubdirectories:true, IncludePrivateSourceCode:true), null); XcodeProject.GenerateSectionsContents(ref PBXBuildFileSection, ref PBXFileReferenceSection, ref PBXSourcesBuildPhaseSection, ref Groups); } private void AppendSourceFiles(List ProjectTargets, ref string PBXBuildFileSection, ref string PBXFileReferenceSection, ref string PBXSourcesBuildPhaseSection, ref Dictionary Groups, XcodeProjectTarget UE4XcodeHelperTarget, string UE4CmdLineRunMFileGuid, ref List IncludeDirectories, ref List SystemIncludeDirectories, ref List PreprocessorDefinitions) { PBXSourcesBuildPhaseSection += "\t\t" + UE4XcodeHelperTarget.SourcesPhaseGuid + " /* Sources */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBXSourcesBuildPhase;" + ProjectFileGenerator.NewLine + "\t\t\tbuildActionMask = 2147483647;" + ProjectFileGenerator.NewLine + "\t\t\tfiles = (" + ProjectFileGenerator.NewLine; PBXFileReferenceSection += "\t\t" + UE4XcodeHelperTarget.ProductGuid + " /* " + UE4XcodeHelperTarget.ProductName + " */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = " + UE4XcodeHelperTarget.ProductName + "; sourceTree = BUILT_PRODUCTS_DIR; };" + ProjectFileGenerator.NewLine; foreach (var CurProject in GeneratedProjectFiles) { bool bHasTarget = false; foreach (var TargetFile in CurProject.ProjectTargets) { if (TargetFile.TargetFilePath != null) { string TargetFileName = Path.GetFileNameWithoutExtension(TargetFile.TargetFilePath); string TargetFileMinusTarget = TargetFileName.Substring(0, TargetFileName.LastIndexOf(".Target")); foreach (XcodeProjectTarget Target in ProjectTargets) { if (TargetFileMinusTarget == Target.TargetName) { bHasTarget = true; break; } } if (bHasTarget) { break; } } } if (!bHasTarget) { continue; } XcodeProjectFile XcodeProject = CurProject as XcodeProjectFile; if (XcodeProject == null) { continue; } // Necessary so that GenerateSectionContents can use the same GUID instead of // auto-generating one for this special-case file. XcodeProject.UE4CmdLineRunFileGuid = UE4CmdLineRunMFileGuid; XcodeProject.UE4CmdLineRunFileRefGuid = UE4CmdLineRunMFileRefGuid; if (bGeneratingRunIOSProject) { foreach (var CurSourceFile in XcodeProject.SourceFiles) { XcodeSourceFile SourceFile = CurSourceFile as XcodeSourceFile; string GroupPath = Path.GetFullPath(Path.GetDirectoryName(SourceFile.FilePath)); XcodeFileGroup Group = XcodeProject.FindGroupByFullPath(ref Groups, GroupPath); if (Group != null) { Group.bReference = true; } } } else { XcodeProject.GenerateSectionsContents(ref PBXBuildFileSection, ref PBXFileReferenceSection, ref PBXSourcesBuildPhaseSection, ref Groups); } foreach (var CurPath in XcodeProject.IntelliSenseIncludeSearchPaths) { AddIncludeDirectory(ref IncludeDirectories, CurPath, Path.GetDirectoryName(XcodeProject.ProjectFilePath)); } foreach (var CurPath in XcodeProject.IntelliSenseSystemIncludeSearchPaths) { AddIncludeDirectory(ref SystemIncludeDirectories, CurPath, Path.GetDirectoryName(XcodeProject.ProjectFilePath)); } foreach (var CurDefinition in XcodeProject.IntelliSensePreprocessorDefinitions) { string Definition = CurDefinition; string AlternateDefinition = Definition.Contains("=0") ? Definition.Replace("=0", "=1") : Definition.Replace("=1", "=0"); if (Definition.Equals("WITH_EDITORONLY_DATA=0")) { Definition = AlternateDefinition; } if (!PreprocessorDefinitions.Contains(Definition) && !PreprocessorDefinitions.Contains(AlternateDefinition) && !Definition.StartsWith("UE_ENGINE_DIRECTORY") && !Definition.StartsWith("ORIGINAL_FILE_NAME")) { PreprocessorDefinitions.Add(Definition); } } } PBXSourcesBuildPhaseSection += "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\trunOnlyForDeploymentPostprocessing = 0;" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine; if (!bGeneratingRocketProjectFiles) { // Add UnrealBuildTool to the master project string ProjectPath = System.IO.Path.Combine(System.IO.Path.Combine(EngineRelativePath, "Source"), "Programs", "UnrealBuildTool", "UnrealBuildTool_Mono.csproj"); AddPreGeneratedProject(ref PBXBuildFileSection, ref PBXFileReferenceSection, ref PBXSourcesBuildPhaseSection, ref Groups, ProjectPath); } } private void AppendReferenceGroups(ref string PBXFileReferenceSection, ref Dictionary Groups) { string RelativePath = "../../../"; foreach (XcodeFileGroup Group in Groups.Values) { if (!string.IsNullOrEmpty(Group.GroupName)) { Group.bReference = true; string GroupPath = RelativePath + Group.GroupPath; // Add File reference. PBXFileReferenceSection += "\t\t" + Group.GroupGuid + " /* " + Group.GroupName + " */ = {" + "isa = PBXFileReference; lastKnownFileType = folder; " + "name = " + Group.GroupName + "; " + "path = " + Utils.CleanDirectorySeparators(GroupPath, '/') + "; sourceTree = \"\"; };" + ProjectFileGenerator.NewLine; } } } /// /// Creates the container item proxy section to be added to the PBX Project. /// /// String to be modified with the container item proxy objects. /// List of container item proxies to add. private void CreateContainerItemProxySection(ref string PBXContainerItemProxySection, List ContainerItemProxies) { foreach(XcodeContainerItemProxy ContainerItemProxy in ContainerItemProxies) { PBXContainerItemProxySection += "\t\t" + ContainerItemProxy.Guid + " /* PBXContainerItemProxy */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBXContainerItemProxy;" + ProjectFileGenerator.NewLine + "\t\t\tcontainerPortal = " + ContainerItemProxy.ProjectGuid + " /* Project object */;" + ProjectFileGenerator.NewLine + "\t\t\tproxyType = 1;" + ProjectFileGenerator.NewLine + "\t\t\tremoteGlobalIDString = " + ContainerItemProxy.LegacyTargetGuid + ";" + ProjectFileGenerator.NewLine + "\t\t\tremoteInfo = \"" + ContainerItemProxy.TargetName + "\";" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine; } } /// /// Creates the target dependency section to be added to the PBX Project. /// /// String to be modified with the target dependency objects. /// List of target dependencies to add. private void CreateTargetDependencySection(ref string PBXTargetDependencySection, List TargetDependencies) { foreach(XcodeTargetDependency TargetDependency in TargetDependencies) { PBXTargetDependencySection += "\t\t" + TargetDependency.Guid + " /* PBXTargetDependency */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBXTargetDependency;" + ProjectFileGenerator.NewLine + "\t\t\ttarget = " + TargetDependency.LegacyTargetGuid + " /* " + TargetDependency.LegacyTargetName + " */;" + ProjectFileGenerator.NewLine + "\t\t\ttargetProxy = " + TargetDependency.ContainerItemProxyGuid + " /* PBXContainerItemProxy */;" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine; } } /// Adds the include directory to the list, after converting it to relative to UE4 root private void AddIncludeDirectory(ref List IncludeDirectories, string IncludeDir, string ProjectDir) { string FullProjectPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath); string FullPath = ""; if (IncludeDir.StartsWith("/") && !IncludeDir.StartsWith(FullProjectPath)) { // Full path to a fulder outside of project FullPath = IncludeDir; } else { FullPath = Path.GetFullPath(Path.Combine(ProjectDir, IncludeDir)); FullPath = Utils.MakePathRelativeTo(FullPath, FullProjectPath); FullPath = FullPath.TrimEnd('/'); } if (!IncludeDirectories.Contains(FullPath) && !FullPath.Contains("FortniteGame/")) // @todo: skipping Fortnite header paths to shorten clang command line for building UE4XcodeHelper { IncludeDirectories.Add(FullPath); } } private void PopulateTargets(List GamePaths, List ProjectTargets, List ContainerItemProxies, List TargetDependencies, XcodeProjectTarget ProjectTarget, List Frameworks) { foreach (string TargetPath in GamePaths) { string TargetName = Utils.GetFilenameWithoutAnyExtensions(Path.GetFileName(TargetPath)); bool WantProjectFileForTarget = true; if (bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles) { bool IsEngineTarget = false; // Check to see if this is an Engine target. That is, the target is located under the "Engine" folder string TargetFileRelativeToEngineDirectory = Utils.MakePathRelativeTo(TargetPath, Path.Combine(EngineRelativePath), AlwaysTreatSourceAsDirectory: false); if (!TargetFileRelativeToEngineDirectory.StartsWith("..") && !Path.IsPathRooted(TargetFileRelativeToEngineDirectory)) { // This is an engine target IsEngineTarget = true; } if (IsEngineTarget) { if (!bAlwaysIncludeEngineModules) { // We were asked to exclude engine modules from the generated projects WantProjectFileForTarget = false; } if (bGeneratingGameProjectFiles && this.GameProjectName == TargetName) { WantProjectFileForTarget = true; } } } if (WantProjectFileForTarget) { string TargetFilePath; var Target = new TargetInfo(UnrealTargetPlatform.Mac, UnrealTargetConfiguration.Development); var TargetRulesObject = RulesCompiler.CreateTargetRules(TargetName, Target, false, out TargetFilePath); List SupportedPlatforms = new List(); TargetRulesObject.GetSupportedPlatforms(ref SupportedPlatforms); LinkEnvironmentConfiguration LinkConfiguration = new LinkEnvironmentConfiguration(); CPPEnvironmentConfiguration CPPConfiguration = new CPPEnvironmentConfiguration(); TargetRulesObject.SetupGlobalEnvironment(Target, ref LinkConfiguration, ref CPPConfiguration); if (!LinkConfiguration.bIsBuildingConsoleApplication) { TargetsThatNeedApp.Add(TargetName); } // @todo: Remove target platform param and merge Mac and iOS targets. For now BuildTarget knows how to build iOS, but cannot run iOS apps, so we need separate DeployTarget. bool bIsMacOnly = !SupportedPlatforms.Contains(UnrealTargetPlatform.IOS); XcodeProjectTarget BuildTarget = new XcodeProjectTarget(TargetName + " - Mac", TargetName, XcodeTargetType.Legacy, "", UnrealTargetPlatform.Mac, bIsMacOnly); if (!bGeneratingRunIOSProject) { ProjectTargets.Add(BuildTarget); } if (ProjectFilePlatform.HasFlag(XcodeProjectFilePlatform.iOS) && SupportedPlatforms.Contains(UnrealTargetPlatform.IOS)) { if ((bGeneratingRocketProjectFiles && TargetName == "UE4Game") || bGeneratingRunIOSProject) { // Generate Framework references. List FrameworkRefs = new List(); foreach (XcodeFramework Framework in Frameworks) { FrameworkRefs.Add(new XcodeFrameworkRef(Framework)); } XcodeProjectTarget IOSDeployTarget = new XcodeProjectTarget(TargetName + " - iOS", TargetName, XcodeTargetType.Native, TargetName + ".app", UnrealTargetPlatform.IOS, false, null, true, FrameworkRefs); ProjectTargets.Add(IOSDeployTarget); } else { XcodeContainerItemProxy ContainerProxy = new XcodeContainerItemProxy(ProjectTarget.Guid, BuildTarget.Guid, BuildTarget.DisplayName); XcodeTargetDependency TargetDependency = new XcodeTargetDependency(BuildTarget.DisplayName, BuildTarget.Guid, ContainerProxy.Guid); XcodeProjectTarget IOSDeployTarget = new XcodeProjectTarget(TargetName + " - iOS", TargetName, XcodeTargetType.Native, TargetName + ".app", UnrealTargetPlatform.IOS, false, new List() { TargetDependency }, true); ProjectTargets.Add(IOSDeployTarget); ContainerItemProxies.Add(ContainerProxy); TargetDependencies.Add(TargetDependency); } } } } } /// /// Writes a scheme file that holds user-specific info needed for debugging. /// /// Target for which we write the scheme file /// Extension of the executable used to run and debug this target (".app" for bundles, "" for command line apps /// Base name of the executable used to run and debug this target /// List of command line arguments for running this target private void WriteSchemeFile(string XcodeProjectPath, XcodeProjectTarget Target, string ExeExtension = "", string ExeBaseName = "", List Args = null) { if (ExeBaseName == "") { ExeBaseName = Target.TargetName; } string TargetBinariesFolder; if (ExeBaseName.StartsWith("/")) { TargetBinariesFolder = Path.GetDirectoryName(ExeBaseName); ExeBaseName = Path.GetFileName(ExeBaseName); } else { TargetBinariesFolder = Path.GetDirectoryName(Directory.GetCurrentDirectory()); if (ExeBaseName == Target.TargetName && ExeBaseName.EndsWith("Game") && ExeExtension == ".app") { TargetBinariesFolder = TargetBinariesFolder.Replace("/Engine", "/") + ExeBaseName + "/Binaries/"; } else { TargetBinariesFolder += "/Binaries/"; } // append the platform directory TargetBinariesFolder += Target.TargetPlatform.ToString(); } string SchemeFileContent = "" + ProjectFileGenerator.NewLine + "" + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine; if (Target.TargetPlatform == UnrealTargetPlatform.Mac) { // For non-rocket projects the default Run targtet is always in Debug config, so we always add -Mac-Debug suffix. // For rocket projects, we have DebugGame config, so the editor runs in Development mode (no suffix), but the game in Debug (so it needs suffix). string ExeConfigSuffix = (!bGeneratingRocketProjectFiles || !ExeBaseName.EndsWith("Editor")) ? "-Mac-Debug" : ""; SchemeFileContent += " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine; } else { SchemeFileContent += " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine; } if (Args != null) { SchemeFileContent += " " + ProjectFileGenerator.NewLine; Args.Add("-debug"); foreach (string Arg in Args) { SchemeFileContent += " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine; } SchemeFileContent += " " + ProjectFileGenerator.NewLine; } string Runnable = TargetBinariesFolder + "/" + ExeBaseName + ExeExtension; SchemeFileContent += " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + " " + ProjectFileGenerator.NewLine + "" + ProjectFileGenerator.NewLine; string SchemesDir = XcodeProjectPath + "/xcuserdata/" + Environment.UserName + ".xcuserdatad/xcschemes"; if (!Directory.Exists(SchemesDir)) { Directory.CreateDirectory(SchemesDir); } string SchemeFilePath = SchemesDir + Path.DirectorySeparatorChar + Target.DisplayName + ".xcscheme"; File.WriteAllText(SchemeFilePath, SchemeFileContent); } private void WriteSchemes(string XcodeProjectPath, List ProjectTargets) { // Delete obsolete schemes string SchemesDir = XcodeProjectPath + "/xcuserdata/" + Environment.UserName + ".xcuserdatad/xcschemes"; if (Directory.Exists(SchemesDir)) { var ObsoleteSchemes = Directory.GetFiles(SchemesDir, "*iOS (*.xcscheme", SearchOption.AllDirectories); foreach (string SchemeFile in ObsoleteSchemes) { File.Delete(SchemeFile); } } // write scheme files for targets foreach (var Target in ProjectTargets) { if (Target.Type == XcodeTargetType.Project || Target.Type == XcodeTargetType.XcodeHelper) { continue; } if (UnrealBuildTool.HasUProjectFile() && Target.TargetName.StartsWith(Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile()))) { if (Target.TargetName.EndsWith("Editor")) { WriteSchemeFile(XcodeProjectPath, Target, ".app", "UE4Editor", new List(new string[] { """ + UnrealBuildTool.GetUProjectFile() + """ })); } else { string ProjectBinariesDir = UnrealBuildTool.GetUProjectPath() + "/Binaries/Mac/"; WriteSchemeFile(XcodeProjectPath, Target, ".app", ProjectBinariesDir + Target.TargetName); } } else if (Target.TargetName.EndsWith("Game")) { string ExeBaseName = Target.TargetName; List Args = null; if (Target.TargetPlatform == UnrealTargetPlatform.Mac) { Args = new List(new string[] { Target.TargetName }); ExeBaseName = "UE4"; } WriteSchemeFile(XcodeProjectPath, Target, ".app", ExeBaseName, Args); } else if (Target.TargetName.EndsWith("Editor") && Target.TargetName != "UE4Editor") { string GameName = Target.TargetName.Replace("Editor", ""); WriteSchemeFile(XcodeProjectPath, Target, ".app", "UE4Editor", new List(new string[] { GameName })); } else if (TargetsThatNeedApp.Contains(Target.TargetName) || (Target.TargetPlatform == UnrealTargetPlatform.IOS)) { WriteSchemeFile(XcodeProjectPath, Target, ".app"); } else { WriteSchemeFile(XcodeProjectPath, Target); } } } private void WriteProject(StringBuilder XcodeProjectFileContent, List ProjectTargets) { // @todo: Windows and Mac should store the project in the same folder string PathBranch = (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) ? "/Engine/Intermediate/IOS" : ""; var XcodeProjectPath = MasterProjectRelativePath + PathBranch + "/" + MasterProjectName + ".xcodeproj"; if (bGeneratingGameProjectFiles && GameProjectName == "UE4Game") { XcodeProjectPath = UnrealBuildTool.GetUProjectPath() + "/" + MasterProjectName + ".xcodeproj"; } if (!Directory.Exists(XcodeProjectPath)) { Directory.CreateDirectory(XcodeProjectPath); } // load the existing project string InnerProjectFileName = XcodeProjectPath + "/project.pbxproj"; string ExistingProjectContents = ""; if (File.Exists(InnerProjectFileName)) { ExistingProjectContents = File.ReadAllText(InnerProjectFileName); } // compare it to the new project string NewProjectContents = XcodeProjectFileContent.ToString(); if (ExistingProjectContents != NewProjectContents) { Log.TraceInformation("Saving updated project file..."); File.WriteAllText(InnerProjectFileName, NewProjectContents); } else { Log.TraceInformation("Skipping project file write, as it didn't change..."); } WriteSchemes(XcodeProjectPath, ProjectTargets); } /// /// Writes the project files to disk /// /// True if successful protected override bool WriteProjectFiles() { Log.TraceInformation("Generating project file..."); // Setup project file content var XcodeProjectFileContent = new StringBuilder(); XcodeProjectFileContent.Append( "// !$*UTF8*$!" + ProjectFileGenerator.NewLine + "{" + ProjectFileGenerator.NewLine + "\tarchiveVersion = 1;" + ProjectFileGenerator.NewLine + "\tclasses = {" + ProjectFileGenerator.NewLine + "\t};" + ProjectFileGenerator.NewLine + "\tobjectVersion = 46;" + ProjectFileGenerator.NewLine + "\tobjects = {" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); // attempt to determine targets for the project List ProjectTargets = new List (); // add mandatory ones XcodeProjectTarget UE4ProjectTarget = new XcodeProjectTarget ("UE4", "UE4", XcodeTargetType.Project); XcodeProjectTarget UE4XcodeHelperTarget = new XcodeProjectTarget ("UE4XcodeHelper", "UE4XcodeHelper", XcodeTargetType.XcodeHelper, "libUE4XcodeHelper.a"); ProjectTargets.AddRange(new XcodeProjectTarget[] { UE4ProjectTarget, UE4XcodeHelperTarget }); if (ProjectFilePlatform.HasFlag(XcodeProjectFilePlatform.iOS)) { XcodeProjectTarget UE4CmdLineRunTarget = new XcodeProjectTarget("UE4CmdLineRun", "UE4CmdLineRun", XcodeTargetType.XCTest, "UE4CmdLineRun.xctest", UnrealTargetPlatform.IOS); ProjectTargets.Add(UE4CmdLineRunTarget); // This GUID will be referenced by each app's test action. UE4CmdLineGuid = UE4CmdLineRunTarget.Guid; } List TargetDependencies = new List(); List ContainerItemProxies = new List(); List Frameworks = new List(); Frameworks.Add(new XcodeFramework("OpenGLES.framework", "System/Library/Frameworks/OpenGLES.framework", "SDKROOT")); XcodeFramework XCTestFramework = new XcodeFramework("XCTest.framework", "Library/Frameworks/XCTest.framework", "DEVELOPER_DIR"); Frameworks.Add(XCTestFramework); var AllTargets = DiscoverTargets(); PopulateTargets(AllTargets, ProjectTargets, ContainerItemProxies, TargetDependencies, UE4ProjectTarget, Frameworks); Log.TraceInformation(string.Format ("Found {0} targets!", ProjectTargets.Count)); string PBXBuildFileSection = "/* Begin PBXBuildFile section */" + ProjectFileGenerator.NewLine; string PBXFileReferenceSection = "/* Begin PBXFileReference section */" + ProjectFileGenerator.NewLine; string PBXResourcesBuildPhaseSection = "/* Begin PBXResourcesBuildPhase section */" + ProjectFileGenerator.NewLine; string PBXSourcesBuildPhaseSection = "/* Begin PBXSourcesBuildPhase section */" + ProjectFileGenerator.NewLine; string PBXFrameworksBuildPhaseSection = "/* Begin PBXFrameworksBuildPhase section */" + ProjectFileGenerator.NewLine; string PBXShellScriptBuildPhaseSection = "/* Begin PBXShellScriptBuildPhase section */" + ProjectFileGenerator.NewLine; Dictionary Groups = new Dictionary(); List IncludeDirectories = new List(); List SystemIncludeDirectories = new List(); List PreprocessorDefinitions = new List(); foreach (XcodeFramework Framework in Frameworks) { // Add file references. PBXFileReferenceSection += "\t\t" + Framework.Guid + " /* " + Framework.Name + " */ = {" + "isa = PBXFileReference; " + "lastKnownFileType = wrapper.framework; " + "name = " + Framework.Name + "; " + "path = " + Framework.Path + "; " + "sourceTree = " + Framework.SourceTree + "; " + "};" + ProjectFileGenerator.NewLine; } // Set up all the test guids that need to be explicitly referenced later string UE4CmdLineRunMFileGuid = MakeXcodeGuid(); UE4CmdLineRunMFileRefGuid = MakeXcodeGuid(); string XCTestBuildFileGUID = MakeXcodeGuid(); string TestFrameworkFiles = "\t\t\t\t" + XCTestBuildFileGUID + " /* XCTest.framework in Frameworks */," + ProjectFileGenerator.NewLine; PBXBuildFileSection += "\t\t" + XCTestBuildFileGUID + " /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; " + "fileRef = " + XCTestFramework.Guid + " /* UE4CmdLineRun.m */; };" + ProjectFileGenerator.NewLine; AppendBuildPhases(ProjectTargets, ref PBXBuildFileSection, ref PBXFileReferenceSection, ref PBXSourcesBuildPhaseSection, ref PBXResourcesBuildPhaseSection, ref PBXFrameworksBuildPhaseSection, ref PBXShellScriptBuildPhaseSection, TestFrameworkFiles, UE4CmdLineRunMFileGuid); AppendSourceFiles(ProjectTargets, ref PBXBuildFileSection, ref PBXFileReferenceSection, ref PBXSourcesBuildPhaseSection, ref Groups, UE4XcodeHelperTarget, UE4CmdLineRunMFileGuid, ref IncludeDirectories, ref SystemIncludeDirectories, ref PreprocessorDefinitions); foreach (XcodeProjectTarget Target in ProjectTargets) { if ((Target.Type == XcodeTargetType.Native || Target.Type == XcodeTargetType.XCTest)) { string FileType = Target.Type == XcodeTargetType.Native ? "wrapper.application" : "wrapper.cfbundle"; string PayloadDir = "Engine"; if (Target.Type == XcodeTargetType.Native && Target.TargetName != "UE4Game") { PayloadDir = Target.TargetName; } PBXFileReferenceSection += "\t\t" + Target.ProductGuid + " /* " + Target.ProductName + " */ = {isa = PBXFileReference; explicitFileType = " + FileType + "; includeInIndex = 0; path = " + PayloadDir + "/Binaries/IOS/Payload/" + Target.ProductName + "; sourceTree = \"\"; };" + ProjectFileGenerator.NewLine; if (!string.IsNullOrEmpty(Target.PlistGuid)) { PBXFileReferenceSection += "\t\t" + Target.PlistGuid + " /* " + Target.TargetName + "-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = \"" + Target.TargetName + "-Info.plist\"; path = " + Target.TargetName + "/Build/IOS/" + Target.TargetName + "-Info.plist; sourceTree = \"\"; };" + ProjectFileGenerator.NewLine; } } } if (bGeneratingRunIOSProject) { AppendReferenceGroups(ref PBXFileReferenceSection, ref Groups); } PBXBuildFileSection += "/* End PBXBuildFile section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; PBXFileReferenceSection += "/* End PBXFileReference section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; PBXResourcesBuildPhaseSection += "/* End PBXResourcesBuildPhase section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; PBXSourcesBuildPhaseSection += "/* End PBXSourcesBuildPhase section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; PBXFrameworksBuildPhaseSection += "/* End PBXFrameworksBuildPhase section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; PBXShellScriptBuildPhaseSection += "/* End PBXShellScriptBuildPhase section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; string PBXTargetDependencySection = "/* Begin PBXTargetDependency section */" + ProjectFileGenerator.NewLine; CreateTargetDependencySection(ref PBXTargetDependencySection, TargetDependencies); PBXTargetDependencySection += "/* End PBXTargetDependency section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; string PBXContainerItemProxySection = "/* Begin PBXContainerItemProxy section */" + ProjectFileGenerator.NewLine; CreateContainerItemProxySection(ref PBXContainerItemProxySection, ContainerItemProxies); PBXContainerItemProxySection += "/* End PBXContainerItemProxy section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine; XcodeProjectFileContent.Append(PBXBuildFileSection); XcodeProjectFileContent.Append(PBXFileReferenceSection); XcodeProjectFileContent.Append(PBXContainerItemProxySection); XcodeProjectFileContent.Append(PBXTargetDependencySection); AppendGroups(ref XcodeProjectFileContent, ref Groups, ProjectTargets , Frameworks); XcodeProjectFileContent.Append(PBXShellScriptBuildPhaseSection); XcodeProjectFileContent.Append(PBXSourcesBuildPhaseSection); XcodeProjectFileContent.Append(PBXFrameworksBuildPhaseSection); XcodeProjectFileContent.Append("/* Begin PBXLegacyTarget section */" + ProjectFileGenerator.NewLine); foreach (var Target in ProjectTargets) { if (Target.Type == XcodeTargetType.Legacy) { AppendTarget (ref XcodeProjectFileContent, Target); } } XcodeProjectFileContent.Append("/* End PBXLegacyTarget section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); XcodeProjectFileContent.Append("/* Begin PBXNativeTarget section */" + ProjectFileGenerator.NewLine); AppendTarget(ref XcodeProjectFileContent, UE4XcodeHelperTarget); foreach (XcodeProjectTarget Target in ProjectTargets) { if (Target.Type == XcodeTargetType.Native || Target.Type == XcodeTargetType.XCTest) { AppendTarget(ref XcodeProjectFileContent, Target); } } XcodeProjectFileContent.Append("/* End PBXNativeTarget section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); XcodeProjectFileContent.Append( "/* Begin PBXProject section */" + ProjectFileGenerator.NewLine + "\t\t" + UE4ProjectTarget.Guid + " /* Project object */ = {" + ProjectFileGenerator.NewLine + "\t\t\tisa = PBXProject;" + ProjectFileGenerator.NewLine + "\t\t\tattributes = {" + ProjectFileGenerator.NewLine + "\t\t\t\tLastUpgradeCheck = 0510;" + ProjectFileGenerator.NewLine + "\t\t\t\tORGANIZATIONNAME = EpicGames;" + ProjectFileGenerator.NewLine + "\t\t\t};" + ProjectFileGenerator.NewLine + "\t\t\tbuildConfigurationList = " + UE4ProjectTarget.BuildConfigGuild + " /* Build configuration list for PBXProject \"" + UE4ProjectTarget.DisplayName + "\" */;" + ProjectFileGenerator.NewLine + "\t\t\tcompatibilityVersion = \"Xcode 3.2\";" + ProjectFileGenerator.NewLine + "\t\t\tdevelopmentRegion = English;" + ProjectFileGenerator.NewLine + "\t\t\thasScannedForEncodings = 0;" + ProjectFileGenerator.NewLine + "\t\t\tknownRegions = (" + ProjectFileGenerator.NewLine + "\t\t\t\ten," + ProjectFileGenerator.NewLine + "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t\tmainGroup = " + MainGroupGuid + ";" + ProjectFileGenerator.NewLine + "\t\t\tproductRefGroup = " + ProductRefGroupGuid + " /* Products */;" + ProjectFileGenerator.NewLine + "\t\t\tprojectDirPath = \"\";" + ProjectFileGenerator.NewLine + "\t\t\tprojectRoot = \"\";" + ProjectFileGenerator.NewLine + "\t\t\ttargets = (" + ProjectFileGenerator.NewLine ); foreach (var Target in ProjectTargets) { if (Target != UE4ProjectTarget) { XcodeProjectFileContent.AppendLine(string.Format ("\t\t\t\t{0} /* {1} */,", Target.Guid, Target.DisplayName)); } } XcodeProjectFileContent.Append( "\t\t\t);" + ProjectFileGenerator.NewLine + "\t\t};" + ProjectFileGenerator.NewLine + "/* End PBXProject section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine ); string PreprocessorDefinitionsString = "\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (" + ProjectFileGenerator.NewLine; foreach (string Definition in PreprocessorDefinitions) { PreprocessorDefinitionsString += "\t\t\t\t\t\"" + Definition + "\"," + ProjectFileGenerator.NewLine; } PreprocessorDefinitionsString += "\t\t\t\t\t\"MONOLITHIC_BUILD=1\"," + ProjectFileGenerator.NewLine; PreprocessorDefinitionsString += "\t\t\t\t);" + ProjectFileGenerator.NewLine; string HeaderSearchPaths = "\t\t\t\tHEADER_SEARCH_PATHS = (" + ProjectFileGenerator.NewLine; foreach (string Path in SystemIncludeDirectories) { HeaderSearchPaths += "\t\t\t\t\t\"" + Path + "\"," + ProjectFileGenerator.NewLine; } HeaderSearchPaths += "\t\t\t\t);" + ProjectFileGenerator.NewLine; HeaderSearchPaths += "\t\t\t\tUSER_HEADER_SEARCH_PATHS = (" + ProjectFileGenerator.NewLine; foreach (string Path in IncludeDirectories) { HeaderSearchPaths += "\t\t\t\t\t\"" + Path + "\"," + ProjectFileGenerator.NewLine; } HeaderSearchPaths += "\t\t\t\t);" + ProjectFileGenerator.NewLine; XcodeProjectFileContent.Append("/* Begin XCBuildConfiguration section */" + ProjectFileGenerator.NewLine); AppendBuildConfigs(ref XcodeProjectFileContent, UE4ProjectTarget, HeaderSearchPaths, PreprocessorDefinitionsString); foreach (var Target in ProjectTargets) { if (Target.Type != XcodeTargetType.Project && Target.Type != XcodeTargetType.XcodeHelper) { AppendBuildConfigs(ref XcodeProjectFileContent, Target); } } AppendBuildConfigs(ref XcodeProjectFileContent, UE4XcodeHelperTarget); XcodeProjectFileContent.Append("/* End XCBuildConfiguration section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); XcodeProjectFileContent.Append("/* Begin XCConfigurationList section */" + ProjectFileGenerator.NewLine); foreach (var Target in ProjectTargets) { if (Target.Type != XcodeTargetType.XcodeHelper) { AppendConfigList(ref XcodeProjectFileContent, Target); } } AppendConfigList(ref XcodeProjectFileContent, UE4XcodeHelperTarget); XcodeProjectFileContent.Append("/* End XCConfigurationList section */" + ProjectFileGenerator.NewLine + ProjectFileGenerator.NewLine); XcodeProjectFileContent.Append( "\t};" + ProjectFileGenerator.NewLine + "\trootObject = " + UE4ProjectTarget.Guid + " /* Project object */;" + ProjectFileGenerator.NewLine + "}" + ProjectFileGenerator.NewLine); WriteProject(XcodeProjectFileContent, ProjectTargets); return true; } [Flags] public enum XcodeProjectFilePlatform { Mac = 1 << 0, iOS = 1 << 1, All = Mac | iOS } /// Which platforms we should generate targets for static public XcodeProjectFilePlatform ProjectFilePlatform = XcodeProjectFilePlatform.All; /// Should we generate a special project to use for iOS signing instead of a normal one static public bool bGeneratingRunIOSProject = false; /// /// Configures project generator based on command-line options /// /// Arguments passed into the program /// True if all platforms should be included protected override void ConfigureProjectFileGeneration(string[] Arguments, ref bool IncludeAllPlatforms) { // Call parent implementation first base.ConfigureProjectFileGeneration(Arguments, ref IncludeAllPlatforms); ProjectFilePlatform = IncludeAllPlatforms ? XcodeProjectFilePlatform.All : XcodeProjectFilePlatform.Mac; foreach (var CurArgument in Arguments) { if (CurArgument.ToLowerInvariant() == "-iosdeployonly") { bGeneratingRunIOSProject = true; ProjectFilePlatform = XcodeProjectFilePlatform.iOS; break; } } } private string UE4CmdLineGuid; private string MainGroupGuid; private string ProductRefGroupGuid; private string FrameworkGroupGuid; private string UE4CmdLineRunMFileRefGuid; // List of targets that have a user interface. static List TargetsThatNeedApp = new List(); } }