Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Let me know How to build multi-platform Addressable bundles at once

Discussion in 'Addressables' started by santapemagic, Aug 20, 2019.

  1. santapemagic

    santapemagic

    Joined:
    Dec 14, 2017
    Posts:
    2
    Hi. this is my first post in Unity forum.

    I'm using Addressables System on my project recently.

    When i build Addressable bundle(by 'Build Player Content'), it export the bundle that project's active build target(following UnityEditor.EditorUserBuildSettings.activeBuildTarget)

    My project is supporting Android/iOS. and also need StandaloneWindows cause Unity Editor needs that.

    so, problem is it.

    It takes too much time that switching build target to build the target bundle. and Each platform Catalog have different RemoteLoadPath.

    I want to Build Bundles in Script instead of [Addressables-Build-Build Player Content]. And In that script, there will be switch-platform command. So, when i call that func, func build the StandaloneWindows bundle - Android bundle - iOS bundle.

    Is it possible? or is there any way to build multi-platform bundles at once?

    Please, Help me...
     
  2. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    This is a core challenge with building for Unity in general. Other teams are working on ways to improve this, but for now we have what we have.

    The main way to deal with it is to write a script to build command line (often on a build server). this would be a good place to start investigating: https://docs.unity3d.com/Manual/CommandLineArguments.html. You can also look into using Unity Cloud Build
     
  3. aurelien-morel-ubiant

    aurelien-morel-ubiant

    Joined:
    Sep 27, 2017
    Posts:
    275
    I did this, this week for Addressables by command line. But I found an that I report in another forum post.
    I can give you a quick command line example for windows or unix system.

    AND

    what you could do to improve the building time @santapemagic is to active the cache server embedded with unity. At least with this your import time will be reduced. Cache server will need at least a first full import time then it will just check the cache server and "download" all the asset that are in the same state when you ask a new switch. For us, on some project we can earn almost 50-70% of import time.
     
    unity_bill likes this.
  4. neoRiley

    neoRiley

    Joined:
    Dec 12, 2008
    Posts:
    148
    I was wondering if you could post the command line example here? I need to build to Windows, iOS and Android ;)

    Thanks very much!
     
  5. aurelien-morel-ubiant

    aurelien-morel-ubiant

    Joined:
    Sep 27, 2017
    Posts:
    275
    It's quite simple, I have a shell script which is this one :
    Code (CSharp):
    1. "${UNITY_EXECUATBLE}" -batchmode -nographics -logFile "${UNITY_LOGS_PATH}" -projectpath "${CI_PROJECT_DIR}/${PROJECT_FOLDER}" -executeMethod Ubiant.Editor.Addressables.AddressablesBuilding.BatchBuild -BUILD_NUMBER="$BUILD_NUMBER" -ADDRESSABLE_SERVER="$ADDRESSABLE_SERVER" -PLATFORMS="$PLATFORMS"
    and a C# script to iterate on all the parameters from this shell script call which is this one :


    Code (CSharp):
    1. namespace Ubiant.Editor.Addressables
    2. {
    3.     using Ubiant.Common.Tools;
    4.     using UnityEditor;
    5.     using UnityEditor.AddressableAssets;
    6.     using UnityEditor.AddressableAssets.Settings;
    7.     using UnityEditor.AddressableAssets.Settings.GroupSchemas;
    8.     using UnityEngine;
    9.     using System.IO;
    10.     using System;
    11.     using System.Globalization;
    12.     using Ubiant.Common.Utils;
    13.     using System.Collections.Generic;
    14.     using System.Text;
    15.     using UnityEngine.AddressableAssets;
    16.     using Ubiant.Designer.PlanCreator;
    17.  
    18.     public class AddressablesBuilding
    19.     {
    20.         private const string ADDRESSABLES_OBJECTS_ASSET_PATH = "Assets/Addressables/Objects";
    21.         private const string ADDRESSABLES_BUILDINGS_ASSET_PATH = "Assets/Addressables/Buildings";
    22.  
    23.         public static AddressablesBuilding s_model;
    24.  
    25.         public string PlatformsToBuild = "global";
    26.         public string VersionTag = "0.0.0";
    27.         public string TargetVersion = "dev";
    28.         public string[] BuildingList = new string[]{ "all" };
    29.         public AddressableAssetSettings AddressableToBuild;
    30.  
    31.         /// <summary>
    32.         /// Method used by Gitlab builder to have an external entry point to Addressables build pipeline.true (Don't use it !)
    33.         /// </summary>
    34.         public static void BatchBuild()
    35.         {
    36.             InstallAddressablesTools();
    37.  
    38.             s_model = new AddressablesBuilding();
    39.             s_model.VersionTag = GlobalTools.GetCommandLineArg("BUILD_NUMBER", "0.0.0");
    40.             s_model.PlatformsToBuild = GlobalTools.GetCommandLineArg("PLATFORMS", "all");
    41.             s_model.TargetVersion = GlobalTools.GetCommandLineArg("ADDRESSABLE_SERVER", "all");
    42.             string buildingConcatList = GlobalTools.GetCommandLineArg("BUILDING_LIST", "all");
    43.             s_model.BuildingList = buildingConcatList.Split('-');
    44.             s_model.BuildAddressables();
    45.         }
    46.  
    47.         [MenuItem("Ubiant/Tools/Addressables/Install tools")]
    48.         public static void InstallAddressablesTools()
    49.         {
    50.             System.Diagnostics.Process process = new System.Diagnostics.Process();
    51.             string extension = Application.platform == RuntimePlatform.WindowsEditor ? ".bat" : ".sh";
    52.             string scriptPath = IOUtility.CombineMultiplePaths(Application.dataPath, "..", $"install_aws_utils{extension}");
    53.             string exeToUse = Application.platform == RuntimePlatform.WindowsEditor ? "cmd.exe" : "/bin/bash";
    54.             Debug.Log( $"scriptPath : {scriptPath}" );
    55.             process = Builder.Utils.StartProcess(exeToUse, $"\"{scriptPath}\"", "", false, "", true);
    56.            
    57.             StringBuilder outputOk = new StringBuilder();
    58.             StringBuilder outputError = new StringBuilder();
    59.  
    60.             while (!process.HasExited)
    61.             {
    62.                 outputOk.Append(process.StandardOutput.ReadToEnd());
    63.                 outputError.Append(process.StandardError.ReadToEnd());
    64.             }
    65.  
    66.             Debug.Log($"Standard output : {outputOk.ToString()}");
    67.             Debug.LogWarning($"Standard error : {outputError.ToString()}");
    68.         }
    69.  
    70.         [MenuItem("Ubiant/Tools/Addressables/Build")]
    71.         public static void BuildAddressablesMenu()
    72.         {
    73.             s_model = new AddressablesBuilding();
    74.             s_model.PlatformsToBuild = EditorUserBuildSettings.activeBuildTarget.ToString().ToLowerInvariant();
    75.             s_model.BuildAddressables();
    76.         }
    77.  
    78.         #region Build Automation Part
    79.  
    80.         /// <summary>
    81.         /// This allow you to build Addressables assets based on the parameters in entry.
    82.         /// </summary>
    83.         public void BuildAddressables()
    84.         {
    85.             int exitCode = 0;
    86.             s_model.AddressableToBuild = AddressableAssetSettingsDefaultObject.GetSettings(false);
    87.  
    88.             //Just in case all word is used, we set it to a true value then we split it to use it later      
    89.             if( s_model.TargetVersion == "all" )
    90.                 TargetVersion = "dev_master";
    91.             string[] targetVersionArray = TargetVersion.Split('_');
    92.  
    93.             if( s_model.PlatformsToBuild == "all" )
    94.                 PlatformsToBuild = "android_ios_standalone_webgl";
    95.  
    96.             // Update Collection With All Object Edition Data
    97.             BuildingElementsCollection.instance.UpdateData(true);
    98.            
    99.             Dictionary<BuildTarget, BuildTargetGroup> dicBuildAndGroup = GenerateBuildTargetAndGroup(s_model.PlatformsToBuild);
    100.             foreach( var buildAndGroup in dicBuildAndGroup )
    101.             {
    102.                 EditorUserBuildSettings.SwitchActiveBuildTarget(buildAndGroup.Value, buildAndGroup.Key);
    103.  
    104.                 //We will now iterate through all target version and through all platform possibility to generate each catalog and assets per platform and server
    105.                 foreach( string target in targetVersionArray )
    106.                 {
    107.                     string profileID = s_model.AddressableToBuild.profileSettings.AddProfile($"AutoGeneratedProfile_{EditorUserBuildSettings.activeBuildTarget}_{target}",
    108.                                                                                     s_model.AddressableToBuild.profileSettings.GetProfileId("Default"));
    109.                     s_model.AddressableToBuild.activeProfileId = profileID;
    110.  
    111.                     //This part is a workaround to avoid addressables issue until unity fixed it. Currently not fixed in 1.1.7 Addressables package.
    112.                     string remoteBuildPath = $"ServerData/[BuildTarget]/{target}";
    113.                     remoteBuildPath = remoteBuildPath.Replace("[BuildTarget]", EditorUserBuildSettings.activeBuildTarget.ToString());
    114.                     string remoteLoadPath = $"{UbiantConstants.instance.AWS_ADDRESSABLES_URL}/[BuildTarget]/{target}";
    115.                     remoteLoadPath = remoteLoadPath.Replace("[BuildTarget]", EditorUserBuildSettings.activeBuildTarget.ToString());
    116.  
    117.                     //Set the value into the last profile created
    118.                     s_model.AddressableToBuild.profileSettings.SetValue(profileID,"RemoteBuildPath",remoteBuildPath);
    119.                     s_model.AddressableToBuild.profileSettings.SetValue(profileID,"RemoteLoadPath",remoteLoadPath);
    120.  
    121.                     //We read the remoteBuildPath to generate the complete path to the folder where all bundles / catalog are generated to use it in others operations
    122.                     //remoteBuildPath = remoteBuildPath.Replace("[BuildTarget]", EditorUserBuildSettings.activeBuildTarget.ToString());
    123.                     string addressablesPath = IOUtility.ReplacePathSeparator( IOUtility.CombinePaths(Directory.GetCurrentDirectory(), remoteBuildPath));
    124.  
    125.                     CleanAndVersionAddressablesFolder(addressablesPath,target);
    126.                     exitCode = BuildAndUploadAddressables(addressablesPath,target);
    127.                 }
    128.             }
    129.  
    130.             if (Application.isBatchMode)
    131.                 EditorApplication.Exit(exitCode);
    132.         }
    133.  
    134.         /// <summary>
    135.         /// Retrieve the BuildTarget and BuildTargetGroup based on string in entry
    136.         /// </summary>
    137.         /// <param name="platformsToBuild">string with this syntax : platform1_platformX</param>
    138.         /// <returns>A dictionnary with BuildTarget and BuildTargetGroup</returns>
    139.         private Dictionary<BuildTarget, BuildTargetGroup> GenerateBuildTargetAndGroup(string platformsToBuild)
    140.         {
    141.             Dictionary<BuildTarget, BuildTargetGroup> buildTargetAndGroup = new Dictionary<BuildTarget, BuildTargetGroup>();
    142.             string[] platforms = platformsToBuild.Split('_');
    143.             for(int platformIndex = 0 ; platformIndex < platforms.Length ; ++platformIndex)
    144.             {
    145.                 if( Enum.TryParse(platforms[platformIndex], true, out BuildTarget buildtarget) )
    146.                     buildTargetAndGroup.Add( buildtarget, BuildPipeline.GetBuildTargetGroup(buildtarget));
    147.                 else if( platforms[platformIndex].Equals("standalone") ) //Special case if the user creates a tag with standalone without specific platform
    148.                 {
    149.                     buildTargetAndGroup.Add( BuildTarget.StandaloneWindows64, BuildPipeline.GetBuildTargetGroup(BuildTarget.StandaloneWindows64));
    150.                     buildTargetAndGroup.Add( BuildTarget.StandaloneOSX, BuildPipeline.GetBuildTargetGroup(BuildTarget.StandaloneOSX));
    151.                     buildTargetAndGroup.Add( BuildTarget.StandaloneLinux64, BuildPipeline.GetBuildTargetGroup(BuildTarget.StandaloneLinux64));
    152.                 }
    153.             }
    154.  
    155.             return buildTargetAndGroup;
    156.         }
    157.  
    158.         /// <summary>
    159.         /// This will clean the Addressable generation folder and will generate the folder if it doesn't exist to generate the versioning file in it.
    160.         /// </summary>
    161.         /// <param name="addressablesPath">The addressables generation path defines by an Addressable profile</param>
    162.         private void CleanAndVersionAddressablesFolder(string addressablesPath, string target)
    163.         {
    164.             if(!Directory.Exists(addressablesPath))
    165.                 Directory.CreateDirectory(addressablesPath);
    166.  
    167.             //Clean up the addressable folder where assets are generated
    168.             DirectoryInfo di = new DirectoryInfo(addressablesPath);
    169.             foreach (FileInfo file in di.GetFiles())
    170.                 file.Delete();
    171.  
    172.             foreach (DirectoryInfo dir in di.GetDirectories())
    173.                 dir.Delete(true);
    174.  
    175.             //Create a versioning file which is basically just an empty file with necessary information in name
    176.             File.Create(IOUtility.CombinePaths(addressablesPath, $"{VersionTag}_{target}_{DateTime.UtcNow.ToString("yyyy-MM-ddTHH-mm-ss", CultureInfo.InvariantCulture)}.versioning")).Close();
    177.         }
    178.  
    179.         /// <summary>
    180.         /// Build the addressables files and upload them with a script on the S3 assets server.
    181.         /// It will display the Standard and the Error output log just in case.
    182.         /// </summary>
    183.         /// <param name="addressablesPath">The addressables generation path defines by an Addressable profile</param>
    184.         private int BuildAndUploadAddressables(string addressablesPath, string target)
    185.         {
    186.             Debug.Log($"===================================> {EditorUserBuildSettings.activeBuildTarget} <=====================================");
    187.             Debug.Log($"===================================> {s_model.AddressableToBuild.profileSettings.GetValueByName(s_model.AddressableToBuild.activeProfileId,"RemoteBuildPath")} <=====================================");
    188.             AddressableAssetSettings.BuildPlayerContent();
    189.  
    190.             //Iterate all catalog generated to rename them to catalog.extension instead of catalog_timestap.extension
    191.             foreach (string filepath in Directory.GetFiles(addressablesPath, "*catalog*"))
    192.             {
    193.                 string simpleCatalogPath = IOUtility.CombinePaths(addressablesPath, $"catalog{Path.GetExtension(filepath)}");
    194.                 File.Move(filepath, simpleCatalogPath);
    195.             }
    196.            
    197.             string buildTarget =  EditorUserBuildSettings.activeBuildTarget.ToString();
    198.             string completeS3Path = $"{buildTarget}/{target}";
    199.  
    200.             string exeToUse = Application.platform == RuntimePlatform.WindowsEditor ? "cmd.exe" : "/bin/bash";
    201.             string extension = Application.platform == RuntimePlatform.WindowsEditor ? ".bat" : ".sh";
    202.             string scriptPath = IOUtility.CombineMultiplePaths(Application.dataPath, "..", $"aws_asset_copy{extension}");
    203.  
    204.             System.Diagnostics.Process process = new System.Diagnostics.Process();
    205.             //Start the shell or batch script from C# code to upload all generated bundle on the Amazon S3 Server
    206.             process = Builder.Utils.StartProcess(exeToUse, $"\"{scriptPath}\" \"{addressablesPath}\" ubiant-unity-assets {completeS3Path}", "", false, "", true);
    207.  
    208.             StringBuilder outputOk = new StringBuilder();
    209.             StringBuilder outputError = new StringBuilder();
    210.  
    211.             while (!process.HasExited)
    212.             {
    213.                 outputOk.Append(process.StandardOutput.ReadToEnd());
    214.                 outputError.Append(process.StandardError.ReadToEnd());
    215.             }
    216.  
    217.             Debug.Log($"Standard output : {outputOk.ToString()}");
    218.             Debug.LogWarning($"Standard error : {outputError.ToString()}");
    219.  
    220.             if( process.ExitCode != 0 )
    221.                 Debug.LogWarning($"[ADDRESSABLES]Something bad happens during assets generation on {EditorUserBuildSettings.activeBuildTarget}.");
    222.  
    223.             return process.ExitCode;
    224.         }
    225.  
    226.         #endregion
    227.  
    228.         #region PreBuild part
    229.  
    230.         /// <summary>
    231.         /// Method to clean up the addressableAssetSettings in entry before performing any operation.
    232.         /// </summary>
    233.         /// <param name="addressableAssetsSettings">AddressableAssetSettings to clean up</param>
    234.         public static void CleanAddressableAssetSettings(AddressableAssetSettings addressableAssetsSettings)
    235.         {
    236.             //First we clean the entire Addressable Asset Settings by removing groups
    237.             for (int i = addressableAssetsSettings.groups.Count - 1; i >= 0; --i)
    238.                 addressableAssetsSettings.RemoveGroup(addressableAssetsSettings.groups[i]);
    239.         }
    240.  
    241.         [MenuItem("Ubiant/Tools/Addressables/Create Buildings Group Testing")]
    242.         public static void GenerateBuildingsAddressableGroups()
    243.         {
    244.             if (s_model.BuildingList.Length > 0)
    245.             {
    246.                 AddressableAssetSettings addressableAssetsSettings = AddressableAssetSettingsDefaultObject.GetSettings(false);
    247.  
    248.                 CleanAddressableAssetSettings(addressableAssetsSettings);
    249.  
    250.                 string[] allBuildingGroupToCreate = AssetDatabase.GetSubFolders(ADDRESSABLES_BUILDINGS_ASSET_PATH);
    251.                 List<string> buildingListToGenerate = new List<string>();
    252.  
    253.                 if (s_model.BuildingList[0] != "all")
    254.                 {
    255.                     for (int idxBuildingFolder = 0; idxBuildingFolder < allBuildingGroupToCreate.Length; ++idxBuildingFolder)
    256.                     {
    257.                         for( int idxBuildingToGenerate = 0; idxBuildingToGenerate < s_model.BuildingList.Length; ++idxBuildingToGenerate )
    258.                         {
    259.                             if(allBuildingGroupToCreate[idxBuildingFolder].Contains(s_model.BuildingList[idxBuildingToGenerate]))
    260.                             {
    261.                                 buildingListToGenerate.Add(allBuildingGroupToCreate[idxBuildingFolder]);
    262.                             }
    263.                         }
    264.                     }
    265.                 }
    266.                 else
    267.                 {
    268.                     buildingListToGenerate.AddRange(allBuildingGroupToCreate);
    269.                 }
    270.  
    271.                 foreach (var groupsContainerFolder in buildingListToGenerate)
    272.                 {
    273.                     GenerateGroupSchemeSet(BundledAssetGroupSchema.BundlePackingMode.PackTogether);
    274.                 }
    275.             }
    276.         }
    277.  
    278.         [MenuItem("Ubiant/Tools/Addressables/Create Objects Group Testing")]
    279.         public static void GenerateObjectsAddressableGroups()
    280.         {
    281.             AddressableAssetSettings addressableAssetsSettings = AddressableAssetSettingsDefaultObject.GetSettings(false);
    282.  
    283.             CleanAddressableAssetSettings(addressableAssetsSettings);
    284.  
    285.             //TODO
    286.             //Choose the right profile here
    287.             //We need to use the autogenerated one so we will build it before this step and build one
    288.            
    289.             //TODO
    290.             //Add the default group before performing the rest. BuildingCollectionElement need to be in the default group cause for now they are ignored.
    291.  
    292.             FindAssetsAndCreateGroupRecursievly(addressableAssetsSettings, ADDRESSABLES_OBJECTS_ASSET_PATH);
    293.         }
    294.  
    295.         /// <summary>
    296.         /// Search each asset into a particular folder path then iterate in all folders to retrieve files.
    297.         /// Each folder that is detected will generate a label into the AddressableAssetSettings and will be concatenated for each file under those folders.
    298.         /// This will add each asset in a group defined by the folders right above the assets for the current step.
    299.         /// This will add each folder as a label in the AddressableAssetSettings in entry.
    300.         /// </summary>
    301.         /// <param name="addressableAssetsSettings">AddressableAssetSettings to setup</param>
    302.         /// <param name="folderPath">folder path to scan</param>
    303.         /// <param name="labels">previous labels to use for the current process</param>
    304.         public static void FindAssetsAndCreateGroupRecursievly(AddressableAssetSettings addressableAssetsSettings, string folderPath, List<string> labels = null)
    305.         {
    306.             string[] subFoldersPath = AssetDatabase.GetSubFolders(folderPath);
    307.             List<string> instantLabels;
    308.  
    309.             foreach (string subFolderPath in subFoldersPath)
    310.             {
    311.                 if(labels == null)
    312.                     labels = new List<string>();
    313.  
    314.                 instantLabels = new List<string>(labels);
    315.  
    316.                 string[] pathPieces = subFolderPath.Split('/');
    317.                 string currentFolderName = pathPieces[pathPieces.Length-1];
    318.                 if (!labels.Contains(currentFolderName))
    319.                 {
    320.                     addressableAssetsSettings.AddLabel(currentFolderName);
    321.                     labels.Add(currentFolderName);
    322.                 }
    323.  
    324.                 string[] assetsInCurrentFolder = AssetDatabase.FindAssets("*", new string[] {subFolderPath });
    325.  
    326.                 AddressableAssetGroupSchemaSet groupSet = null;
    327.                 AddressableAssetGroup newGroup = null;
    328.  
    329.                 int folderCount = 0;
    330.  
    331.                 foreach (var assetGuid in assetsInCurrentFolder)
    332.                 {
    333.                     string assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
    334.                     string groupName = GetLastFolderFromPath(assetPath);
    335.                     int currentPathDepth = assetPath.Split('/').Length - 1;
    336.  
    337.                     if (!AssetDatabase.IsValidFolder(assetPath) && pathPieces.Length == currentPathDepth)
    338.                     {
    339.                         if(groupSet == null)
    340.                             groupSet = GenerateGroupSchemeSet(BundledAssetGroupSchema.BundlePackingMode.PackSeparately);
    341.  
    342.                         if (newGroup == null)
    343.                             newGroup = addressableAssetsSettings.CreateGroup(groupName, false, false, false, groupSet.Schemas);
    344.  
    345.                         string[] sAssetsCutPath = assetPath.Split('/');
    346.                         sAssetsCutPath = $"{sAssetsCutPath[sAssetsCutPath.Length - 2]}/{sAssetsCutPath[sAssetsCutPath.Length - 1]}".Split('.');
    347.  
    348.                         AddressableAssetEntry assetEntry = addressableAssetsSettings.CreateOrMoveEntry(assetGuid, newGroup);
    349.                         assetEntry.SetAddress(sAssetsCutPath[0]);
    350.                         foreach( var label in labels )
    351.                             assetEntry.SetLabel(label, true);
    352.                     }
    353.                     else
    354.                     {
    355.                         folderCount++;
    356.                     }
    357.                 }
    358.  
    359.                 if(folderCount > 0)
    360.                     FindAssetsAndCreateGroupRecursievly(addressableAssetsSettings, subFolderPath, labels);
    361.  
    362.                 labels = instantLabels;
    363.             }
    364.         }
    365.  
    366.         /// <summary>
    367.         /// Method to get the folder name just above a file.
    368.         /// CAUTION : This will not return the right thing if you specified a folder.
    369.         /// </summary>
    370.         /// <param name="filePath"></param>
    371.         /// <returns></returns>
    372.         public static string GetLastFolderFromPath(string filePath)
    373.         {
    374.             string[] pathSplit = filePath.Split('/');
    375.             return pathSplit[pathSplit.Length - 2];
    376.         }
    377.  
    378.         /// <summary>
    379.         /// This will generate a generic groupSchema for the objects.
    380.         /// </summary>
    381.         /// <param name="packingMode">Let you choose between the 3 possible packing mode from Addressables bundle.</param>
    382.         /// <returns></returns>
    383.         public static AddressableAssetGroupSchemaSet GenerateGroupSchemeSet(BundledAssetGroupSchema.BundlePackingMode packingMode)
    384.         {
    385.             AddressableAssetGroupSchemaSet groupSet = new AddressableAssetGroupSchemaSet();
    386.  
    387.             //We generate a BundleAssetGroupSchema to specify each group parameters
    388.             BundledAssetGroupSchema bundleGroupSchema = ScriptableObject.CreateInstance<BundledAssetGroupSchema>();
    389.             bundleGroupSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4;
    390.             bundleGroupSchema.IncludeInBuild = false;
    391.             //missing bundleAssetprovider
    392.             bundleGroupSchema.ForceUniqueProvider = false;
    393.             bundleGroupSchema.UseAssetBundleCache = true;
    394.             bundleGroupSchema.UseAssetBundleCrc = true;
    395.             bundleGroupSchema.Timeout = 0;
    396.             bundleGroupSchema.ChunkedTransfer = false;
    397.             bundleGroupSchema.RedirectLimit = -1;
    398.             bundleGroupSchema.RetryCount = 0;
    399.             //TODO Find a way to setup build path
    400.             //TODO Find a way to setup load path
    401.             bundleGroupSchema.BundleMode = packingMode;
    402.  
    403.             ContentUpdateGroupSchema contentGroupschema = ScriptableObject.CreateInstance<ContentUpdateGroupSchema>();
    404.             contentGroupschema.StaticContent = false;
    405.  
    406.             groupSet.AddSchema(bundleGroupSchema, null);
    407.             groupSet.AddSchema(contentGroupschema, null);
    408.  
    409.             return groupSet;
    410.         }
    411.  
    412.         #endregion
    413.     }
    414. }
    Some part are maybe still in progress cause I would improved it a lot but due to a lack of time I still not finish it :D
    But in the current state he does all what we need.

    Barely it's triggered by a git tag on our git repository. Then based on this tag, C# build all needed platform and when it's done all is upload on our aws server and deploy in "dev" or in "production".

    If it's too complicated to read like this, I can simplify it during my next free time.
     
  6. RahulT

    RahulT

    Joined:
    Feb 15, 2019
    Posts:
    2
    Hello All,

    Is there any update on the above topic where specifically, is there a possibility to build addressable asset for different platforms without using EditorUserBuildSettings.SwitchActiveBuildTarget?
    @unity_bill - Is there any plans to integrate the same in upcoming versions of unity?

    We are also having a project supporting ioS,Android,UWP and we want to automate this as part of the build pipeline.

    Thanks!
     
    ImpossibleRobert likes this.
  7. joonturbo

    joonturbo

    Joined:
    Jun 24, 2013
    Posts:
    75
    I ended up with this solution that seems to work.
    The key that I was missing was making sure that EditorApplication.isCompiling is false, because otherwise addressables won't be built.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEditor;
    5. using UnityEditor.AddressableAssets;
    6. using UnityEditor.AddressableAssets.Settings;
    7. using UnityEditor.Compilation;
    8. using UnityEngine;
    9.  
    10. public class BuildScriptsAddressables
    11. {
    12.     static bool isListening = false;
    13.  
    14.     public static void SetPlatformWindows()
    15.     {
    16.         EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows);
    17.         EditorUserBuildSettings.selectedStandaloneTarget = BuildTarget.StandaloneWindows64;
    18.      
    19.         BuildAddressables();
    20.     }
    21.  
    22.     public static void SetPlatformMacOS()
    23.     {
    24.         EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone, BuildTarget.StandaloneOSX);
    25.         EditorUserBuildSettings.selectedStandaloneTarget = BuildTarget.StandaloneOSX;
    26.  
    27.      
    28.         BuildAddressables();
    29.     }
    30.  
    31.     public static void BuildAddressables(object o = null)
    32.     {
    33.         if (EditorApplication.isCompiling)
    34.         {
    35.             Debug.Log("Delaying until compilation is finished...");
    36.      
    37.             if(!isListening)
    38.                 CompilationPipeline.compilationFinished += BuildAddressables;
    39.             isListening = true;
    40.             return;
    41.         }
    42.  
    43.         if(isListening)
    44.             CompilationPipeline.compilationFinished -= BuildAddressables;
    45.  
    46.         //AddressableAssetSettingsDefaultObject.Settings = AddressableAssetSettings.Create();
    47.         Debug.Log("Building Addressables!!! START PLATFORM: platform: " + Application.platform + " target: " + EditorUserBuildSettings.selectedStandaloneTarget);
    48.      
    49.         AddressableAssetSettings.CleanPlayerContent();
    50.         AddressableAssetSettings.BuildPlayerContent();
    51.      
    52.         Debug.Log("Building Addressables!!! DONE");
    53.     }
    54. }
    55.  
    and calling it from a bash script with

    Code (CSharp):
    1. Unity.app/Contents/MacOS/Unity -quit -batchmode -projectPath ~/git/MYPROJECT/ -executeMethod BuildScriptsAddressables.SetPlatformMacOS -logfile "output/build_macos_pre.log" -buildTarget OSXUniversal