Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/IOSExports.cs

387 lines
15 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Tools.DotNETCommon;
namespace UnrealBuildTool
{
/// <summary>
/// Public IOS functions exposed to UAT
/// </summary>
public static class IOSExports
{
/// <summary>
///
/// </summary>
/// <param name="InProject"></param>
/// <param name="Distribution"></param>
/// <param name="MobileProvision"></param>
/// <param name="SigningCertificate"></param>
/// <param name="TeamUUID"></param>
/// <param name="bAutomaticSigning"></param>
public static void GetProvisioningData(FileReference InProject, bool Distribution, out string MobileProvision, out string SigningCertificate, out string TeamUUID, out bool bAutomaticSigning)
{
IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)).ReadProjectSettings(InProject);
if (ProjectSettings == null)
{
MobileProvision = null;
SigningCertificate = null;
TeamUUID = null;
bAutomaticSigning = false;
return;
}
if (ProjectSettings.bAutomaticSigning)
{
MobileProvision = null;
SigningCertificate = null;
TeamUUID = ProjectSettings.TeamID;
bAutomaticSigning = true;
}
else
{
IOSProvisioningData Data = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.IOS)).ReadProvisioningData(ProjectSettings, Distribution);
if (Data == null)
{ // no provisioning, swith to automatic
MobileProvision = null;
SigningCertificate = null;
TeamUUID = ProjectSettings.TeamID;
bAutomaticSigning = true;
}
else
{
MobileProvision = Data.MobileProvision;
SigningCertificate = Data.SigningCertificate;
TeamUUID = Data.TeamUUID;
bAutomaticSigning = false;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="Config"></param>
/// <param name="ProjectFile"></param>
/// <param name="InProjectName"></param>
/// <param name="InProjectDirectory"></param>
/// <param name="InExecutablePath"></param>
/// <param name="InEngineDir"></param>
/// <param name="bForDistribution"></param>
/// <param name="CookFlavor"></param>
/// <param name="bIsDataDeploy"></param>
/// <param name="bCreateStubIPA"></param>
/// <param name="BuildReceiptFileName"></param>
/// <returns></returns>
public static bool PrepForUATPackageOrDeploy(UnrealTargetConfiguration Config, FileReference ProjectFile, string InProjectName, DirectoryReference InProjectDirectory, string InExecutablePath, DirectoryReference InEngineDir, bool bForDistribution, string CookFlavor, bool bIsDataDeploy, bool bCreateStubIPA, FileReference BuildReceiptFileName)
{
TargetReceipt Receipt = TargetReceipt.Read(BuildReceiptFileName);
return new UEDeployIOS().PrepForUATPackageOrDeploy(Config, ProjectFile, InProjectName, InProjectDirectory.FullName, InExecutablePath, InEngineDir.FullName, bForDistribution, CookFlavor, bIsDataDeploy, bCreateStubIPA, Receipt);
}
/// <summary>
///
/// </summary>
/// <param name="ProjectFile"></param>
/// <param name="Config"></param>
/// <param name="ProjectDirectory"></param>
/// <param name="bIsUE4Game"></param>
/// <param name="GameName"></param>
/// <param name="bIsClient"></param>
/// <param name="ProjectName"></param>
/// <param name="InEngineDir"></param>
/// <param name="AppDirectory"></param>
/// <param name="BuildReceiptFileName"></param>
/// <param name="bSupportsPortrait"></param>
/// <param name="bSupportsLandscape"></param>
/// <param name="bSkipIcons"></param>
/// <returns></returns>
public static bool GeneratePList(FileReference ProjectFile, UnrealTargetConfiguration Config, DirectoryReference ProjectDirectory, bool bIsUE4Game, string GameName, bool bIsClient, string ProjectName, DirectoryReference InEngineDir, DirectoryReference AppDirectory, FileReference BuildReceiptFileName, out bool bSupportsPortrait, out bool bSupportsLandscape, out bool bSkipIcons)
{
TargetReceipt Receipt = TargetReceipt.Read(BuildReceiptFileName);
return new UEDeployIOS().GeneratePList(ProjectFile, Config, ProjectDirectory.FullName, bIsUE4Game, GameName, bIsClient, ProjectName, InEngineDir.FullName, AppDirectory.FullName, Receipt, out bSupportsPortrait, out bSupportsLandscape, out bSkipIcons);
}
/// <summary>
///
/// </summary>
/// <param name="PlatformType"></param>
/// <param name="SourceFile"></param>
/// <param name="TargetFile"></param>
public static void StripSymbols(UnrealTargetPlatform PlatformType, FileReference SourceFile, FileReference TargetFile)
{
IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(PlatformType)).ReadProjectSettings(null);
IOSToolChain ToolChain = new IOSToolChain(null, ProjectSettings);
ToolChain.StripSymbols(SourceFile, TargetFile);
}
/// <summary>
///
/// </summary>
/// <param name="ProjectFile"></param>
/// <param name="Executable"></param>
/// <param name="StageDirectory"></param>
/// <param name="Platform"></param>
public static void GenerateAssetCatalog(FileReference ProjectFile, FileReference Executable, DirectoryReference StageDirectory, UnrealTargetPlatform Platform)
{
// Determine whether the user has modified icons that require a remote Mac to build.
bool bUserImagesExist = false;
DirectoryReference ResourcesDir = IOSToolChain.GenerateAssetCatalog(ProjectFile, Platform, ref bUserImagesExist);
// Don't attempt to do anything remotely if the user is using the default UE4 images.
if (!bUserImagesExist)
{
return;
}
// Also don't attempt to use a remote Mac if packaging for TVOS on PC.
if (Platform == UnrealTargetPlatform.TVOS && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
{
return;
}
// Compile the asset catalog immediately
if(BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
{
FileReference OutputFile = FileReference.Combine(StageDirectory, "Assets.car");
RemoteMac Remote = new RemoteMac(ProjectFile);
Remote.RunAssetCatalogTool(Platform, ResourcesDir, OutputFile);
}
else
{
// Get the output file
FileReference OutputFile = IOSToolChain.GetAssetCatalogFile(Platform, Executable);
// Delete the Assets.car file to force the asset catalog to build every time, because
// removals of files or copies of icons (for instance) with a timestamp earlier than
// the last generated Assets.car will result in nothing built.
if (FileReference.Exists(OutputFile))
{
FileReference.Delete(OutputFile);
}
// Run the process locally
using(Process Process = new Process())
{
Process.StartInfo.FileName = "/usr/bin/xcrun";
Process.StartInfo.Arguments = IOSToolChain.GetAssetCatalogArgs(Platform, ResourcesDir.FullName, OutputFile.Directory.FullName);;
Process.StartInfo.UseShellExecute = false;
Utils.RunLocalProcess(Process);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="Platform"></param>
/// <param name="PlatformGameConfig"></param>
/// <param name="AppName"></param>
/// <param name="MobileProvisionFile"></param>
/// <param name="bForDistribution"></param>
/// <param name="IntermediateDir"></param>
public static void WriteEntitlements(UnrealTargetPlatform Platform, ConfigHierarchy PlatformGameConfig,
string AppName, FileReference MobileProvisionFile, bool bForDistribution, string IntermediateDir)
{
// get some info from the mobileprovisioning file
// the iCloud identifier and the bundle id may differ
string iCloudContainerIdentifier = "";
string iCloudContainerIdentifiersXML = "<array><string>iCloud.$(CFBundleIdentifier)</string></array>";
string UbiquityContainerIdentifiersXML = "<array><string>iCloud.$(CFBundleIdentifier)</string></array>";
string iCloudServicesXML = "<array><string>CloudKit</string><string>CloudDocuments</string></array>";
string UbiquityKVStoreIdentifiersXML = "\t<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>";
string OutputFileName = Path.Combine(IntermediateDir, AppName + ".entitlements");
if (MobileProvisionFile != null && File.Exists(MobileProvisionFile.FullName))
{
Console.WriteLine("Write entitlements from provisioning file {0}", MobileProvisionFile);
MobileProvisionContents MobileProvisionContent = MobileProvisionContents.Read(MobileProvisionFile);
iCloudContainerIdentifier = MobileProvisionContent.GetNodeValueByName("com.apple.developer.icloud-container-identifiers");
iCloudContainerIdentifiersXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.icloud-container-identifiers");
string entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.icloud-services");
if (!entitlementXML.Contains("*") || Platform == UnrealTargetPlatform.TVOS)
{
// for iOS, replace the generic value (*) with the default
iCloudServicesXML = entitlementXML;
}
entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.ubiquity-container-identifiers");
if (!entitlementXML.Contains("*") || !bForDistribution)
{
// for distribution, replace the generic value (*) with the default
UbiquityContainerIdentifiersXML = entitlementXML;
}
entitlementXML = MobileProvisionContent.GetNodeXMLValueByName("com.apple.developer.ubiquity-kvstore-identifier");
if (!entitlementXML.Contains("*") || !bForDistribution)
{
// for distribution, replace the generic value (*) with the default
UbiquityKVStoreIdentifiersXML = entitlementXML;
}
}
else
{
Console.WriteLine("Couldn't locate the mobile provisioning file {0}", MobileProvisionFile);
}
// write the entitlements file
{
bool bCloudKitSupported = false;
PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableCloudKitSupport", out bCloudKitSupported);
Directory.CreateDirectory(Path.GetDirectoryName(OutputFileName));
// we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value,
// which is based on distribution or not (true means debuggable)
StringBuilder Text = new StringBuilder();
Text.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
Text.AppendLine("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
Text.AppendLine("<plist version=\"1.0\">");
Text.AppendLine("<dict>");
Text.AppendLine("\t<key>get-task-allow</key>");
Text.AppendLine(string.Format("\t<{0}/>", bForDistribution ? "false" : "true"));
if (bCloudKitSupported)
{
if (iCloudContainerIdentifiersXML != "")
{
Text.AppendLine("\t<key>com.apple.developer.icloud-container-identifiers</key>");
Text.AppendLine(iCloudContainerIdentifiersXML);
}
if (iCloudServicesXML != "")
{
Text.AppendLine("\t<key>com.apple.developer.icloud-services</key>");
Text.AppendLine(iCloudServicesXML);
}
if (UbiquityContainerIdentifiersXML != "")
{
Text.AppendLine("\t<key>com.apple.developer.ubiquity-container-identifiers</key>");
Text.AppendLine(UbiquityContainerIdentifiersXML);
}
if (UbiquityKVStoreIdentifiersXML != "")
{
Text.AppendLine("\t<key>com.apple.developer.ubiquity-kvstore-identifier</key>");
Text.AppendLine(UbiquityKVStoreIdentifiersXML);
}
Text.AppendLine("\t<key>com.apple.developer.icloud-container-environment</key>");
Text.AppendLine(string.Format("\t<string>{0}</string>", bForDistribution ? "Production" : "Development"));
}
bool bRemoteNotificationsSupported = false;
PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport", out bRemoteNotificationsSupported);
// for TVOS we need push notifications when building for distribution with CloudKit
if (bCloudKitSupported && bForDistribution && Platform == UnrealTargetPlatform.TVOS)
{
bRemoteNotificationsSupported = true;
}
if (bRemoteNotificationsSupported)
{
Text.AppendLine("\t<key>aps-environment</key>");
Text.AppendLine(string.Format("\t<string>{0}</string>", bForDistribution ? "production" : "development"));
}
// for Sign in with Apple
bool bSignInWithAppleSupported = false;
PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableSignInWithAppleSupport", out bSignInWithAppleSupported);
if (bSignInWithAppleSupported)
{
Text.AppendLine("\t<key>com.apple.developer.applesignin</key>");
Text.AppendLine("\t<array><string>Default</string></array>");
}
// End of entitlements
Text.AppendLine("</dict>");
Text.AppendLine("</plist>");
if (File.Exists(OutputFileName))
{
// read existing file
string ExisitingFileContents = File.ReadAllText(OutputFileName);
bool bFileChanged = !ExisitingFileContents.Equals(Text.ToString(), StringComparison.Ordinal);
// overwrite file if there are content changes
if (bFileChanged)
{
File.WriteAllText(OutputFileName, Text.ToString());
}
}
else
{
File.WriteAllText(OutputFileName, Text.ToString());
}
}
// create a pList key named ICloudContainerIdentifier
// to be used at run-time when intializing the CloudKit services
if (iCloudContainerIdentifier != "")
{
string PListFile = IntermediateDir + "/" + AppName + "-Info.plist";
if (File.Exists(PListFile))
{
string OldPListData = File.ReadAllText(PListFile);
XDocument XDoc;
try
{
XDoc = XDocument.Parse(OldPListData);
if (XDoc.DocumentType != null)
{
XDoc.DocumentType.InternalSubset = null;
}
XElement dictElement = XDoc.Root.Element("dict");
if (dictElement != null)
{
XElement containerIdKeyNew = new XElement("key", "ICloudContainerIdentifier");
XElement containerIdValueNew = new XElement("string", iCloudContainerIdentifier);
XElement containerIdKey = dictElement.Elements("key").FirstOrDefault(x => x.Value == "ICloudContainerIdentifier");
if (containerIdKey != null)
{
// if ICloudContainerIdentifier already exists in the pList file, update its value
XElement containerIdValue = containerIdKey.ElementsAfterSelf("string").FirstOrDefault();
if (containerIdValue != null)
{
containerIdValue.Value = iCloudContainerIdentifier;
}
else
{
containerIdKey.AddAfterSelf(containerIdValueNew);
}
}
else
{
// add ICloudContainerIdentifier to the pList
dictElement.Add(containerIdKeyNew);
dictElement.Add(containerIdValueNew);
}
XDoc.Save(PListFile);
}
}
catch (Exception e)
{
throw new BuildException("plist is invalid {0}\n{1}", e, OldPListData);
}
}
}
}
}
}