diff --git a/CHANGELOG.md b/CHANGELOG.md index 566fc8355..d70c54f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -## [55.2.2] +## [55.2.3] +- [Build] Improved app build time by running Android, iOS and Library builds in parallel, and ensuring `init` (dotnet restore) runs only once across dependent tasks. + + - [iOS26][Tip] Added more padding. ## [55.2.1] diff --git a/build/AwesomeBuildsystem/Core/TaskRunner.csx b/build/AwesomeBuildsystem/Core/TaskRunner.csx index 1fc9f149b..099998af2 100644 --- a/build/AwesomeBuildsystem/Core/TaskRunner.csx +++ b/build/AwesomeBuildsystem/Core/TaskRunner.csx @@ -58,6 +58,7 @@ public class TaskDefinition public static class TaskRunner { private static Dictionary tasks = new Dictionary(); + private static System.Collections.Concurrent.ConcurrentDictionary _executedTasks = new System.Collections.Concurrent.ConcurrentDictionary(); /// /// Creates a new async task definition @@ -90,27 +91,44 @@ public static class TaskRunner } /// - /// Runs a single task by name or alias + /// Runs a single task by name or alias. + /// Dependencies declared with IsDependentOn are resolved and run first. + /// Each task is guaranteed to run at most once per build session. /// /// Name or alias of the task to run - public static async Task RunAsync(string taskNameOrAlias) + public static Task RunAsync(string taskNameOrAlias) { var actualTaskName = ResolveTaskName(taskNameOrAlias); - if (actualTaskName != null && tasks.TryGetValue(actualTaskName, out var task)) + if (actualTaskName == null || !tasks.TryGetValue(actualTaskName, out var task)) { - Console.WriteLine($"Running task: {actualTaskName}" + (actualTaskName != taskNameOrAlias ? $" (alias: {taskNameOrAlias})" : "")); - - if (task.BeforeAction != null) await task.BeforeAction(); - if (task.Action != null) await task.Action(); - if (task.AfterAction != null) await task.AfterAction(); - - Console.WriteLine($"Completed task: {actualTaskName}"); + Console.WriteLine($"Task '{taskNameOrAlias}' not found"); + return Task.CompletedTask; } - else + + // GetOrAdd ensures the task body executes exactly once even when called concurrently. + return _executedTasks.GetOrAdd(actualTaskName, _ => ExecuteTaskAsync(task, actualTaskName, taskNameOrAlias)); + } + + private static async Task ExecuteTaskAsync(TaskDefinition task, string actualTaskName, string taskNameOrAlias) + { + // Run all dependencies first, in parallel when there are multiple. + if (task.Dependencies.Count == 1) { - Console.WriteLine($"Task '{taskNameOrAlias}' not found"); + await RunAsync(task.Dependencies[0]); } + else if (task.Dependencies.Count > 1) + { + await Task.WhenAll(task.Dependencies.Select(dep => RunAsync(dep))); + } + + Console.WriteLine($"Running task: {actualTaskName}" + (actualTaskName != taskNameOrAlias ? $" (alias: {taskNameOrAlias})" : "")); + + if (task.BeforeAction != null) await task.BeforeAction(); + if (task.Action != null) await task.Action(); + if (task.AfterAction != null) await task.AfterAction(); + + Console.WriteLine($"Completed task: {actualTaskName}"); } /// diff --git a/build/build.csx b/build/build.csx index 3557c7278..6c9498f87 100755 --- a/build/build.csx +++ b/build/build.csx @@ -165,7 +165,7 @@ TaskRunner .Alias("bi") .IsDependentOn("init") .DoesBefore(() => { Console.WriteLine("##[group]buildiOS 🛠"); return Task.CompletedTask; }) - .Does(() => iOS.Build(LibraryPath, "Debug")) + .Does(() => iOS.Build(ComponentsPath, "Debug")) .DoesAfter(() => { Console.WriteLine("##[endgroup]"); return Task.CompletedTask; }); TaskRunner @@ -332,7 +332,11 @@ TaskRunner .Alias("ball") .DoesBefore(() => { Console.WriteLine("##[group]buildAll 🛠"); return Task.CompletedTask; }) .Does(async () => { - await TaskRunner.RunAsync(new[] { "buildAndroid", "buildiOS", "buildLibrary" }, false); + await Task.WhenAll( + TaskRunner.RunAsync("buildAndroid"), + TaskRunner.RunAsync("buildiOS"), + TaskRunner.RunAsync("buildLibrary") + ); }) .DoesAfter(() => { Console.WriteLine("##[endgroup]"); return Task.CompletedTask; });