authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-13 20:45:10+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-15 20:20:32+02:00
logb9ea8ac3d350a7461b110946bfc6dd02465d3eee
treec2fa9afce7dc7e013c4fad3a26aa2aa569a7f9ab
parentbf936607c8904fdb0d3e2f22f85b5b94b6bb7b17
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge pull request 'package management: --help enhancement, --print-tree, and validate package hash consistency' (#37214) from build-print-tree into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37214

3 files changed, 409 insertions(+), 197 deletions(-)

lib/compiler/Maker.zig+268-76
...@@ -23,6 +23,7 @@ const Color = std.zig.Color;...@@ -23,6 +23,7 @@ const Color = std.zig.Color;
23const Client = std.zig.Client;23const Client = std.zig.Client;
24const Server = std.zig.Server;24const Server = std.zig.Server;
25const EnvVar = std.zig.EnvVar;25const EnvVar = std.zig.EnvVar;
26const ErrorBundle = std.zig.ErrorBundle;
26const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename;27const default_local_zig_cache_basename = std.zig.default_local_zig_cache_basename;
27const stringToEnum = std.meta.stringToEnum;28const stringToEnum = std.meta.stringToEnum;
2829
...@@ -35,6 +36,7 @@ const ScannedConfig = @import("Maker/ScannedConfig.zig");...@@ -35,6 +36,7 @@ const ScannedConfig = @import("Maker/ScannedConfig.zig");
35const PkgConfig = @import("Maker/PkgConfig.zig");36const PkgConfig = @import("Maker/PkgConfig.zig");
36const Fetch = @import("Maker/Fetch.zig");37const Fetch = @import("Maker/Fetch.zig");
37const Package = @import("Maker/Package.zig");38const Package = @import("Maker/Package.zig");
39const Manifest = Package.Manifest;
3840
39pub const std_options: std.Options = .{41pub const std_options: std.Options = .{
40 .side_channels_mitigations = .none,42 .side_channels_mitigations = .none,
...@@ -101,8 +103,30 @@ const InstallPaths = struct {...@@ -101,8 +103,30 @@ const InstallPaths = struct {
101};103};
102104
103const PrintNode = struct {105const PrintNode = struct {
104 parent: ?*PrintNode,106 parent: ?*const PrintNode,
105 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 }
106};130};
107131
108const ErrorStyle = enum {132const ErrorStyle = enum {
...@@ -125,7 +149,7 @@ const ErrorStyle = enum {...@@ -125,7 +149,7 @@ const ErrorStyle = enum {
125};149};
126const MultilineErrors = enum { indent, newline, none };150const MultilineErrors = enum { indent, newline, none };
127const Summary = enum { all, new, failures, line, none };151const Summary = enum { all, new, failures, line, none };
128const PrintConfiguration = enum { none, zon, path };152const PrintConfiguration = enum { none, zon, path, tree, stop };
129153
130const GeneratedFile = struct {154const GeneratedFile = struct {
131 data: ?[]const u8,155 data: ?[]const u8,
...@@ -276,7 +300,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -276,7 +300,6 @@ pub fn main(init: process.Init.Minimal) !void {
276 var cached_passthru_configure: std.ArrayList(u32) = .empty;300 var cached_passthru_configure: std.ArrayList(u32) = .empty;
277 var forks: std.ArrayList(Fork) = .empty;301 var forks: std.ArrayList(Fork) = .empty;
278 var system_pkg_dir_path: ?[]const u8 = null;302 var system_pkg_dir_path: ?[]const u8 = null;
279 var fetch_only = false;
280 var fetch_mode: Fetch.JobQueue.Mode = .needed;303 var fetch_mode: Fetch.JobQueue.Mode = .needed;
281 var debug_target: ?[]const u8 = null;304 var debug_target: ?[]const u8 = null;
282 var cache_poison: std.Build.Graph.CachePoison = .pure;305 var cache_poison: std.Build.Graph.CachePoison = .pure;
...@@ -361,12 +384,12 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -361,12 +384,12 @@ pub fn main(init: process.Init.Minimal) !void {
361 override_local_cache_dir = nextArgOrFatal(args, &arg_i);384 override_local_cache_dir = nextArgOrFatal(args, &arg_i);
362 } else if (mem.eql(u8, arg, "--pkg-dir")) {385 } else if (mem.eql(u8, arg, "--pkg-dir")) {
363 override_pkg_dir = nextArgOrFatal(args, &arg_i);386 override_pkg_dir = nextArgOrFatal(args, &arg_i);
387 } else if (mem.eql(u8, arg, "--print-tree")) {
388 print_configuration = .tree;
389 } else if (mem.eql(u8, arg, "--configure")) {
390 print_configuration = .stop;
364 } else if (mem.eql(u8, arg, "--fetch")) {391 } else if (mem.eql(u8, arg, "--fetch")) {
365 fetch_only = true;392 fetch_mode = .all;
366 } else if (mem.cutPrefix(u8, arg, "--fetch=")) |rest| {
367 fetch_only = true;
368 fetch_mode = stringToEnum(Fetch.JobQueue.Mode, rest) orelse
369 fatal("expected [needed|all] after \"--fetch=\", found {q}", .{rest});
370 } else if (mem.cutPrefix(u8, arg, "--fork=")) |rest| {393 } else if (mem.cutPrefix(u8, arg, "--fork=")) |rest| {
371 try forks.append(arena, .init(rest));394 try forks.append(arena, .init(rest));
372 } else if (mem.eql(u8, arg, "--fork")) {395 } else if (mem.eql(u8, arg, "--fork")) {
...@@ -593,7 +616,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -593,7 +616,7 @@ pub fn main(init: process.Init.Minimal) !void {
593 }616 }
594 }617 }
595618
596 const early_exit_mode = fetch_only or help_menu or steps_menu or print_configuration != .none;619 const early_exit_mode = fetch_mode == .all or help_menu or steps_menu or print_configuration != .none;
597 const server_mode = !early_exit_mode and (watch_flag or webui_listen != null or fuzz != null or listen);620 const server_mode = !early_exit_mode and (watch_flag or webui_listen != null or fuzz != null or listen);
598621
599 process.raiseFileDescriptorLimit();622 process.raiseFileDescriptorLimit();
...@@ -601,10 +624,16 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -601,10 +624,16 @@ pub fn main(init: process.Init.Minimal) !void {
601 const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err|624 const cwd_path = std.zig.getResolvedCwd(io, arena) catch |err|
602 fatal("resolving current directory path failed: {t}", .{err});625 fatal("resolving current directory path failed: {t}", .{err});
603626
604 var build_root = try findBuildRoot(arena, io, .{627 var build_root = findBuildRoot(arena, io, .{
605 .cwd_path = cwd_path,628 .cwd_path = cwd_path,
606 .build_file = build_file,629 .build_file = build_file,
607 });630 }) catch |err| switch (err) {
631 error.BuildZigNotFound => {
632 if (help_menu) printUsageWithoutConfiguration(&graph, "build.zig not found");
633 process.exit(1);
634 },
635 else => |e| return e,
636 };
608 defer build_root.deinit(io);637 defer build_root.deinit(io);
609638
610 graph.build_root_directory = build_root.directory;639 graph.build_root_directory = build_root.directory;
...@@ -740,7 +769,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -740,7 +769,7 @@ pub fn main(init: process.Init.Minimal) !void {
740 var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty;769 var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty;
741 defer configure_source_files.deinit(gpa);770 defer configure_source_files.deinit(gpa);
742771
743 var configure_error_bundle: std.zig.ErrorBundle = .empty;772 var configure_error_bundle: ErrorBundle = .empty;
744 defer configure_error_bundle.deinit(gpa);773 defer configure_error_bundle.deinit(gpa);
745774
746 // If this fails, we can still start the server and wait for user775 // If this fails, we can still start the server and wait for user
...@@ -761,7 +790,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -761,7 +790,6 @@ pub fn main(init: process.Init.Minimal) !void {
761 .parent_progress_node = main_progress_node,790 .parent_progress_node = main_progress_node,
762 .fetch_mode = fetch_mode,791 .fetch_mode = fetch_mode,
763 .system_pkg_dir_path = system_pkg_dir_path,792 .system_pkg_dir_path = system_pkg_dir_path,
764 .fetch_only = fetch_only,
765 .print_configuration = print_configuration,793 .print_configuration = print_configuration,
766 .forks = forks.items,794 .forks = forks.items,
767 .src_files = &configure_source_files,795 .src_files = &configure_source_files,
...@@ -789,6 +817,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -789,6 +817,8 @@ pub fn main(init: process.Init.Minimal) !void {
789 return cleanExit(io, &scanned_config);817 return cleanExit(io, &scanned_config);
790 },818 },
791 .path => unreachable,819 .path => unreachable,
820 .tree => unreachable,
821 .stop => return cleanExit(io, &scanned_config),
792 }822 }
793823
794 var maker: Maker = .{824 var maker: Maker = .{
...@@ -1043,7 +1073,10 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1043,7 +1073,10 @@ pub fn main(init: process.Init.Minimal) !void {
1043 } else |err| {1073 } else |err| {
1044 const can_fs_watch = switch (err) {1074 const can_fs_watch = switch (err) {
1045 error.AlreadyReported => false,1075 error.AlreadyReported => false,
1046 error.FailedButCacheIntact => true,1076 error.FailedButCacheIntact => c: {
1077 if (help_menu) printUsageWithoutConfiguration(&graph, "build.zig compilation failure");
1078 break :c true;
1079 },
1047 else => |e| w: {1080 else => |e| w: {
1048 log.err("configuration failed: {t}", .{e});1081 log.err("configuration failed: {t}", .{e});
1049 break :w false;1082 break :w false;
...@@ -1065,6 +1098,17 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -1065,6 +1098,17 @@ pub fn main(init: process.Init.Minimal) !void {
1065 }1098 }
1066}1099}
10671100
1101fn printUsageWithoutConfiguration(graph: *Graph, reason: []const u8) noreturn {
1102 const io = graph.io;
1103 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
1104 const writer = initStdoutWriter(io);
1105 ScannedConfig.printUsageWithoutConfiguration(graph.zig_exe, writer, reason) catch |err| switch (err) {
1106 error.WriteFailed => fatal("failed writing to stdout: {t}", .{stdout_writer_allocation.err.?}),
1107 };
1108 stdout_writer_allocation.flush() catch |err| fatal("failed writing to stdout: {t}", .{err});
1109 return process.exit(0);
1110}
1111
1068/// Temporarily adds the reconfigure pseudostep to step_stack, calls1112/// Temporarily adds the reconfigure pseudostep to step_stack, calls
1069/// `Watch.update`, and then pops it again.1113/// `Watch.update`, and then pops it again.
1070fn updateWatch(maker: *Maker, watch: *Watch) !void {1114fn updateWatch(maker: *Maker, watch: *Watch) !void {
...@@ -1088,11 +1132,10 @@ const ConfigureOptions = struct {...@@ -1088,11 +1132,10 @@ const ConfigureOptions = struct {
1088 parent_progress_node: std.Progress.Node,1132 parent_progress_node: std.Progress.Node,
1089 fetch_mode: Fetch.JobQueue.Mode,1133 fetch_mode: Fetch.JobQueue.Mode,
1090 system_pkg_dir_path: ?[]const u8,1134 system_pkg_dir_path: ?[]const u8,
1091 fetch_only: bool,
1092 print_configuration: PrintConfiguration,1135 print_configuration: PrintConfiguration,
1093 forks: []Fork,1136 forks: []Fork,
1094 src_files: *Cache.Manifest.SelfContainedFiles,1137 src_files: *Cache.Manifest.SelfContainedFiles,
1095 error_bundle: ?*std.zig.ErrorBundle = null,1138 error_bundle: ?*ErrorBundle = null,
1096};1139};
10971140
1098fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {1141fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
...@@ -1336,11 +1379,27 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1336,11 +1379,27 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1336 return error.FailedButCacheIntact;1379 return error.FailedButCacheIntact;
1337 }1380 }
13381381
1339 if (options.fetch_only) {1382 if (options.print_configuration == .tree) {
1383 const NO_COLOR = EnvVar.NO_COLOR.isSet(&graph.environ_map);
1384 const CLICOLOR_FORCE = EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map);
1385 printPkgTree(.{
1386 .writer = initStdoutWriter(io),
1387 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),
1388 }, arena, &job_queue, &fetch) catch |err| switch (err) {
1389 error.WriteFailed => return stdout_writer_allocation.err.?,
1390 else => |e| return e,
1391 };
1392 try stdout_writer_allocation.flush();
1393 process.exit(0);
1394 }
1395
1396 if (options.fetch_mode == .all) {
1340 _ = io.lockStderr(&.{}, .no_color) catch {};1397 _ = io.lockStderr(&.{}, .no_color) catch {};
1341 process.exit(0);1398 process.exit(0);
1342 }1399 }
13431400
1401 try validateFingerprints(gpa, &job_queue);
1402
1344 // Create the dependencies.zig file for configurer to1403 // Create the dependencies.zig file for configurer to
1345 // obtain via `@import("@dependencies")`.1404 // obtain via `@import("@dependencies")`.
1346 {1405 {
...@@ -1403,7 +1462,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1403,7 +1462,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1403 for (fetches) |f| {1462 for (fetches) |f| {
1404 if (!f.have_manifest) continue;1463 if (!f.have_manifest) continue;
1405 if (config_man) |man| {1464 if (config_man) |man| {
1406 const manifest_path = try f.package_root.join(arena, Package.Manifest.basename);1465 const manifest_path = try f.package_root.join(arena, Manifest.basename);
1407 _ = try man.addInputPath(manifest_path, .{});1466 _ = try man.addInputPath(manifest_path, .{});
1408 }1467 }
1409 const mod = f.cli_module orelse continue;1468 const mod = f.cli_module orelse continue;
...@@ -1619,7 +1678,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1619,7 +1678,8 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1619 _ = io.lockStderr(&.{}, .no_color) catch {};1678 _ = io.lockStderr(&.{}, .no_color) catch {};
1620 process.exit(0);1679 process.exit(0);
1621 },1680 },
1622 .none, .zon => {},1681 .none, .zon, .stop => {},
1682 .tree => unreachable,
1623 }1683 }
16241684
1625 const configuration = c: {1685 const configuration = c: {
...@@ -1657,6 +1717,147 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1657,6 +1717,147 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1657 };1717 };
1658}1718}
16591719
1720const ValidFingerprintKey = struct {
1721 project_id: Package.ProjectId,
1722 version: std.SemanticVersion,
1723
1724 const Context = struct {
1725 pub fn hash(this: @This(), a: ValidFingerprintKey) u32 {
1726 _ = this;
1727 var hasher: std.hash.Wyhash = .init(a.project_id.fingerprint_id);
1728 hasher.update(&a.project_id.padded_name);
1729 hasher.update(@ptrCast((&a.version.major)[0..1]));
1730 hasher.update(@ptrCast((&a.version.minor)[0..1]));
1731 hasher.update(@ptrCast((&a.version.patch)[0..1]));
1732 if (a.version.pre) |pre| hasher.update(pre);
1733 return @truncate(hasher.final());
1734 }
1735 pub fn eql(this: @This(), a: ValidFingerprintKey, b: ValidFingerprintKey, i: usize) bool {
1736 _ = this;
1737 _ = i;
1738 return a.project_id.eql(&b.project_id) and a.version.order(b.version) == .eq;
1739 }
1740 };
1741};
1742
1743const ValidFingerprintTable = std.array_hash_map.Custom(
1744 ValidFingerprintKey,
1745 struct { fetch: *Fetch, pkg_hash: Package.Hash, printed: bool },
1746 ValidFingerprintKey.Context,
1747 true,
1748);
1749
1750/// Ensure that the dependency tree did not have any fingerprint matches with the same semantic version but
1751/// differing package hashes.
1752fn validateFingerprints(gpa: Allocator, job_queue: *const Fetch.JobQueue) !void {
1753 var any_errors = false;
1754
1755 var table: ValidFingerprintTable = .empty;
1756 defer table.deinit(gpa);
1757
1758 for (job_queue.table.keys(), job_queue.table.values()) |pkg_hash, fetch| {
1759 if (!fetch.have_manifest) continue;
1760 const gop = try table.getOrPut(gpa, .{
1761 .project_id = .init(fetch.manifest.name, fetch.manifest.id),
1762 .version = fetch.manifest.version,
1763 });
1764 if (gop.found_existing) {
1765 any_errors = true;
1766 const fingerprint: Package.Fingerprint = .init(fetch.manifest.id, fetch.manifest.name);
1767 const stripped_version: std.SemanticVersion = .{
1768 .major = fetch.manifest.version.major,
1769 .minor = fetch.manifest.version.minor,
1770 .patch = fetch.manifest.version.patch,
1771 .pre = fetch.manifest.version.pre,
1772 };
1773 if (!gop.value_ptr.printed) {
1774 gop.value_ptr.printed = true;
1775 log.err("contradictory hash for package with fingerprint 0x{x} and version {f}: {s}", .{
1776 fingerprint.int(), stripped_version, gop.value_ptr.pkg_hash.toSlice(),
1777 });
1778 }
1779 log.err("contradictory hash for package with fingerprint 0x{x} and version {f}: {s}", .{
1780 fingerprint.int(), stripped_version, pkg_hash.toSlice(),
1781 });
1782 } else {
1783 gop.value_ptr.* = .{ .fetch = fetch, .pkg_hash = pkg_hash, .printed = false };
1784 }
1785 }
1786 if (any_errors) {
1787 log.info("potential cause: package ownership dispute", .{});
1788 log.info("potential cause: maintainer neglected to bump version after making changes", .{});
1789 log.info("potential cause: dependency on unreleased changes", .{});
1790 log.info("hint: use --print-tree to visualize all package versions and hashes", .{});
1791 return error.FailedButCacheIntact;
1792 }
1793}
1794
1795fn printPkgTree(
1796 terminal: Io.Terminal,
1797 arena: Allocator,
1798 job_queue: *const Fetch.JobQueue,
1799 root_fetch: *const Fetch,
1800) !void {
1801 if (!root_fetch.have_manifest) return;
1802 const names = root_fetch.manifest.dependencies.keys();
1803 for (names, root_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| {
1804 var print_node: PrintNode = .{
1805 .parent = null,
1806 .last = i == names.len - 1,
1807 };
1808 try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash);
1809 }
1810}
1811
1812fn printPkgTreeChild(
1813 terminal: Io.Terminal,
1814 arena: Allocator,
1815 job_queue: *const Fetch.JobQueue,
1816 parent_print_node: *const PrintNode,
1817 dep_name: []const u8,
1818 opt_hash: ?[]const u8,
1819) !void {
1820 const w = terminal.writer;
1821 try parent_print_node.printPrefixedChild(w, terminal.mode);
1822 try w.writeAll(dep_name);
1823
1824 const hash = opt_hash orelse {
1825 try terminal.setColor(.red);
1826 try w.writeAll(" (missing hash)\n");
1827 try terminal.setColor(.reset);
1828 return;
1829 };
1830 try terminal.setColor(.dim);
1831 try w.print(" {s}", .{hash});
1832 try terminal.setColor(.reset);
1833 const child_fetch = job_queue.table.get(.fromSlice(hash)) orelse {
1834 try terminal.setColor(.red);
1835 try w.writeAll(" (fetch failed)\n");
1836 try terminal.setColor(.reset);
1837 return;
1838 };
1839 switch (child_fetch.lazy_status) {
1840 .eager, .available => {},
1841 .unavailable => {
1842 try terminal.setColor(.yellow);
1843 try w.writeAll(" (not fetched)\n");
1844 try terminal.setColor(.reset);
1845 return;
1846 },
1847 }
1848 try w.writeByte('\n');
1849 if (!child_fetch.have_manifest) return;
1850
1851 const names = child_fetch.manifest.dependencies.keys();
1852 for (names, child_fetch.manifest.dependencies.values(), 0..) |name, *dep, i| {
1853 var print_node: PrintNode = .{
1854 .parent = parent_print_node,
1855 .last = i == names.len - 1,
1856 };
1857 try printPkgTreeChild(terminal, arena, job_queue, &print_node, name, dep.hash);
1858 }
1859}
1860
1660fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {1861fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
1661 const environ_map = &graph.environ_map;1862 const environ_map = &graph.environ_map;
1662 const io = graph.io;1863 const io = graph.io;
...@@ -1933,8 +2134,8 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {...@@ -1933,8 +2134,8 @@ fn cmdFetch(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
1933 try ast.render(gpa, &aw.writer, fixups);2134 try ast.render(gpa, &aw.writer, fixups);
1934 const rendered = aw.written();2135 const rendered = aw.written();
19352136
1936 build_root.directory.handle.writeFile(io, .{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| {2137 build_root.directory.handle.writeFile(io, .{ .sub_path = Manifest.basename, .data = rendered }) catch |err| {
1937 fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });2138 fatal("unable to write {s} file: {t}", .{ Manifest.basename, err });
1938 };2139 };
19392140
1940 return process.cleanExit(io);2141 return process.cleanExit(io);
...@@ -2136,7 +2337,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {...@@ -2136,7 +2337,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
2136 const s = Dir.path.sep_str;2337 const s = Dir.path.sep_str;
2137 const template_paths = [_][]const u8{2338 const template_paths = [_][]const u8{
2138 default_build_zig_basename,2339 default_build_zig_basename,
2139 Package.Manifest.basename,2340 Manifest.basename,
2140 "src" ++ s ++ "main.zig",2341 "src" ++ s ++ "main.zig",
2141 "src" ++ s ++ "root.zig",2342 "src" ++ s ++ "root.zig",
2142 };2343 };
...@@ -2160,7 +2361,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {...@@ -2160,7 +2361,7 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
2160 return process.cleanExit(io);2361 return process.cleanExit(io);
2161 },2362 },
2162 .minimal => {2363 .minimal => {
2163 Templates.writeSimpleFile(io, Io.Dir.cwd(), Package.Manifest.basename,2364 Templates.writeSimpleFile(io, Io.Dir.cwd(), Manifest.basename,
2164 \\.{{2365 \\.{{
2165 \\ .name = .{s},2366 \\ .name = .{s},
2166 \\ .version = "0.0.1",2367 \\ .version = "0.0.1",
...@@ -2174,8 +2375,8 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {...@@ -2174,8 +2375,8 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
2174 builtin.zig_version_string,2375 builtin.zig_version_string,
2175 fingerprint.int(),2376 fingerprint.int(),
2176 }) catch |err| switch (err) {2377 }) catch |err| switch (err) {
2177 else => fatal("failed to create {q}: {t}", .{ Package.Manifest.basename, err }),2378 else => fatal("failed to create {q}: {t}", .{ Manifest.basename, err }),
2178 error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Package.Manifest.basename}),2379 error.PathAlreadyExists => fatal("refusing to overwrite {q}", .{Manifest.basename}),
2179 };2380 };
2180 Templates.writeSimpleFile(io, Io.Dir.cwd(), default_build_zig_basename,2381 Templates.writeSimpleFile(io, Io.Dir.cwd(), default_build_zig_basename,
2181 \\const std = @import("std");2382 \\const std = @import("std");
...@@ -2190,12 +2391,12 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {...@@ -2190,12 +2391,12 @@ fn cmdInit(gpa: Allocator, graph: *Graph, args: []const []const u8) !void {
2190 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.2391 // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.
2191 error.PathAlreadyExists => {2392 error.PathAlreadyExists => {
2192 log.info("successfully populated {q}, preserving existing {q}", .{2393 log.info("successfully populated {q}, preserving existing {q}", .{
2193 Package.Manifest.basename, default_build_zig_basename,2394 Manifest.basename, default_build_zig_basename,
2194 });2395 });
2195 return process.cleanExit(io);2396 return process.cleanExit(io);
2196 },2397 },
2197 };2398 };
2198 log.info("successfully populated {q} and {q}", .{ Package.Manifest.basename, default_build_zig_basename });2399 log.info("successfully populated {q} and {q}", .{ Manifest.basename, default_build_zig_basename });
2199 return process.cleanExit(io);2400 return process.cleanExit(io);
2200 },2401 },
2201 }2402 }
...@@ -2928,18 +3129,7 @@ fn printTreeStep(...@@ -2928,18 +3129,7 @@ fn printTreeStep(
2928 .failures => make_step.state == .success,3129 .failures => make_step.state == .success,
2929 };3130 };
2930 if (skip) return;3131 if (skip) return;
2931 try printPrefix(parent_node, stderr);3132 try parent_node.printPrefixedChild(writer, stderr.mode);
2932
2933 if (parent_node.parent != null) {
2934 if (parent_node.last) {
2935 try printChildNodePrefix(stderr);
2936 } else {
2937 try writer.writeAll(switch (stderr.mode) {
2938 .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─
2939 else => "+- ",
2940 });
2941 }
2942 }
29433133
2944 if (!first) try stderr.setColor(.dim);3134 if (!first) try stderr.setColor(.dim);
29453135
...@@ -3168,23 +3358,22 @@ fn printStepFailure(...@@ -3168,23 +3358,22 @@ fn printStepFailure(
3168 }3358 }
3169}3359}
31703360
3171fn printPrefix(node: *PrintNode, stderr: Io.Terminal) !void {3361fn printTreeChild(writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void {
3172 const parent = node.parent orelse return;3362 return writer.writeAll(switch (mode) {
3173 const writer = stderr.writer;3363 .escape_codes => "\x1B\x28\x30\x74\x71\x1B\x28\x42 ", // ├─
3174 if (parent.parent == null) return;3364 else => "+- ",
3175 try printPrefix(parent, stderr);3365 });
3176 if (parent.last) {
3177 try writer.writeAll(" ");
3178 } else {
3179 try writer.writeAll(switch (stderr.mode) {
3180 .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │
3181 else => "| ",
3182 });
3183 }
3184}3366}
31853367
3186fn printChildNodePrefix(stderr: Io.Terminal) !void {3368fn printTreeSibling(writer: *Writer, mode: Io.Terminal.Mode) Writer.Error!void {
3187 try stderr.writer.writeAll(switch (stderr.mode) {3369 return writer.writeAll(switch (mode) {
3370 .escape_codes => "\x1B\x28\x30\x78\x1B\x28\x42 ", // │
3371 else => "| ",
3372 });
3373}
3374
3375fn printTreeChildLast(writer: *Writer, mode: Io.Terminal.Mode) !void {
3376 try writer.writeAll(switch (mode) {
3188 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─3377 .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─
3189 else => "+- ",3378 else => "+- ",
3190 });3379 });
...@@ -3278,7 +3467,7 @@ pub fn invalidateResult(maker: *Maker, step: *Step) error{MustReconfigure}!bool...@@ -3278,7 +3467,7 @@ pub fn invalidateResult(maker: *Maker, step: *Step) error{MustReconfigure}!bool
3278pub fn printErrorMessages(3467pub fn printErrorMessages(
3279 maker: *Maker,3468 maker: *Maker,
3280 failing_step_index: Configuration.Step.Index,3469 failing_step_index: Configuration.Step.Index,
3281 options: std.zig.ErrorBundle.RenderOptions,3470 options: ErrorBundle.RenderOptions,
3282 stderr: Io.Terminal,3471 stderr: Io.Terminal,
3283 error_style: ErrorStyle,3472 error_style: ErrorStyle,
3284 multiline_errors: MultilineErrors,3473 multiline_errors: MultilineErrors,
...@@ -3304,7 +3493,7 @@ pub fn printErrorMessages(...@@ -3304,7 +3493,7 @@ pub fn printErrorMessages(
3304 while (step_stack.pop()) |step_index| : (indent += 1) {3493 while (step_stack.pop()) |step_index| : (indent += 1) {
3305 if (indent > 0) {3494 if (indent > 0) {
3306 try writer.splatByteAll(' ', (indent - 1) * 3);3495 try writer.splatByteAll(' ', (indent - 1) * 3);
3307 try printChildNodePrefix(stderr);3496 try printTreeChildLast(writer, stderr.mode);
3308 }3497 }
33093498
3310 try writer.writeAll(step_index.ptr(c).name.slice(c));3499 try writer.writeAll(step_index.ptr(c).name.slice(c));
...@@ -4038,7 +4227,9 @@ const FindBuildRootOptions = struct {...@@ -4038,7 +4227,9 @@ const FindBuildRootOptions = struct {
4038 cwd_path: []const u8,4227 cwd_path: []const u8,
4039};4228};
40404229
4041fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !BuildRoot {4230const FindBuildRootError = error{ OutOfMemory, BuildZigNotFound };
4231
4232fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) FindBuildRootError!BuildRoot {
4042 const build_zig_basename = if (options.build_file) |bf|4233 const build_zig_basename = if (options.build_file) |bf|
4043 Dir.path.basename(bf)4234 Dir.path.basename(bf)
4044 else4235 else
...@@ -4089,11 +4280,12 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build...@@ -4089,11 +4280,12 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build
4089 dirname = Dir.path.dirname(dirname orelse options.cwd_path) orelse {4280 dirname = Dir.path.dirname(dirname orelse options.cwd_path) orelse {
4090 log.info("initialize {s} template file with \"zig init\"", .{std.zig.build_zig_basename});4281 log.info("initialize {s} template file with \"zig init\"", .{std.zig.build_zig_basename});
4091 log.info("see \"zig --help\" for more options", .{});4282 log.info("see \"zig --help\" for more options", .{});
4092 fatal("no build.zig file found, in the current directory or any parent directories", .{});4283 log.err("no build.zig file found, in the current directory or any parent directories", .{});
4284 return error.BuildZigNotFound;
4093 };4285 };
4094 continue;4286 continue;
4095 },4287 },
4096 else => |e| return e,4288 else => |e| fatal("accessing {q} while searching for build.zig file failed: {t}", .{ joined_path, e }),
4097 }4289 }
4098 }4290 }
4099}4291}
...@@ -4101,8 +4293,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build...@@ -4101,8 +4293,8 @@ fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !Build
4101const Fork = struct {4293const Fork = struct {
4102 path: Path,4294 path: Path,
4103 manifest_ast: std.zig.Ast,4295 manifest_ast: std.zig.Ast,
4104 manifest: Package.Manifest,4296 manifest: Manifest,
4105 error_bundle: std.zig.ErrorBundle.Wip,4297 error_bundle: ErrorBundle.Wip,
4106 failed: bool,4298 failed: bool,
4107 arena_allocator: std.heap.ArenaAllocator,4299 arena_allocator: std.heap.ArenaAllocator,
41084300
...@@ -4135,12 +4327,12 @@ const Fork = struct {...@@ -4135,12 +4327,12 @@ const Fork = struct {
4135 fork.arena_allocator = .init(gpa);4327 fork.arena_allocator = .init(gpa);
4136 const arena = fork.arena_allocator.allocator();4328 const arena = fork.arena_allocator.allocator();
41374329
4138 var error_bundle: std.zig.ErrorBundle.Wip = try .init(gpa);4330 var error_bundle: ErrorBundle.Wip = try .init(gpa);
4139 defer error_bundle.deinit();4331 defer error_bundle.deinit();
41404332
4141 const manifest_path = try fork.path.join(arena, Package.Manifest.basename);4333 const manifest_path = try fork.path.join(arena, Manifest.basename);
41424334
4143 Package.Manifest.load(4335 Manifest.load(
4144 io,4336 io,
4145 arena,4337 arena,
4146 manifest_path,4338 manifest_path,
...@@ -4190,20 +4382,20 @@ fn loadManifest(...@@ -4190,20 +4382,20 @@ fn loadManifest(
4190 arena: Allocator,4382 arena: Allocator,
4191 io: Io,4383 io: Io,
4192 options: LoadManifestOptions,4384 options: LoadManifestOptions,
4193) !struct { Package.Manifest, std.zig.Ast } {4385) !struct { Manifest, std.zig.Ast } {
4194 const rng: std.Random.IoSource = .{ .io = io };4386 const rng: std.Random.IoSource = .{ .io = io };
41954387
4196 const manifest_bytes = while (true) {4388 const manifest_bytes = while (true) {
4197 break options.dir.readFileAllocOptions(4389 break options.dir.readFileAllocOptions(
4198 io,4390 io,
4199 Package.Manifest.basename,4391 Manifest.basename,
4200 arena,4392 arena,
4201 .limited(Package.Manifest.max_bytes),4393 .limited(Manifest.max_bytes),
4202 .@"1",4394 .@"1",
4203 0,4395 0,
4204 ) catch |err| switch (err) {4396 ) catch |err| switch (err) {
4205 error.FileNotFound => {4397 error.FileNotFound => {
4206 Templates.writeSimpleFile(io, options.dir, Package.Manifest.basename,4398 Templates.writeSimpleFile(io, options.dir, Manifest.basename,
4207 \\.{{4399 \\.{{
4208 \\ .name = .{s},4400 \\ .name = .{s},
4209 \\ .version = "0.0.1",4401 \\ .version = "0.0.1",
...@@ -4217,29 +4409,29 @@ fn loadManifest(...@@ -4217,29 +4409,29 @@ fn loadManifest(
4217 builtin.zig_version_string,4409 builtin.zig_version_string,
4218 Package.Fingerprint.generate(rng.interface(), options.root_name).int(),4410 Package.Fingerprint.generate(rng.interface(), options.root_name).int(),
4219 }) catch |e| {4411 }) catch |e| {
4220 fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e });4412 fatal("unable to write {s}: {t}", .{ Manifest.basename, e });
4221 };4413 };
4222 continue;4414 continue;
4223 },4415 },
4224 else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }),4416 else => |e| fatal("unable to load {s}: {t}", .{ Manifest.basename, e }),
4225 };4417 };
4226 };4418 };
4227 var ast = try std.zig.Ast.parse(gpa, manifest_bytes, .{ .mode = .zon });4419 var ast = try std.zig.Ast.parse(gpa, manifest_bytes, .{ .mode = .zon });
4228 errdefer ast.deinit(gpa);4420 errdefer ast.deinit(gpa);
42294421
4230 if (ast.errors.len > 0) {4422 if (ast.errors.len > 0) {
4231 try std.zig.printAstErrorsToStderr(gpa, io, ast, Package.Manifest.basename, options.color);4423 try std.zig.printAstErrorsToStderr(gpa, io, ast, Manifest.basename, options.color);
4232 process.exit(2);4424 process.exit(2);
4233 }4425 }
42344426
4235 var manifest = try Package.Manifest.parse(gpa, &ast, rng.interface(), .{});4427 var manifest = try Manifest.parse(gpa, &ast, rng.interface(), .{});
4236 errdefer manifest.deinit(gpa);4428 errdefer manifest.deinit(gpa);
42374429
4238 if (manifest.errors.len > 0) {4430 if (manifest.errors.len > 0) {
4239 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);4431 var wip_errors: ErrorBundle.Wip = try .init(gpa);
4240 defer wip_errors.deinit();4432 defer wip_errors.deinit();
42414433
4242 const src_path = try wip_errors.addString(Package.Manifest.basename);4434 const src_path = try wip_errors.addString(Manifest.basename);
4243 try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors);4435 try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors);
42444436
4245 var error_bundle = try wip_errors.toOwnedBundle("");4437 var error_bundle = try wip_errors.toOwnedBundle("");
...@@ -4263,8 +4455,8 @@ fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![...@@ -4263,8 +4455,8 @@ fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![
4263 else => continue,4455 else => continue,
4264 };4456 };
4265 if (!std.zig.isValidId(result.items)) return "foo";4457 if (!std.zig.isValidId(result.items)) return "foo";
4266 if (result.items.len > Package.Manifest.max_name_len)4458 if (result.items.len > Manifest.max_name_len)
4267 result.shrinkRetainingCapacity(Package.Manifest.max_name_len);4459 result.shrinkRetainingCapacity(Manifest.max_name_len);
42684460
4269 return result.toOwnedSlice(arena);4461 return result.toOwnedSlice(arena);
4270}4462}
lib/compiler/Maker/Package.zig+6-2
...@@ -8,13 +8,17 @@ pub const Fingerprint = packed struct(u64) {...@@ -8,13 +8,17 @@ pub const Fingerprint = packed struct(u64) {
8 id: u32,8 id: u32,
9 checksum: u32,9 checksum: u32,
1010
11 pub fn generate(rng: std.Random, name: []const u8) Fingerprint {11 pub fn init(id: u32, name: []const u8) Fingerprint {
12 return .{12 return .{
13 .id = rng.intRangeLessThan(u32, 1, 0xffffffff),13 .id = id,
14 .checksum = std.hash.Crc32.hash(name),14 .checksum = std.hash.Crc32.hash(name),
15 };15 };
16 }16 }
1717
18 pub fn generate(rng: std.Random, name: []const u8) Fingerprint {
19 return .init(rng.intRangeLessThan(u32, 1, 0xffffffff), name);
20 }
21
18 pub fn validate(n: Fingerprint, name: []const u8) bool {22 pub fn validate(n: Fingerprint, name: []const u8) bool {
19 switch (n.id) {23 switch (n.id) {
20 0x00000000, 0xffffffff => return false,24 0x00000000, 0xffffffff => return false,
lib/compiler/Maker/ScannedConfig.zig+135-119
...@@ -238,6 +238,18 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {...@@ -238,6 +238,18 @@ pub fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
238 }238 }
239}239}
240240
241pub fn printUsageWithoutConfiguration(zig_exe: []const u8, w: *Writer, reason: []const u8) Writer.Error!void {
242 try w.print(
243 \\Usage: {s} build [steps] [options]
244 \\
245 \\Steps: (unavailable due to {s})
246 \\Project-Specific Options: (unavailable due to {s})
247 \\{s}
248 \\ (unavailable due to {s})
249 \\{s}
250 , .{ zig_exe, reason, reason, system_integration_usage, reason, general_usage });
251}
252
241pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {253pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
242 const arena = graph.arena;254 const arena = graph.arena;
243255
...@@ -274,37 +286,7 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {...@@ -274,37 +286,7 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
274 }286 }
275 }287 }
276288
277 try w.writeAll(289 try w.writeAll(system_integration_usage);
278 \\
279 \\System Integration Options:
280 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
281 \\ --sysroot [path] Set the system root directory (usually /)
282 \\ --libc [file] Provide a file which specifies libc paths
283 \\
284 \\ --system [pkgdir] Disable package fetching; enable all integrations
285 \\ -fsys=[name] Enable a system integration
286 \\ -fno-sys=[name] Disable a system integration
287 \\
288 \\ -fdarling, -fno-darling Integration with system-installed Darling to
289 \\ execute macOS programs on Linux hosts
290 \\ (default: no)
291 \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute
292 \\ foreign-architecture programs on Linux hosts
293 \\ (default: no)
294 \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc
295 \\ (e.g. glibc or musl) built for multiple foreign
296 \\ architectures, allowing execution of non-native
297 \\ programs that link with libc.
298 \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on
299 \\ ARM64 macOS hosts. (default: no)
300 \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to
301 \\ execute WASI binaries. (default: no)
302 \\ -fwine, -fno-wine Integration with system-installed Wine to execute
303 \\ Windows programs on Linux hosts. (default: no)
304 \\
305 \\ Available System Integrations: Enabled:
306 \\
307 );
308 if (sc.configuration.system_integrations.len == 0) {290 if (sc.configuration.system_integrations.len == 0) {
309 try w.writeAll(" (none) -\n");291 try w.writeAll(" (none) -\n");
310 } else {292 } else {
...@@ -318,92 +300,126 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {...@@ -318,92 +300,126 @@ pub fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
318 }300 }
319 }301 }
320302
321 try w.writeAll(303 try w.writeAll(general_usage);
322 \\
323 \\General Options:
324 \\ -h, --help Print this help to stdout and exit
325 \\ -l, --list-steps Print available steps to stdout and exit
326 \\
327 \\ -p, --prefix [path] Where to install files (default: zig-out)
328 \\ --prefix-lib-dir [path] Where to install libraries
329 \\ --prefix-exe-dir [path] Where to install executables
330 \\ --prefix-include-dir [path] Where to install C header files
331 \\ --release[=mode] Request release mode, optionally specifying a
332 \\ preferred optimization mode: fast, safe, small
333 \\
334 \\ --verbose Print commands before executing them
335 \\ --color [auto|off|on] Enable or disable colored error messages
336 \\ --error-style [style] Control how build errors are printed
337 \\ verbose (Default) Report errors with full context
338 \\ minimal Report errors after summary, excluding context like command lines
339 \\ verbose_clear Like 'verbose', but clear the terminal at the start of each update
340 \\ minimal_clear Like 'minimal', but clear the terminal at the start of each update
341 \\ --multiline-errors [style] Control how multi-line error messages are printed
342 \\ indent (Default) Indent non-initial lines to align with initial line
343 \\ newline Include a leading newline so that the error message is on its own lines
344 \\ none Print as usual so the first line is misaligned
345 \\ --summary [mode] Control the printing of the build summary
346 \\ all Print the build summary in its entirety
347 \\ new Omit cached steps
348 \\ failures (Default if short-lived) Only print failed steps
349 \\ line (Default if long-lived) Only print the single-line summary
350 \\ none Do not print the build summary
351 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
352 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
353 \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
354 \\ --test-timeout <timeout> Limit execution time of unit tests, terminating if exceeded.
355 \\ The timeout must include a unit: ns, us, ms, s, m, h
356 \\ --watch Continuously rebuild when source files are modified
357 \\ --debounce <ms> Delay before rebuilding after changed file detected
358 \\ --webui[=ip] Enable the web interface on the given IP address
359 \\ --fuzz[=limit] Continuously search for unit test failures with an optional
360 \\ limit to the max number of iterations. The argument supports
361 \\ an optional 'K', 'M', or 'G' suffix (e.g. '10K'). Implies
362 \\ '--webui' when no limit is specified.
363 \\ --listen=- Enable the build server protocol on stdio
364 \\ --time-report Force full rebuild and provide detailed information on
365 \\ compilation time of Zig source code (implies '--webui')
366 \\ -fincremental Enable incremental compilation
367 \\ -fno-incremental Disable incremental compilation
368 \\
369 \\Package Management Options:
370 \\ --fetch[=mode] Fetch dependency tree (optionally choose laziness) and exit
371 \\ needed (Default) Lazy dependencies are fetched as needed
372 \\ all Lazy dependencies are always fetched
373 \\ --fork=[path], --fork [path] Override one or more projects from dependency tree
374 \\
375 \\Advanced Options:
376 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
377 \\ -fno-reference-trace Disable reference trace
378 \\ -fallow-so-scripts Allows .so files to be GNU ld scripts
379 \\ -fno-allow-so-scripts (default) .so files must be ELF files
380 \\ --error-limit [num] Set the maximum amount of distinct error values
381 \\ --build-file [file] Override path to build.zig
382 \\ --cache-dir [path] Override path to local Zig cache directory
383 \\ --zig-lib=[arg] Override path to Zig lib directory
384 \\ --seed [integer] For shuffling dependency traversal order (default: random)
385 \\ --cache-poison[=mode] Override configuration caching behavior
386 \\ pure (default) Avoid false positive cache hits
387 \\ poisoned Don't cache the configuration
388 \\ disallowed Panics when cache would be poisoned
389 \\ ignored A little poison never hurt anybody
390 \\ --print-configuration Render configuration as .zon to stdout
391 \\ --print-configuration-path Print the path to the binary configuration file to stdout
392 \\ --build-id[=style] At a minor link-time expense, embeds a build ID in binaries
393 \\ fast 8-byte non-cryptographic hash (COFF, ELF, WASM)
394 \\ sha1, tree 20-byte cryptographic hash (ELF, WASM)
395 \\ md5 16-byte cryptographic hash (ELF)
396 \\ uuid 16-byte random UUID (ELF, WASM)
397 \\ 0x[hexstring] Constant ID, maximum 32 bytes (ELF, WASM)
398 \\ none (default) No build ID
399 \\ --debug-log [scope] Enable debugging the compiler
400 \\ --debug-pkg-config Fail if unknown pkg-config flags encountered
401 \\ --verbose-link Enable compiler debug output for linking
402 \\ --verbose-air Enable compiler debug output for Zig AIR
403 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
404 \\ --verbose-cimport Enable compiler debug output for C imports
405 \\ --verbose-cc Enable compiler debug output for C compilation
406 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
407 \\
408 );
409}304}
305
306const system_integration_usage =
307 \\
308 \\System Integration Options:
309 \\ --search-prefix [path] Add a path to look for binaries, libraries, headers
310 \\ --sysroot [path] Set the system root directory (usually /)
311 \\ --libc [file] Provide a file which specifies libc paths
312 \\
313 \\ --system [pkgdir] Disable package fetching; enable all integrations
314 \\ -fsys=[name] Enable a system integration
315 \\ -fno-sys=[name] Disable a system integration
316 \\
317 \\ -fdarling, -fno-darling Integration with system-installed Darling to
318 \\ execute macOS programs on Linux hosts
319 \\ (default: no)
320 \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute
321 \\ foreign-architecture programs on Linux hosts
322 \\ (default: no)
323 \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc
324 \\ (e.g. glibc or musl) built for multiple foreign
325 \\ architectures, allowing execution of non-native
326 \\ programs that link with libc.
327 \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on
328 \\ ARM64 macOS hosts. (default: no)
329 \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to
330 \\ execute WASI binaries. (default: no)
331 \\ -fwine, -fno-wine Integration with system-installed Wine to execute
332 \\ Windows programs on Linux hosts. (default: no)
333 \\
334 \\ Available System Integrations: Enabled:
335 \\
336;
337
338const general_usage =
339 \\
340 \\General Options:
341 \\ -h, --help Print this help to stdout and exit
342 \\ -l, --list-steps Print available steps to stdout and exit
343 \\
344 \\ -p, --prefix [path] Where to install files (default: zig-out)
345 \\ --prefix-lib-dir [path] Where to install libraries
346 \\ --prefix-exe-dir [path] Where to install executables
347 \\ --prefix-include-dir [path] Where to install C header files
348 \\ --release[=mode] Request release mode, optionally specifying a
349 \\ preferred optimization mode: fast, safe, small
350 \\
351 \\ --verbose Print commands before executing them
352 \\ --color [auto|off|on] Enable or disable colored error messages
353 \\ --error-style [style] Control how build errors are printed
354 \\ verbose (Default) Report errors with full context
355 \\ minimal Report errors after summary, excluding context like command lines
356 \\ verbose_clear Like "verbose", but clear the terminal at the start of each update
357 \\ minimal_clear Like "minimal", but clear the terminal at the start of each update
358 \\ --multiline-errors [style] Control how multi-line error messages are printed
359 \\ indent (Default) Indent non-initial lines to align with initial line
360 \\ newline Include a leading newline so that the error message is on its own lines
361 \\ none Print as usual so the first line is misaligned
362 \\ --summary [mode] Control the printing of the build summary
363 \\ all Print the build summary in its entirety
364 \\ new Omit cached steps
365 \\ failures (Default if short-lived) Only print failed steps
366 \\ line (Default if long-lived) Only print the single-line summary
367 \\ none Do not print the build summary
368 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
369 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
370 \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
371 \\ --test-timeout <timeout> Limit execution time of unit tests, terminating if exceeded.
372 \\ The timeout must include a unit: ns, us, ms, s, m, h
373 \\ --watch Continuously rebuild when source files are modified
374 \\ --debounce <ms> Delay before rebuilding after changed file detected
375 \\ --webui[=ip] Enable the web interface on the given IP address
376 \\ --fuzz[=limit] Continuously search for unit test failures with an optional
377 \\ limit to the max number of iterations. The argument supports
378 \\ an optional 'K', 'M', or 'G' suffix (e.g. "10K"). Implies
379 \\ "--webui" when no limit is specified.
380 \\ --time-report Force full rebuild and provide detailed information on
381 \\ compilation time of Zig source code (implies "--webui")
382 \\ -fincremental Enable incremental compilation
383 \\ -fno-incremental Disable incremental compilation
384 \\ --configure Exit after the configuration phase
385 \\
386 \\Package Management Options:
387 \\ --fetch Fetch all dependencies including lazy ones; exit without configuration
388 \\ --fork=[path], --fork [path] Override one or more projects from dependency tree
389 \\ --print-tree Write current state of package dependencies to stdout
390 \\
391 \\Advanced Options:
392 \\ --listen=- Enable the build server protocol on stdio
393 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
394 \\ -fno-reference-trace Disable reference trace
395 \\ -fallow-so-scripts Allows .so files to be GNU ld scripts
396 \\ -fno-allow-so-scripts (default) .so files must be ELF files
397 \\ --error-limit [num] Set the maximum amount of distinct error values
398 \\ --build-file [file] Override path to build.zig
399 \\ --cache-dir [path] Override path to local Zig cache directory
400 \\ --zig-lib=[arg] Override path to Zig lib directory
401 \\ --seed [integer] For shuffling dependency traversal order (default: random)
402 \\ --cache-poison[=mode] Override configuration caching behavior
403 \\ pure (default) Avoid false positive cache hits
404 \\ poisoned Don't cache the configuration
405 \\ disallowed Panics when cache would be poisoned
406 \\ ignored A little poison never hurt anybody
407 \\ --print-configuration Render configuration as .zon to stdout. Implies "--configure"
408 \\ --print-configuration-path Print the path to the binary configuration file to stdout.
409 \\ --build-id[=style] At a minor link-time expense, embeds a build ID in binaries
410 \\ fast 8-byte non-cryptographic hash (COFF, ELF, WASM)
411 \\ sha1, tree 20-byte cryptographic hash (ELF, WASM)
412 \\ md5 16-byte cryptographic hash (ELF)
413 \\ uuid 16-byte random UUID (ELF, WASM)
414 \\ 0x[hexstring] Constant ID, maximum 32 bytes (ELF, WASM)
415 \\ none (default) No build ID
416 \\ --debug-log [scope] Enable debugging the compiler
417 \\ --debug-pkg-config Fail if unknown pkg-config flags encountered
418 \\ --verbose-link Enable compiler debug output for linking
419 \\ --verbose-air Enable compiler debug output for Zig AIR
420 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
421 \\ --verbose-cimport Enable compiler debug output for C imports
422 \\ --verbose-cc Enable compiler debug output for C compilation
423 \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features
424 \\
425;