Files
UnrealEngine/Engine/Source/Programs/UnrealBuildTool/Platform/IOS/UEBuildIOS.cs
2025-05-18 13:04:45 +08:00

1372 lines
46 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using UnrealBuildBase;
namespace UnrealBuildTool
{
partial struct UnrealArch
{
// @todo add x64 simulators to run on old macs?
/// <summary>
/// IOS Simulator
/// </summary>
public static UnrealArch IOSSimulator { get; } = FindOrAddByName("iossimulator", bIsX64: false);
/// <summary>
/// TVOS Simulator
/// </summary>
public static UnrealArch TVOSSimulator { get; } = FindOrAddByName("tvossimulator", bIsX64: false);
private static IReadOnlyDictionary<UnrealArch, string> AppleToolchainArchitectures = new Dictionary<UnrealArch, string>()
{
{ UnrealArch.Arm64, "arm64" },
{ UnrealArch.X64, "x86_64" },
{ UnrealArch.IOSSimulator, "arm64" },
{ UnrealArch.TVOSSimulator, "arm64" },
};
/// <summary>
/// Apple-specific low level name for the generic platforms
/// </summary>
public string AppleName
{
get
{
if (AppleToolchainArchitectures.ContainsKey(this))
{
return AppleToolchainArchitectures[this];
}
throw new BuildException($"Unknown architecture {ToString()} passed to UnrealArch.AppleName");
}
}
}
/// <summary>
/// IOS-specific target settings
/// </summary>
public partial class IOSTargetRules
{
/// <summary>
/// Whether to strip iOS symbols or not (implied by Shipping config).
/// </summary>
[XmlConfigFile(Category = "BuildConfiguration")]
[CommandLine("-stripsymbols", Value = "true")]
public bool bStripSymbols = false;
/// <summary>
///
/// </summary>
public bool bShipForBitcode = false;
/// <summary>
/// If true, then a stub IPA will be generated when compiling is done (minimal files needed for a valid IPA).
/// </summary>
[CommandLine("-CreateStub", Value = "true")]
public bool bCreateStubIPA = false;
/// <summary>
/// Whether to generate a native Xcode project as a wrapper for the framework.
/// </summary>
public bool bGenerateFrameworkWrapperProject = false;
/// <summary>
/// Don't generate crashlytics data
/// </summary>
[CommandLine("-alwaysgeneratedsym", Value = "true")]
[CommandLine("-EnableDSYM", Value = "true")]
[XmlConfigFile(Category = "BuildConfiguration", Name = "bUseDSYMFiles")]
public bool bGeneratedSYM = false;
/// <summary>
/// Don't generate crashlytics data
/// </summary>
[CommandLine("-skipcrashlytics")]
public bool bSkipCrashlytics = false;
/// <summary>
/// Disables clang build verification checks on static libraries
/// </summary>
[CommandLine("-skipclangvalidation", Value = "true")]
[XmlConfigFile(Category = "BuildConfiguration", Name = "bSkipClangValidation")]
public bool bSkipClangValidation = false;
/// <summary>
/// Mark the build for distribution
/// </summary>
[CommandLine("-distribution")]
public bool bForDistribution = false;
/// <summary>
/// Manual override for the provision to use. Should be a full path.
/// </summary>
[CommandLine("-ImportProvision=")]
public string? ImportProvision = null;
/// <summary>
/// Imports the given certificate (inc private key) into a temporary keychain before signing.
/// </summary>
[CommandLine("-ImportCertificate=")]
public string? ImportCertificate = null;
/// <summary>
/// Password for the imported certificate
/// </summary>
[CommandLine("-ImportCertificatePassword=")]
public string? ImportCertificatePassword = null;
/// <summary>
/// Cached project settings for the target (set in ResetTarget)
/// </summary>
public IOSProjectSettings? ProjectSettings = null;
/// <summary>
/// Enables address sanitizer (ASan)
/// </summary>
[CommandLine("-EnableASan")]
public bool bEnableAddressSanitizer = false;
/// <summary>
/// Enables thread sanitizer (TSan)
/// </summary>
[CommandLine("-EnableTSan")]
public bool bEnableThreadSanitizer = false;
/// <summary>
/// Enables undefined behavior sanitizer (UBSan)
/// </summary>
[CommandLine("-EnableUBSan")]
public bool bEnableUndefinedBehaviorSanitizer = false;
/// <summary>
/// Constructor
/// </summary>
public IOSTargetRules()
{
string? AddressSanitizer = Environment.GetEnvironmentVariable("ENABLE_ADDRESS_SANITIZER");
if (AddressSanitizer != null && AddressSanitizer == "YES")
{
bEnableAddressSanitizer = true;
}
string? ThreadSanitizer = Environment.GetEnvironmentVariable("ENABLE_THREAD_SANITIZER");
if (ThreadSanitizer != null && ThreadSanitizer == "YES")
{
bEnableThreadSanitizer = true;
}
string? UndefSanitizerMode = Environment.GetEnvironmentVariable("ENABLE_UNDEFINED_BEHAVIOR_SANITIZER");
if (UndefSanitizerMode != null && UndefSanitizerMode == "YES")
{
bEnableUndefinedBehaviorSanitizer = true;
}
}
}
/// <summary>
/// Read-only wrapper for IOS-specific target settings
/// </summary>
public partial class ReadOnlyIOSTargetRules
{
/// <summary>
/// The private mutable settings object
/// </summary>
private IOSTargetRules Inner;
/// <summary>
/// Constructor
/// </summary>
/// <param name="Inner">The settings object to wrap</param>
public ReadOnlyIOSTargetRules(IOSTargetRules Inner)
{
this.Inner = Inner;
}
/// <summary>
/// Accessors for fields on the inner TargetRules instance
/// </summary>
#region Read-only accessor properties
#pragma warning disable CS1591
public bool bStripSymbols => Inner.bStripSymbols;
public bool bGenerateFrameworkWrapperProject => Inner.bGenerateFrameworkWrapperProject;
public bool bGeneratedSYM => Inner.bGeneratedSYM;
public bool bCreateStubIPA => Inner.bCreateStubIPA;
public bool bSkipCrashlytics => Inner.bSkipCrashlytics;
public bool bSkipClangValidation => Inner.bSkipClangValidation;
public bool bForDistribution => Inner.bForDistribution;
public string? ImportProvision => Inner.ImportProvision;
public string? ImportCertificate => Inner.ImportCertificate;
public string? ImportCertificatePassword => Inner.ImportCertificatePassword;
public float RuntimeVersion => Single.Parse(Inner.ProjectSettings!.RuntimeVersion, System.Globalization.CultureInfo.InvariantCulture);
public bool bEnableAddressSanitizer => Inner.bEnableAddressSanitizer;
public bool bEnableThreadSanitizer => Inner.bEnableThreadSanitizer;
public bool bEnableUndefinedBehaviorSanitizer => Inner.bEnableUndefinedBehaviorSanitizer;
#pragma warning restore CS1591
#endregion
}
/// <summary>
/// Stores project-specific IOS settings. Instances of this object are cached by IOSPlatform.
/// </summary>
public class IOSProjectSettings
{
/// <summary>
/// The cached project file location
/// </summary>
public readonly FileReference? ProjectFile;
/// <summary>
/// Whether to build the iOS project as a framework.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bBuildAsFramework")]
[CommandLine("-build-as-framework")]
public readonly bool bBuildAsFramework = false;
/// <summary>
/// Whether to generate a native Xcode project as a wrapper for the framework.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGenerateFrameworkWrapperProject")]
public readonly bool bGenerateFrameworkWrapperProject = false;
/// <summary>
/// Whether to generate a dSYM file or not.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGeneratedSYMFile")]
[CommandLine("-generatedsymfile")]
public readonly bool bGeneratedSYMFile = false;
/// <summary>
/// Whether to generate a dSYM bundle (as opposed to single file dSYM)
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGeneratedSYMBundle")]
[CommandLine("-generatedsymbundle")]
public readonly bool bGeneratedSYMBundle = false;
/// <summary>
/// Whether to generate a dSYM file or not.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bGenerateCrashReportSymbols")]
public readonly bool bGenerateCrashReportSymbols = false;
/// <summary>
/// The minimum supported version
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumiOSVersion")]
private readonly string? MinimumIOSVersion = null;
/// <summary>
/// Whether to support iPhone
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsIPhone")]
private readonly bool bSupportsIPhone = true;
/// <summary>
/// Whether to support iPad
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsIPad")]
private readonly bool bSupportsIPad = true;
/// <summary>
/// additional linker flags for shipping
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalShippingLinkerFlags")]
public readonly string AdditionalShippingLinkerFlags = "";
/// <summary>
/// additional linker flags for non-shipping
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalLinkerFlags")]
public readonly string AdditionalLinkerFlags = "";
/// <summary>
/// mobile provision to use for code signing
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MobileProvision")]
public readonly string MobileProvision = "";
/// <summary>
/// signing certificate to use for code signing
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "SigningCertificate")]
public readonly string SigningCertificate = "";
/// <summary>
/// true if notifications are enabled
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableStoreKitSupport")]
public readonly bool bEnableStoreKitSupport = true;
/// <summary>
/// true if notifications are enabled
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableRemoteNotificationsSupport")]
public readonly bool bNotificationsEnabled = false;
/// <summary>
/// true if notifications are enabled
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableBackgroundFetch")]
public readonly bool bBackgroundFetchEnabled = false;
/// <summary>
/// true if iTunes file sharing support is enabled
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsITunesFileSharing")]
public readonly bool bFileSharingEnabled = false;
/// <summary>
/// The bundle identifier
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier")]
public readonly string BundleIdentifier = "";
/// <summary>
/// true if using Xcode managed provisioning, else false
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bAutomaticSigning")]
public readonly bool bAutomaticSigning = false;
/// <summary>
/// The IOS Team ID
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "IOSTeamID")]
public readonly string TeamID = "";
/// <summary>
/// true to change FORCEINLINE to a regular INLINE.
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bDisableForceInline")]
public readonly bool bDisableForceInline = false;
/// <summary>
/// true if IDFA are enabled
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableAdvertisingIdentifier")]
public readonly bool bEnableAdvertisingIdentifier = false;
/// <summary>
/// true when building for distribution
/// </summary>
[ConfigFile(ConfigHierarchyType.Game, "/Script/UnrealEd.ProjectPackagingSettings", "ForDistribution")]
public readonly bool bForDistribution = false;
/// <summary>
/// override for the app's display name if different from the project name
/// </summary>
[ConfigFile(ConfigHierarchyType.Game, "/Script/UnrealEd.ProjectPackagingSettings", "BundleName")]
public readonly string BundleName = "";
/// <summary>
/// longer display name than BundleName if needed
/// </summary>
[ConfigFile(ConfigHierarchyType.Game, "/Script/UnrealEd.ProjectPackagingSettings", "BundleDisplayName")]
public readonly string BundleDisplayName = "";
/// <summary>
/// Which version of the iOS to allow at run time
/// </summary>
public virtual string RuntimeVersion
{
get
{
switch (MinimumIOSVersion)
{
case "IOS_15":
return "15.0";
case "IOS_16":
return "16.0";
case "IOS_17":
return "17.0";
case "IOS_18":
return "18.0";
case "IOS_Minimum":
default:
if (String.IsNullOrEmpty(MinimumIOSVersion) || MinimumIOSVersion.StartsWith("IOS_"))
{
return UEBuildPlatformSDK.GetSDKForPlatform("IOS")!.GetSoftwareInfo()!.Min!;
}
else
{
// Assume the user is overriding the defaults, ie: "17.4.1"
return MinimumIOSVersion;
}
}
}
}
/// <summary>
/// Enables iOS 16 dynamic linker workaround
/// </summary>
[ConfigFile(ConfigHierarchyType.Engine, "/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableIOS16DynLinkerBugWAR")]
public readonly bool bEnableIOS16DynLinkerBugWAR = false;
/// <summary>
/// which devices the game is allowed to run on
/// </summary>
public virtual string RuntimeDevices
{
get
{
if (bSupportsIPad && !bSupportsIPhone)
{
return "2";
}
else if (!bSupportsIPad && bSupportsIPhone)
{
return "1";
}
else
{
return "1,2";
}
}
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="ProjectFile">The project file to read settings for</param>
/// <param name="Bundle">Bundle identifier needed when project file is empty</param>
public IOSProjectSettings(FileReference? ProjectFile, string? Bundle)
: this(ProjectFile, UnrealTargetPlatform.IOS, Bundle)
{
}
/// <summary>
/// Protected constructor. Used by TVOSProjectSettings.
/// </summary>
/// <param name="ProjectFile">The project file to read settings for</param>
/// <param name="Platform">The platform to read settings for</param>
/// <param name="Bundle">Bundle identifier needed when project file is empty</param>
protected IOSProjectSettings(FileReference? ProjectFile, UnrealTargetPlatform Platform, string? Bundle)
{
this.ProjectFile = ProjectFile;
ConfigCache.ReadSettings(DirectoryReference.FromFile(ProjectFile), Platform, this);
if ((ProjectFile == null || String.IsNullOrEmpty(ProjectFile.FullName)) && !String.IsNullOrEmpty(Bundle))
{
BundleIdentifier = Bundle;
}
BundleIdentifier = BundleIdentifier.Replace("[PROJECT_NAME]", ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UnrealGame")).Replace("_", "");
}
}
/// <summary>
/// IOS provisioning data
/// </summary>
class IOSProvisioningData
{
public string? SigningCertificate;
public FileReference? MobileProvisionFile;
public string? MobileProvisionUUID;
public string? MobileProvisionName;
public string? TeamUUID;
public string? BundleIdentifier;
public bool bHaveCertificate = false;
public string? MobileProvision => MobileProvisionFile?.GetFileName();
public IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bForDistribution, ILogger Logger)
: this(ProjectSettings, false, bForDistribution, Logger)
{
}
protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribution, ILogger Logger)
{
SigningCertificate = ProjectSettings.SigningCertificate;
string? MobileProvision = ProjectSettings.MobileProvision;
FileReference? ProjectFile = ProjectSettings.ProjectFile;
CodeSigningConfig.Initialize(ProjectFile, bIsTVOS);
if (!String.IsNullOrEmpty(SigningCertificate))
{
List<string> Certs = AppleCodeSign.FindCertificates();
List<FileReference> Provisions = AppleCodeSign.FindProvisions(ProjectSettings.BundleIdentifier, bForDistribution, out _);
}
else
{
SigningCertificate = bForDistribution ? "iPhone Distribution" : "iPhone Developer";
bHaveCertificate = true;
}
if (!String.IsNullOrEmpty(MobileProvision))
{
DirectoryReference MobileProvisionDir;
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
{
MobileProvisionDir = DirectoryReference.Combine(new DirectoryReference(Environment.GetEnvironmentVariable("HOME")!), "Library", "MobileDevice", "Provisioning Profiles");
}
else
{
MobileProvisionDir = DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.LocalApplicationData)!, "Apple Computer", "MobileDevice", "Provisioning Profiles");
}
FileReference PossibleMobileProvisionFile = FileReference.Combine(MobileProvisionDir, MobileProvision);
if (FileReference.Exists(PossibleMobileProvisionFile))
{
MobileProvisionFile = PossibleMobileProvisionFile;
}
}
if (MobileProvisionFile == null || !bHaveCertificate)
{
SigningCertificate = "";
MobileProvision = "";
MobileProvisionFile = null;
Logger.LogInformation("Provision not specified or not found for {Project}, searching for compatible match...", ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UnrealGame"));
if (AppleCodeSign.FindCertAndProvision(ProjectSettings.BundleIdentifier, out MobileProvisionFile, out SigningCertificate))
{
MobileProvision = MobileProvisionFile!.FullName;
}
if (MobileProvisionFile != null)
{
Logger.LogInformation("Provision found for {Project}, Provision: {Provision}, Certificate: {Certificate}", ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UnrealGame"), MobileProvisionFile, SigningCertificate);
}
}
// add to the dictionary
SigningCertificate = SigningCertificate.Replace("\"", "");
// read the provision to get the UUID
if (MobileProvisionFile == null)
{
Logger.LogInformation("No matching provision file was discovered for {ProjectFile}. Please ensure you have a compatible provision installed.", ProjectFile);
}
else if (!FileReference.Exists(MobileProvisionFile))
{
Logger.LogInformation("Selected mobile provision for {ProjectFile} ({MobileProvisionFile}) was not found. Please ensure you have a compatible provision installed.", ProjectFile, MobileProvisionFile);
}
else
{
byte[] AllBytes = FileReference.ReadAllBytes(MobileProvisionFile);
uint StartIndex = (uint)AllBytes.Length;
uint EndIndex = (uint)AllBytes.Length;
for (uint i = 0; i + 4 < AllBytes.Length; i++)
{
if (AllBytes[i] == '<' && AllBytes[i + 1] == '?' && AllBytes[i + 2] == 'x' && AllBytes[i + 3] == 'm' && AllBytes[i + 4] == 'l')
{
StartIndex = i;
break;
}
}
if (StartIndex < AllBytes.Length)
{
for (uint i = StartIndex; i + 7 < AllBytes.Length; i++)
{
if (AllBytes[i] == '<' && AllBytes[i + 1] == '/' && AllBytes[i + 2] == 'p' && AllBytes[i + 3] == 'l' && AllBytes[i + 4] == 'i' && AllBytes[i + 5] == 's' && AllBytes[i + 6] == 't' && AllBytes[i + 7] == '>')
{
EndIndex = i + 7;
break;
}
}
}
if (StartIndex < AllBytes.Length && EndIndex < AllBytes.Length)
{
byte[] TextBytes = new byte[EndIndex - StartIndex];
Buffer.BlockCopy(AllBytes, (int)StartIndex, TextBytes, 0, (int)(EndIndex - StartIndex));
string AllText = Encoding.UTF8.GetString(TextBytes);
int idx = AllText.IndexOf("<key>UUID</key>");
if (idx > 0)
{
idx = AllText.IndexOf("<string>", idx);
if (idx > 0)
{
idx += "<string>".Length;
MobileProvisionUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
}
}
idx = AllText.IndexOf("<key>com.apple.developer.team-identifier</key>");
if (idx > 0)
{
idx = AllText.IndexOf("<string>", idx);
if (idx > 0)
{
idx += "<string>".Length;
TeamUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
}
}
idx = AllText.IndexOf("<key>application-identifier</key>");
if (idx > 0)
{
idx = AllText.IndexOf("<string>", idx);
if (idx > 0)
{
idx += "<string>".Length;
string FullID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
BundleIdentifier = FullID.Substring(FullID.IndexOf('.') + 1);
}
}
idx = AllText.IndexOf("<key>Name</key>");
if (idx > 0)
{
idx = AllText.IndexOf("<string>", idx);
if (idx > 0)
{
idx += "<string>".Length;
MobileProvisionName = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx);
}
}
}
if (String.IsNullOrEmpty(MobileProvisionUUID) || String.IsNullOrEmpty(TeamUUID))
{
MobileProvision = null;
SigningCertificate = null;
Logger.LogInformation("Failed to parse the mobile provisioning profile.");
}
}
}
void IPPDataReceivedHandler(object Sender, DataReceivedEventArgs Line, ILogger Logger)
{
if ((Line != null) && (Line.Data != null))
{
if (Line.Data.StartsWith("IPP WARNING:"))
{
// Don't output IPP warnings to the console as they may not be warnings relevant to the build and could cause build failures.
Logger.LogDebug("{LineData}", Line.Data);
}
else
{
Logger.LogInformation("{LineData}", Line.Data);
}
if (!String.IsNullOrEmpty(SigningCertificate))
{
if (Line.Data.Contains("CERTIFICATE-") && Line.Data.Contains(SigningCertificate))
{
bHaveCertificate = true;
}
}
else
{
int cindex = Line.Data.IndexOf("CERTIFICATE-");
int pindex = Line.Data.IndexOf("PROVISION-");
if (cindex > -1 && pindex > -1)
{
cindex += "CERTIFICATE-".Length;
SigningCertificate = Line.Data.Substring(cindex, pindex - cindex - 1);
pindex += "PROVISION-".Length;
if (pindex < Line.Data.Length)
{
MobileProvisionFile = new FileReference(Line.Data.Substring(pindex));
}
}
}
}
}
}
class IOSArchitectureConfig : UnrealArchitectureConfig
{
public IOSArchitectureConfig()
: base(UnrealArchitectureMode.SingleTargetCompileSeparately, new[] { UnrealArch.Arm64, UnrealArch.IOSSimulator })
{
}
public override UnrealArchitectures ActiveArchitectures(FileReference? ProjectFile, string? TargetName)
{
// always use arm64 unless overridden on command line
return new UnrealArchitectures(UnrealArch.Arm64);
}
}
class IOSPlatform : AppleBuildPlatform
{
List<IOSProjectSettings> CachedProjectSettings = new List<IOSProjectSettings>();
List<IOSProjectSettings> CachedProjectSettingsByBundle = new List<IOSProjectSettings>();
Dictionary<string, IOSProvisioningData> ProvisionCache = new Dictionary<string, IOSProvisioningData>();
public IOSPlatform(UEBuildPlatformSDK InSDK, ILogger Logger)
: this(InSDK, UnrealTargetPlatform.IOS, Logger)
{
}
protected IOSPlatform(UEBuildPlatformSDK InSDK, UnrealTargetPlatform TargetPlatform, ILogger Logger)
: base(TargetPlatform, InSDK, new IOSArchitectureConfig(), Logger)
{
}
public override List<FileReference> FinalizeBinaryPaths(FileReference BinaryName, FileReference? ProjectFile, ReadOnlyTargetRules Target)
{
List<FileReference> BinaryPaths = new List<FileReference>();
if (Target.bShouldCompileAsDLL)
{
BinaryPaths.Add(FileReference.Combine(BinaryName.Directory, Target.Configuration.ToString(), Target.Name + ".framework", Target.Name));
}
else
{
BinaryPaths.Add(BinaryName);
}
return BinaryPaths;
}
public override void ResetTarget(TargetRules Target)
{
Target.bDeployAfterCompile = true;
Target.IOSPlatform.ProjectSettings = ((IOSPlatform)GetBuildPlatform(Target.Platform)).ReadProjectSettings(Target.ProjectFile);
if (!AppleExports.UseModernXcode(Target.ProjectFile))
{
// always strip in shipping configuration (commandline could have set it also)
if (Target.Configuration == UnrealTargetConfiguration.Shipping)
{
Target.IOSPlatform.bStripSymbols = true;
}
// if we are stripping the executable, or if the project requested it, or if it's a buildmachine, generate the dsym
if (Target.IOSPlatform.bStripSymbols || Target.IOSPlatform.ProjectSettings.bGeneratedSYMFile || Unreal.IsBuildMachine())
{
Target.IOSPlatform.bGeneratedSYM = true;
}
}
// Set bShouldCompileAsDLL when building as a framework
Target.bShouldCompileAsDLL = Target.IOSPlatform.ProjectSettings.bBuildAsFramework;
}
public override void ValidateTarget(TargetRules Target)
{
base.ValidateTarget(Target);
if (!String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CLANG_STATIC_ANALYZER_MODE")))
{
Target.StaticAnalyzer = StaticAnalyzer.Default;
Target.StaticAnalyzerOutputType = (Environment.GetEnvironmentVariable("CLANG_ANALYZER_OUTPUT")?.Contains("html", StringComparison.OrdinalIgnoreCase) == true) ? StaticAnalyzerOutputType.Html : StaticAnalyzerOutputType.Text;
Target.StaticAnalyzerMode = String.Equals(Environment.GetEnvironmentVariable("CLANG_STATIC_ANALYZER_MODE"), "shallow", StringComparison.OrdinalIgnoreCase) ? StaticAnalyzerMode.Shallow : StaticAnalyzerMode.Deep;
}
else if (Target.StaticAnalyzer == StaticAnalyzer.Clang)
{
Target.StaticAnalyzer = StaticAnalyzer.Default;
}
// Disable linking and ignore build outputs if we're using a static analyzer
if (Target.StaticAnalyzer == StaticAnalyzer.Default)
{
Target.bDisableLinking = true;
Target.bIgnoreBuildOutputs = true;
// Clang static analysis requires non unity builds
Target.bUseUnityBuild = false;
if (Target.bStaticAnalyzerIncludeGenerated)
{
Target.bAlwaysUseUnityForGeneratedFiles = false;
}
}
// we assume now we are building with IOS8 or later
if (Target.bCompileAgainstEngine)
{
Target.GlobalDefinitions.Add("HAS_METAL=1");
Target.ExtraModuleNames.Add("MetalRHI");
}
else
{
Target.GlobalDefinitions.Add("HAS_METAL=0");
}
if (Target.bShouldCompileAsDLL)
{
int PreviousDefinition = Target.GlobalDefinitions.FindIndex(s => s.Contains("BUILD_EMBEDDED_APP"));
if (PreviousDefinition >= 0)
{
Target.GlobalDefinitions.RemoveAt(PreviousDefinition);
}
Target.GlobalDefinitions.Add("BUILD_EMBEDDED_APP=1");
if (Target.Platform == UnrealTargetPlatform.IOS)
{
Target.ExportPublicHeader = "Headers/PreIOSEmbeddedView.h";
}
}
Target.bCheckSystemHeadersForModification = false;
if (Target.StaticAllocator == StaticAllocatorType.None)
{
// Prefer MB2 as MB3 requires extended virtual address space entitlement
Target.StaticAllocator = StaticAllocatorType.Binned2;
}
if (Target.IOSPlatform.bEnableAddressSanitizer)
{
Target.StaticAllocator = StaticAllocatorType.Ansi;
}
}
public override void ValidateModule(UEBuildModule Module, ReadOnlyTargetRules Target, ILogger Logger)
{
base.ValidateModule(Module, Target, Logger);
// @todo temporarily disabling due to VisionOS rquiring newer Xcode than this will allow - we may remove this entirely
#if false
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac && !Target.IOSPlatform.bSkipClangValidation)
{
ApplePlatformSDK SDK = (ApplePlatformSDK?)GetSDK() ?? new ApplePlatformSDK(Logger);
foreach (FileReference LibLoc in Module.PublicLibraries)
{
switch (LibLoc.GetExtension())
{
case ".a":
{
// When static lib, grep it
string Args = "-c \"strings ";
Args += LibLoc.FullName;
Args += " | grep -m1 -i \\(clang\"";
string StdOutResult = Utils.RunLocalProcessAndReturnStdOut("bash", Args);
if (String.IsNullOrEmpty(StdOutResult))
{
continue;
}
// This Regex will extract a 2-4 segment version code from string containing a 2-5 segment code
// ie: if given string: "Apple clang version 14.0.0 (clang-1400.0.17.3.1)"
// it'll extract: "1400.0.17.3" (note the dropped 5th segment)
Match M = Regex.Match(StdOutResult, @"(\(clang-(?<ver>\d+.\d+(.(\d+))?(.(\d+))?)(.(\d+))?\))");
if (M.Success)
{
string LibString = M.Groups["ver"].ToString();
Version? LibVersion = new Version(LibString);
if (LibVersion != null && LibVersion > SDK.MinimumStaticLibClangVersion)
{
throw new BuildException("iOS Static Library:'{0}' is built with a version of clang newer than UE supports ({1} > {2}). \nPlease rebuild {3} with the minimum supported version of Xcode/clang.", LibLoc.GetFileName(), LibString, SDK.MinimumStaticLibClangVersion, LibLoc);
}
}
}
break;
default:
// For now, we don't validate any other types of libs (dylib, Framework, etc)
break;
}
}
}
#endif
}
/// <summary>
/// Determines if the given name is a build product for a target.
/// </summary>
/// <param name="FileName">The name to check</param>
/// <param name="NamePrefixes">Target or application names that may appear at the start of the build product name (eg. "UnrealEditor", "ShooterGameEditor")</param>
/// <param name="NameSuffixes">Suffixes which may appear at the end of the build product name</param>
/// <returns>True if the string matches the name of a build product, false otherwise</returns>
public override bool IsBuildProduct(string FileName, string[] NamePrefixes, string[] NameSuffixes)
{
return IsBuildProductName(FileName, NamePrefixes, NameSuffixes, "")
|| IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".stub")
|| IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".dylib")
|| IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".dSYM")
|| IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".dSYM.zip")
|| IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".o");
}
/// <summary>
/// Get the extension to use for the given binary type
/// </summary>
/// <param name="InBinaryType"> The binary type being built</param>
/// <returns>string The binary extenstion (ie 'exe' or 'dll')</returns>
public override string GetBinaryExtension(UEBuildBinaryType InBinaryType)
{
switch (InBinaryType)
{
case UEBuildBinaryType.DynamicLinkLibrary:
return ".dylib";
case UEBuildBinaryType.Executable:
return "";
case UEBuildBinaryType.StaticLibrary:
return ".a";
}
return base.GetBinaryExtension(InBinaryType);
}
public IOSProjectSettings ReadProjectSettings(FileReference? ProjectFile, string? Bundle = "")
{
IOSProjectSettings? ProjectSettings = null;
// Use separate lists to prevent an overridden Bundle id polluting the standard project file.
bool bCacheByBundle = !String.IsNullOrEmpty(Bundle);
if (bCacheByBundle)
{
lock (CachedProjectSettingsByBundle)
{
ProjectSettings = CachedProjectSettingsByBundle.FirstOrDefault(x => x.ProjectFile == ProjectFile && x.BundleIdentifier == Bundle);
if (ProjectSettings == null)
{
ProjectSettings = CreateProjectSettings(ProjectFile, Bundle);
CachedProjectSettingsByBundle.Add(ProjectSettings);
}
}
}
else
{
lock (CachedProjectSettings)
{
ProjectSettings = CachedProjectSettings.FirstOrDefault(x => x.ProjectFile == ProjectFile);
if (ProjectSettings == null)
{
ProjectSettings = CreateProjectSettings(ProjectFile, Bundle);
CachedProjectSettings.Add(ProjectSettings);
}
}
}
return ProjectSettings;
}
protected virtual IOSProjectSettings CreateProjectSettings(FileReference? ProjectFile, string? Bundle)
{
return new IOSProjectSettings(ProjectFile, Bundle);
}
public IOSProvisioningData ReadProvisioningData(FileReference? ProjectFile, bool bForDistribution = false, string? Bundle = "")
{
IOSProjectSettings ProjectSettings = ReadProjectSettings(ProjectFile, Bundle);
return ReadProvisioningData(ProjectSettings, bForDistribution);
}
public IOSProvisioningData ReadProvisioningData(IOSProjectSettings ProjectSettings, bool bForDistribution = false)
{
string ProvisionKey = ProjectSettings.BundleIdentifier + " " + bForDistribution.ToString();
IOSProvisioningData? ProvisioningData;
lock (ProvisionCache)
{
if (!ProvisionCache.TryGetValue(ProvisionKey, out ProvisioningData))
{
ProvisioningData = CreateProvisioningData(ProjectSettings, bForDistribution);
ProvisionCache.Add(ProvisionKey, ProvisioningData);
}
}
return ProvisioningData;
}
protected virtual IOSProvisioningData CreateProvisioningData(IOSProjectSettings ProjectSettings, bool bForDistribution)
{
return new IOSProvisioningData(ProjectSettings, bForDistribution, Logger);
}
public override string[] GetDebugInfoExtensions(ReadOnlyTargetRules InTarget, UEBuildBinaryType InBinaryType)
{
if (InTarget.IOSPlatform.bGeneratedSYM)
{
IOSProjectSettings ProjectSettings = ReadProjectSettings(InTarget.ProjectFile);
// which format?
if (ProjectSettings.bGeneratedSYMBundle)
{
return new string[] { ".dSYM.zip" };
}
else
{
return new string[] { ".dSYM" };
}
}
return Array.Empty<string>();
}
public override bool CanUseXGE()
{
return BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac;
}
public override bool CanUseFASTBuild()
{
return true;
}
public bool HasCustomIcons(DirectoryReference ProjectDirectoryName, ILogger Logger)
{
string IconDir = Path.Combine(ProjectDirectoryName.FullName, "Build", "IOS", "Resources", "Graphics");
if (Directory.Exists(IconDir))
{
foreach (string f in Directory.EnumerateFiles(IconDir))
{
if (f.Contains("Icon") && Path.GetExtension(f).Contains(".png"))
{
Logger.LogInformation("Requiring custom build because project {Project} has custom icons", Path.GetFileName(ProjectDirectoryName.FullName));
return true;
}
}
}
return false;
}
/// <summary>
/// Check for the default configuration
/// return true if the project uses the default build config
/// </summary>
public override bool HasDefaultBuildConfig(UnrealTargetPlatform Platform, DirectoryReference ProjectDirectoryName)
{
string[] BoolKeys = new string[] {
"bShipForBitcode", "bGeneratedSYMFile",
"bGeneratedSYMBundle", "bEnableRemoteNotificationsSupport", "bEnableCloudKitSupport",
"bGenerateCrashReportSymbols", "bEnableBackgroundFetch"
};
string[] StringKeys = new string[] {
"MinimumiOSVersion",
"AdditionalLinkerFlags",
"AdditionalShippingLinkerFlags"
};
// check for custom icons
if (HasCustomIcons(ProjectDirectoryName, Logger))
{
return false;
}
// look up iOS specific settings
if (!DoProjectSettingsMatchDefault(Platform, ProjectDirectoryName, "/Script/IOSRuntimeSettings.IOSRuntimeSettings",
BoolKeys, null, StringKeys, Logger))
{
return false;
}
// check the base settings
return base.HasDefaultBuildConfig(Platform, ProjectDirectoryName);
}
/// <summary>
/// Check for the build requirement due to platform requirements
/// return true if the project requires a build
/// </summary>
public override bool RequiresBuild(UnrealTargetPlatform Platform, DirectoryReference ProjectDirectoryName)
{
// check for custom icons
return HasCustomIcons(ProjectDirectoryName, Logger);
}
public override bool ShouldCompileMonolithicBinary(UnrealTargetPlatform InPlatform)
{
// This platform currently always compiles monolithic
return true;
}
/// <summary>
/// Modify the rules for a newly created module, where the target is a different host platform.
/// This is not required - but allows for hiding details of a particular platform.
/// </summary>
/// <param name="ModuleName">The name of the module</param>
/// <param name="Rules">The module rules</param>
/// <param name="Target">The target being build</param>
public override void ModifyModuleRulesForOtherPlatform(string ModuleName, ModuleRules Rules, ReadOnlyTargetRules Target)
{
bool bIsPlatformAvailableForTarget = UEBuildPlatform.IsPlatformAvailableForTarget(Platform, Target, bIgnoreSDKCheck: true);
bool bIsPlatformAvailableForTargetWithSDK = UEBuildPlatform.IsPlatformAvailableForTarget(Platform, Target);
// don't do any target platform stuff if SDK is not available
if (!bIsPlatformAvailableForTarget)
{
return;
}
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Mac))
{
// For Windows we use the MetalDeveloperTools and we will check if the toolchain is present at runtime
bool bBuildShaderFormats = Target.bForceBuildShaderFormats || Target.Platform == UnrealTargetPlatform.Win64;
if (!Target.bBuildRequiresCookedData)
{
if (ModuleName == "Engine")
{
if (Target.bBuildDeveloperTools)
{
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatformSettings");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatformSettings");
if (bIsPlatformAvailableForTargetWithSDK)
{
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatform");
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatformControls");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatform");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatformControls");
}
}
}
else if (ModuleName == "TargetPlatform")
{
if (bIsPlatformAvailableForTargetWithSDK)
{
bBuildShaderFormats = true;
Rules.DynamicallyLoadedModuleNames.Add("TextureFormatASTC");
Rules.DynamicallyLoadedModuleNames.Add("TextureFormatETC2");
if (Target.bBuildDeveloperTools && Target.bCompileAgainstEngine)
{
Rules.DynamicallyLoadedModuleNames.Add("AudioFormatADPCM");
}
}
}
}
// allow standalone tools to use targetplatform modules, without needing Engine
if (ModuleName == "TargetPlatform")
{
if (Target.bForceBuildTargetPlatforms)
{
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatformSettings");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatformSettings");
if (bIsPlatformAvailableForTargetWithSDK)
{
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatform");
Rules.DynamicallyLoadedModuleNames.Add("IOSTargetPlatformControls");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatform");
Rules.DynamicallyLoadedModuleNames.Add("TVOSTargetPlatformControls");
}
}
if (bBuildShaderFormats)
{
Rules.DynamicallyLoadedModuleNames.Add("MetalShaderFormat");
}
}
if (ModuleName == "UnrealEd" && bIsPlatformAvailableForTargetWithSDK)
{
Rules.DynamicallyLoadedModuleNames.Add("IOSPlatformEditor");
}
}
}
public override void ModifyModuleRulesForActivePlatform(string ModuleName, ModuleRules Rules, ReadOnlyTargetRules Target)
{
if (ModuleName == "Launch")
{
Rules.PrivateDependencyModuleNames.AddRange(new string[] {
"AudioMixerAudioUnit",
"IOSAudio",
"LaunchDaemonMessages",
});
Rules.DynamicallyLoadedModuleNames.AddRange(new string[] {
"IOSLocalNotification",
"IOSRuntimeSettings",
});
// needed for Metal layer
Rules.PublicFrameworks.Add("QuartzCore");
IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProjectSettings(Target.ProjectFile);
if (ProjectSettings.bEnableIOS16DynLinkerBugWAR &&
Target.Platform == UnrealTargetPlatform.IOS)
{
Rules.PublicDependencyModuleNames.Add("Interpose");
}
}
}
/// <summary>
/// Whether this platform should create debug information or not
/// </summary>
/// <param name="Target">The target being built</param>
/// <returns>bool true if debug info should be generated, false if not</returns>
public override bool ShouldCreateDebugInfo(ReadOnlyTargetRules Target)
{
return true;
}
/// <summary>
/// Setup the target environment for building
/// </summary>
/// <param name="Target">Settings for the target being compiled</param>
/// <param name="CompileEnvironment">The compile environment for this target</param>
/// <param name="LinkEnvironment">The link environment for this target</param>
public override void SetUpEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment)
{
base.SetUpEnvironment(Target, CompileEnvironment, LinkEnvironment);
IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(Target.Platform)).ReadProjectSettings(Target.ProjectFile);
if (!ProjectFileGenerator.bGenerateProjectFiles)
{
Logger.LogInformation("Compiling against OS Version {RuntimeVersion} [minimum allowed at runtime]", ProjectSettings.RuntimeVersion);
}
CompileEnvironment.Definitions.Add("PLATFORM_IOS=1");
// set a define that is _only_ on iPhones, and not AppleTV, etc
if (Target.Platform == UnrealTargetPlatform.IOS)
{
CompileEnvironment.Definitions.Add("UE_PLATFORM_IOS_ONLY=1");
}
CompileEnvironment.Definitions.Add("WITH_TTS=0");
CompileEnvironment.Definitions.Add("WITH_SPEECH_RECOGNITION=0");
CompileEnvironment.Definitions.Add("WITH_EDITOR=0");
CompileEnvironment.Definitions.Add("USE_NULL_RHI=0");
if (ProjectSettings.bNotificationsEnabled)
{
CompileEnvironment.Definitions.Add("NOTIFICATIONS_ENABLED=1");
}
else
{
CompileEnvironment.Definitions.Add("NOTIFICATIONS_ENABLED=0");
}
if (ProjectSettings.bBackgroundFetchEnabled)
{
CompileEnvironment.Definitions.Add("BACKGROUNDFETCH_ENABLED=1");
}
else
{
CompileEnvironment.Definitions.Add("BACKGROUNDFETCH_ENABLED=0");
}
if (ProjectSettings.bFileSharingEnabled)
{
CompileEnvironment.Definitions.Add("FILESHARING_ENABLED=1");
}
else
{
CompileEnvironment.Definitions.Add("FILESHARING_ENABLED=0");
}
CompileEnvironment.Definitions.Add("UE_DISABLE_FORCE_INLINE=" + (ProjectSettings.bDisableForceInline ? "1" : "0"));
if (Target.Architecture == UnrealArch.IOSSimulator || Target.Architecture == UnrealArch.TVOSSimulator)
{
CompileEnvironment.Definitions.Add("WITH_IOS_SIMULATOR=1");
}
else
{
CompileEnvironment.Definitions.Add("WITH_IOS_SIMULATOR=0");
}
if (ProjectSettings.bEnableAdvertisingIdentifier)
{
CompileEnvironment.Definitions.Add("ENABLE_ADVERTISING_IDENTIFIER=1");
}
// if the project has an Oodle compression Dll, enable the decompressor on IOS
if (Target.ProjectFile != null)
{
DirectoryReference ProjectDir = Target.ProjectFile.Directory;
string OodleDllPath = DirectoryReference.Combine(ProjectDir, "Binaries/ThirdParty/Oodle/Mac/libUnrealPakPlugin.dylib").FullName;
if (File.Exists(OodleDllPath))
{
Logger.LogDebug(" Registering custom oodle compressor for {Platform}", UnrealTargetPlatform.IOS.ToString());
CompileEnvironment.Definitions.Add("REGISTER_OODLE_CUSTOM_COMPRESSOR=1");
}
}
// convert runtime version into standardized integer
float TargetFloat = Target.IOSPlatform.RuntimeVersion;
int IntPart = (int)TargetFloat;
int FracPart = (int)((TargetFloat - IntPart) * 10);
int TargetNum = IntPart * 10000 + FracPart * 100;
CompileEnvironment.Definitions.Add("MINIMUM_UE_COMPILED_IOS_VERSION=" + TargetNum);
LinkEnvironment.AdditionalFrameworks.Add(new UEBuildFramework("GameKit"));
LinkEnvironment.AdditionalFrameworks.Add(new UEBuildFramework("DeviceCheck"));
if (ProjectSettings.bEnableStoreKitSupport)
{
CompileEnvironment.Definitions.Add("UE_WITH_STORE_KIT=1");
LinkEnvironment.AdditionalFrameworks.Add(new UEBuildFramework("StoreKit"));
}
else
{
CompileEnvironment.Definitions.Add("UE_WITH_STORE_KIT=0");
}
}
/// <summary>
/// Setup the binaries for this specific platform.
/// </summary>
/// <param name="Target">The target being built</param>
/// <param name="ExtraModuleNames"></param>
public override void AddExtraModules(ReadOnlyTargetRules Target, List<string> ExtraModuleNames)
{
if (Target.Type != TargetType.Program)
{
ExtraModuleNames.Add("IOSPlatformFeatures");
}
}
/// <summary>
/// Creates a toolchain instance for the given platform.
/// </summary>
/// <param name="Target">The target being built</param>
/// <returns>New toolchain instance.</returns>
public override UEToolChain CreateToolChain(ReadOnlyTargetRules Target)
{
ClangToolChainOptions Options = ClangToolChainOptions.None;
if (Target.IOSPlatform.bEnableAddressSanitizer)
{
Options |= ClangToolChainOptions.EnableAddressSanitizer;
}
if (Target.IOSPlatform.bEnableThreadSanitizer)
{
Options |= ClangToolChainOptions.EnableThreadSanitizer;
}
if (Target.IOSPlatform.bEnableUndefinedBehaviorSanitizer)
{
Options |= ClangToolChainOptions.EnableUndefinedBehaviorSanitizer;
}
IOSProjectSettings ProjectSettings = ReadProjectSettings(Target.ProjectFile);
return new IOSToolChain(Target, ProjectSettings, Options, Logger);
}
/// <inheritdoc/>
public override void Deploy(TargetReceipt Receipt)
{
if (Receipt.HasValueForAdditionalProperty("CompileAsDll", "true"))
{
// IOSToolchain.PostBuildSync handles the copy, nothing else to do here
}
else
{
new UEDeployIOS(Logger).PrepTargetForDeployment(Receipt);
}
}
}
class IOSPlatformFactory : UEBuildPlatformFactory
{
public override UnrealTargetPlatform TargetPlatform => UnrealTargetPlatform.IOS;
/// <summary>
/// Register the platform with the UEBuildPlatform class
/// </summary>
public override void RegisterBuildPlatforms(ILogger Logger)
{
ApplePlatformSDK SDK = new ApplePlatformSDK(Logger);
// Register this build platform for IOS
UEBuildPlatform.RegisterBuildPlatform(new IOSPlatform(SDK, Logger), Logger);
UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.IOS, UnrealPlatformGroup.Apple);
UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.IOS, UnrealPlatformGroup.IOS);
}
}
}