using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Ubisoft.Hotel.Package.Editor { public class UnityBuildProperty : PackageProperty { public const string SYMBOLS_HT_CHEATS_ON = "HT_CHEATS_ON"; public const string SYMBOLS_HT_ADDRESSABLES_STORE_CDN_ON = "HT_ADDRESSABLES_STORE_CDN_ON"; public const string SYMBOLS_HT_PACKAGE_PREFIX = "HT_PACKAGE_"; private static readonly List PackagesToDefineSymbols = new List() { "com.unity.addressables", "com.unity.textmeshpro", }; public static UnityBuildProperty CreateInstanceWithParams(string name) { UnityBuildProperty returnValue = CreateInstance(); returnValue.name = name; returnValue.Clear(); return returnValue; } public static bool IsAHotelPackage(string packageName) { return !string.IsNullOrEmpty(packageName) && packageName.StartsWith("com.ubisoft.hotel"); } public static bool IsDefineSymbolEnabledForPackage(string packageName) { return !string.IsNullOrEmpty(packageName) && (packageName.StartsWith("com.ubisoft") || PackagesToDefineSymbols.Contains(packageName)); } private static string GetPackageShortName(string packageName) { string returnValue = packageName; if (!string.IsNullOrEmpty(packageName)) { string[] tokens = packageName.Split('.'); int count = tokens.Length; if (count > 0) { returnValue = tokens[count - 1]; if (returnValue.ToUpper() == "SDK" && count > 1) { returnValue = tokens[count - 2] + "_" + returnValue; } } } return returnValue; } public static string GetPackageSymbol(string packageName, bool active, PackageDefinition.EImplementation implementation = PackageDefinition.EImplementation.Prod) { string returnValue = null; if (IsDefineSymbolEnabledForPackage(packageName)) { string shortName = GetPackageShortName(packageName); // PackaName may look like com.ubisoft.hotel.protobuf-unity, which would take protobuf-unity as short name and // HT_PACKAGE_PROTOBUF_UNITY_PROD as possible symbol which might mess up with preprocessor shortName = shortName.Replace("-", "_"); shortName = shortName.ToUpper(); returnValue = $"{SYMBOLS_HT_PACKAGE_PREFIX}{shortName}_"; if (active) { returnValue += implementation.ToString().ToUpper(); } else { returnValue += "OFF"; } } return returnValue; } private static ProvisioningProfileType EProvisioningProfileTypeToProvisioningProfileType(EProvisioningProfileType value) { ProvisioningProfileType returnValue = ProvisioningProfileType.Distribution; switch (value) { case EProvisioningProfileType.Automatic: returnValue = ProvisioningProfileType.Automatic; break; case EProvisioningProfileType.Development: returnValue = ProvisioningProfileType.Development; break; } return returnValue; } private static string EProvisioningProfileTypeToKey(EProvisioningProfileType value) { // Constants obtained from https://gist.github.com/cocoaNib/502900f24846eb17bb29 string returnValue = null; switch (value) { case EProvisioningProfileType.AdHoc: returnValue = "ad-hoc"; break; case EProvisioningProfileType.AppStore: returnValue = "app-store"; break; case EProvisioningProfileType.Development: returnValue = "development"; break; case EProvisioningProfileType.Enterprise: returnValue = "enterprise"; break; } return returnValue; } [SerializeField] public enum EProvisioningProfileType { Default, // Disabled Automatic, AdHoc, AppStore, Development, Enterprise }; [SerializeField] public enum EKeystoreStrategy { AsUnitySettings, // Leave it as it is defined in Unity settings Disabled, // App is not signed Password // Set the password in Vault so cicd can use it without exposing it. Consumers can provide passwords via script too to ease manual builds on Unity Editor. } [SerializeField] private string m_bundleId; [SerializeField] private string m_version; [SerializeField] private int m_versionCode; [SerializeField] private List m_defineSymbols; [SerializeField] private EBoolean m_isDevelopmentBuild; [SerializeField] private EBoolean m_isConnectProfilerEnabled; [SerializeField] private EBoolean m_enableHeadlessMode; [SerializeField] private EBoolean m_buildAppBundle; [SerializeField] private EBoolean m_generateSymbols; [SerializeField] private EBoolean m_deploySymbols; /// /// List of paths to the scenes to be included in the build. /// Paths are relative to the project folder where the asset is stored. Example: "Assets/Scenes/MainScene.unity". /// /// The first scene in this array is used as the first scene unless m_firstScenePath has a non-empty value. /// [SerializeField] private List m_scenePaths; /// /// Path to the scene that should be used as the first scene. If it's empty then the first scene defined in m_scenePaths will be used instead. /// This field is useful when you define several UnityBuildProperty objects, each with a list of scenes to include. /// [SerializeField] private string m_firstScenePath; /// /// A provisioning profile Universally Unique Identifier (UUID) that Xcode will use to build your iOS app in Manual Signing mode. /// [SerializeField] private string m_iOSManualProvisioningProfileID; /// /// Set this property with your Apple Developer Team ID. You can find this on the Apple Developer website under Account > Membership . /// This sets the Team ID for the generated Xcode project, allowing developers to use the Build and Run functionality. /// An Apple Developer Team ID must be set here for automatic signing of your app. /// [SerializeField] private string m_appleDeveloperTeamID; [SerializeField] private EProvisioningProfileType m_provisioningProfileType; [SerializeField] private EKeystoreStrategy m_keystoreStrategy; [SerializeField] private string m_keystoreName; // KeystorePass to use when Android signing is enabled. // If no keystorePass is provided then it will be fetched from the environment variable UBM_UNITY_KEYSTORE_PASS right before building. [SerializeField] private string m_keystorePass; [SerializeField] private string m_keyaliasName; // KeyaliasPass to use when Android signing is enabled. // If no keyaliasPass is provided then it will be fetched from the environment variable UBM_UNITY_KEYSTORE_PASS right before building. [SerializeField] private string m_keyaliasPass; // Id of the key in Vault that stores keystore/keyalias passwords to use to sign an Android build. [SerializeField] private string m_keystoreVaultId; /// /// Some of the System libraries are not included by default, and you will need to use the -r compilation option to enable them. /// This propety is meant to contain these libraries. /// [SerializeField] private List m_compilerReferences; /// /// Object responsible for handling csc.rsp (https://docs.unity3d.com/2020.3/Documentation/Manual/PlatformDependentCompilation.html#:~:text=Global%20custom%20%23-,defines,-You%20can%20define). /// private HtCscRsp m_cscRsp; private HtCscRsp CscRsp { get { if (m_cscRsp == null) { m_cscRsp = new HtCscRsp(); } return m_cscRsp; } } public string BundleId { get { return m_bundleId; } set { m_bundleId = value; } } public string Version { get { return m_version; } set { m_version = value; } } public int VersionCode { get { return m_versionCode; } set { m_versionCode = value; } } public EBoolean IsDevelopmentBuild { get { return m_isDevelopmentBuild; } set { m_isDevelopmentBuild = value; } } public EBoolean IsConnectProfilerEnabled { get { return m_isConnectProfilerEnabled; } set { m_isConnectProfilerEnabled = value; } } public EBoolean IsBuildAppBundle { get { return m_buildAppBundle; } set { m_buildAppBundle = value; } } public EBoolean GenerateSymbols { get { return m_generateSymbols; } set { m_generateSymbols = value; } } public EBoolean DeploySymbols { get { return m_deploySymbols; } set { m_deploySymbols = value; } } public EBoolean EnableHeadlessMode { get { return m_enableHeadlessMode; } set { m_enableHeadlessMode = value; } } internal List SceneList { get { return m_scenePaths; } set { m_scenePaths = value; } } public string IOSManualProvisioningProfileID { get { return m_iOSManualProvisioningProfileID; } private set => m_iOSManualProvisioningProfileID = value; } public string AppleDeveloperTeamID { get { return m_appleDeveloperTeamID; } private set { m_appleDeveloperTeamID = value; } } public EProvisioningProfileType IOSManualProvisioningProfileType { get { return m_provisioningProfileType; } private set { m_provisioningProfileType = value; } } public string IOSManualProvisioningProfileTypeKey { get { return EProvisioningProfileTypeToKey(IOSManualProvisioningProfileType); } } public EKeystoreStrategy KeystoreStrategy { get { return m_keystoreStrategy; } set { m_keystoreStrategy = value; } } public string KeystoreName { get { return m_keystoreName; } set { m_keystoreName = value; } } public string KeystorePass { get { return m_keystorePass; } set { m_keystorePass = value; } } public string KeyaliasName { get { return m_keyaliasName; } set { m_keyaliasName = value; } } public string KeyaliasPass { get { return m_keyaliasPass; } set { m_keyaliasPass = value; } } public string KeystoreVaultId { get { return m_keystoreVaultId; } set { m_keystoreVaultId = value; } } public void Clear() { BundleId = null; Version = null; VersionCode = -1; if (m_defineSymbols != null) { m_defineSymbols.Clear(); } IsDevelopmentBuild = EBoolean.AsUnitySettings; IsConnectProfilerEnabled = EBoolean.AsUnitySettings; IsBuildAppBundle = EBoolean.AsUnitySettings; GenerateSymbols = EBoolean.AsUnitySettings; DeploySymbols = EBoolean.AsUnitySettings; EnableHeadlessMode = EBoolean.AsUnitySettings; FirstScenePath = null; if (m_scenePaths != null) { m_scenePaths.Clear(); } IOSManualProvisioningProfileID = null; AppleDeveloperTeamID = null; IOSManualProvisioningProfileType = EProvisioningProfileType.Default; if (m_compilerReferences != null) { m_compilerReferences.Clear(); } KeystoreStrategy = EKeystoreStrategy.AsUnitySettings; KeystoreName = null; KeystorePass = null; KeyaliasName = null; KeyaliasPass = null; KeystoreVaultId = null; } public override void Apply(BuildSuite buildSuite) { buildSuite.OrderProperty(this); } public void Join(UnityBuildProperty p) { if (p != null) { if (string.IsNullOrEmpty(BundleId)) { BundleId = p.BundleId; } else if (!string.IsNullOrEmpty(p.BundleId) && BundleId != p.BundleId) { Log($"Can't assign {Debug.FormatTextInUserContext(p.BundleId)} as bunldeId " + $"because there is already a valid bundleId assigned: {Debug.FormatTextInUserContext(BundleId)}", ELogLevel.Error); } if (string.IsNullOrEmpty(Version)) { Version = p.Version; } else if (!string.IsNullOrEmpty(p.Version) && Version != p.Version) { Log($"Can't assign {Debug.FormatTextInUserContext(p.Version)} as version " + $"because there is already a valid version assigned: {Debug.FormatTextInUserContext(Version)}", ELogLevel.Error); } if (VersionCode == -1) { if (p.VersionCode == 0) { Log($"Can't assign {Debug.FormatTextInUserContext(p.VersionCode.ToString())} as versionCode " + $"because version code needs to be a positive number. Please change it in property {Debug.FormatTextInUserContext(p.Name)}", ELogLevel.Error); } else { VersionCode = p.VersionCode; } } else if (p.VersionCode > -1 && VersionCode != p.VersionCode) { Log($"Can't assign {Debug.FormatTextInUserContext(p.VersionCode.ToString())} as versionCode " + $"because there is already a valid versionCode assigned: {Debug.FormatTextInUserContext(VersionCode.ToString())}", ELogLevel.Error); } if (p.m_defineSymbols != null) { foreach (string symbol in p.m_defineSymbols) { AddDefineSymbol(symbol); } } if (p.m_compilerReferences != null) { foreach (string symbol in p.m_compilerReferences) { AddCompilerReference(symbol); } } // IsDevelopmentBuild if (p.IsDevelopmentBuild != EBoolean.AsUnitySettings) { if (IsDevelopmentBuild == EBoolean.AsUnitySettings) { IsDevelopmentBuild = p.IsDevelopmentBuild; } else if (IsDevelopmentBuild != p.IsDevelopmentBuild) { Log($"Can't assign {Debug.FormatTextInUserContext(p.IsDevelopmentBuild.ToString())} as IsDevelopmentBuild " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(IsDevelopmentBuild.ToString())}", ELogLevel.Error); } } // IsConnectProfilerEnabled if (p.IsConnectProfilerEnabled != EBoolean.AsUnitySettings) { if (IsConnectProfilerEnabled == EBoolean.AsUnitySettings) { IsConnectProfilerEnabled = p.IsConnectProfilerEnabled; } else if (IsConnectProfilerEnabled != p.IsConnectProfilerEnabled) { Log($"Can't assign {Debug.FormatTextInUserContext(p.IsConnectProfilerEnabled.ToString())} as IsConnectProfilerEnabled " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(IsConnectProfilerEnabled.ToString())}", ELogLevel.Error); } } // IsBuildAppBundle if (p.IsBuildAppBundle != EBoolean.AsUnitySettings) { if (IsBuildAppBundle == EBoolean.AsUnitySettings) { IsBuildAppBundle = p.IsBuildAppBundle; } else if (IsBuildAppBundle != p.IsBuildAppBundle) { Log($"Can't assign {Debug.FormatTextInUserContext(p.IsBuildAppBundle.ToString())} as IsBuildAppBundle " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(IsBuildAppBundle.ToString())}", ELogLevel.Error); } } // GenerateSymbols if (p.GenerateSymbols != EBoolean.AsUnitySettings) { if (GenerateSymbols == EBoolean.AsUnitySettings) { GenerateSymbols = p.GenerateSymbols; } else if (GenerateSymbols != p.GenerateSymbols) { Log($"Can't assign {Debug.FormatTextInUserContext(p.GenerateSymbols.ToString())} as GenerateSymbols " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(GenerateSymbols.ToString())}", ELogLevel.Error); } } // DeploySymbols if (p.DeploySymbols != EBoolean.AsUnitySettings) { if (DeploySymbols == EBoolean.AsUnitySettings) { DeploySymbols = p.DeploySymbols; } else if (DeploySymbols != p.DeploySymbols) { Log($"Can't assign {Debug.FormatTextInUserContext(p.DeploySymbols.ToString())} as DeploySymbols " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(DeploySymbols.ToString())}", ELogLevel.Error); } } // EnableHeadlessMode if (p.EnableHeadlessMode != EBoolean.AsUnitySettings) { if (EnableHeadlessMode == EBoolean.AsUnitySettings) { EnableHeadlessMode = p.EnableHeadlessMode; } else if (EnableHeadlessMode != p.EnableHeadlessMode) { Log($"Can't assign {Debug.FormatTextInUserContext(p.EnableHeadlessMode.ToString())} as EnableHeadlessMode " + $" because there is already a valid value assigned: {Debug.FormatTextInUserContext(EnableHeadlessMode.ToString())}", ELogLevel.Error); } } JoinSceneList(p); // // iOS XCode signing // if (!string.IsNullOrEmpty(p.IOSManualProvisioningProfileID)) { if (string.IsNullOrEmpty(IOSManualProvisioningProfileID)) { IOSManualProvisioningProfileID = p.IOSManualProvisioningProfileID; } else if (IOSManualProvisioningProfileID != p.IOSManualProvisioningProfileID) { Log($"Can't assign {Debug.FormatTextInUserContext(p.IOSManualProvisioningProfileID)} as IOSManualProvisioningProfileID " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(IOSManualProvisioningProfileID)}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.AppleDeveloperTeamID)) { if (string.IsNullOrEmpty(AppleDeveloperTeamID)) { AppleDeveloperTeamID = p.AppleDeveloperTeamID; } else if (AppleDeveloperTeamID != p.AppleDeveloperTeamID) { Log($"Can't assign {Debug.FormatTextInUserContext(p.AppleDeveloperTeamID)} as AppleDeveloperTeamID " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(AppleDeveloperTeamID)}", ELogLevel.Error); } } if (p.IOSManualProvisioningProfileType != EProvisioningProfileType.Default) { if (IOSManualProvisioningProfileType == EProvisioningProfileType.Default) { IOSManualProvisioningProfileType = p.IOSManualProvisioningProfileType; } else if (IOSManualProvisioningProfileType != p.IOSManualProvisioningProfileType) { Log($"Can't assign {Debug.FormatTextInUserContext(p.IOSManualProvisioningProfileType.ToString())} as ProvisioningProfileType " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(IOSManualProvisioningProfileType.ToString())}", ELogLevel.Error); } } // // Android keystore // if (p.KeystoreStrategy != EKeystoreStrategy.AsUnitySettings) { if (KeystoreStrategy == EKeystoreStrategy.AsUnitySettings) { KeystoreStrategy = p.KeystoreStrategy; } else if (KeystoreStrategy != p.KeystoreStrategy) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeystoreStrategy.ToString())} as KeystoreStrategy " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeystoreStrategy.ToString())}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.KeystoreName)) { if (string.IsNullOrEmpty(KeystoreName)) { KeystoreName = p.KeystoreName; } else if (KeystoreName != p.KeystoreName) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeystoreName)} as KeystoreName " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeystoreName)}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.KeystorePass)) { if (string.IsNullOrEmpty(KeystorePass)) { KeystorePass = p.KeystorePass; } else if (KeystorePass != p.KeystorePass) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeystorePass)} as KeystorePass " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeystorePass)}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.KeyaliasName)) { if (string.IsNullOrEmpty(KeyaliasName)) { KeyaliasName = p.KeyaliasName; } else if (KeyaliasName != p.KeyaliasName) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeyaliasName)} as KeyaliasName " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeyaliasName)}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.KeyaliasPass)) { if (string.IsNullOrEmpty(KeyaliasPass)) { KeyaliasPass = p.KeyaliasPass; } else if (KeyaliasPass != p.KeyaliasPass) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeyaliasPass)} as KeyaliasPass " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeyaliasPass)}", ELogLevel.Error); } } if (!string.IsNullOrEmpty(p.KeystoreVaultId)) { if (string.IsNullOrEmpty(KeystoreVaultId)) { KeystoreVaultId = p.KeystoreVaultId; } else if (KeystoreVaultId != p.KeystoreVaultId) { Log($"Can't assign {Debug.FormatTextInUserContext(p.KeystoreVaultId)} as KeystoreVaultId " + $"because there is already a valid value assigned: {Debug.FormatTextInUserContext(KeystoreVaultId)}", ELogLevel.Error); } } } } public void PreBuild() { // Use activeBuildTarget so changes are applied to the consumer's current configuration BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); // // Bundle Id // if (!string.IsNullOrEmpty(BundleId)) { PlayerSettings.SetApplicationIdentifier(buildTargetGroup, BundleId); } if (!string.IsNullOrEmpty(Version)) { PlayerSettings.bundleVersion = Version; } if (VersionCode > 0) { PlayerSettings.iOS.buildNumber = VersionCode.ToString(); PlayerSettings.Android.bundleVersionCode = VersionCode; } //CscRsp.Reset(); // // Development Build // if (IsDevelopmentBuild != EBoolean.AsUnitySettings) { EditorUserBuildSettings.development = IsDevelopmentBuild == EBoolean.True; } // // ConnectProfiler // if (IsConnectProfilerEnabled != EBoolean.AsUnitySettings) { EditorUserBuildSettings.connectProfiler = IsConnectProfilerEnabled == EBoolean.True; } // // IsBuildAppBundle // if (IsBuildAppBundle != EBoolean.AsUnitySettings) { EditorUserBuildSettings.buildAppBundle = IsBuildAppBundle == EBoolean.True; } // // GenerateSymbols // if (GenerateSymbols != EBoolean.AsUnitySettings) { #if UNITY_2021_1_OR_NEWER EditorUserBuildSettings.androidCreateSymbols = GenerateSymbols == EBoolean.True ? AndroidCreateSymbols.Public : AndroidCreateSymbols.Disabled; #else EditorUserBuildSettings.androidCreateSymbolsZip = GenerateSymbols == EBoolean.True; #endif } // // EnableHeadlessMode // #if UNITY_2021_1_OR_NEWER if (EnableHeadlessMode != EBoolean.AsUnitySettings) { EditorUserBuildSettings.standaloneBuildSubtarget = EnableHeadlessMode == EBoolean.True ? StandaloneBuildSubtarget.Server : StandaloneBuildSubtarget.Player; } #else if (EnableHeadlessMode != EBoolean.AsUnitySettings) { EditorUserBuildSettings.enableHeadlessMode = EnableHeadlessMode == EBoolean.True; } #endif // Scene list ApplySceneList(); // // iOS XCode signing // if (!string.IsNullOrEmpty(IOSManualProvisioningProfileID)) { PlayerSettings.iOS.iOSManualProvisioningProfileID = IOSManualProvisioningProfileID; } if (!string.IsNullOrEmpty(AppleDeveloperTeamID)) { PlayerSettings.iOS.appleDeveloperTeamID = AppleDeveloperTeamID; } if (IOSManualProvisioningProfileType != EProvisioningProfileType.Default) { PlayerSettings.iOS.iOSManualProvisioningProfileType = EProvisioningProfileTypeToProvisioningProfileType(IOSManualProvisioningProfileType); } // Android switch (KeystoreStrategy) { case EKeystoreStrategy.Disabled: PlayerSettings.Android.useCustomKeystore = false; break; case EKeystoreStrategy.Password: PlayerSettings.Android.useCustomKeystore = true; if (string.IsNullOrEmpty(KeystoreName)) { ReportKeyMissingAttributeError("KeystoreName"); } else { PlayerSettings.Android.keystoreName = KeystoreName; } if (string.IsNullOrEmpty(KeyaliasName)) { ReportKeyMissingAttributeError("KeyaliasName"); } else { PlayerSettings.Android.keyaliasName = KeyaliasName; } // If no keystorePass is provided then it will be fetched from the environment variable UBM_UNITY_KEYSTORE_PASS // right before building if (!string.IsNullOrEmpty(KeystorePass)) { PlayerSettings.Android.keystorePass = KeystorePass; } // If no keystorePass is provided then it will be fetched from the environment variable UBM_UNITY_KEYALIAS_PASS // right before building if (!string.IsNullOrEmpty(KeyaliasPass)) { PlayerSettings.Android.keyaliasPass = KeyaliasPass; } break; } // // Apply symbols // ProcessDefineSymbols(buildTargetGroup); } private void ReportKeyMissingAttributeError(string attributeName) { Log($"Can't sign the app because no {Debug.FormatTextInUserContext(attributeName)} has been provided.", ELogLevel.Error); } private void ProcessDefineSymbols(BuildTargetGroup buildTargetGroup) { // Trim define symbols from a previous round HashSet symbols = HtPlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup, true); // Pluggable symbols list is delimited by HT_PLUGGABLE_SYMBOLS_BEGIN and HT_PLUGGABLE_SYMBOLS_END // so we can delete the pluggable list from a previous round _ = symbols.Add(HtPlayerSettings.HT_PLUGGABLE_SYMBOLS_BEGIN); if (m_defineSymbols != null && m_defineSymbols.Count > 0) { // Sort pluggable symbols alphabetically SortedSet sortedSymbols = new SortedSet(); _ = sortedSymbols.AddRange(m_defineSymbols); // Add the pluggable symbols list to the definitive list _ = symbols.AddRange(sortedSymbols); } _ = symbols.Add(HtPlayerSettings.HT_PLUGGABLE_SYMBOLS_END); HtPlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, symbols); } public void PostBuild() { } public bool ContainsDefineSymbol(string symbol) { return m_defineSymbols != null && m_defineSymbols.Contains(symbol); } public void AddDefineSymbols(HashSet symbols) { if (symbols != null) { foreach (string s in symbols) { AddDefineSymbol(s); } } } public void AddDefineSymbol(string symbol) { if (string.IsNullOrEmpty(symbol)) { Log($"Empty symbols are not valid", ELogLevel.Warning); } else { if (m_defineSymbols == null) { m_defineSymbols = new List(); } // Make sure the symbol is not already added if (!m_defineSymbols.Contains(symbol)) { // Make sure that the opposite symbol is not already added string notSymbol = NotSymbol(symbol); if (m_defineSymbols.Contains(notSymbol)) { Log($"Define symbol {Debug.FormatTextInUserContext(symbol)} wasn't added " + $"because {Debug.FormatTextInUserContext(notSymbol)} is already defined", ELogLevel.Error); } else { m_defineSymbols.Add(symbol); } } } } public void RemoveDefineSymbol(string symbol) { if (ContainsDefineSymbol(symbol)) { _ = m_defineSymbols.Remove(symbol); } } private string NotSymbol(string symbol) { return symbol.StartsWith("!") ? symbol.Substring(1) : $"!{symbol}"; } public bool ContainsCompilerReference(string value) { return m_compilerReferences != null && m_compilerReferences.Contains(value); } public void AddCompilerReference(string value) { if (string.IsNullOrEmpty(value)) { Log($"Empty compiler references are not valid", ELogLevel.Warning); } else { if (m_compilerReferences == null) { m_compilerReferences = new List(); } // Make sure the reference is not already added if (!m_compilerReferences.Contains(value)) { m_compilerReferences.Add(value); } } } public void RemoveCompilerReference(string value) { if (ContainsCompilerReference(value)) { _ = m_compilerReferences.Remove(value); } } /// /// Add a list of scenes to be included in the build. /// The first scene in the array passed in is used as the first Unity scene. /// /// List of paths to scenes to include in the build. /// Paths are relative to the project folder where the asset is stored. Example: "Assets/Scenes/MainScene.unity". /// public void AddSceneList(List scenePaths) { if (m_scenePaths == null) { m_scenePaths = new List(); } // Strip out duplicates _ = m_scenePaths.AddRange(scenePaths, true); } /// /// Path to the scene that should be used as the first scene. If it's empty then the first scene defined in the list of scenePaths< /// passed in to AddSceneList()will be used instead. /// This field is useful when you define several UnityBuildProperty objects, each with a list of scenes to include. /// public string FirstScenePath { get { return m_firstScenePath; } set { m_firstScenePath = value; } } public void SetSignSettings_iOS(string appleDeveloperTeamId, EProvisioningProfileType provisioningProfileType, string provisioningProfileId) { AppleDeveloperTeamID = appleDeveloperTeamId; IOSManualProvisioningProfileType = provisioningProfileType; IOSManualProvisioningProfileID = provisioningProfileId; } private void JoinSceneList(UnityBuildProperty p) { // Join scene lists if (p.SceneList != null && p.SceneList.Count > 0) { AddSceneList(p.SceneList); } if (!string.IsNullOrEmpty(FirstScenePath) && !string.IsNullOrEmpty(p.FirstScenePath)) { if (FirstScenePath != p.FirstScenePath) { string msg = $"Two different scenes are set as first scene: {Debug.FormatTextInUserContext(FirstScenePath)} and "; msg += Debug.FormatTextInUserContext(p.FirstScenePath); Debug.EditorLogError(msg); } } else if (string.IsNullOrEmpty(FirstScenePath) && !string.IsNullOrEmpty(p.FirstScenePath)) { FirstScenePath = p.FirstScenePath; } } private void ApplySceneList() { if (m_scenePaths != null && m_scenePaths.Count > 0) { // Find valid Scene paths and make a list of EditorBuildSettingsScene List editorBuildSettingsScenes = new List(); // Set FirstScenePath as the first scene if it's not empty if (!string.IsNullOrEmpty(FirstScenePath)) { int index = m_scenePaths.IndexOf(FirstScenePath); if (index > 0) { _ = m_scenePaths.Remove(FirstScenePath); index = -1; } if (index == -1) { m_scenePaths.Insert(0, FirstScenePath); } } foreach (string scene in m_scenePaths) { // Make sure that the scene is a path to an existing scene file if (!HtAssetDatabase.IsASceneFile(scene)) { Debug.EditorLogError($"{Debug.FormatTextInUserContext(scene)} must be a scene file (.unity)"); } else if (!HtAssetDatabase.ExistsFile(scene)) { Debug.EditorLogError($"{Debug.FormatTextInUserContext(scene)} is not a valid path to a scene file"); } else { editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scene, true)); } } // Set the Build Settings window Scene list EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray(); } } } }