You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2948322 on 2016/04/19 by Nick.Shin update libwebsockets to v1.7.4 part 4 of 4 - doing this in stages for tracking purposes #jira UEPLAT-1246 - Update libWebsockets #jira UEPLAT-1221 - update websocket library #jira UEPLAT-1204 - Rebuild libwebsockets with SSL Change 2970016 on 2016/05/07 by Nick.Shin undo all of the following upgrades: - zlib - openssl - libcurl - libwebsockets and reset webrtc #jira UE-30298 - Fortnite and Orion crash on login Change 3020547 on 2016/06/20 by Jeff.Campeau Support for applocal deployment of binaries -applocaldir added to UAT to specify a per project directory to gather packages for applocal deployment Added ApplocalPrerequisitesDirectory property to packaging settings to set -applocaldir for packages built from the editor. Change 3020552 on 2016/06/20 by Jeff.Campeau Add switch to disable debug symbol cache creation Change 3020567 on 2016/06/20 by Jeff.Campeau constexpr enabled for Xbox One Change 3020568 on 2016/06/20 by Jeff.Campeau Separate setting for debug #jira UEPLAT-1348 Change 3020628 on 2016/06/20 by Jeff.Campeau Use global view instead of relying on current thread (fixes issues if pump message is called from a different thread). Change 3020629 on 2016/06/20 by Jeff.Campeau Use Slate tick to drive message processing during blocking loads (improves PLM). Change 3020633 on 2016/06/20 by Jeff.Campeau Fix Xbox One toolchain pathing to work for environment variables with and without trailing slashes. Pass requested XDK edition to VCVars batch file (fixes issues in building with a newer XDK installed SxS than the engine is configured for). Change 3020873 on 2016/06/21 by Lee.Clark PS4 - Fix missing audio when using A3D. Change 3021225 on 2016/06/21 by Keith.Judge Optimized away a bunch of GetVertexShader() calls in FSplineMeshVertexFactoryShaderParameters::SetMesh(). Saves about 0.9ms across a 100ms capture. Change 3021286 on 2016/06/21 by Dmitry.Rekman Linux: symbolication for memory profiler. - Also repaired/improved finding function name from debug info and overall callstack parsing. #tests Ran Linux editor and TestPAL, crashed multiple times Change 3021512 on 2016/06/21 by Mark.Satterthwaite Compile fixes for new clang version. Change 3021521 on 2016/06/21 by Mark.Satterthwaite Duplicate Fortnite CL #3013418: Don't release Metal buffers directly into the buffer pool, instead defer this until the command-buffer is known to have finished. This prevents the CPU from trying to modify the buffer while the GPU is still reading it if the GPU has fallen so far behind the CPU and therefore eliminates one possible cause of invalid access on the GPU. Change 3021528 on 2016/06/21 by Mark.Satterthwaite Make the Metal buffer pool cull after 30 frames - this will better match Apple's GART which unwires unused allocations after 1sec of idle, making them as expensive to use allocating a new buffer. Change 3021595 on 2016/06/21 by Mark.Satterthwaite Consolidate the way in which optional Metal features are exposed across macOS, iOS & tvOS devices & expose all the available features, unifying more of the code across all three platforms. - Implement Metal Depth-16 and stencil-texture-view support where available. - Use counting queries in Metal when available and expected (i.e. Metal_SM4+ or Metal_MRT+). - On latest iOS devices running Metal export support for GRHISupportsBaseVertexIndex & GRHISupportsFirstInstance when available. - On latest iOS devices running Metal export support for indirect buffer draw and dispatch calls. - Specify Metal layered rendering like any other and change the code so that there aren't preprocessor defines in the code. This lets the Metal MRT path assert if anyone ever tries to issue a layered draw call as its only supported on Mac. - Add support to Metal for setting UAVs from a uniform buffer. - Remove unused GlobalUniform header from MetalRHI. - Remove unnecessary FrameCount delay from Metal resource free lists. Change 3021702 on 2016/06/21 by Mark.Satterthwaite Fix mis-use of FreeListMutex instead of PoolMutex in MetalContext. Change 3022152 on 2016/06/21 by Nick.Shin Back out revision 5 from //UE4/Dev-Platform/Engine/Source/Runtime/NetworkFileSystem/Private/NetworkFileServerHttp.cpp forgot to un-do this when the giant revert (CL: #2970016) was done #jira UE-22166 HTML5 Cook on the fly will launch and then close browser Change 3022409 on 2016/06/21 by Dmitry.Rekman Fixed inability to run Setup.sh on Ubuntu 14.04 (UE-29289). - Contains PR #2258 (contributed by wshearn). Change 3022541 on 2016/06/22 by Lee.Clark PS4 - Make sure the render target masks are set correctly for disabled render targets. Fixes a validation check for shaders expecting to write to NULL render targets. Change 3022973 on 2016/06/22 by Michael.Trepka Fixed a problem in MacToolChain where both BuildConfiguration.bGeneratedSYMFile and BuildConfiguration.bUsePDBFiles set to false would not disable dSYM generation. Change 3023106 on 2016/06/22 by Dmitry.Rekman Linux: enable code to catch memory stomps during async loading. - Changed FLinuxPlatformMemory::BinnedAllocFrom/FreeToOS() to use mmap()/munmap(). Updated platform function signature accordingly. #tests Ran OrionServer on Linux (and OrionClient on Windows as a zero probe), tested also with running Linux editor with binned malloc. Change 3023256 on 2016/06/22 by Mark.Satterthwaite Fix compile errors from latest Metal changes that broke iOS. Change 3023268 on 2016/06/22 by Mark.Satterthwaite Use the Linux mmap/munmap/mprotect code to implement BinnedAllocFromOS/BinnedFreeToOS/PageProtect on Apple platforms as they are all equivalent. Change 3023651 on 2016/06/22 by Mark.Satterthwaite Make Metal SM5 the default for 10.11.5 or later, this time most of the features should be working on AMD & Nvidia, though tiled-reflections and distance-field AO/Shadows must still be forcibly disabled on Intel. Change 3023777 on 2016/06/22 by Brent.Pease + Update config for Android and iOS Change 3023781 on 2016/06/22 by Chris.Babcock Use mmap/munmap for Android BinnedAllocFromOS #ue4 #android Change 3023947 on 2016/06/22 by Mark.Satterthwaite Added a command-line switch for Mac "-RedirectNSLog" that will capture the output of NSLog using an NSPipe and write it into our log instead when not running under the debugger, thereby capturing output from underlying libraries that would otherwise be lost when outside a debugger. Change 3024434 on 2016/06/23 by Lee.Clark PS4 - Remove Delta Color Compression support Change 3024735 on 2016/06/23 by Mark.Satterthwaite Changes to MetalStatistics module initialisation. Change 3024741 on 2016/06/23 by Mark.Satterthwaite Change the way we test for Metal GPU support to avoid instantiating any MTLDevice's until MetalRHI is loaded so we can properly instantiate optional modules. Change 3025477 on 2016/06/23 by Brent.Pease + Added UnrealPluginLanguage.cs based on AndroidPluginLanguage.cs + Change UEDeployAndroid.cs to use UnrealPluginLanguage Change 3026085 on 2016/06/23 by Jeff.Campeau Separate Xbox One target settings for editor only values Move values that need to be loaded by the editor into the default inis (editor won't load target specific inis) Eliminate circular dependency on Xbox One settings (causing settings to either not load or memory corruption on shutdown) Clean up cached ini sections for UBT/UAT Change 3026093 on 2016/06/23 by Jeff.Campeau Cleanup unused files Change 3026745 on 2016/06/24 by Mark.Satterthwaite + Implemented rudimentary runtime debugging of MetalRHI with several different levels of complexity controlled by the new console-variable "rhi.Metal.RuntimeDebugLevel". + The MetalStateCache must also correctly set the render-pass descriptor when the previous render-target load action was 'Clear' as we will need to switch render targets or we'll mistakenly clear again. + Queries must not reset the MetalStateCache when they overflow the available buffer space, they simply break the encoder. - By default "rhi.Metal.RuntimeDebugLevel" is off & it is compiled out for Shipping - when enabled each higher level collects more data or changes the MetalRHI behaviour to make a command-buffer failure mode debuggable at the expense of performance. In order for this to work the number of command-buffers that may be allocated from the command-queue is also controllable via a console-variable "rhi.Metal.CommandQueueSize" which must be set prior to startup. Change 3026831 on 2016/06/24 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3026940 on 2016/06/24 by Brent.Pease PR #2458: added GetGameBundleId blueprint function (Contributed by derekvanvliet) Change 3027396 on 2016/06/24 by Brent.Pease Add BuildGraph.csproj to get Xamarin .sln builds working again Change 3029211 on 2016/06/27 by Michael.Trepka Added a setting (on by default) to make the game window preserve its content's aspect ratio while being resized by user Change 3029518 on 2016/06/27 by Josh.Adams Rollback //UE4/Dev-Platform/Engine/Build/BatchFiles/Mac/xcodeunlock.sh to revision 1 Change 3030385 on 2016/06/28 by Keith.Judge Fix XB1 deployment issues with AppXManifest for Paragon. Change 3030416 on 2016/06/28 by Lee.Clark PS4 - Fix Mediaplayer IsPlaying Change 3030922 on 2016/06/28 by Keith.Judge XB1 - Add pass through _RenderThread resource creation functions to D3D11.x RHI. Stops the render/RHI threads from stalling so much. Change 3030948 on 2016/06/28 by Jeff.Campeau Revert to using constant chunk indexes instead of polling the OS. (OS API has a bug that will not be fixed.) Change 3031016 on 2016/06/28 by Brent.Pease UEPLAT-1244 - Archive dSYM file UEPLAT-1359 - Support creating dSYM bundle Changes: + Added ios settings flag for dsym bundle + Added ios setting for creating xcode archive + xcode archive is now only put in the mac host or build machine's ~/Library/Developer/Xcode/Archives folder, it is not copied into the -archive folder like the ipa is Change 3031352 on 2016/06/28 by Bob.Tellez Added tps file for xcodeunlock Change 3031604 on 2016/06/28 by Mark.Satterthwaite Add an additional cast to flush-to-zero in the Metal shader translator bounds-checking code to avoid an internal GPU compiler error. Change 3031879 on 2016/06/28 by Brent.Pease + PhysX libraries for bitcode support on tvOS. Change 3032374 on 2016/06/29 by Keith.Judge Enable Oodle Handler Component on Xbox One Change 3032407 on 2016/06/29 by Keith.Judge Xbox One - Disable UpdateBufferStats when STATS is 0. Small perf gain. Change 3032432 on 2016/06/29 by Keith.Judge XB1 CPU Perf - Make CreateVertexDeclaration_RenderThread much faster by implementing a pass through to the RHI function rather than stalling the RHI thread. Change 3033474 on 2016/06/29 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change3033603on 2016/06/29 by Brent.Pease + Support UnrealPluginLanguage for IOS and TVOS builds + Allow ios/tvos UPL files an opportunity to modify a package's info.plist file Change 3034004 on 2016/06/30 by Lee.Clark PS4 - Use SDK 3.508.101 Change 3034007 on 2016/06/30 by Lee.Clark PS4 - VRTracker now uses SCE_VR_TRACKER_ROBUSTNESS_LEVEL_HIGH Change 3034173 on 2016/06/30 by Lee.Clark PS4 - Make sure depth target is null when eliminating fast clears. Fixes a GNM Validation failure Change 3034498 on 2016/06/30 by Jeff.Campeau Use MSBuild version 14 when generating project files. Change 3034943 on 2016/06/30 by Mark.Satterthwaite For Metal modify the MTLVertexDescriptor when calls to SetStreamSource provide different strides than the initial vertex-declaration which then requires compiling a different MTLRenderPipelineState. This silences errors reported by the Metal debug layer where the larger stride would see the shader read beyond the end of the buffer, but the smaller stride provided by SetStreamSource would work correctly. This may eliminate some more causes of Page-Fault GPU restarts. Change 3035416 on 2016/07/01 by Lee.Clark PS4 - Replace export vertex shader check with a CFLAG Change 3036093 on 2016/07/01 by Brent.Pease + Fix dSYM bundle path on Windows ios builds + Search for DeltaCopy directory if its not found from the user settings + Do not build an XCArchive file when building for ios on non-mac platforms Change 3036726 on 2016/07/02 by Brent.Pease + Add missing tvOS bitcode library Change 3037455 on 2016/07/05 by Lee.Clark PS4 - Default SmoothFrameRate to false Change 3037470 on 2016/07/05 by Keith.Judge Xbox One - Allow framerate smoothing, but default to off for consistency with PS4. Change 3038322 on 2016/07/05 by Jeremiah.Waldron Fix for asset packages with Unicode characters in their name not being loaded on Android. Fixing AndroidString's WIDECHAR* Strstr(WIDECHAR*, WIDECHAR*) function - Using an implementation of wcsstr, with a fix integrated for the incorrect implementation in Android ndk master branch, instead of converting the incoming WIDECHAR strings to ANSI strings just to be able to use strstr on them. The previous method was prone to causing the FString's Find and Contains functions to return false positives because when we convert wide to ansi, any character over the value of 255 is just set to '?' This in turn caused any package path strings with one of those characters in it to fail to load since they would be interpreted as having a question mark in their path, which is an invalid path character. #jira UE-18643 #android Change 3038693 on 2016/07/05 by Mark.Satterthwaite Change the way we store debug info for Metal shaders - the KeepDebugInfo flag now does precisely that, storing the shader code into the Metal header along with the path it was generated from. That means if KeepDebugInfo is enabled and Optimise is disabled then we can still access the shader code from within the Metal tools. On iOS/tvOS the shader code will be runtime compiled so that it shows up in the tooling but on Macs it will prefer to load the optimised version and simply unpack the shader code to the path from which it was compiled. This only works if we change the Metal shader compiler to emit temporary Metal files to the OS temporary directory so that the tools can read the file directly. Change 3039880 on 2016/07/06 by Mark.Satterthwaite Implement point-light shadow caching for RHI's that support specifying the render target layer from the vertex shader in lieu of geometry shaders. This requires changing FScreenVSForGS to TScreenVSForGS so that there can be a vertex-shader-layer variant (only compiled on RHI's that use it) whose output matches FCopyShadowMapsCubePS input. This alternative rendering path also requires adding the instance count to DrawRectangle so that we can render to all faces of the cubemap from a single draw call. Change 3040407 on 2016/07/06 by Michael.Trepka Added View->Enter Full Screen menu item for games on Mac Change 3040550 on 2016/07/06 by Mark.Satterthwaite Intel Metal compiler requires bool expressions be cast to 'int' rather than 'uint'. Change 3041098 on 2016/07/07 by Peter.Sauerbrei initial changes to get IOS builds from PC in launcher release of engine Change 3041310 on 2016/07/07 by Keith.Judge Xbox One - Optimize away GetDesc() and checks for D3D11_USAGE_DYNAMIC deep in the state cache as it's impossible for any buffer to have that flag set. Change 3041327 on 2016/07/07 by Keith.Judge Xbox One - Remove pointless memory barrier call in D3D11Query Change 3041352 on 2016/07/07 by Keith.Judge Xbox One - Add _RenderThread version of UpdateTexture2D() that just passes through to the RHI one. Saves a fairly significant chunk of time on the render/RHI thread when this is called a lot. Change 3041419 on 2016/07/07 by Jeff.Campeau Xbox One toolchain fix for VS2015 Update 3 Change 3041635 on 2016/07/07 by Jeff.Campeau Don't change game render resolution in response to WM_SIZE messages when in fullscreen (the game will restore the correct video mode when the window is restored). #jira OR-15578 Change 3041735 on 2016/07/07 by Peter.Sauerbrei re-enabled the build parameters for launcher release builds in IOS Change 3041783 on 2016/07/07 by Joe.Graf Changed bAutpApplyFailed to bAutoApplyFailed (typo) Change 3041784 on 2016/07/07 by Joe.Graf Fixed missing %s from a log message in UResavePackagesCommandlet Change 3042434 on 2016/07/08 by Lee.Clark PS4 - Fix compilation failure when Unsafe Command Buffers are enabled. Change 3042658 on 2016/07/08 by Lee.Clark PS4 - Use SDK 3.508.201 Change 3042970 on 2016/07/08 by Josh.Adams Redoing CL in 3040890 in Dev-Platform Change 3043243 on 2016/07/08 by Chris.Babcock clamped allowed slot range for gameplay debugger's categories copy of CL#3040313from //UE4/Dev-Framework #jira UE-32866 Change 3043500 on 2016/07/08 by Mark.Satterthwaite Downgrade "-Wdelete-non-virtual-dtor " to a warning again for Xcode 8 on iOS/tvOS to match macOS. Change 3044628 on 2016/07/11 by Mark.Satterthwaite Disable ShaderCache by default - it was implemented prior to RHI-thread/parallel-execution so it isn't thread-safe and can't be enabled by default on Mac now that Metal supports these features. #jira UE-32989 Change 3044948 on 2016/07/11 by Dmitry.Rekman Fix editor crash (happened on Linux, but not really specific to it) (UE-32973) - We can pass flags with shadows enabled when rendering asset thumbnails; thus disable shadows always when r.ShadowQuality is 0. (Redoing CL 3029574 by Bob). - Also fix mismatched 'noperspective' qualifier that caused NVidia drivers to refuse to link GL shaders. #jira UE-32973 Change 3045322 on 2016/07/11 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) Change 3046028 on 2016/07/12 by Lee.Clark PS4 - Check for correct SDK installation Allow use of 7th core when Morpheus is enabled Change 3046339 on 2016/07/12 by Peter.Sauerbrei fix for incorrect error message when iPhone Plus icon is the correct size [CL 3046645 by Josh Adams in Main branch]
1354 lines
50 KiB
C#
1354 lines
50 KiB
C#
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using AutomationTool;
|
|
using UnrealBuildTool;
|
|
using System.Text.RegularExpressions;
|
|
using Ionic.Zip;
|
|
using Ionic.Zlib;
|
|
using System.Security.Principal;
|
|
|
|
static class IOSEnvVarNames
|
|
{
|
|
// Should we code sign when staging? (defaults to 1 if not present)
|
|
static public readonly string CodeSignWhenStaging = "uebp_CodeSignWhenStaging";
|
|
}
|
|
|
|
public class IOSPlatform : Platform
|
|
{
|
|
bool bCreatedIPA = false;
|
|
|
|
private string PlatformName = null;
|
|
private string SDKName = null;
|
|
|
|
public IOSPlatform()
|
|
:this(UnrealTargetPlatform.IOS)
|
|
{
|
|
}
|
|
|
|
public IOSPlatform(UnrealTargetPlatform TargetPlatform)
|
|
:base(TargetPlatform)
|
|
{
|
|
PlatformName = TargetPlatform.ToString();
|
|
SDKName = (TargetPlatform == UnrealTargetPlatform.TVOS) ? "appletvos" : "iphoneos";
|
|
}
|
|
|
|
// Run the integrated IPP code
|
|
// @todo ipp: How to log the output in a nice UAT way?
|
|
protected static int RunIPP(string IPPArguments)
|
|
{
|
|
List<string> Args = new List<string>();
|
|
|
|
StringBuilder Token = null;
|
|
int Index = 0;
|
|
bool bInQuote = false;
|
|
while (Index < IPPArguments.Length)
|
|
{
|
|
if (IPPArguments[Index] == '\"')
|
|
{
|
|
bInQuote = !bInQuote;
|
|
}
|
|
else if (IPPArguments[Index] == ' ' && !bInQuote)
|
|
{
|
|
if (Token != null)
|
|
{
|
|
Args.Add(Token.ToString());
|
|
Token = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Token == null)
|
|
{
|
|
Token = new StringBuilder();
|
|
}
|
|
Token.Append(IPPArguments[Index]);
|
|
}
|
|
Index++;
|
|
}
|
|
|
|
if (Token != null)
|
|
{
|
|
Args.Add(Token.ToString());
|
|
}
|
|
|
|
return RunIPP(Args.ToArray());
|
|
}
|
|
|
|
protected static int RunIPP(string[] Args)
|
|
{
|
|
return 5;//@TODO: Reintegrate IPP to IOS.Automation iPhonePackager.Program.RealMain(Args);
|
|
}
|
|
|
|
public override int RunCommand(string Command)
|
|
{
|
|
// run generic IPP commands through the interface
|
|
if (Command.StartsWith("IPP:"))
|
|
{
|
|
return RunIPP(Command.Substring(4));
|
|
}
|
|
|
|
// non-zero error code
|
|
return 4;
|
|
}
|
|
|
|
public virtual UnrealBuildTool.UEDeployIOS GetDeployHandler(FileReference InProject)
|
|
{
|
|
Console.WriteLine("Getting IOS Deploy()");
|
|
return new UnrealBuildTool.UEDeployIOS(InProject);
|
|
}
|
|
|
|
protected string MakeIPAFileName( UnrealTargetConfiguration TargetConfiguration, ProjectParams Params )
|
|
{
|
|
string ProjectIPA = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", PlatformName, (Params.Distribution ? "Distro_" : "") + Params.ShortProjectName);
|
|
if (TargetConfiguration != UnrealTargetConfiguration.Development)
|
|
{
|
|
ProjectIPA += "-" + PlatformType.ToString() + "-" + TargetConfiguration.ToString();
|
|
}
|
|
ProjectIPA += ".ipa";
|
|
return ProjectIPA;
|
|
}
|
|
|
|
protected string MakeExeFileName( UnrealTargetConfiguration TargetConfiguration, ProjectParams Params )
|
|
{
|
|
string ProjectIPA = Path.Combine(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", PlatformName, Params.ShortProjectName);
|
|
if (TargetConfiguration != UnrealTargetConfiguration.Development)
|
|
{
|
|
ProjectIPA += "-" + PlatformType.ToString() + "-" + TargetConfiguration.ToString();
|
|
}
|
|
ProjectIPA += ".ipa";
|
|
return ProjectIPA;
|
|
}
|
|
|
|
// Determine if we should code sign
|
|
protected bool GetCodeSignDesirability(ProjectParams Params)
|
|
{
|
|
//@TODO: Would like to make this true, as it's the common case for everyone else
|
|
bool bDefaultNeedsSign = true;
|
|
|
|
bool bNeedsSign = false;
|
|
string EnvVar = InternalUtils.GetEnvironmentVariable(IOSEnvVarNames.CodeSignWhenStaging, bDefaultNeedsSign ? "1" : "0", /*bQuiet=*/ false);
|
|
if (!bool.TryParse(EnvVar, out bNeedsSign))
|
|
{
|
|
int BoolAsInt;
|
|
if (int.TryParse(EnvVar, out BoolAsInt))
|
|
{
|
|
bNeedsSign = BoolAsInt != 0;
|
|
}
|
|
else
|
|
{
|
|
bNeedsSign = bDefaultNeedsSign;
|
|
}
|
|
}
|
|
|
|
if (!String.IsNullOrEmpty(Params.BundleName))
|
|
{
|
|
// Have to sign when a bundle name is specified
|
|
bNeedsSign = true;
|
|
}
|
|
|
|
return bNeedsSign;
|
|
}
|
|
|
|
public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
|
|
{
|
|
Log("Package {0}", Params.RawProjectPath);
|
|
|
|
// ensure the ue4game binary exists, if applicable
|
|
string FullExePath = CombinePaths(Path.GetDirectoryName(Params.ProjectGameExeFilename), SC.StageExecutables[0] + (UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac ? ".stub" : ""));
|
|
if (!SC.IsCodeBasedProject && !FileExists_NoExceptions(FullExePath))
|
|
{
|
|
LogError("Failed to find game binary " + FullExePath);
|
|
throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find binary {0}. You may need to build the UE4 project with your target configuration and platform.", FullExePath);
|
|
}
|
|
|
|
//@TODO: We should be able to use this code on both platforms, when the following issues are sorted:
|
|
// - Raw executable is unsigned & unstripped (need to investigate adding stripping to IPP)
|
|
// - IPP needs to be able to codesign a raw directory
|
|
// - IPP needs to be able to take a .app directory instead of a Payload directory when doing RepackageFromStage (which would probably be renamed)
|
|
// - Some discrepancy in the loading screen pngs that are getting packaged, which needs to be investigated
|
|
// - Code here probably needs to be updated to write 0 byte files as 1 byte (difference with IPP, was required at one point when using Ionic.Zip to prevent issues on device, maybe not needed anymore?)
|
|
if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
|
|
{
|
|
// copy in all of the artwork and plist
|
|
var DeployHandler = GetDeployHandler(Params.RawProjectPath);
|
|
|
|
DeployHandler.PrepForUATPackageOrDeploy(Params.RawProjectPath,
|
|
Params.ShortProjectName,
|
|
Path.GetDirectoryName(Params.RawProjectPath.FullName),
|
|
CombinePaths(Path.GetDirectoryName(Params.ProjectGameExeFilename), SC.StageExecutables[0]),
|
|
CombinePaths(SC.LocalRoot, "Engine"),
|
|
Params.Distribution,
|
|
"",
|
|
false);
|
|
|
|
// figure out where to pop in the staged files
|
|
string AppDirectory = string.Format("{0}/Payload/{1}.app",
|
|
Path.GetDirectoryName(Params.ProjectGameExeFilename),
|
|
Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename));
|
|
|
|
// delete the old cookeddata
|
|
InternalUtils.SafeDeleteDirectory(AppDirectory + "/cookeddata", true);
|
|
InternalUtils.SafeDeleteFile(AppDirectory + "/ue4commandline.txt", true);
|
|
|
|
if (!Params.IterativeDeploy)
|
|
{
|
|
// copy the Staged files to the AppDirectory
|
|
string[] StagedFiles = Directory.GetFiles (SC.StageDirectory, "*", SearchOption.AllDirectories);
|
|
foreach (string Filename in StagedFiles)
|
|
{
|
|
string DestFilename = Filename.Replace (SC.StageDirectory, AppDirectory);
|
|
Directory.CreateDirectory (Path.GetDirectoryName (DestFilename));
|
|
InternalUtils.SafeCopyFile (Filename, DestFilename, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// copy just the root stage directory files
|
|
string[] StagedFiles = Directory.GetFiles (SC.StageDirectory, "*", SearchOption.TopDirectoryOnly);
|
|
foreach (string Filename in StagedFiles)
|
|
{
|
|
string DestFilename = Filename.Replace (SC.StageDirectory, AppDirectory);
|
|
Directory.CreateDirectory (Path.GetDirectoryName (DestFilename));
|
|
InternalUtils.SafeCopyFile (Filename, DestFilename, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SC.StageTargetConfigurations.Count != 1)
|
|
{
|
|
throw new AutomationException("iOS is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
|
|
}
|
|
bCreatedIPA = false;
|
|
bool bNeedsIPA = false;
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
String NonUFSManifestPath = SC.GetNonUFSDeploymentDeltaPath();
|
|
// check to determine if we need to update the IPA
|
|
if (File.Exists(NonUFSManifestPath))
|
|
{
|
|
string NonUFSFiles = File.ReadAllText(NonUFSManifestPath);
|
|
string[] Lines = NonUFSFiles.Split('\n');
|
|
bNeedsIPA = Lines.Length > 0 && !string.IsNullOrWhiteSpace(Lines[0]);
|
|
}
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(Params.Provision))
|
|
{
|
|
UnrealBuildTool.IOSPlatformContext BuildPlatContext = new IOSPlatformContext(Params.RawProjectPath);
|
|
BuildPlatContext.SetUpProjectEnvironment();
|
|
Params.Provision = BuildPlatContext.MobileProvision;
|
|
}
|
|
if (String.IsNullOrEmpty(Params.Certificate))
|
|
{
|
|
UnrealBuildTool.IOSPlatformContext BuildPlatContext = new IOSPlatformContext(Params.RawProjectPath);
|
|
BuildPlatContext.SetUpProjectEnvironment();
|
|
Params.Certificate = BuildPlatContext.SigningCertificate;
|
|
}
|
|
|
|
var TargetConfiguration = SC.StageTargetConfigurations[0];
|
|
|
|
// Scheme name and configuration for code signing with Xcode project
|
|
string SchemeName = Params.IsCodeBasedProject ? Params.RawProjectPath.GetFileNameWithoutExtension() : "UE4";
|
|
string SchemeConfiguration = TargetConfiguration.ToString();
|
|
if (Params.Client)
|
|
{
|
|
SchemeConfiguration += " Client";
|
|
}
|
|
|
|
if (UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
|
|
{
|
|
var ProjectIPA = MakeIPAFileName(TargetConfiguration, Params);
|
|
var ProjectStub = Path.GetFullPath(Params.ProjectGameExeFilename);
|
|
|
|
// package a .ipa from the now staged directory
|
|
var IPPExe = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/IOS/IPhonePackager.exe");
|
|
|
|
LogLog("ProjectName={0}", Params.ShortProjectName);
|
|
LogLog("ProjectStub={0}", ProjectStub);
|
|
LogLog("ProjectIPA={0}", ProjectIPA);
|
|
LogLog("IPPExe={0}", IPPExe);
|
|
|
|
bool cookonthefly = Params.CookOnTheFly || Params.SkipCookOnTheFly;
|
|
|
|
// if we are incremental check to see if we need to even update the IPA
|
|
if (!Params.IterativeDeploy || !File.Exists(ProjectIPA) || bNeedsIPA)
|
|
{
|
|
// delete the .ipa to make sure it was made
|
|
DeleteFile(ProjectIPA);
|
|
|
|
bCreatedIPA = true;
|
|
|
|
if (RemoteToolChain.bUseRPCUtil)
|
|
{
|
|
string IPPArguments = "RepackageFromStage \"" + (Params.IsCodeBasedProject ? Params.RawProjectPath.FullName : "Engine") + "\"";
|
|
IPPArguments += " -config " + TargetConfiguration.ToString();
|
|
IPPArguments += " -schemename " + SchemeName + " -schemeconfig \"" + SchemeConfiguration + "\"";
|
|
|
|
if (TargetConfiguration == UnrealTargetConfiguration.Shipping)
|
|
{
|
|
IPPArguments += " -compress=best";
|
|
}
|
|
|
|
// Determine if we should sign
|
|
bool bNeedToSign = GetCodeSignDesirability(Params);
|
|
|
|
if (!String.IsNullOrEmpty(Params.BundleName))
|
|
{
|
|
// Have to sign when a bundle name is specified
|
|
bNeedToSign = true;
|
|
IPPArguments += " -bundlename " + Params.BundleName;
|
|
}
|
|
|
|
if (bNeedToSign)
|
|
{
|
|
IPPArguments += " -sign";
|
|
if (Params.Distribution)
|
|
{
|
|
IPPArguments += " -distribution";
|
|
}
|
|
}
|
|
|
|
IPPArguments += (cookonthefly ? " -cookonthefly" : "");
|
|
IPPArguments += " -stagedir \"" + CombinePaths(Params.BaseStageDirectory, PlatformName) + "\"";
|
|
IPPArguments += " -project \"" + Params.RawProjectPath + "\"";
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
IPPArguments += " -iterate";
|
|
}
|
|
if (!string.IsNullOrEmpty(Params.Provision))
|
|
{
|
|
IPPArguments += " -provision \"" + Params.Provision + "\"";
|
|
}
|
|
if (!string.IsNullOrEmpty(Params.Certificate))
|
|
{
|
|
IPPArguments += " -certificate \"" + Params.Certificate + "\"";
|
|
}
|
|
if (PlatformName == "TVOS")
|
|
{
|
|
IPPArguments += " -tvos";
|
|
}
|
|
RunAndLog(CmdEnv, IPPExe, IPPArguments);
|
|
}
|
|
else
|
|
{
|
|
List<string> IPPArguments = new List<string>();
|
|
IPPArguments.Add("RepackageFromStage");
|
|
IPPArguments.Add(Params.IsCodeBasedProject ? Params.RawProjectPath.FullName : "Engine");
|
|
IPPArguments.Add("-config");
|
|
IPPArguments.Add(TargetConfiguration.ToString());
|
|
IPPArguments.Add("-schemename");
|
|
IPPArguments.Add(SchemeName);
|
|
IPPArguments.Add("-schemeconfig");
|
|
IPPArguments.Add("\"" + SchemeConfiguration + "\"");
|
|
|
|
if (TargetConfiguration == UnrealTargetConfiguration.Shipping)
|
|
{
|
|
IPPArguments.Add("-compress=best");
|
|
}
|
|
|
|
// Determine if we should sign
|
|
bool bNeedToSign = GetCodeSignDesirability(Params);
|
|
|
|
if (!String.IsNullOrEmpty(Params.BundleName))
|
|
{
|
|
// Have to sign when a bundle name is specified
|
|
bNeedToSign = true;
|
|
IPPArguments.Add("-bundlename");
|
|
IPPArguments.Add(Params.BundleName);
|
|
}
|
|
|
|
if (bNeedToSign)
|
|
{
|
|
IPPArguments.Add("-sign");
|
|
}
|
|
|
|
if (cookonthefly)
|
|
{
|
|
IPPArguments.Add(" -cookonthefly");
|
|
}
|
|
IPPArguments.Add(" -stagedir");
|
|
IPPArguments.Add(CombinePaths(Params.BaseStageDirectory, "IOS"));
|
|
IPPArguments.Add(" -project");
|
|
IPPArguments.Add(Params.RawProjectPath.FullName);
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
IPPArguments.Add(" -iterate");
|
|
}
|
|
if (!string.IsNullOrEmpty(Params.Provision))
|
|
{
|
|
IPPArguments.Add(" -provision");
|
|
IPPArguments.Add(Params.Provision);
|
|
}
|
|
if (!string.IsNullOrEmpty(Params.Certificate))
|
|
{
|
|
IPPArguments.Add(" -certificate");
|
|
IPPArguments.Add(Params.Certificate);
|
|
}
|
|
|
|
if (RunIPP(IPPArguments.ToArray()) != 0)
|
|
{
|
|
throw new AutomationException("IPP Failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
// verify the .ipa exists
|
|
if (!FileExists(ProjectIPA))
|
|
{
|
|
throw new AutomationException(ExitCode.Error_FailedToCreateIPA, "PACKAGE FAILED - {0} was not created", ProjectIPA);
|
|
}
|
|
|
|
if (WorkingCL > 0)
|
|
{
|
|
// Open files for add or edit
|
|
var ExtraFilesToCheckin = new List<string>
|
|
{
|
|
ProjectIPA
|
|
};
|
|
|
|
// check in the .ipa along with everything else
|
|
UE4Build.AddBuildProductsToChangelist(WorkingCL, ExtraFilesToCheckin);
|
|
}
|
|
|
|
//@TODO: This automatically deploys after packaging, useful for testing on PC when iterating on IPP
|
|
//Deploy(Params, SC);
|
|
}
|
|
else
|
|
{
|
|
// create the ipa
|
|
string IPAName = CombinePaths(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Binaries", PlatformName, (Params.Distribution ? "Distro_" : "") + Params.ShortProjectName + (SC.StageTargetConfigurations[0] != UnrealTargetConfiguration.Development ? ("-" + PlatformName + "-" + SC.StageTargetConfigurations[0].ToString()) : "") + ".ipa");
|
|
|
|
if (!Params.IterativeDeploy || !File.Exists(IPAName) || bNeedsIPA)
|
|
{
|
|
bCreatedIPA = true;
|
|
|
|
// code sign the app
|
|
CodeSign(Path.GetDirectoryName(Params.ProjectGameExeFilename), Params.IsCodeBasedProject ? Params.ShortProjectName : Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename), Params.RawProjectPath, SC.StageTargetConfigurations[0], SC.LocalRoot, Params.ShortProjectName, Path.GetDirectoryName(Params.RawProjectPath.FullName), SC.IsCodeBasedProject, Params.Distribution, Params.Provision, Params.Certificate, SchemeName, SchemeConfiguration);
|
|
|
|
// now generate the ipa
|
|
PackageIPA(Path.GetDirectoryName(Params.ProjectGameExeFilename), Params.IsCodeBasedProject ? Params.ShortProjectName : Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename), Params.ShortProjectName, Path.GetDirectoryName(Params.RawProjectPath.FullName), SC.StageTargetConfigurations[0], Params.Distribution);
|
|
}
|
|
}
|
|
|
|
PrintRunTime();
|
|
}
|
|
|
|
private string EnsureXcodeProjectExists(FileReference RawProjectPath, string LocalRoot, string ShortProjectName, string ProjectRoot, bool IsCodeBasedProject, out bool bWasGenerated)
|
|
{
|
|
// first check for ue4.xcodeproj
|
|
bWasGenerated = false;
|
|
string RawProjectDir = RawProjectPath.Directory.FullName;
|
|
string XcodeProj = RawProjectPath.FullName.Replace(".uproject", "_" + PlatformName + ".xcworkspace");
|
|
if (!Directory.Exists(RawProjectDir + "/Source") && !Directory.Exists(RawProjectDir + "/Intermediate/Source"))
|
|
{
|
|
XcodeProj = CombinePaths(CmdEnv.LocalRoot, "Engine", Path.GetFileName(XcodeProj));
|
|
}
|
|
Console.WriteLine ("Project: " + XcodeProj);
|
|
{
|
|
// project.xcodeproj doesn't exist, so generate temp project
|
|
string Arguments = "-project=\"" + RawProjectPath + "\"";
|
|
Arguments += " -platforms=" + PlatformName + " -game -nointellisense -" + PlatformName + "deployonly -ignorejunk";
|
|
|
|
// If engine is installed then UBT doesn't need to be built
|
|
if (Automation.IsEngineInstalled())
|
|
{
|
|
// Get the path to UBT
|
|
string InstalledUBT = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/UnrealBuildTool.exe");
|
|
Arguments = "-XcodeProjectFile " + Arguments;
|
|
RunUBT(CmdEnv, InstalledUBT, Arguments);
|
|
}
|
|
else
|
|
{
|
|
string Script = CombinePaths(CmdEnv.LocalRoot, "Engine/Build/BatchFiles/Mac/GenerateProjectFiles.sh");
|
|
string CWD = Directory.GetCurrentDirectory();
|
|
Directory.SetCurrentDirectory(Path.GetDirectoryName(Script));
|
|
Run(Script, Arguments, null, ERunOptions.Default);
|
|
Directory.SetCurrentDirectory(CWD);
|
|
}
|
|
|
|
bWasGenerated = true;
|
|
|
|
if (!Directory.Exists (XcodeProj))
|
|
{
|
|
// something very bad happened
|
|
throw new AutomationException("iOS couldn't find the appropriate Xcode Project " + XcodeProj);
|
|
}
|
|
}
|
|
|
|
return XcodeProj;
|
|
}
|
|
|
|
private void CodeSign(string BaseDirectory, string GameName, FileReference RawProjectPath, UnrealTargetConfiguration TargetConfig, string LocalRoot, string ProjectName, string ProjectDirectory, bool IsCode, bool Distribution = false, string Provision = null, string Certificate = null, string SchemeName = null, string SchemeConfiguration = null)
|
|
{
|
|
// check for the proper xcodeproject
|
|
bool bWasGenerated = false;
|
|
string XcodeProj = EnsureXcodeProjectExists (RawProjectPath, LocalRoot, ProjectName, ProjectDirectory, IsCode, out bWasGenerated);
|
|
|
|
string Arguments = "UBT_NO_POST_DEPLOY=true";
|
|
Arguments += " /usr/bin/xcrun xcodebuild build -workspace \"" + XcodeProj + "\"";
|
|
Arguments += " -scheme '";
|
|
Arguments += SchemeName != null ? SchemeName : GameName;
|
|
Arguments += "'";
|
|
Arguments += " -configuration \"" + (SchemeConfiguration != null ? SchemeConfiguration : TargetConfig.ToString()) + "\"";
|
|
Arguments += " -destination generic/platform=" + (PlatformName == "TVOS" ? "tvOS" : "iOS");
|
|
Arguments += " -sdk " + SDKName;
|
|
if (!string.IsNullOrEmpty(Certificate))
|
|
{
|
|
Arguments += " CODE_SIGN_IDENTITY=\"" + Certificate + "\"";
|
|
}
|
|
else
|
|
{
|
|
Arguments += " CODE_SIGN_IDENTITY=" + (Distribution ? "\"iPhone Distribution\"" : "\"iPhone Developer\"");
|
|
}
|
|
if (!string.IsNullOrEmpty(Provision))
|
|
{
|
|
// read the provision to get the UUID
|
|
if (File.Exists(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Provision))
|
|
{
|
|
string UUID = "";
|
|
string AllText = File.ReadAllText(Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/" + Provision);
|
|
int idx = AllText.IndexOf("<key>UUID</key>");
|
|
if (idx > 0)
|
|
{
|
|
idx = AllText.IndexOf("<string>", idx);
|
|
if (idx > 0)
|
|
{
|
|
idx += "<string>".Length;
|
|
UUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
|
|
Arguments += " PROVISIONING_PROFILE=" + UUID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ProcessResult Result = Run ("/usr/bin/env", Arguments, null, ERunOptions.Default);
|
|
if (bWasGenerated)
|
|
{
|
|
InternalUtils.SafeDeleteDirectory( XcodeProj, true);
|
|
}
|
|
if (Result.ExitCode != 0)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_FailedToCodeSign, "CodeSign Failed");
|
|
}
|
|
}
|
|
|
|
private void PackageIPA(string BaseDirectory, string GameName, string ProjectName, string ProjectDirectory, UnrealTargetConfiguration TargetConfig, bool Distribution = false)
|
|
{
|
|
// create the ipa
|
|
string IPAName = CombinePaths(ProjectDirectory, "Binaries", PlatformName, (Distribution ? "Distro_" : "") + ProjectName + (TargetConfig != UnrealTargetConfiguration.Development ? ("-" + PlatformName + "-" + TargetConfig.ToString()) : "") + ".ipa");
|
|
// delete the old one
|
|
if (File.Exists(IPAName))
|
|
{
|
|
File.Delete(IPAName);
|
|
}
|
|
|
|
// make the subdirectory if needed
|
|
string DestSubdir = Path.GetDirectoryName(IPAName);
|
|
if (!Directory.Exists(DestSubdir))
|
|
{
|
|
Directory.CreateDirectory(DestSubdir);
|
|
}
|
|
|
|
// set up the directories
|
|
string ZipWorkingDir = String.Format("Payload/{0}.app/", GameName);
|
|
string ZipSourceDir = string.Format("{0}/Payload/{1}.app", BaseDirectory, GameName);
|
|
|
|
// create the file
|
|
using (ZipFile Zip = new ZipFile())
|
|
{
|
|
// Set encoding to support unicode filenames
|
|
Zip.AlternateEncodingUsage = ZipOption.Always;
|
|
Zip.AlternateEncoding = Encoding.UTF8;
|
|
|
|
// set the compression level
|
|
if (Distribution)
|
|
{
|
|
Zip.CompressionLevel = CompressionLevel.BestCompression;
|
|
}
|
|
|
|
// add the entire directory
|
|
Zip.AddDirectory(ZipSourceDir, ZipWorkingDir);
|
|
|
|
// Update permissions to be UNIX-style
|
|
// Modify the file attributes of any added file to unix format
|
|
foreach (ZipEntry E in Zip.Entries)
|
|
{
|
|
const byte FileAttributePlatform_NTFS = 0x0A;
|
|
const byte FileAttributePlatform_UNIX = 0x03;
|
|
const byte FileAttributePlatform_FAT = 0x00;
|
|
|
|
const int UNIX_FILETYPE_NORMAL_FILE = 0x8000;
|
|
//const int UNIX_FILETYPE_SOCKET = 0xC000;
|
|
//const int UNIX_FILETYPE_SYMLINK = 0xA000;
|
|
//const int UNIX_FILETYPE_BLOCKSPECIAL = 0x6000;
|
|
const int UNIX_FILETYPE_DIRECTORY = 0x4000;
|
|
//const int UNIX_FILETYPE_CHARSPECIAL = 0x2000;
|
|
//const int UNIX_FILETYPE_FIFO = 0x1000;
|
|
|
|
const int UNIX_EXEC = 1;
|
|
const int UNIX_WRITE = 2;
|
|
const int UNIX_READ = 4;
|
|
|
|
|
|
int MyPermissions = UNIX_READ | UNIX_WRITE;
|
|
int OtherPermissions = UNIX_READ;
|
|
|
|
int PlatformEncodedBy = (E.VersionMadeBy >> 8) & 0xFF;
|
|
int LowerBits = 0;
|
|
|
|
// Try to preserve read-only if it was set
|
|
bool bIsDirectory = E.IsDirectory;
|
|
|
|
// Check to see if this
|
|
bool bIsExecutable = false;
|
|
if (Path.GetFileNameWithoutExtension(E.FileName).Equals(GameName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
bIsExecutable = true;
|
|
}
|
|
|
|
if (bIsExecutable)
|
|
{
|
|
// The executable will be encrypted in the final distribution IPA and will compress very poorly, so keeping it
|
|
// uncompressed gives a better indicator of IPA size for our distro builds
|
|
E.CompressionLevel = CompressionLevel.None;
|
|
}
|
|
|
|
if ((PlatformEncodedBy == FileAttributePlatform_NTFS) || (PlatformEncodedBy == FileAttributePlatform_FAT))
|
|
{
|
|
FileAttributes OldAttributes = E.Attributes;
|
|
//LowerBits = ((int)E.Attributes) & 0xFFFF;
|
|
|
|
if ((OldAttributes & FileAttributes.Directory) != 0)
|
|
{
|
|
bIsDirectory = true;
|
|
}
|
|
|
|
// Permissions
|
|
if ((OldAttributes & FileAttributes.ReadOnly) != 0)
|
|
{
|
|
MyPermissions &= ~UNIX_WRITE;
|
|
OtherPermissions &= ~UNIX_WRITE;
|
|
}
|
|
}
|
|
|
|
if (bIsDirectory || bIsExecutable)
|
|
{
|
|
MyPermissions |= UNIX_EXEC;
|
|
OtherPermissions |= UNIX_EXEC;
|
|
}
|
|
|
|
// Re-jigger the external file attributes to UNIX style if they're not already that way
|
|
if (PlatformEncodedBy != FileAttributePlatform_UNIX)
|
|
{
|
|
int NewAttributes = bIsDirectory ? UNIX_FILETYPE_DIRECTORY : UNIX_FILETYPE_NORMAL_FILE;
|
|
|
|
NewAttributes |= (MyPermissions << 6);
|
|
NewAttributes |= (OtherPermissions << 3);
|
|
NewAttributes |= (OtherPermissions << 0);
|
|
|
|
// Now modify the properties
|
|
E.AdjustExternalFileAttributes(FileAttributePlatform_UNIX, (NewAttributes << 16) | LowerBits);
|
|
}
|
|
}
|
|
|
|
// Save it out
|
|
Zip.Save(IPAName);
|
|
}
|
|
}
|
|
|
|
public override void GetFilesToDeployOrStage(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
// if (UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
|
|
{
|
|
// copy the icons/launch screens from the engine
|
|
{
|
|
string SourcePath = CombinePaths(SC.LocalRoot, "Engine", "Build", "IOS", "Resources", "Graphics");
|
|
SC.StageFiles(StagedFileType.NonUFS, SourcePath, "*.png", false, null, "", true, false);
|
|
}
|
|
|
|
// copy any additional framework assets that will be needed at runtime
|
|
{
|
|
string SourcePath = CombinePaths( ( SC.IsCodeBasedProject ? SC.ProjectRoot : SC.LocalRoot + "\\Engine" ), "Intermediate", "IOS", "FrameworkAssets" );
|
|
if ( Directory.Exists( SourcePath ) )
|
|
{
|
|
SC.StageFiles( StagedFileType.NonUFS, SourcePath, "*.*", true, null, "", true, false );
|
|
}
|
|
}
|
|
|
|
// copy the icons/launch screens from the game (may stomp the engine copies)
|
|
{
|
|
string SourcePath = CombinePaths(SC.ProjectRoot, "Build", "IOS", "Resources", "Graphics");
|
|
SC.StageFiles(StagedFileType.NonUFS, SourcePath, "*.png", false, null, "", true, false);
|
|
}
|
|
|
|
// copy the plist (only if code signing, as it's protected by the code sign blob in the executable and can't be modified independently)
|
|
if (GetCodeSignDesirability(Params))
|
|
{
|
|
string SourcePath = CombinePaths((SC.IsCodeBasedProject ? SC.ProjectRoot : SC.LocalRoot + "/Engine"), "Intermediate", PlatformName);
|
|
string TargetPListFile = Path.Combine(SourcePath, (SC.IsCodeBasedProject ? SC.ShortProjectName : "UE4Game") + "-Info.plist");
|
|
// if (!File.Exists(TargetPListFile))
|
|
{
|
|
// ensure the plist, entitlements, and provision files are properly copied
|
|
Console.WriteLine("CookPlat {0}, this {1}", GetCookPlatform(false, false, ""), ToString());
|
|
if (!SC.IsCodeBasedProject)
|
|
{
|
|
UnrealBuildTool.UnrealBuildTool.SetRemoteIniPath(SC.ProjectRoot);
|
|
}
|
|
GetDeployHandler(new FileReference(SC.ProjectRoot)).GeneratePList((SC.IsCodeBasedProject ? SC.ProjectRoot : SC.LocalRoot + "/Engine"), !SC.IsCodeBasedProject, (SC.IsCodeBasedProject ? SC.ShortProjectName : "UE4Game"), SC.ShortProjectName, SC.LocalRoot + "/Engine", (SC.IsCodeBasedProject ? SC.ProjectRoot : SC.LocalRoot + "/Engine") + "/Binaries/" + PlatformName + "/Payload/" + (SC.IsCodeBasedProject ? SC.ShortProjectName : "UE4Game") + ".app");
|
|
}
|
|
|
|
SC.StageFiles(StagedFileType.NonUFS, SourcePath, Path.GetFileName(TargetPListFile), false, null, "", false, false, "Info.plist");
|
|
}
|
|
}
|
|
|
|
// copy the movies from the project
|
|
{
|
|
SC.StageFiles(StagedFileType.NonUFS, CombinePaths(SC.ProjectRoot, "Build/IOS/Resources/Movies"), "*", false, null, "", true, false);
|
|
SC.StageFiles(StagedFileType.NonUFS, CombinePaths(SC.ProjectRoot, "Content/Movies"), "*", true, null, "", true, false);
|
|
}
|
|
}
|
|
|
|
public override void GetFilesToArchive(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
if (SC.StageTargetConfigurations.Count != 1)
|
|
{
|
|
throw new AutomationException("iOS is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
|
|
}
|
|
var TargetConfiguration = SC.StageTargetConfigurations[0];
|
|
var ProjectIPA = MakeIPAFileName( TargetConfiguration, Params );
|
|
|
|
// verify the .ipa exists
|
|
if (!FileExists(ProjectIPA))
|
|
{
|
|
throw new AutomationException("ARCHIVE FAILED - {0} was not found", ProjectIPA);
|
|
}
|
|
|
|
ConfigCacheIni PlatformGameConfig;
|
|
bool bXCArchive = false;
|
|
if (Params.EngineConfigs.TryGetValue(SC.StageTargetPlatform.PlatformType, out PlatformGameConfig))
|
|
{
|
|
PlatformGameConfig.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGenerateXCArchive", out bXCArchive);
|
|
}
|
|
|
|
if (bXCArchive && Utils.IsRunningOnMono)
|
|
{
|
|
// Always put the archive in the current user's Library/Developer/Xcode/Archives path
|
|
WindowsIdentity id = WindowsIdentity.GetCurrent();
|
|
string ArchivePath = "/Users/" + id.Name + "/Library/Developer/Xcode/Archives";
|
|
if (!DirectoryExists(ArchivePath))
|
|
{
|
|
CreateDirectory(ArchivePath);
|
|
}
|
|
|
|
Console.WriteLine("Generating xc archive package in " + ArchivePath);
|
|
|
|
string ArchiveName = Path.Combine(ArchivePath, Path.GetFileNameWithoutExtension(ProjectIPA) + ".xcarchive");
|
|
if (!DirectoryExists(ArchiveName))
|
|
{
|
|
CreateDirectory(ArchiveName);
|
|
}
|
|
DeleteDirectoryContents(ArchiveName);
|
|
|
|
// create the Products archive folder
|
|
CreateDirectory(Path.Combine(ArchiveName, "Products", "Applications"));
|
|
|
|
// copy in the application
|
|
string AppName = Path.GetFileNameWithoutExtension(ProjectIPA) + ".app";
|
|
using (ZipFile Zip = new ZipFile(ProjectIPA))
|
|
{
|
|
Zip.ExtractAll(ArchivePath, ExtractExistingFileAction.OverwriteSilently);
|
|
|
|
List<string> Dirs = new List<string>(Directory.EnumerateDirectories(Path.Combine(ArchivePath, "Payload"), "*.app"));
|
|
AppName = Dirs[0].Substring(Dirs[0].LastIndexOf(UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac ? "\\" : "/") + 1);
|
|
CopyDirectory_NoExceptions(Path.Combine(ArchivePath, "Payload", AppName), Path.Combine(ArchiveName, "Products", "Applications", AppName));
|
|
}
|
|
|
|
// copy in the dSYM if found
|
|
var ProjectExe = MakeExeFileName( TargetConfiguration, Params );
|
|
string dSYMName = (SC.IsCodeBasedProject ? Path.GetFileNameWithoutExtension(ProjectExe) : "UE4Game") + ".dSYM";
|
|
string dSYMSrcPath = Path.Combine(SC.ProjectBinariesFolder, dSYMName);
|
|
|
|
if(DirectoryExists(new string[] { dSYMSrcPath } ))
|
|
{
|
|
// Create the dsyms archive folder
|
|
CreateDirectory(Path.Combine(ArchiveName, "dSYMs"));
|
|
string dSYMDstPath = Path.Combine(ArchiveName, "dSYMs", dSYMName);
|
|
// /Volumes/MacOSDrive1/pfEpicWorkspace/Dev-Platform/Samples/Sandbox/PlatformShowcase/Binaries/IOS/PlatformShowcase.dSYM/Contents/Resources/DWARF/PlatformShowcase
|
|
CopyFile_NoExceptions(Path.Combine(dSYMSrcPath, "Contents", "Resources", "DWARF", SC.IsCodeBasedProject ? Path.GetFileNameWithoutExtension(ProjectExe) : "UE4Game"), dSYMDstPath);
|
|
}
|
|
else if (File.Exists(dSYMSrcPath))
|
|
{
|
|
// Create the dsyms archive folder
|
|
CreateDirectory(Path.Combine(ArchiveName, "dSYMs"));
|
|
string dSYMDstPath = Path.Combine(ArchiveName, "dSYMs", dSYMName);
|
|
CopyFile_NoExceptions(dSYMSrcPath, dSYMDstPath);
|
|
}
|
|
|
|
// copy in the bitcode symbol maps if found
|
|
string[] bcmapfiles = Directory.GetFiles(SC.ProjectBinariesFolder, "*.bcsymbolmap");
|
|
if(bcmapfiles.Length > 0)
|
|
{
|
|
// Create the dsyms archive folder
|
|
CreateDirectory(Path.Combine(ArchiveName, "BCSymbolMaps"));
|
|
foreach (string symbolSrcFilePath in bcmapfiles)
|
|
{
|
|
string symbolLeafFileName = Path.GetFileName(symbolSrcFilePath);
|
|
string bcDstFilePath = Path.Combine(ArchiveName, "BCSymbolMaps", symbolLeafFileName);
|
|
CopyFile_NoExceptions(symbolSrcFilePath, bcDstFilePath);
|
|
}
|
|
}
|
|
|
|
// get the settings from the app plist file
|
|
string AppPlist = Path.Combine(ArchiveName, "Products", "Applications", AppName, "Info.plist");
|
|
string OldPListData = File.Exists(AppPlist) ? File.ReadAllText(AppPlist) : "";
|
|
|
|
// bundle identifier
|
|
int index = OldPListData.IndexOf("CFBundleIdentifier");
|
|
index = OldPListData.IndexOf("<string>", index) + 8;
|
|
int length = OldPListData.IndexOf("</string>", index) - index;
|
|
string BundleIdentifier = OldPListData.Substring(index, length);
|
|
|
|
// short version
|
|
index = OldPListData.IndexOf("CFBundleShortVersionString");
|
|
index = OldPListData.IndexOf("<string>", index) + 8;
|
|
length = OldPListData.IndexOf("</string>", index) - index;
|
|
string BundleShortVersion = OldPListData.Substring(index, length);
|
|
|
|
// bundle version
|
|
index = OldPListData.IndexOf("CFBundleVersion");
|
|
index = OldPListData.IndexOf("<string>", index) + 8;
|
|
length = OldPListData.IndexOf("</string>", index) - index;
|
|
string BundleVersion = OldPListData.Substring(index, length);
|
|
|
|
// date we made this
|
|
const string Iso8601DateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ";
|
|
string TimeStamp = DateTime.UtcNow.ToString(Iso8601DateTimeFormat);
|
|
|
|
// create the archive plist
|
|
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>ApplicationProperties</key>");
|
|
Text.AppendLine("\t<dict>");
|
|
Text.AppendLine("\t\t<key>ApplicationPath</key>");
|
|
Text.AppendLine("\t\t<string>Applications/" + AppName + "</string>");
|
|
Text.AppendLine("\t\t<key>CFBundleIdentifier</key>");
|
|
Text.AppendLine(string.Format("\t\t<string>{0}</string>", BundleIdentifier));
|
|
Text.AppendLine("\t\t<key>CFBundleShortVersionString</key>");
|
|
Text.AppendLine(string.Format("\t\t<string>{0}</string>", BundleShortVersion));
|
|
Text.AppendLine("\t\t<key>CFBundleVersion</key>");
|
|
Text.AppendLine(string.Format("\t\t<string>{0}</string>", BundleVersion));
|
|
Text.AppendLine("\t\t<key>SigningIdentity</key>");
|
|
Text.AppendLine(string.Format("\t\t<string>{0}</string>", Params.Certificate));
|
|
Text.AppendLine("\t</dict>");
|
|
Text.AppendLine("\t<key>ArchiveVersion</key>");
|
|
Text.AppendLine("\t<integer>2</integer>");
|
|
Text.AppendLine("\t<key>CreationDate</key>");
|
|
Text.AppendLine(string.Format("\t<date>{0}</date>", TimeStamp));
|
|
Text.AppendLine("\t<key>DefaultToolchainInfo</key>");
|
|
Text.AppendLine("\t<dict>");
|
|
Text.AppendLine("\t\t<key>DisplayName</key>");
|
|
Text.AppendLine("\t\t<string>Xcode 7.3 Default</string>");
|
|
Text.AppendLine("\t\t<key>Identifier</key>");
|
|
Text.AppendLine("\t\t<string>com.apple.dt.toolchain.XcodeDefault</string>");
|
|
Text.AppendLine("\t</dict>");
|
|
Text.AppendLine("\t<key>Name</key>");
|
|
Text.AppendLine(string.Format("\t<string>{0}</string>", SC.ShortProjectName));
|
|
Text.AppendLine("\t<key>SchemeName</key>");
|
|
Text.AppendLine(string.Format("\t<string>{0}</string>", SC.ShortProjectName));
|
|
Text.AppendLine("</dict>");
|
|
Text.AppendLine("</plist>");
|
|
File.WriteAllText(Path.Combine(ArchiveName, "Info.plist"), Text.ToString());
|
|
}
|
|
else if (bXCArchive && !Utils.IsRunningOnMono)
|
|
{
|
|
LogWarning("Can not produce an XCArchive on windows");
|
|
}
|
|
SC.ArchiveFiles(Path.GetDirectoryName(ProjectIPA), Path.GetFileName(ProjectIPA));
|
|
}
|
|
|
|
public override bool RetrieveDeployedManifests(ProjectParams Params, DeploymentContext SC, out List<string> UFSManifests, out List<string> NonUFSManifests)
|
|
{
|
|
bool Result = true;
|
|
UFSManifests = new List<string>();
|
|
NonUFSManifests = new List<string>();
|
|
var DeployServer = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/IOS/DeploymentServer.exe");
|
|
try
|
|
{
|
|
var TargetConfiguration = SC.StageTargetConfigurations[0];
|
|
string BundleIdentifier = "";
|
|
if (File.Exists(Params.BaseStageDirectory + "/" + PlatformName + "/Info.plist"))
|
|
{
|
|
string Contents = File.ReadAllText(SC.StageDirectory + "/Info.plist");
|
|
int Pos = Contents.IndexOf("CFBundleIdentifier");
|
|
Pos = Contents.IndexOf("<string>", Pos) + 8;
|
|
int EndPos = Contents.IndexOf("</string>", Pos);
|
|
BundleIdentifier = Contents.Substring(Pos, EndPos - Pos);
|
|
}
|
|
RunAndLog(CmdEnv, DeployServer, "Backup -file \"" + CombinePaths(Params.BaseStageDirectory, PlatformName, SC.UFSDeployedManifestFileName) + "\" -file \"" + CombinePaths(Params.BaseStageDirectory, PlatformName, SC.NonUFSDeployedManifestFileName) + "\"" + (String.IsNullOrEmpty(Params.Device) ? "" : " -device " + Params.Device.Substring(Params.Device.IndexOf("@") + 1)) + " -bundle " + BundleIdentifier);
|
|
|
|
string[] ManifestFiles = Directory.GetFiles(CombinePaths(Params.BaseStageDirectory, PlatformName), "*_Manifest_UFS*.txt");
|
|
UFSManifests.AddRange(ManifestFiles);
|
|
|
|
ManifestFiles = Directory.GetFiles(CombinePaths(Params.BaseStageDirectory, PlatformName), "*_Manifest_NonUFS*.txt");
|
|
NonUFSManifests.AddRange(ManifestFiles);
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
// delete any files that did get copied
|
|
string[] Manifests = Directory.GetFiles(CombinePaths(Params.BaseStageDirectory, PlatformName), "*_Manifest_*.txt");
|
|
foreach (string Manifest in Manifests)
|
|
{
|
|
File.Delete(Manifest);
|
|
}
|
|
Result = false;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
public override void Deploy(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
if (SC.StageTargetConfigurations.Count != 1)
|
|
{
|
|
throw new AutomationException ("iOS is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
|
|
}
|
|
if (Params.Distribution)
|
|
{
|
|
throw new AutomationException("iOS cannot deploy a package made for distribution.");
|
|
}
|
|
var TargetConfiguration = SC.StageTargetConfigurations[0];
|
|
var ProjectIPA = MakeIPAFileName(TargetConfiguration, Params);
|
|
var StagedIPA = SC.StageDirectory + "\\" + Path.GetFileName(ProjectIPA);
|
|
|
|
// verify the .ipa exists
|
|
if (!FileExists(StagedIPA))
|
|
{
|
|
StagedIPA = ProjectIPA;
|
|
if(!FileExists(StagedIPA))
|
|
{
|
|
throw new AutomationException("DEPLOY FAILED - {0} was not found", ProjectIPA);
|
|
}
|
|
}
|
|
|
|
// if iterative deploy, determine the file delta
|
|
string BundleIdentifier = "";
|
|
bool bNeedsIPA = true;
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
if (File.Exists(Params.BaseStageDirectory + "/" + PlatformName + "/Info.plist"))
|
|
{
|
|
string Contents = File.ReadAllText(SC.StageDirectory + "/Info.plist");
|
|
int Pos = Contents.IndexOf("CFBundleIdentifier");
|
|
Pos = Contents.IndexOf("<string>", Pos) + 8;
|
|
int EndPos = Contents.IndexOf("</string>", Pos);
|
|
BundleIdentifier = Contents.Substring(Pos, EndPos - Pos);
|
|
}
|
|
|
|
// check to determine if we need to update the IPA
|
|
String NonUFSManifestPath = SC.GetNonUFSDeploymentDeltaPath();
|
|
if (File.Exists(NonUFSManifestPath))
|
|
{
|
|
string NonUFSFiles = File.ReadAllText(NonUFSManifestPath);
|
|
string[] Lines = NonUFSFiles.Split('\n');
|
|
bNeedsIPA = Lines.Length > 0 && !string.IsNullOrWhiteSpace(Lines[0]);
|
|
}
|
|
}
|
|
|
|
// Add a commandline for this deploy, if the config allows it.
|
|
string AdditionalCommandline = (Params.FileServer || Params.CookOnTheFly) ? "" : (" -additionalcommandline " + "\"" + Params.RunCommandline + "\"");
|
|
|
|
// deploy the .ipa
|
|
var DeployServer = CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/IOS/DeploymentServer.exe");
|
|
|
|
// check for it in the stage directory
|
|
string CurrentDir = Directory.GetCurrentDirectory();
|
|
Directory.SetCurrentDirectory(CombinePaths(CmdEnv.LocalRoot, "Engine/Binaries/DotNET/IOS/"));
|
|
if (!Params.IterativeDeploy || bCreatedIPA || bNeedsIPA)
|
|
{
|
|
RunAndLog(CmdEnv, DeployServer, "Install -ipa \"" + Path.GetFullPath(StagedIPA) + "\"" + (String.IsNullOrEmpty(Params.Device) ? "" : " -device " + Params.Device.Substring(Params.Device.IndexOf("@") + 1)) + AdditionalCommandline);
|
|
}
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
// push over the changed files
|
|
RunAndLog(CmdEnv, DeployServer, "Deploy -manifest \"" + CombinePaths(Params.BaseStageDirectory, PlatformName, DeploymentContext.UFSDeployDeltaFileName) + "\"" + (String.IsNullOrEmpty(Params.Device) ? "" : " -device " + Params.Device.Substring(Params.Device.IndexOf("@") + 1)) + AdditionalCommandline + " -bundle " + BundleIdentifier);
|
|
}
|
|
Directory.SetCurrentDirectory (CurrentDir);
|
|
PrintRunTime();
|
|
}
|
|
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly, string CookFlavor)
|
|
{
|
|
return "IOS";
|
|
}
|
|
|
|
public override bool DeployPakInternalLowerCaseFilenames()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool DeployLowerCaseFilenames(bool bUFSFile)
|
|
{
|
|
// we shouldn't modify the case on files like Info.plist or the icons
|
|
return bUFSFile;
|
|
}
|
|
|
|
public override string LocalPathToTargetPath(string LocalPath, string LocalRoot)
|
|
{
|
|
return LocalPath.Replace("\\", "/").Replace(LocalRoot, "../../..");
|
|
}
|
|
|
|
public override bool IsSupported { get { return true; } }
|
|
|
|
public override bool LaunchViaUFE { get { return UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac; } }
|
|
|
|
public override bool UseAbsLog
|
|
{
|
|
get
|
|
{
|
|
return !LaunchViaUFE;
|
|
}
|
|
}
|
|
|
|
public override string Remap(string Dest)
|
|
{
|
|
return "cookeddata/" + Dest;
|
|
}
|
|
public override List<string> GetDebugFileExtentions()
|
|
{
|
|
return new List<string> { ".dsym" };
|
|
}
|
|
|
|
public override ProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
|
|
{
|
|
if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
|
|
{
|
|
/* string AppDirectory = string.Format("{0}/Payload/{1}.app",
|
|
Path.GetDirectoryName(Params.ProjectGameExeFilename),
|
|
Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename));
|
|
string GameName = Path.GetFileNameWithoutExtension (ClientApp);
|
|
if (GameName.Contains ("-IOS-"))
|
|
{
|
|
GameName = GameName.Substring (0, GameName.IndexOf ("-IOS-"));
|
|
}
|
|
string GameApp = AppDirectory + "/" + GameName;
|
|
bWasGenerated = false;
|
|
XcodeProj = EnsureXcodeProjectExists (Params.RawProjectPath, CmdEnv.LocalRoot, Params.ShortProjectName, GetDirectoryName(Params.RawProjectPath), Params.IsCodeBasedProject, out bWasGenerated);
|
|
string Arguments = "UBT_NO_POST_DEPLOY=true /usr/bin/xcrun xcodebuild test -project \"" + XcodeProj + "\"";
|
|
Arguments += " -scheme '";
|
|
Arguments += GameName;
|
|
Arguments += " - iOS'";
|
|
Arguments += " -configuration " + Params.ClientConfigsToBuild [0].ToString();
|
|
Arguments += " -destination 'platform=iOS,id=" + Params.Device.Substring(Params.Device.IndexOf("@")+1) + "'";
|
|
Arguments += " TEST_HOST=\"";
|
|
Arguments += GameApp;
|
|
Arguments += "\" BUNDLE_LOADER=\"";
|
|
Arguments += GameApp + "\"";*/
|
|
string BundleIdentifier = "";
|
|
if (File.Exists(Params.BaseStageDirectory + "/"+ PlatformName + "/Info.plist"))
|
|
{
|
|
string Contents = File.ReadAllText(Params.BaseStageDirectory + "/" + PlatformName + "/Info.plist");
|
|
int Pos = Contents.IndexOf("CFBundleIdentifier");
|
|
Pos = Contents.IndexOf("<string>", Pos) + 8;
|
|
int EndPos = Contents.IndexOf("</string>", Pos);
|
|
BundleIdentifier = Contents.Substring(Pos, EndPos - Pos);
|
|
}
|
|
string Arguments = "/usr/bin/instruments";
|
|
Arguments += " -w '" + Params.Device.Substring(Params.Device.IndexOf("@") + 1) + "'";
|
|
Arguments += " -t 'Activity Monitor'";
|
|
Arguments += " -D \"" + Params.BaseStageDirectory + "/" + PlatformName + "/launch.trace\"";
|
|
Arguments += " '" + BundleIdentifier + "'";
|
|
ProcessResult ClientProcess = Run ("/usr/bin/env", Arguments, null, ClientRunFlags | ERunOptions.NoWaitForExit);
|
|
return ClientProcess;
|
|
}
|
|
else
|
|
{
|
|
ProcessResult Result = new ProcessResult("DummyApp", null, false, null);
|
|
Result.ExitCode = 0;
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
public override void PostRunClient(ProcessResult Result, ProjectParams Params)
|
|
{
|
|
if (UnrealBuildTool.BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
|
|
{
|
|
string LaunchTracePath = Params.BaseStageDirectory + "/" + PlatformName + "/launch.trace";
|
|
Console.WriteLine ("Deleting " + LaunchTracePath);
|
|
if (Directory.Exists(LaunchTracePath))
|
|
{
|
|
Directory.Delete (LaunchTracePath, true);
|
|
}
|
|
|
|
switch (Result.ExitCode)
|
|
{
|
|
case 253:
|
|
throw new AutomationException(ExitCode.Error_DeviceNotSetupForDevelopment, "Launch Failure");
|
|
case 255:
|
|
throw new AutomationException(ExitCode.Error_DeviceOSNewerThanSDK, "Launch Failure");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int GetChunkCount(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
var ChunkListFilename = GetChunkPakManifestListFilename(Params, SC);
|
|
var ChunkArray = ReadAllLines(ChunkListFilename);
|
|
return ChunkArray.Length;
|
|
}
|
|
|
|
private static string GetChunkPakManifestListFilename(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
return CombinePaths(GetTmpPackagingPath(Params, SC), "pakchunklist.txt");
|
|
}
|
|
|
|
private static string GetTmpPackagingPath(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
return CombinePaths(Path.GetDirectoryName(Params.RawProjectPath.FullName), "Saved", "TmpPackaging", SC.StageTargetPlatform.GetCookPlatform(SC.DedicatedServer, false, Params.CookFlavor));
|
|
}
|
|
|
|
private static StringBuilder AppendKeyValue(StringBuilder Text, string Key, object Value, int Level)
|
|
{
|
|
// create indent level
|
|
string Indent = "";
|
|
for (int i = 0; i < Level; ++i)
|
|
{
|
|
Indent += "\t";
|
|
}
|
|
|
|
// output key if we have one
|
|
if (Key != null)
|
|
{
|
|
Text.AppendLine (Indent + "<key>" + Key + "</key>");
|
|
}
|
|
|
|
// output value
|
|
if (Value is Array)
|
|
{
|
|
Text.AppendLine (Indent + "<array>");
|
|
Array ValArray = Value as Array;
|
|
foreach (var Item in ValArray)
|
|
{
|
|
AppendKeyValue (Text, null, Item, Level + 1);
|
|
}
|
|
Text.AppendLine (Indent + "</array>");
|
|
}
|
|
else if (Value is Dictionary<string, object>)
|
|
{
|
|
Text.AppendLine (Indent + "<dict>");
|
|
Dictionary<string,object> ValDict = Value as Dictionary<string, object>;
|
|
foreach (var Item in ValDict)
|
|
{
|
|
AppendKeyValue (Text, Item.Key, Item.Value, Level + 1);
|
|
}
|
|
Text.AppendLine (Indent + "</dict>");
|
|
}
|
|
else if (Value is string)
|
|
{
|
|
Text.AppendLine (Indent + "<string>" + Value + "</string>");
|
|
}
|
|
else if (Value is bool)
|
|
{
|
|
if ((bool)Value == true)
|
|
{
|
|
Text.AppendLine (Indent + "<true/>");
|
|
}
|
|
else
|
|
{
|
|
Text.AppendLine (Indent + "<false/>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine ("PLIST: Unknown array item type");
|
|
}
|
|
return Text;
|
|
}
|
|
|
|
private static void GeneratePlist(Dictionary<string, object> KeyValues, string PlistFile)
|
|
{
|
|
// generate the plist file
|
|
StringBuilder Text = new StringBuilder();
|
|
|
|
// boiler plate top
|
|
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>");
|
|
|
|
foreach (var KeyValue in KeyValues)
|
|
{
|
|
AppendKeyValue(Text, KeyValue.Key, KeyValue.Value, 1);
|
|
}
|
|
Text.AppendLine("</dict>");
|
|
Text.AppendLine("</plist>");
|
|
|
|
// write the file out
|
|
if (!Directory.Exists(Path.GetDirectoryName(PlistFile)))
|
|
{
|
|
Directory.CreateDirectory(Path.GetDirectoryName(PlistFile));
|
|
}
|
|
File.WriteAllText(PlistFile, Text.ToString());
|
|
}
|
|
|
|
private static void GenerateAssetPlist(string BundleIdentifier, string[] Tags, string AssetDir)
|
|
{
|
|
Dictionary<string, object> KeyValues = new Dictionary<string, object> ();
|
|
KeyValues.Add ("CFBundleIdentifier", BundleIdentifier);
|
|
KeyValues.Add ("Tags", Tags);
|
|
GeneratePlist(KeyValues, CombinePaths(AssetDir, "Info.plist"));
|
|
}
|
|
|
|
private static void GenerateAssetPackManifestPlist(KeyValuePair<string, string>[] ChunkData, string AssetDir)
|
|
{
|
|
Dictionary<string, object>[] Resources = new Dictionary<string, object>[ChunkData.Length];
|
|
for (int i = 0; i < ChunkData.Length; ++i)
|
|
{
|
|
Dictionary<string, object> Data = new Dictionary<string, object> ();
|
|
Data.Add ("URL", CombinePaths ("OnDemandResources", ChunkData[i].Value));
|
|
Data.Add ("bundleKey", ChunkData [i].Key);
|
|
Data.Add ("isStreamable", false);
|
|
Resources [i] = Data;
|
|
}
|
|
|
|
Dictionary<string, object> KeyValues = new Dictionary<string, object> ();
|
|
KeyValues.Add ("resources", Resources);
|
|
GeneratePlist(KeyValues, CombinePaths(AssetDir, "AssetPackManifest.plist"));
|
|
}
|
|
|
|
private static void GenerateOnDemandResourcesPlist(KeyValuePair<string, string>[] ChunkData, string AssetDir)
|
|
{
|
|
Dictionary<string, object> RequestTags = new Dictionary<string, object> ();
|
|
Dictionary<string, object> AssetPacks = new Dictionary<string, object> ();
|
|
Dictionary<string, object> Requests = new Dictionary<string, object> ();
|
|
for (int i = 0; i < ChunkData.Length; ++i)
|
|
{
|
|
string ChunkName = "Chunk" + (i + 1).ToString ();
|
|
RequestTags.Add (ChunkName, new string[] { ChunkData [i].Key });
|
|
AssetPacks.Add (ChunkData [i].Key, new string[] { ("pak" + ChunkName + "-ios.pak").ToLowerInvariant () });
|
|
Dictionary<string, object> Packs = new Dictionary<string, object> ();
|
|
Packs.Add ("NSAssetPacks", new string[] { ChunkData [i].Key });
|
|
Requests.Add (ChunkName, Packs);
|
|
}
|
|
|
|
Dictionary<string, object> KeyValues = new Dictionary<string, object> ();
|
|
KeyValues.Add ("NSBundleRequestTags", RequestTags);
|
|
KeyValues.Add ("NSBundleResourceRequestAssetPacks", AssetPacks);
|
|
KeyValues.Add ("NSBundleResourceRequestTags", Requests);
|
|
GeneratePlist(KeyValues, CombinePaths(AssetDir, "OnDemandResources.plist"));
|
|
}
|
|
|
|
public override void PostStagingFileCopy(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
if (Params.CreateChunkInstall)
|
|
{
|
|
// get the bundle identifier
|
|
string BundleIdentifier = "";
|
|
if (File.Exists(Params.BaseStageDirectory + "/" + PlatformName + "/Info.plist"))
|
|
{
|
|
string Contents = File.ReadAllText(SC.StageDirectory + "/Info.plist");
|
|
int Pos = Contents.IndexOf("CFBundleIdentifier");
|
|
Pos = Contents.IndexOf("<string>", Pos) + 8;
|
|
int EndPos = Contents.IndexOf("</string>", Pos);
|
|
BundleIdentifier = Contents.Substring(Pos, EndPos - Pos);
|
|
}
|
|
|
|
// generate the ODR resources
|
|
// create the ODR directory
|
|
string DestSubdir = SC.StageDirectory + "/OnDemandResources";
|
|
if (!Directory.Exists(DestSubdir))
|
|
{
|
|
Directory.CreateDirectory(DestSubdir);
|
|
}
|
|
|
|
// read the chunk list and generate the data
|
|
var ChunkCount = GetChunkCount(Params, SC);
|
|
var ChunkData = new KeyValuePair<string, string>[ChunkCount - 1];
|
|
for (int i = 1; i < ChunkCount; ++i)
|
|
{
|
|
// chunk name
|
|
string ChunkName = "Chunk" + i.ToString ();
|
|
|
|
// asset name
|
|
string AssetPack = BundleIdentifier + ".Chunk" + i.ToString () + ".assetpack";
|
|
|
|
// bundle key
|
|
byte[] bytes = new byte[ChunkName.Length * sizeof(char)];
|
|
System.Buffer.BlockCopy(ChunkName.ToCharArray(), 0, bytes, 0, bytes.Length);
|
|
string BundleKey = BundleIdentifier + ".asset-pack-" + BitConverter.ToString(System.Security.Cryptography.MD5.Create().ComputeHash(bytes)).Replace("-", string.Empty);
|
|
|
|
// add to chunk data
|
|
ChunkData[i-1] = new KeyValuePair<string, string>(BundleKey, AssetPack);
|
|
|
|
// create the sub directory
|
|
string AssetDir = CombinePaths (DestSubdir, AssetPack);
|
|
if (!Directory.Exists(AssetDir))
|
|
{
|
|
Directory.CreateDirectory(AssetDir);
|
|
}
|
|
|
|
// generate the Info.plist for each ODR bundle (each chunk for install past 0)
|
|
GenerateAssetPlist (BundleKey, new string[] { ChunkName }, AssetDir);
|
|
|
|
// copy the files to the OnDemandResources directory
|
|
string PakName = "pakchunk" + i.ToString ();
|
|
string FileName = PakName + "-" + PlatformName.ToLower() + ".pak";
|
|
string P4Change = "UnknownCL";
|
|
string P4Branch = "UnknownBranch";
|
|
if (CommandUtils.P4Enabled)
|
|
{
|
|
P4Change = CommandUtils.P4Env.ChangelistString;
|
|
P4Branch = CommandUtils.P4Env.BuildRootEscaped;
|
|
}
|
|
string ChunkInstallBasePath = CombinePaths(SC.ProjectRoot, "ChunkInstall", SC.FinalCookPlatform);
|
|
string RawDataPath = CombinePaths(ChunkInstallBasePath, P4Branch + "-CL-" + P4Change, PakName);
|
|
string RawDataPakPath = CombinePaths(RawDataPath, PakName + "-" + SC.FinalCookPlatform + ".pak");
|
|
string DestFile = CombinePaths (AssetDir, FileName);
|
|
CopyFile (RawDataPakPath, DestFile);
|
|
}
|
|
|
|
// generate the AssetPackManifest.plist
|
|
GenerateAssetPackManifestPlist (ChunkData, SC.StageDirectory);
|
|
|
|
// generate the OnDemandResources.plist
|
|
GenerateOnDemandResourcesPlist (ChunkData, SC.StageDirectory);
|
|
}
|
|
}
|
|
|
|
public override bool StageMovies
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool RequiresPackageToDeploy
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override List<string> GetFilesForCRCCheck()
|
|
{
|
|
List<string> FileList = base.GetFilesForCRCCheck();
|
|
FileList.Add("Info.plist");
|
|
return FileList;
|
|
}
|
|
|
|
#region Hooks
|
|
|
|
public override void PreBuildAgenda(UE4Build Build, UE4Build.BuildAgenda Agenda)
|
|
{
|
|
if (UnrealBuildTool.BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
|
|
{
|
|
Agenda.DotNetProjects.Add(@"Engine\Source\Programs\IOS\MobileDeviceInterface\MobileDeviceInterface.csproj");
|
|
Agenda.DotNetProjects.Add(@"Engine\Source\Programs\IOS\iPhonePackager\iPhonePackager.csproj");
|
|
Agenda.DotNetProjects.Add(@"Engine\Source\Programs\IOS\DeploymentServer\DeploymentServer.csproj");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|