From 55af41b1aa9f9eebfbbe4bc24e4b3ba78c00f7ed Mon Sep 17 00:00:00 2001 From: Mohammad1384a Date: Fri, 21 Nov 2025 19:24:48 +0400 Subject: [PATCH 1/2] Fix for --tree argument --- src/Cake.Cli/Hosts/TreeScriptHost.cs | 104 +++++++++++++-------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/src/Cake.Cli/Hosts/TreeScriptHost.cs b/src/Cake.Cli/Hosts/TreeScriptHost.cs index f07a819623..cf23ff8d75 100644 --- a/src/Cake.Cli/Hosts/TreeScriptHost.cs +++ b/src/Cake.Cli/Hosts/TreeScriptHost.cs @@ -9,14 +9,13 @@ using Cake.Core; using Cake.Core.Graph; using Cake.Core.Scripting; +using ThreadingTask = System.Threading.Tasks.Task; -namespace Cake.Cli -{ +namespace Cake.Cli { /// /// The script host used for showing task descriptions. /// - public sealed class TreeScriptHost : ScriptHost - { + public sealed class TreeScriptHost : ScriptHost { private const int _maxDepth = 0; private const string _cross = "├─"; private const string _corner = "└─"; @@ -30,98 +29,97 @@ public sealed class TreeScriptHost : ScriptHost /// The context. /// The console. public TreeScriptHost(ICakeEngine engine, ICakeContext context, IConsole console) - : base(engine, context) - { + : base(engine, context) { _console = console ?? throw new ArgumentNullException(nameof(console)); } /// - public override Task RunTargetAsync(string target) - { + public override Task RunTargetAsync(string target) { PrintTaskTree(); - - return System.Threading.Tasks.Task.FromResult(null); + return ThreadingTask.FromResult(null); } /// - public override Task RunTargetsAsync(IEnumerable targets) - { + public override Task RunTargetsAsync(IEnumerable targets) { PrintTaskTree(); - - return System.Threading.Tasks.Task.FromResult(null); + return ThreadingTask.FromResult(null); } - private void PrintTaskTree() - { - var topLevelTasks = GetTopLevelTasks(); + private void PrintTaskTree() { + // Build the full graph once (includes Dependencies + Dependees) + var graph = CakeGraphBuilder.Build(Tasks); + var topLevelTasks = GetTopLevelTasks(graph); + _console.WriteLine(); - foreach (ICakeTaskInfo task in topLevelTasks) - { - PrintTask(task, string.Empty, false, 0); + foreach (var task in topLevelTasks) { + // root tasks start at depth 0, no branch characters yet + PrintTask(task, graph, string.Empty, isLast: false, depth: 0); _console.WriteLine(); } } - private List GetTopLevelTasks() - { + private List GetTopLevelTasks(CakeGraph graph) { // Display "Default" first, then alphabetical - var graph = CakeGraphBuilder.Build(Tasks); - return Tasks.Where(task => !graph.Edges.Any( - edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase))) + // Top-level = tasks that never appear as Start (no outgoing edges) + return Tasks + .Where(task => !graph.Edges.Any( + edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase))) .OrderByDescending(task => task.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)) .ThenBy(task => task.Name, StringComparer.OrdinalIgnoreCase) .ToList(); } - private void PrintTask(ICakeTaskInfo task, string indent, bool isLast, int depth) - { + private void PrintTask( + ICakeTaskInfo task, + CakeGraph graph, + string indent, + bool isLast, + int depth) { // Builds ASCII graph _console.Write(indent); - if (isLast) - { + if (isLast) { _console.Write(_corner); indent += " "; - } - else if (depth > 0) - { + } else if (depth > 0) { _console.Write(_cross); indent += _vertical; } PrintName(task, depth); - if ((_maxDepth > 0) && (depth >= _maxDepth)) - { + if ((_maxDepth > 0) && (depth >= _maxDepth)) { return; } - for (var i = 0; i < task.Dependencies.Count; i++) - { - // First() is safe as CakeGraphBuilder has already validated graph is valid - var childTask = Tasks - .Where(x => x.Name.Equals(task.Dependencies[i].Name, StringComparison.OrdinalIgnoreCase)) - .First(); + // Children = all tasks that have an edge Start -> End = current task. + // This respects both IsDependentOn and IsDependeeOf, + // because CakeGraphBuilder already encoded both into the graph. + var childTasks = graph.Edges + .Where(edge => edge.End.Equals(task.Name, StringComparison.OrdinalIgnoreCase)) + .Select(edge => Tasks.First(t => + t.Name.Equals(edge.Start, StringComparison.OrdinalIgnoreCase))) + .Distinct() + .OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase) + .ToList(); + + for (var i = 0; i < childTasks.Count; i++) { + var childTask = childTasks[i]; + var childIsLast = i == childTasks.Count - 1; - PrintTask(childTask, indent, i == (task.Dependencies.Count - 1), depth + 1); + PrintTask(childTask, graph, indent, childIsLast, depth + 1); } } - private void PrintName(ICakeTaskInfo task, int depth) - { + private void PrintName(ICakeTaskInfo task, int depth) { var originalColor = _console.ForegroundColor; - if (depth == 0) - { + if (depth == 0) { _console.ForegroundColor = ConsoleColor.Cyan; - } - else if (task is CakeTask cakeTask && - (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any())) - { + } else if (task is CakeTask cakeTask && + (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any())) { _console.ForegroundColor = ConsoleColor.Green; - } - else - { + } else { _console.ForegroundColor = ConsoleColor.Gray; } @@ -129,4 +127,4 @@ private void PrintName(ICakeTaskInfo task, int depth) _console.ForegroundColor = originalColor; } } -} \ No newline at end of file +} From 2500f3312212396764a1258b285986c39b17b067 Mon Sep 17 00:00:00 2001 From: Mohammad1384a Date: Fri, 21 Nov 2025 19:24:48 +0400 Subject: [PATCH 2/2] Fix style for --tree argument --- src/Cake.Cli/Hosts/TreeScriptHost.cs | 68 ++++++++++++++++++---------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/Cake.Cli/Hosts/TreeScriptHost.cs b/src/Cake.Cli/Hosts/TreeScriptHost.cs index f07a819623..458f795c67 100644 --- a/src/Cake.Cli/Hosts/TreeScriptHost.cs +++ b/src/Cake.Cli/Hosts/TreeScriptHost.cs @@ -9,6 +9,7 @@ using Cake.Core; using Cake.Core.Graph; using Cake.Core.Scripting; +using ThreadingTask = System.Threading.Tasks.Task; namespace Cake.Cli { @@ -31,7 +32,7 @@ public sealed class TreeScriptHost : ScriptHost /// The console. public TreeScriptHost(ICakeEngine engine, ICakeContext context, IConsole console) : base(engine, context) - { + { _console = console ?? throw new ArgumentNullException(nameof(console)); } @@ -39,43 +40,51 @@ public TreeScriptHost(ICakeEngine engine, ICakeContext context, IConsole console public override Task RunTargetAsync(string target) { PrintTaskTree(); - - return System.Threading.Tasks.Task.FromResult(null); + return ThreadingTask.FromResult(null); } /// public override Task RunTargetsAsync(IEnumerable targets) { PrintTaskTree(); - - return System.Threading.Tasks.Task.FromResult(null); + return ThreadingTask.FromResult(null); } private void PrintTaskTree() { - var topLevelTasks = GetTopLevelTasks(); + // Build the full graph once (includes Dependencies + Dependees) + var graph = CakeGraphBuilder.Build(Tasks); + var topLevelTasks = GetTopLevelTasks(graph); + _console.WriteLine(); - foreach (ICakeTaskInfo task in topLevelTasks) + foreach (var task in topLevelTasks) { - PrintTask(task, string.Empty, false, 0); + // root tasks start at depth 0, no branch characters yet + PrintTask(task, graph, string.Empty, isLast: false, depth: 0); _console.WriteLine(); } } - private List GetTopLevelTasks() + private List GetTopLevelTasks(CakeGraph graph) { // Display "Default" first, then alphabetical - var graph = CakeGraphBuilder.Build(Tasks); - return Tasks.Where(task => !graph.Edges.Any( - edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase))) + // Top-level = tasks that never appear as Start (no outgoing edges) + return Tasks + .Where(task => !graph.Edges.Any( + edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase))) .OrderByDescending(task => task.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)) .ThenBy(task => task.Name, StringComparer.OrdinalIgnoreCase) .ToList(); } - private void PrintTask(ICakeTaskInfo task, string indent, bool isLast, int depth) - { + private void PrintTask( + ICakeTaskInfo task, + CakeGraph graph, + string indent, + bool isLast, + int depth) + { // Builds ASCII graph _console.Write(indent); if (isLast) @@ -96,14 +105,23 @@ private void PrintTask(ICakeTaskInfo task, string indent, bool isLast, int depth return; } - for (var i = 0; i < task.Dependencies.Count; i++) - { - // First() is safe as CakeGraphBuilder has already validated graph is valid - var childTask = Tasks - .Where(x => x.Name.Equals(task.Dependencies[i].Name, StringComparison.OrdinalIgnoreCase)) - .First(); + // Children = all tasks that have an edge Start -> End = current task. + // This respects both IsDependentOn and IsDependeeOf, + // because CakeGraphBuilder already encoded both into the graph. + var childTasks = graph.Edges + .Where(edge => edge.End.Equals(task.Name, StringComparison.OrdinalIgnoreCase)) + .Select(edge => Tasks.First(t => + t.Name.Equals(edge.Start, StringComparison.OrdinalIgnoreCase))) + .Distinct() + .OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase) + .ToList(); - PrintTask(childTask, indent, i == (task.Dependencies.Count - 1), depth + 1); + for (var i = 0; i < childTasks.Count; i++) + { + var childTask = childTasks[i]; + var childIsLast = i == childTasks.Count - 1; + + PrintTask(childTask, graph, indent, childIsLast, depth + 1); } } @@ -112,12 +130,12 @@ private void PrintName(ICakeTaskInfo task, int depth) var originalColor = _console.ForegroundColor; if (depth == 0) - { + { _console.ForegroundColor = ConsoleColor.Cyan; } else if (task is CakeTask cakeTask && - (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any())) - { + (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any())) + { _console.ForegroundColor = ConsoleColor.Green; } else @@ -129,4 +147,4 @@ private void PrintName(ICakeTaskInfo task, int depth) _console.ForegroundColor = originalColor; } } -} \ No newline at end of file +}