Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/IOS/UEDeployIOS.cs
Robert Manuszewski e6072fb050 UnrealBuildTool: Added UnrealHeaderTool version checking when checking if it's up to date.
#change UBT will check for API version of all UnrealHeaderTool binaries to detect partial syncs
#change Added BuildHostPlatform class for runtime platform abstraction

[CL 2245408 by Robert Manuszewski in Main branch]
2014-08-06 07:05:15 -04:00

437 lines
18 KiB
C#

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace UnrealBuildTool.IOS
{
class UEDeployIOS : UEBuildDeploy
{
/**
* Register the platform with the UEBuildDeploy class
*/
public override void RegisterBuildDeploy()
{
// TODO: print debug info and handle any cases that would keep this from registering
UEBuildDeploy.RegisterBuildDeploy(UnrealTargetPlatform.IOS, this);
}
private static void CopyFileWithReplacements(string SourceFilename, string DestFilename, Dictionary<string, string> Replacements)
{
if (!File.Exists(SourceFilename))
{
return;
}
// make the dst filename with the same structure as it was in SourceDir
if (File.Exists(DestFilename))
{
File.Delete(DestFilename);
}
// make the subdirectory if needed
string DestSubdir = Path.GetDirectoryName(DestFilename);
if (!Directory.Exists(DestSubdir))
{
Directory.CreateDirectory(DestSubdir);
}
// some files are handled specially
string Ext = Path.GetExtension(SourceFilename);
if (Ext == ".plist")
{
string Contents = File.ReadAllText(SourceFilename);
// replace some varaibles
foreach (var Pair in Replacements)
{
Contents = Contents.Replace(Pair.Key, Pair.Value);
}
// write out file
File.WriteAllText(DestFilename, Contents);
}
else
{
File.Copy(SourceFilename, DestFilename);
// remove any read only flags
FileInfo DestFileInfo = new FileInfo(DestFilename);
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
}
public override bool PrepForUATPackageOrDeploy(string InProjectName, string InProjectDirectory, string InExecutablePath, string InEngineDir, bool bForDistribution, string CookFlavor)
{
bool bIsUE4Game = InExecutablePath.Contains ("UE4Game");
string BinaryPath = Path.GetDirectoryName (InExecutablePath);
string GameExeName = Path.GetFileName (InExecutablePath);
string GameName = bIsUE4Game ? "UE4Game" : InProjectName;
string PayloadDirectory = BinaryPath + "/Payload";
string AppDirectory = PayloadDirectory + "/" + GameName + ".app";
string CookedContentDirectory = AppDirectory + "/cookeddata";
string BuildDirectory = InProjectDirectory + "/Build/IOS";
string IntermediateDirectory = (bIsUE4Game ? InEngineDir : InProjectDirectory) + "/Intermediate/IOS";
// don't delete the payload directory, just update the data in it
//if (Directory.Exists(PayloadDirectory))
//{
// Directory.Delete(PayloadDirectory, true);
//}
Directory.CreateDirectory(BinaryPath);
Directory.CreateDirectory(PayloadDirectory);
Directory.CreateDirectory(AppDirectory);
Directory.CreateDirectory(BuildDirectory);
Directory.CreateDirectory(CookedContentDirectory);
// create the entitlements file
WriteEntitlementsFile(Path.Combine(IntermediateDirectory, GameName + ".entitlements"));
// delete some old files if they exist
if (Directory.Exists(AppDirectory + "/_CodeSignature"))
{
Directory.Delete(AppDirectory + "/_CodeSignature", true);
}
if (File.Exists(AppDirectory + "/CustomResourceRules.plist"))
{
File.Delete(AppDirectory + "/CustomResourceRules.plist");
}
if (File.Exists(AppDirectory + "/embedded.mobileprovision"))
{
File.Delete(AppDirectory + "/embedded.mobileprovision");
}
if (File.Exists(AppDirectory + "/PkgInfo"))
{
File.Delete(AppDirectory + "/PkgInfo");
}
// install the provision
FileInfo DestFileInfo;
string ProvisionWithPrefix = InEngineDir + "/Build/IOS/UE4Game.mobileprovision";
if (File.Exists(BuildDirectory + "/" + InProjectName + ".mobileprovision"))
{
ProvisionWithPrefix = BuildDirectory + "/" + InProjectName + ".mobileprovision";
}
else
{
if (File.Exists(BuildDirectory + "/NotForLicensees/" + InProjectName + ".mobileprovision"))
{
ProvisionWithPrefix = BuildDirectory + "/NotForLicensees/" + InProjectName + ".mobileprovision";
}
else if (!File.Exists(ProvisionWithPrefix))
{
ProvisionWithPrefix = InEngineDir + "/Build/IOS/NotForLicensees/UE4Game.mobileprovision";
}
}
if (File.Exists (ProvisionWithPrefix))
{
Directory.CreateDirectory (Environment.GetEnvironmentVariable ("HOME") + "/Library/MobileDevice/Provisioning Profiles/");
if (File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + ".mobileprovision"))
{
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + ".mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
File.Copy (ProvisionWithPrefix, Environment.GetEnvironmentVariable ("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + ".mobileprovision", true);
DestFileInfo = new FileInfo (Environment.GetEnvironmentVariable ("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + ".mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
else
{
// copy all provisions from the game directory, the engine directory, and the notforlicensees directory
// copy all of the provisions from the game directory to the library
{
if (Directory.Exists(BuildDirectory))
{
foreach (string Provision in Directory.EnumerateFiles(BuildDirectory, "*.mobileprovision", SearchOption.AllDirectories))
{
if (!File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)) || File.GetLastWriteTime(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision))
{
if (File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)))
{
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision));
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
File.Copy(Provision, Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision), true);
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision));
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
}
}
}
// copy all of the provisions from the engine directory to the library
{
if (Directory.Exists(InEngineDir + "/Build/IOS"))
{
foreach (string Provision in Directory.EnumerateFiles(InEngineDir + "/Build/IOS", "*.mobileprovision", SearchOption.AllDirectories))
{
if (!File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)) || File.GetLastWriteTime(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision))
{
if (File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision)))
{
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision));
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
File.Copy(Provision, Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision), true);
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Path.GetFileName(Provision));
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
}
}
}
}
// install the distribution provision
ProvisionWithPrefix = InEngineDir + "/Build/IOS/UE4Game_Distro.mobileprovision";
if (File.Exists(BuildDirectory + "/" + InProjectName + "_Distro.mobileprovision"))
{
ProvisionWithPrefix = BuildDirectory + "/" + InProjectName + "_Distro.mobileprovision";
}
else
{
if (File.Exists(BuildDirectory + "/NotForLicensees/" + InProjectName + "_Distro.mobileprovision"))
{
ProvisionWithPrefix = BuildDirectory + "/NotForLicensees/" + InProjectName + "_Distro.mobileprovision";
}
else if (!File.Exists(ProvisionWithPrefix))
{
ProvisionWithPrefix = InEngineDir + "/Build/IOS/NotForLicensees/UE4Game_Distro.mobileprovision";
}
}
if (File.Exists(ProvisionWithPrefix))
{
if (File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + "_Distro.mobileprovision"))
{
DestFileInfo = new FileInfo(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + "_Distro.mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
File.Copy(ProvisionWithPrefix, Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + "_Distro.mobileprovision", true);
DestFileInfo = new FileInfo (Environment.GetEnvironmentVariable ("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + InProjectName + "_Distro.mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
// copy plist file
string PListFile = InEngineDir + "/Build/IOS/UE4Game-Info.plist";
if (File.Exists(BuildDirectory + "/" + InProjectName + "-Info.plist"))
{
PListFile = BuildDirectory + "/" + InProjectName + "-Info.plist";
}
Dictionary<string, string> Replacements = new Dictionary<string, string>();
Replacements.Add("${EXECUTABLE_NAME}", GameName);
Replacements.Add("${BUNDLE_IDENTIFIER}", InProjectName.Replace("_", ""));
CopyFileWithReplacements(PListFile, AppDirectory + "/Info.plist", Replacements);
CopyFileWithReplacements(PListFile, IntermediateDirectory + "/" + GameName + "-Info.plist", Replacements);
// ensure the destination is writable
if (File.Exists(AppDirectory + "/" + GameName))
{
FileInfo GameFileInfo = new FileInfo(AppDirectory + "/" + GameName);
GameFileInfo.Attributes = GameFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
// copy the GameName binary
File.Copy(BinaryPath + "/" + GameExeName, AppDirectory + "/" + GameName, true);
// copy engine assets in
CopyFiles(InEngineDir + "/Build/IOS/Resources/Graphics", AppDirectory, "*.png", true);
// merge game assets on top
if (Directory.Exists(BuildDirectory + "/Resources/Graphics"))
{
CopyFiles(BuildDirectory + "/Resources/Graphics", AppDirectory, "*.png", true);
}
// copy additional engine framework assets in
string FrameworkAssetsPath = InEngineDir + "/Intermediate/IOS/FrameworkAssets";
// Let project override assets if they exist
if ( Directory.Exists( InProjectDirectory + "/Intermediate/IOS/FrameworkAssets" ) )
{
FrameworkAssetsPath = InProjectDirectory + "/Intermediate/IOS/FrameworkAssets";
}
if ( Directory.Exists( FrameworkAssetsPath ) )
{
CopyFolder( FrameworkAssetsPath, AppDirectory, true );
}
//CopyFiles(BuildDirectory, PayloadDirectory, null, "iTunesArtwork", null);
CopyFolder(InEngineDir + "/Content/Stats", AppDirectory + "/cookeddata/engine/content/stats", true);
return true;
}
public override bool PrepTargetForDeployment(UEBuildTarget InTarget)
{
string GameName = InTarget.AppName;
string BuildPath = InTarget.ProjectDirectory + "/Binaries/IOS";
string ProjectDirectory = InTarget.ProjectDirectory;
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac && Environment.GetEnvironmentVariable("UBT_NO_POST_DEPLOY") != "true")
{
string DecoratedGameName;
if (InTarget.Configuration == UnrealTargetConfiguration.Development)
{
DecoratedGameName = GameName;
}
else
{
DecoratedGameName = String.Format("{0}-{1}-{2}", GameName, InTarget.Platform.ToString(), InTarget.Configuration.ToString());
}
return PrepForUATPackageOrDeploy(GameName, ProjectDirectory, BuildPath + "/" + DecoratedGameName, "../../Engine", false, "");
}
else
{
// If it is requested, send the app bundle back to the platform executing these commands.
if (BuildConfiguration.bCopyAppBundleBackToDevice)
{
Log.TraceInformation("Copying binaries back to this device...");
IOSToolChain Toolchain = UEToolChain.GetPlatformToolChain(CPPTargetPlatform.IOS) as IOSToolChain;
try
{
string BinaryDir = Path.GetDirectoryName(InTarget.OutputPath) + "\\";
if (BinaryDir.EndsWith(InTarget.AppName + "\\Binaries\\IOS\\") && InTarget.Rules.Type != TargetRules.TargetType.Game)
{
BinaryDir = BinaryDir.Replace(InTarget.Rules.Type.ToString(), "Game");
}
// Get the app bundle's name
string AppFullName = InTarget.AppName;
if (InTarget.Configuration != UnrealTargetConfiguration.Development)
{
AppFullName += "-" + InTarget.Platform.ToString();
AppFullName += "-" + InTarget.Configuration.ToString();
}
foreach (string BinaryPath in Toolchain.BuiltBinaries)
{
if (!BinaryPath.Contains("Dummy"))
{
RPCUtilHelper.CopyFile(Toolchain.ConvertPath(BinaryPath), BinaryPath, false);
}
}
Log.TraceInformation("Copied binaries successfully.");
}
catch (Exception)
{
Log.TraceInformation("Copying binaries back to this device failed.");
}
}
// install the provision
/* string ProvisionWithPrefix = "../../Engine/Build/IOS/UE4Game.mobileprovision";
if (File.Exists(BuildPath + "/" + GameName + ".mobileprovision"))
{
ProvisionWithPrefix = BuildPath + "/" + GameName + ".mobileprovision";
}
else
{
if (File.Exists(BuildPath + "/NotForLicensees/" + GameName + ".mobileprovision"))
{
ProvisionWithPrefix = BuildPath + "/NotForLicensees/" + GameName + ".mobileprovision";
}
else if (!File.Exists(ProvisionWithPrefix))
{
ProvisionWithPrefix = "../../Engine/Build/IOS/NotForLicensees/UE4Game.mobileprovision";
}
}
string LibraryDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/";
if (File.Exists(ProvisionWithPrefix))
{
Directory.CreateDirectory(LibraryDir);
File.Copy(ProvisionWithPrefix, LibraryDir + GameName + ".mobileprovision", true);
FileInfo DestFileInfo = new FileInfo(LibraryDir + GameName + ".mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}
// install the distribution provision
ProvisionWithPrefix = "../../Engine/Build/IOS/UE4Game_Distro.mobileprovision";
if (File.Exists(BuildPath + "/" + GameName + "_Distro.mobileprovision"))
{
ProvisionWithPrefix = BuildPath + "/" + GameName + "_Distro.mobileprovision";
}
else
{
if (File.Exists(BuildPath + "/NotForLicensees/" + GameName + "_Distro.mobileprovision"))
{
ProvisionWithPrefix = BuildPath + "/NotForLicensees/" + GameName + "_Distro.mobileprovision";
}
else if (!File.Exists(ProvisionWithPrefix))
{
ProvisionWithPrefix = "../../Engine/Build/IOS/NotForLicensees/UE4Game_Distro.mobileprovision";
}
}
if (File.Exists(ProvisionWithPrefix))
{
File.Copy(ProvisionWithPrefix, LibraryDir + GameName + "_Distro.mobileprovision", true);
FileInfo DestFileInfo = new FileInfo(LibraryDir + GameName + "_Distro.mobileprovision");
DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly;
}*/
}
return true;
}
private void WriteEntitlementsFile(string OutputFilename)
{
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)
File.WriteAllText(OutputFilename, string.Format("<plist><dict><key>get-task-allow</key><{0}/></dict></plist>",
/*Config.bForDistribution ? "false" : */"true"));
}
static void SafeFileCopy(FileInfo SourceFile, string DestinationPath, bool bOverwrite)
{
FileInfo DI = new FileInfo(DestinationPath);
if (DI.Exists && bOverwrite)
{
DI.IsReadOnly = false;
DI.Delete();
}
SourceFile.CopyTo(DestinationPath, bOverwrite);
}
private void CopyFiles(string SourceDirectory, string DestinationDirectory, string TargetFiles, bool bOverwrite = false)
{
DirectoryInfo SourceFolderInfo = new DirectoryInfo(SourceDirectory);
FileInfo[] SourceFiles = SourceFolderInfo.GetFiles(TargetFiles);
foreach (FileInfo SourceFile in SourceFiles)
{
string DestinationPath = Path.Combine(DestinationDirectory, SourceFile.Name);
SafeFileCopy(SourceFile, DestinationPath, bOverwrite);
}
}
private void CopyFolder(string SourceDirectory, string DestinationDirectory, bool bOverwrite = false)
{
Directory.CreateDirectory(DestinationDirectory);
RecursiveFolderCopy(new DirectoryInfo(SourceDirectory), new DirectoryInfo(DestinationDirectory), bOverwrite);
}
static private void RecursiveFolderCopy(DirectoryInfo SourceFolderInfo, DirectoryInfo DestFolderInfo, bool bOverwrite = false)
{
foreach (FileInfo SourceFileInfo in SourceFolderInfo.GetFiles())
{
string DestinationPath = Path.Combine(DestFolderInfo.FullName, SourceFileInfo.Name);
SafeFileCopy(SourceFileInfo, DestinationPath, bOverwrite);
}
foreach (DirectoryInfo SourceSubFolderInfo in SourceFolderInfo.GetDirectories())
{
string DestFolderName = Path.Combine(DestFolderInfo.FullName, SourceSubFolderInfo.Name);
Directory.CreateDirectory(DestFolderName);
RecursiveFolderCopy(SourceSubFolderInfo, new DirectoryInfo(DestFolderName), bOverwrite);
}
}
}
}