diff --git a/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/BuildProcessor.cs b/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/BuildProcessor.cs index d0729c3..59e69ac 100644 --- a/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/BuildProcessor.cs +++ b/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/BuildProcessor.cs @@ -4,13 +4,12 @@ using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; -using UnityEditor.Callbacks; using UnityEditorInternal; using UnityEngine; namespace StansAssets.Build.Meta.Editor { - class BuildProcessor : IPreprocessBuildWithReport + class BuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport { const int k_CallbackOrder = 1; static readonly string k_BuildMetadataPath = $"Assets/Resources/{nameof(BuildMetadata)}.asset"; @@ -43,9 +42,8 @@ public void OnPreprocessBuild(BuildReport report) SaveBuildMetadata(buildMetadata); } - - [PostProcessBuild(k_CallbackOrder)] - public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + + public void OnPostprocessBuild(BuildReport report) { DeleteBuildMetadata(); if (s_IncrementBuildNumberEnable) diff --git a/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/IncrementBuildNumber.cs b/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/IncrementBuildNumber.cs index 26b882f..89b88e7 100644 --- a/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/IncrementBuildNumber.cs +++ b/com.stansassets.build/BuildMetadata/Editor/BuildProcessing/IncrementBuildNumber.cs @@ -84,7 +84,10 @@ static void SaveBuildMetadata(BuildMetadata buildMetadata, BuildTarget buildTarg var buildNumber = 0; if (versionsSheet == null) { - spreadsheet.CreateGoogleSheet(sheetName); + spreadsheet.CreateGoogleSheet(new GoogleSheet.SheetProperties + { + Title = sheetName + }); spreadsheet.AppendGoogleCell(rangeAppend, s_Headers); if (spreadsheet.SyncErrorMassage != null) { diff --git a/com.stansassets.build/BuildPipeline/Common.meta b/com.stansassets.build/BuildPipeline/Common.meta new file mode 100644 index 0000000..b0cbd97 --- /dev/null +++ b/com.stansassets.build/BuildPipeline/Common.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fd6b8c23a58141ff9d29586249f32e3a +timeCreated: 1642602818 \ No newline at end of file diff --git a/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs b/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs new file mode 100644 index 0000000..b94da4f --- /dev/null +++ b/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace StansAssets.Build.Pipeline +{ + public static class ListExtensions + { + // borrowed from https://stackoverflow.com/a/22801345 + public static void AddSorted(this List @this, T item) where T : IComparable + { + if (@this.Count == 0) + { + @this.Add(item); + return; + } + + if (@this[@this.Count - 1].CompareTo(item) <= 0) + { + @this.Add(item); + return; + } + + if (@this[0].CompareTo(item) >= 0) + { + @this.Insert(0, item); + return; + } + + var index = @this.BinarySearch(item); + if (index < 0) + index = ~index; + + @this.Insert(index, item); + } + } +} diff --git a/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs.meta b/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs.meta new file mode 100644 index 0000000..18b4aaf --- /dev/null +++ b/com.stansassets.build/BuildPipeline/Common/ListExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 545da5753044449abbdd3b31bbe7b825 +timeCreated: 1642602827 \ No newline at end of file diff --git a/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs b/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs new file mode 100644 index 0000000..52cb9ea --- /dev/null +++ b/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace StansAssets.Build.Pipeline +{ + //TODO: move certain methods to the com.stansassets.foundation package + public static class ReflectionUtils + { + static readonly string[] s_BuiltInAssemblyPrefixes = { "Mono.", "Unity.", "UnityEngine", "UnityEditor", "System", "mscorlib" }; + + public static IEnumerable FindImplementationsOf(bool ignoreBuiltIn = false) + { + var baseType = typeof(T); + return FindImplementationsOf(baseType, ignoreBuiltIn); + } + + public static IEnumerable FindImplementationsOf(Type baseType, bool ignoreBuiltIn = false) + { + var assemblies = GetAssembles(ignoreBuiltIn); + + return assemblies + .SelectMany(assembly => assembly.GetTypes()) + .Where(type => baseType.IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract); + } + + public static IEnumerable FindMethodsWithAttributes(bool ignoreBuiltIn = false) + { + var assemblies = GetAssembles(ignoreBuiltIn); + + return assemblies + .SelectMany(assembly => assembly.GetTypes()) + .SelectMany(type => type.GetMethods()) + .Where(methodInfo => methodInfo.GetCustomAttributes(typeof(T), false).Length > 0); + + } + + public static bool HasDefaultConstructor(Type type) + { + return type.GetConstructors().Any(constructor => !constructor.GetParameters().Any()); + } + + static IEnumerable GetAssembles(bool ignoreBuiltIn = false) + { + IEnumerable assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + if (ignoreBuiltIn) + { + assemblies = assemblies.Where(assembly => { + var assemblyName = assembly.GetName().Name; + return !s_BuiltInAssemblyPrefixes.Any(prefix => assemblyName.StartsWith(prefix)); + }); + } + + return assemblies; + } + + } +} diff --git a/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs.meta b/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs.meta new file mode 100644 index 0000000..8bd6c43 --- /dev/null +++ b/com.stansassets.build/BuildPipeline/Common/ReflectionUtils.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bbb0923fa5f3456097add24890748901 +timeCreated: 1642603424 \ No newline at end of file diff --git a/com.stansassets.build/BuildPipeline/Core/BuildProcessor.cs b/com.stansassets.build/BuildPipeline/Core/BuildProcessor.cs index c99ff25..5b121e3 100644 --- a/com.stansassets.build/BuildPipeline/Core/BuildProcessor.cs +++ b/com.stansassets.build/BuildPipeline/Core/BuildProcessor.cs @@ -1,18 +1,19 @@ using System; -using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; -using UnityEditor.Callbacks; using UnityEngine; +using UnityEngine.SceneManagement; namespace StansAssets.Build.Pipeline { - class BuildProcessor : IPreprocessBuildWithReport + class BuildProcessor : IPreprocessBuildWithReport, IProcessSceneWithReport, IPostprocessBuildWithReport { + const int k_CallbackOrder = 0; + static IBuildTasksContainer s_BuildTasks; static IBuildContext s_BuildContext; - public int callbackOrder => 0; + public int callbackOrder => k_CallbackOrder; public void OnPreprocessBuild(BuildReport report) { @@ -24,8 +25,20 @@ public void OnPreprocessBuild(BuildReport report) } } - [PostProcessBuild(0)] - public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + public void OnProcessScene(Scene scene, BuildReport report) + { + if (Application.isPlaying) + return; + + var postProcessSceneTasks = s_BuildTasks.ScenePostProcessTasks; + if (postProcessSceneTasks.Count == 0) + return; + + var sceneTasksRunner = new ScenePostProcessTasksRunner(); + sceneTasksRunner.Run(s_BuildContext, scene, postProcessSceneTasks); + } + + public void OnPostprocessBuild(BuildReport report) { foreach (var task in s_BuildTasks.PostBuildTasks) { @@ -33,6 +46,8 @@ public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProj } } + public static int GetCallbackOrder() => k_CallbackOrder; + static void RunBuildTask(IBuildContext buildContext, IBuildTask task) { switch (task) @@ -48,20 +63,6 @@ static void RunBuildTask(IBuildContext buildContext, IBuildTask task) } } - [PostProcessScene(0)] - public static void OnPostProcessScene() - { - if (Application.isPlaying) - return; - - var postProcessSceneTasks = s_BuildTasks.ScenePostProcessTasks; - if (postProcessSceneTasks.Count == 0) - return; - - var sceneTasksRunner = new ScenePostProcessTasksRunner(); - sceneTasksRunner.Run(s_BuildContext, postProcessSceneTasks); - } - public static IBuildTasksContainer GenerateBuildTasksContainer() { var tasksProvider = CreateBuildTasksProvider(); diff --git a/com.stansassets.build/BuildPipeline/Core/DefaultBuildTasksProvider.cs b/com.stansassets.build/BuildPipeline/Core/DefaultBuildTasksProvider.cs index b78f778..c3597ff 100644 --- a/com.stansassets.build/BuildPipeline/Core/DefaultBuildTasksProvider.cs +++ b/com.stansassets.build/BuildPipeline/Core/DefaultBuildTasksProvider.cs @@ -1,5 +1,6 @@ -using System; using StansAssets.Foundation; +using System; +using UnityEngine; namespace StansAssets.Build.Pipeline { @@ -8,10 +9,25 @@ class DefaultBuildTasksProvider : IBuildTasksProvider public IBuildTasksContainer GetBuildTasks(IUserEditorBuildSettings buildSettings) { var tasksContainer = new BuildTasksContainer(); + + CollectBuildTasks(tasksContainer); + CollectScenePostProcessTasks(tasksContainer); + + return tasksContainer; + } + + /// + /// Collects 'Pre' and 'Post' build tasks of type + /// + static void CollectBuildTasks(BuildTasksContainer tasksContainer) + { var buildTasks = ReflectionUtility.FindImplementationsOf(); + foreach (var taskType in buildTasks) { - //TODO check if type has empty constructor and throw appropriate exception if it's not + if (!HasDefaultConstructor(taskType)) + continue; + var task = Activator.CreateInstance(taskType) as IBuildTask; switch (task) { @@ -24,18 +40,33 @@ public IBuildTasksContainer GetBuildTasks(IUserEditorBuildSettings buildSettings tasksContainer.AddPostProcessTask(task); break; default: - throw new InvalidOperationException($"Unknown task type: {task.GetType().FullName}"); + throw new InvalidOperationException($"Unknown task type: {taskType.FullName}"); } } + } + static void CollectScenePostProcessTasks(BuildTasksContainer tasksContainer) + { var scenePostProcessTasks = ReflectionUtility.FindImplementationsOf(); foreach (var taskType in scenePostProcessTasks) { - var buildTask = Activator.CreateInstance(taskType) as IScenePostProcessTask; - tasksContainer.AddScenePostProcessTask(buildTask); + if (!HasDefaultConstructor(taskType)) + continue; + + var buildStep = Activator.CreateInstance(taskType) as IScenePostProcessTask; + tasksContainer.AddScenePostProcessTask(buildStep); } + } - return tasksContainer; + static bool HasDefaultConstructor(Type taskType) + { + var hasConstructor = ReflectionUtils.HasDefaultConstructor(taskType); + if (!hasConstructor) + { + Debug.LogError($"The task {taskType.FullName} has no parameterless constructor and will be ignored"); + } + + return hasConstructor; } } } diff --git a/com.stansassets.build/BuildPipeline/Core/ScenePostProcessTasksRunner.cs b/com.stansassets.build/BuildPipeline/Core/ScenePostProcessTasksRunner.cs index f8ddf0b..5066d3f 100644 --- a/com.stansassets.build/BuildPipeline/Core/ScenePostProcessTasksRunner.cs +++ b/com.stansassets.build/BuildPipeline/Core/ScenePostProcessTasksRunner.cs @@ -8,15 +8,14 @@ class ScenePostProcessTasksRunner { readonly List m_TempComponentsCollection = new List(); - public void Run(IBuildContext buildContext, IReadOnlyList postProcessSceneTasks) + public void Run(IBuildContext buildContext, Scene scene, IReadOnlyList postProcessSceneTasks) { - var currentScene = SceneManager.GetActiveScene(); - var rootGameObjects = currentScene.GetRootGameObjects(); + var rootGameObjects = scene.GetRootGameObjects(); var componentsMap = GetComponentsMap(rootGameObjects); foreach (var task in postProcessSceneTasks) { - task.OnPostprocessScene(buildContext, currentScene); + task.OnPostprocessScene(buildContext, scene); foreach (var pair in componentsMap) { var gameObject = pair.Key; diff --git a/com.stansassets.build/BuildPipeline/Interfaces/Main/IBuildTasksContainer.cs b/com.stansassets.build/BuildPipeline/Interfaces/Main/IBuildTasksContainer.cs index 28c1c53..ffdd9b3 100644 --- a/com.stansassets.build/BuildPipeline/Interfaces/Main/IBuildTasksContainer.cs +++ b/com.stansassets.build/BuildPipeline/Interfaces/Main/IBuildTasksContainer.cs @@ -3,22 +3,22 @@ namespace StansAssets.Build.Pipeline { /// - /// Provides build steps. + /// Provides build tasks. /// public interface IBuildTasksContainer { /// - /// Pre process build steps. Order matters. + /// Pre process build tasks. Order matters. /// IReadOnlyList PreBuildTasks { get; } /// - /// Post process build steps. Order matters. + /// Post process build tasks. Order matters. /// IReadOnlyList PostBuildTasks { get; } /// - /// Scene process build steps. Order matters. + /// Scene process build tasks. Order matters. /// IReadOnlyList ScenePostProcessTasks { get; } } diff --git a/com.stansassets.build/BuildPipeline/Interfaces/Tasks/Async/IAsyncBuildTask.cs b/com.stansassets.build/BuildPipeline/Interfaces/Tasks/Async/IAsyncBuildTask.cs index ae85e0b..0164980 100644 --- a/com.stansassets.build/BuildPipeline/Interfaces/Tasks/Async/IAsyncBuildTask.cs +++ b/com.stansassets.build/BuildPipeline/Interfaces/Tasks/Async/IAsyncBuildTask.cs @@ -3,7 +3,7 @@ namespace StansAssets.Build.Pipeline { /// - /// Represents a single async build step. + /// Represents a single async build task. /// public interface IAsyncBuildTask : IBuildTask { diff --git a/com.stansassets.build/BuildPipeline/Models/BuildTasksContainer.cs b/com.stansassets.build/BuildPipeline/Models/BuildTasksContainer.cs index d23559d..4df35e4 100644 --- a/com.stansassets.build/BuildPipeline/Models/BuildTasksContainer.cs +++ b/com.stansassets.build/BuildPipeline/Models/BuildTasksContainer.cs @@ -17,6 +17,7 @@ public class BuildTasksContainer : IBuildTasksContainer public BuildTasksContainer() { + m_PreBuildTasks = new List(); m_PostBuildTasks = new List(); m_ScenePostProcessTasks = new List(); diff --git a/com.stansassets.build/BuildPipeline/UserInterface/BuildPipelineTab/BuildPipelineTab.cs b/com.stansassets.build/BuildPipeline/UserInterface/BuildPipelineTab/BuildPipelineTab.cs index 93f1886..fdb8245 100644 --- a/com.stansassets.build/BuildPipeline/UserInterface/BuildPipelineTab/BuildPipelineTab.cs +++ b/com.stansassets.build/BuildPipeline/UserInterface/BuildPipelineTab/BuildPipelineTab.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using StansAssets.Build.Editor; using StansAssets.Plugins.Editor; -using UnityEditor; using UnityEngine.UIElements; namespace StansAssets.Build.Pipeline @@ -10,9 +10,9 @@ namespace StansAssets.Build.Pipeline [UsedImplicitly] class BuildPipelineTab : BaseTab, IBuildSystemWindowTab { - readonly VisualElement m_PreProcessTasksContainer; - readonly VisualElement m_PostProcessTasksContainer; - readonly VisualElement m_ScenePostProcessTasksContainer; + const string k_PreProcessStageBaseName = "preProcessTasks"; + const string k_SceneProcessStageBaseName = "sceneProcessTasks"; + const string k_PostProcessStageBaseName = "postProcessTasks"; readonly Label m_TasksProviderName; @@ -28,39 +28,74 @@ public BuildPipelineTab() { m_TasksProviderName = this.Q