// 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();
}
}