| ... | @@ -103,8 +103,30 @@ const InstallPaths = struct { | ... | @@ -103,8 +103,30 @@ const InstallPaths = struct { |
| 103 | }; | 103 | }; |
| 104 | | 104 | |
| 105 | const PrintNode = struct { | 105 | const PrintNode = struct { |
| 106 | parent: ?*PrintNode, | 106 | parent: ?*const PrintNode, |
| 107 | last: bool = false, | 107 | last: bool = false, |
| | 108 | |
| | 109 | fn printPrefix(node: *const PrintNode, writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void { |
| | 110 | const parent = node.parent orelse return; |
| | 111 | if (parent.parent == null) return; |
| | 112 | try printPrefix(parent, writer, mode); |
| | 113 | if (parent.last) { |
| | 114 | try writer.writeAll(" "); |
| | 115 | } else { |
| | 116 | try printTreeSibling(writer, mode); |
| | 117 | } |
| | 118 | } |
| | 119 | |
| | 120 | fn printPrefixedChild(node: *const PrintNode, writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void { |
| | 121 | try printPrefix(node, writer, mode); |
| | 122 | if (node.parent != null) { |
| | 123 | if (node.last) { |
| | 124 | try printTreeChildLast(writer, mode); |
| | 125 | } else { |
| | 126 | try printTreeChild(writer, mode); |
| | 127 | } |
| | 128 | } |
| | 129 | } |
| 108 | }; | 130 | }; |
| 109 | | 131 | |
| 110 | const ErrorStyle = enum { | 132 | const ErrorStyle = enum { |
| ... | @@ -127,7 +149,7 @@ const ErrorStyle = enum { | ... | @@ -127,7 +149,7 @@ const ErrorStyle = enum { |
| 127 | }; | 149 | }; |
| 128 | const MultilineErrors = enum { indent, newline, none }; | 150 | const MultilineErrors = enum { indent, newline, none }; |
| 129 | const Summary = enum { all, new, failures, line, none }; | 151 | const Summary = enum { all, new, failures, line, none }; |
| 130 | const PrintConfiguration = enum { none, zon, path }; | 152 | const PrintConfiguration = enum { none, zon, path, tree }; |
| 131 | | 153 | |
| 132 | const GeneratedFile = struct { | 154 | const GeneratedFile = struct { |
| 133 | data: ?[]const u8, | 155 | data: ?[]const u8, |
| ... | @@ -363,6 +385,8 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -363,6 +385,8 @@ pub fn main(init: process.Init.Minimal) !void { |
| 363 | override_local_cache_dir = nextArgOrFatal(args, &arg_i); | 385 | override_local_cache_dir = nextArgOrFatal(args, &arg_i); |
| 364 | } else if (mem.eql(u8, arg, "--pkg-dir")) { | 386 | } else if (mem.eql(u8, arg, "--pkg-dir")) { |
| 365 | override_pkg_dir = nextArgOrFatal(args, &arg_i); | 387 | override_pkg_dir = nextArgOrFatal(args, &arg_i); |
| | 388 | } else if (mem.eql(u8, arg, "--print-tree")) { |
| | 389 | print_configuration = .tree; |
| 366 | } else if (mem.eql(u8, arg, "--fetch")) { | 390 | } else if (mem.eql(u8, arg, "--fetch")) { |
| 367 | fetch_only = true; | 391 | fetch_only = true; |
| 368 | } else if (mem.cutPrefix(u8, arg, "--fetch=")) |rest| { | 392 | } else if (mem.cutPrefix(u8, arg, "--fetch=")) |rest| { |
| ... | @@ -797,6 +821,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -797,6 +821,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 797 | return cleanExit(io, &scanned_config); | 821 | return cleanExit(io, &scanned_config); |
| 798 | }, | 822 | }, |
| 799 | .path => unreachable, | 823 | .path => unreachable, |
| | 824 | .tree => unreachable, |
| 800 | } | 825 | } |
| 801 | | 826 | |
| 802 | var maker: Maker = .{ | 827 | var maker: Maker = .{ |
| ... | @@ -1357,8 +1382,20 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1357,8 +1382,20 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1357 | return error.FailedButCacheIntact; | 1382 | return error.FailedButCacheIntact; |
| 1358 | } | 1383 | } |
| 1359 | | 1384 | |
| 1360 | if (options.fetch_only) { | 1385 | if (options.fetch_only or options.print_configuration == .tree) { |
| 1361 | _ = io.lockStderr(&.{}, .no_color) catch {}; | 1386 | _ = io.lockStderr(&.{}, .no_color) catch {}; |
| | 1387 | if (options.print_configuration == .tree) { |
| | 1388 | const NO_COLOR = EnvVar.NO_COLOR.isSet(&graph.environ_map); |
| | 1389 | const CLICOLOR_FORCE = EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map); |
| | 1390 | printPkgTree(.{ |
| | 1391 | .writer = initStdoutWriter(io), |
| | 1392 | .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE), |
| | 1393 | }, arena, &job_queue, &fetch) catch |err| switch (err) { |
| | 1394 | error.WriteFailed => return stdout_writer_allocation.err.?, |
| | 1395 | else => |e| return e, |
| | 1396 | }; |
| | 1397 | try stdout_writer_allocation.flush(); |
| | 1398 | } |
| 1362 | process.exit(0); | 1399 | process.exit(0); |
| 1363 | } | 1400 | } |
| 1364 | | 1401 | |
| ... | @@ -1641,6 +1678,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1641,6 +1678,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1641 | process.exit(0); | 1678 | process.exit(0); |
| 1642 | }, | 1679 | }, |
| 1643 | .none, .zon => {}, | 1680 | .none, .zon => {}, |
| | 1681 | .tree => unreachable, |
| 1644 | } | 1682 | } |
| 1645 | | 1683 | |
| 1646 | const configuration = c: { | 1684 | const configuration = c: { |
| ... | @@ -1678,6 +1716,72 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1678,6 +1716,72 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1678 | }; | 1716 | }; |
| 1679 | } | 1717 | } |
| 1680 | | 1718 | |
| | 1719 | fn printPkgTree( |
| | 1720 | terminal: Io.Terminal, |
| | 1721 | arena: Allocator, |
| | 1722 | job_queue: *const Fetch.JobQueue, |
| | 1723 | root_fetch: *const Fetch, |
| | 1724 | ) !void { |
| | 1725 | if (!root_fetch.have_manifest) return; |
| | 1726 | const names = root_fetch.manifest.dependencies.keys(); |
| | 1727 | for (names, root_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| { |
| | 1728 | var print_node: PrintNode = .{ |
| | 1729 | .parent = null, |
| | 1730 | .last = i == names.len - 1, |
| | 1731 | }; |
| | 1732 | try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash); |
| | 1733 | } |
| | 1734 | } |
| | 1735 | |
| | 1736 | fn printPkgTreeChild( |
| | 1737 | terminal: Io.Terminal, |
| | 1738 | arena: Allocator, |
| | 1739 | job_queue: *const Fetch.JobQueue, |
| | 1740 | parent_print_node: *const PrintNode, |
| | 1741 | dep_name: []const u8, |
| | 1742 | opt_hash: ?[]const u8, |
| | 1743 | ) !void { |
| | 1744 | const w = terminal.writer; |
| | 1745 | try parent_print_node.printPrefixedChild(w, terminal.mode); |
| | 1746 | try w.writeAll(dep_name); |
| | 1747 | |
| | 1748 | const hash = opt_hash orelse { |
| | 1749 | try terminal.setColor(.red); |
| | 1750 | try w.writeAll(" (missing hash)\n"); |
| | 1751 | try terminal.setColor(.reset); |
| | 1752 | return; |
| | 1753 | }; |
| | 1754 | try terminal.setColor(.dim); |
| | 1755 | try w.print(" {s}", .{hash}); |
| | 1756 | try terminal.setColor(.reset); |
| | 1757 | const child_fetch = job_queue.table.get(.fromSlice(hash)) orelse { |
| | 1758 | try terminal.setColor(.red); |
| | 1759 | try w.writeAll(" (fetch failed)\n"); |
| | 1760 | try terminal.setColor(.reset); |
| | 1761 | return; |
| | 1762 | }; |
| | 1763 | switch (child_fetch.lazy_status) { |
| | 1764 | .eager, .available => {}, |
| | 1765 | .unavailable => { |
| | 1766 | try terminal.setColor(.yellow); |
| | 1767 | try w.writeAll(" (not fetched)\n"); |
| | 1768 | try terminal.setColor(.reset); |
| | 1769 | return; |
| | 1770 | }, |
| | 1771 | } |
| | 1772 | try w.writeByte('\n'); |
| | 1773 | if (!child_fetch.have_manifest) return; |
| | 1774 | |
| | 1775 | const names = child_fetch.manifest.dependencies.keys(); |
| | 1776 | for (names, child_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| { |
| | 1777 | var print_node: PrintNode = .{ |
| | 1778 | .parent = parent_print_node, |
| | 1779 | .last = i == names.len - 1, |
| | 1780 | }; |
| | 1781 | try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash); |
| | 1782 | } |
| | 1783 | } |
| | 1784 | |
| 1681 | fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { | 1785 | fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void { |
| 1682 | const environ_map = &graph.environ_map; | 1786 | const environ_map = &graph.environ_map; |
| 1683 | const io = graph.io; | 1787 | const io = graph.io; |
| ... | @@ -2949,18 +3053,7 @@ fn printTreeStep( | ... | @@ -2949,18 +3053,7 @@ fn printTreeStep( |
| 2949 | .failures => make_step.state == .success, | 3053 | .failures => make_step.state == .success, |
| 2950 | }; | 3054 | }; |
| 2951 | if (skip) return; | 3055 | if (skip) return; |
| 2952 | try printPrefix(parent_node, stderr); | 3056 | try parent_node.printPrefixedChild(writer, stderr.mode); |
| 2953 | | | |
| 2954 | if (parent_node.parent != null) { | | |
| 2955 | if (parent_node.last) { | | |
| 2956 | try printChildNodePrefix(stderr); | | |
| 2957 | } else { | | |
| 2958 | try writer.writeAll(switch (stderr.mode) { | | |
| 2959 | .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ | | |
| 2960 | else => "+- ", | | |
| 2961 | }); | | |
| 2962 | } | | |
| 2963 | } | | |
| 2964 | | 3057 | |
| 2965 | if (!first) try stderr.setColor(.dim); | 3058 | if (!first) try stderr.setColor(.dim); |
| 2966 | | 3059 | |
| ... | @@ -3189,23 +3282,22 @@ fn printStepFailure( | ... | @@ -3189,23 +3282,22 @@ fn printStepFailure( |
| 3189 | } | 3282 | } |
| 3190 | } | 3283 | } |
| 3191 | | 3284 | |
| 3192 | fn printPrefix(node: *PrintNode, stderr: Io.Terminal) !void { | 3285 | fn printTreeChild(writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void { |
| 3193 | const parent = node.parent orelse return; | 3286 | return writer.writeAll(switch (mode) { |
| 3194 | const writer = stderr.writer; | 3287 | .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─ |
| 3195 | if (parent.parent == null) return; | 3288 | else => "+- ", |
| 3196 | try printPrefix(parent, stderr); | 3289 | }); |
| 3197 | if (parent.last) { | 3290 | } |
| 3198 | try writer.writeAll(" "); | 3291 | |
| 3199 | } else { | 3292 | fn printTreeSibling(writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void { |
| 3200 | try writer.writeAll(switch (stderr.mode) { | 3293 | return writer.writeAll(switch (mode) { |
| 3201 | .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ | 3294 | .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │ |
| 3202 | else => "| ", | 3295 | else => "| ", |
| 3203 | }); | 3296 | }); |
| 3204 | } | | |
| 3205 | } | 3297 | } |
| 3206 | | 3298 | |
| 3207 | fn printChildNodePrefix(stderr: Io.Terminal) !void { | 3299 | fn printTreeChildLast(writer: *Writer, mode: Io.Terminal.Mode) !void { |
| 3208 | try stderr.writer.writeAll(switch (stderr.mode) { | 3300 | try writer.writeAll(switch (mode) { |
| 3209 | .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ | 3301 | .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ |
| 3210 | else => "+- ", | 3302 | else => "+- ", |
| 3211 | }); | 3303 | }); |
| ... | @@ -3325,7 +3417,7 @@ pub fn printErrorMessages( | ... | @@ -3325,7 +3417,7 @@ pub fn printErrorMessages( |
| 3325 | while (step_stack.pop()) |step_index| : (indent += 1) { | 3417 | while (step_stack.pop()) |step_index| : (indent += 1) { |
| 3326 | if (indent > 0) { | 3418 | if (indent > 0) { |
| 3327 | try writer.splatByteAll(' ', (indent - 1) * 3); | 3419 | try writer.splatByteAll(' ', (indent - 1) * 3); |
| 3328 | try printChildNodePrefix(stderr); | 3420 | try printTreeChildLast(writer, stderr.mode); |
| 3329 | } | 3421 | } |
| 3330 | | 3422 | |
| 3331 | try writer.writeAll(step_index.ptr(c).name.slice(c)); | 3423 | try writer.writeAll(step_index.ptr(c).name.slice(c)); |