Last active
May 18, 2022 14:32
-
-
Save msklywenn/a3022c730561ef34c5bd14278ac14f1f to your computer and use it in GitHub Desktop.
Unity Batch Builder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
// Version 2, December 2004 | |
// | |
// Copyright (C) 2004 Sam Hocevar <[email protected]> | |
// | |
// Everyone is permitted to copy and distribute verbatim or modified | |
// copies of this license document, and changing it is allowed as long | |
// as the name is changed. | |
// | |
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
// | |
// 0. You just DO WHAT THE FUCK YOU WANT TO. | |
#define FMOD // comment if you're not using fmod | |
using UnityEditor; | |
using UnityEngine; | |
using System.IO; | |
using System; | |
class BatchBuilderSettings : ScriptableObject | |
{ | |
[Serializable] | |
public struct Settings | |
{ | |
public string Name; | |
public bool Active; | |
public BuildTarget Target; | |
public BuildTargetGroup Group; | |
public string Symbols; | |
public string Path; | |
public string Filename; | |
public UnityEditor.AnimatedValues.AnimBool Editing; | |
} | |
public Settings[] Builds = new Settings[0]; | |
[Serializable] | |
public struct Symbol | |
{ | |
public bool Active; | |
public string Name; | |
} | |
public Symbol[] GeneralSymbols = new Symbol[0]; | |
public bool Debug = false; | |
public bool LZ4Compression = true; | |
} | |
class BatchBuilder : EditorWindow | |
{ | |
const string settingsPath = "Assets/Editor/Resources/BatchBuild.asset"; | |
BatchBuilderSettings settings; | |
Vector2 scrollTargets; | |
Vector2 scrollSymbols; | |
enum Step | |
{ | |
SetSymbols, | |
#if FMOD | |
AudioCopy, | |
#endif | |
Compilation, | |
Build | |
} | |
int building = -1; | |
int succeeded = 0; | |
int failed = 0; | |
Step step = Step.SetSymbols; | |
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); | |
[MenuItem("Tools/Batch Builder")] | |
static void Open() | |
{ | |
EditorWindow w = GetWindow<BatchBuilder>(); | |
w.minSize = new Vector2(750, 320); | |
} | |
void OnEnable() | |
{ | |
settings = AssetDatabase.LoadAssetAtPath<BatchBuilderSettings>(settingsPath); | |
if (settings == null) | |
{ | |
settings = CreateInstance<BatchBuilderSettings>(); | |
AssetDatabase.CreateAsset(settings, settingsPath); | |
AssetDatabase.SaveAssets(); | |
} | |
EditorApplication.update += Update; | |
foreach (BatchBuilderSettings.Settings s in settings.Builds) | |
s.Editing.valueChanged.AddListener(Repaint); | |
} | |
void OnDisable() | |
{ | |
EditorApplication.update -= Update; | |
} | |
void OnGUI() | |
{ | |
GUI.enabled = building == -1; | |
GUILayout.Label("Targets", EditorStyles.boldLabel); | |
GUILayout.BeginHorizontal(); | |
{ | |
GUILayout.Label("Name", EditorStyles.boldLabel, GUILayout.Width(200)); | |
GUILayout.FlexibleSpace(); | |
GUILayout.Label("Directory", EditorStyles.boldLabel, GUILayout.Width(200)); | |
GUILayout.Label("Filename", EditorStyles.boldLabel, GUILayout.Width(200)); | |
GUILayout.Space(60); // width of edit, X | |
} | |
GUILayout.EndHorizontal(); | |
EditorGUI.BeginChangeCheck(); | |
{ | |
Undo.RecordObject(settings, "Edit Batch Build Settings"); | |
BatchBuilderSettings.Settings[] builds = settings.Builds; | |
scrollTargets = EditorGUILayout.BeginScrollView(scrollTargets); | |
{ | |
for (int i = 0; i < builds.Length; i++) | |
{ | |
bool deleted = false; | |
GUILayout.BeginHorizontal(); | |
{ | |
if (builds[i].Editing.target) | |
{ | |
builds[i].Active = EditorGUILayout.Toggle(builds[i].Active, GUILayout.Width(15)); | |
builds[i].Name = EditorGUILayout.TextField(builds[i].Name); | |
GUILayout.FlexibleSpace(); | |
builds[i].Path = EditorGUILayout.TextField(builds[i].Path, GUILayout.Width(200)); | |
builds[i].Filename = EditorGUILayout.TextField(builds[i].Filename, GUILayout.Width(200)); | |
} | |
else | |
{ | |
builds[i].Active = EditorGUILayout.ToggleLeft(builds[i].Name, builds[i].Active); | |
GUILayout.FlexibleSpace(); | |
GUILayout.Label(builds[i].Path, GUILayout.Width(200)); | |
GUILayout.Label(builds[i].Filename, GUILayout.Width(200)); | |
} | |
bool wasEditing = builds[i].Editing.target; | |
builds[i].Editing.target = GUILayout.Toggle(builds[i].Editing.target, "Edit", "Button"); | |
if (wasEditing && !builds[i].Editing.target) | |
GUI.FocusControl(string.Empty); | |
if (GUILayout.Button("X", GUILayout.Width(20))) | |
{ | |
deleted = true; | |
ArrayUtility.RemoveAt(ref settings.Builds, i); | |
GUI.FocusControl(string.Empty); | |
i--; | |
} | |
} | |
GUILayout.EndHorizontal(); | |
if (!deleted && builds[i].Editing.target) | |
{ | |
EditorGUILayout.BeginFadeGroup(builds[i].Editing.faded); | |
{ | |
builds[i].Target = (BuildTarget)EditorGUILayout.EnumPopup("Target", builds[i].Target); | |
builds[i].Group = (BuildTargetGroup)EditorGUILayout.EnumPopup("Group", builds[i].Group); | |
builds[i].Symbols = EditorGUILayout.TextField("Symbols", builds[i].Symbols); | |
} | |
EditorGUILayout.EndFadeGroup(); | |
} | |
} | |
} | |
EditorGUILayout.EndScrollView(); | |
GUILayout.BeginHorizontal(); | |
GUILayout.FlexibleSpace(); | |
if (GUILayout.Button("Add")) | |
{ | |
ArrayUtility.Insert(ref settings.Builds, settings.Builds.Length, | |
new BatchBuilderSettings.Settings() | |
{ | |
Name = "New Build", | |
Path = "Builds/New", | |
Filename = PlayerSettings.productName + ".exe", | |
Target = BuildTarget.StandaloneWindows64, | |
Group = BuildTargetGroup.Standalone, | |
Symbols = "TMPPRESENT", | |
Active = false, | |
Editing = new UnityEditor.AnimatedValues.AnimBool(true), | |
}); | |
} | |
GUILayout.EndHorizontal(); | |
GUILayout.Label("General Symbols", EditorStyles.boldLabel); | |
scrollSymbols = EditorGUILayout.BeginScrollView(scrollSymbols); | |
{ | |
for (int i = 0; i < settings.GeneralSymbols.Length; i++) | |
{ | |
GUILayout.BeginHorizontal(); | |
{ | |
settings.GeneralSymbols[i].Active = EditorGUILayout.Toggle(settings.GeneralSymbols[i].Active, GUILayout.Width(15)); | |
settings.GeneralSymbols[i].Name = GUILayout.TextField(settings.GeneralSymbols[i].Name); | |
if (GUILayout.Button("X", GUILayout.Width(20))) | |
{ | |
ArrayUtility.RemoveAt(ref settings.GeneralSymbols, i); | |
i--; | |
} | |
} | |
GUILayout.EndHorizontal(); | |
} | |
} | |
EditorGUILayout.EndScrollView(); | |
GUILayout.BeginHorizontal(); | |
GUILayout.FlexibleSpace(); | |
if (GUILayout.Button("Add")) | |
ArrayUtility.Insert(ref settings.GeneralSymbols, settings.GeneralSymbols.Length, | |
settings.GeneralSymbols[settings.GeneralSymbols.Length - 1]); | |
GUILayout.EndHorizontal(); | |
GUILayout.Label("Options", EditorStyles.boldLabel); | |
GUILayout.BeginHorizontal(); | |
{ | |
settings.Debug = EditorGUILayout.ToggleLeft("Debug", settings.Debug); | |
settings.LZ4Compression = EditorGUILayout.ToggleLeft("LZ4 Compression", settings.LZ4Compression); | |
GUILayout.FlexibleSpace(); | |
} | |
GUILayout.EndHorizontal(); | |
} | |
if (EditorGUI.EndChangeCheck()) | |
EditorUtility.SetDirty(settings); | |
if (building == -1) | |
{ | |
if (GUILayout.Button("Build")) | |
{ | |
timer.Start(); | |
building = 0; | |
succeeded = 0; | |
failed = 0; | |
step = Step.SetSymbols; | |
} | |
} | |
else | |
{ | |
GUI.enabled = true; | |
Rect rect = EditorGUILayout.GetControlRect(false, 20); | |
int totalSteps = Enum.GetNames(typeof(Step)).Length; | |
float progress = (building * totalSteps + (int)step) / (float)(settings.Builds.Length * totalSteps); | |
string text = string.Format("{0}: {1}", settings.Builds[building].Name, step); | |
EditorGUI.ProgressBar(rect, progress, text); | |
} | |
} | |
private void Update() | |
{ | |
if (building >= 0) | |
{ | |
Repaint(); | |
if (settings.Builds[building].Active) | |
{ | |
switch (step) | |
{ | |
case Step.SetSymbols: | |
{ | |
SetSymbols(settings.Builds[building]); | |
#if FMOD | |
step = Step.AudioCopy; | |
#else | |
step = Step.Compilation; | |
#endif | |
break; | |
} | |
#if FMOD | |
case Step.AudioCopy: | |
{ | |
FMODUnity.EventManager.CopyToStreamingAssets(); | |
step = Step.Compilation; | |
break; | |
} | |
#endif | |
case Step.Compilation: | |
{ | |
if (!EditorApplication.isCompiling) | |
step = Step.Build; | |
break; | |
} | |
case Step.Build: | |
{ | |
if (Build(settings.Builds[building])) | |
{ | |
building = -1; | |
Debug.LogWarning("Builds canceled"); | |
} | |
else | |
{ | |
step = Step.SetSymbols; | |
if (building != -1) | |
building++; | |
} | |
break; | |
} | |
} | |
} | |
else | |
{ | |
building++; | |
} | |
if (building >= settings.Builds.Length) | |
{ | |
building = -1; | |
timer.Stop(); | |
Debug.LogFormat("Builds complete: {0} succeeded, {1} failed ({2})", succeeded, failed, timer.Elapsed); | |
if (succeeded > 0) | |
{ | |
EditorUtility.RevealInFinder("Builds"); | |
EditorApplication.Beep(); | |
} | |
} | |
} | |
} | |
void SetSymbols(BatchBuilderSettings.Settings build) | |
{ | |
string extraSymbols = ""; | |
foreach (BatchBuilderSettings.Symbol symbol in settings.GeneralSymbols) | |
if (symbol.Active) | |
extraSymbols += ";" + symbol.Name; | |
PlayerSettings.SetScriptingDefineSymbolsForGroup(build.Group, build.Symbols + extraSymbols); | |
} | |
bool Build(BatchBuilderSettings.Settings build) | |
{ | |
if (Directory.Exists(build.Path)) | |
Directory.Delete(build.Path, true); | |
Directory.CreateDirectory(build.Path); | |
string file = Path.Combine(build.Path, build.Filename); | |
BuildOptions options = BuildOptions.StrictMode; | |
if (settings.Debug) | |
options |= BuildOptions.Development | BuildOptions.ConnectWithProfiler; | |
else if (settings.LZ4Compression) | |
options |= BuildOptions.CompressWithLz4; | |
Debug.LogFormat("Starting build {0} ({1})", build.Name, PlayerSettings.GetScriptingDefineSymbolsForGroup(build.Group)); | |
bool success = false, canceled = false; | |
try | |
{ | |
string error = BuildPipeline.BuildPlayer(EditorBuildSettings.scenes, file, build.Target, options); | |
if (string.IsNullOrEmpty(error)) | |
success = true; | |
if (!success) | |
{ | |
if (error == "Building Player was cancelled") | |
canceled = true; | |
else | |
Debug.LogError("Error while building " + build.Name + ": " + error); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Debug.LogError("Error while building " + build.Name + ": " + ex.Message); | |
} | |
if (success) | |
succeeded++; | |
else | |
failed++; | |
return canceled; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment