From 0505318efe0d2757a344dded9ae1607f948f7511 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Feb 2026 17:08:55 -0800 Subject: [PATCH] make runner gets compiled and run and --print-configuration prints some deserialized stuff --- lib/compiler/configure_runner.zig | 65 ++- lib/compiler/maker.zig | 650 ++++++++++++++++-------------- lib/compiler/maker/Fuzz.zig | 18 +- lib/compiler/maker/Graph.zig | 83 +--- lib/compiler/maker/Package.zig | 18 + lib/compiler/maker/Step.zig | 12 + lib/compiler/maker/WebServer.zig | 35 +- lib/std/Build.zig | 24 +- lib/std/zig/Configuration.zig | 104 ++++- src/Compilation.zig | 4 +- src/main.zig | 63 +-- 11 files changed, 596 insertions(+), 480 deletions(-) diff --git a/lib/compiler/configure_runner.zig b/lib/compiler/configure_runner.zig index 460173735c57b7fda4d8db5dac70a81e7df166dd..ad3ef25a8d33b5d64aa3ee52d68b4f14e7ea05b3 100644 --- a/lib/compiler/configure_runner.zig +++ b/lib/compiler/configure_runner.zig @@ -1,18 +1,19 @@ const builtin = @import("builtin"); const std = @import("std"); +const Allocator = std.mem.Allocator; +const Color = std.zig.Color; +const Configuration = std.Build.Configuration; +const File = std.Io.File; const Io = std.Io; +const Step = std.Build.Step; +const Writer = std.Io.Writer; const assert = std.debug.assert; +const fatal = std.process.fatal; const fmt = std.fmt; +const log = std.log; const mem = std.mem; const process = std.process; -const File = std.Io.File; -const Step = std.Build.Step; -const Allocator = std.mem.Allocator; -const fatal = std.process.fatal; -const Writer = std.Io.Writer; -const Color = std.zig.Color; -const Configuration = std.Build.Configuration; pub const root = @import("@build"); pub const dependencies = @import("@dependencies"); @@ -95,7 +96,6 @@ pub fn main(init: process.Init.Minimal) !void { .query = .{}, .result = try std.zig.system.resolveTargetQuery(io, .{}), }, - .time_report = false, }; graph.cache.addPrefix(.{ .path = null, .handle = cwd }); @@ -141,9 +141,9 @@ pub fn main(init: process.Init.Minimal) !void { fatal(" access the help menu with 'zig build -h'", .{}); } } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| { - graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM"); + try graph.system_integration_options.put(arena, name, .user_enabled); } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| { - graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM"); + try graph.system_integration_options.put(arena, name, .user_disabled); } else if (mem.eql(u8, arg, "--release")) { graph.release_mode = .any; } else if (mem.cutPrefix(u8, arg, "--release=")) |text| { @@ -177,8 +177,6 @@ pub fn main(init: process.Init.Minimal) !void { } else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| { builder.build_id = std.zig.BuildId.parse(style) catch |err| fatal("unable to parse --build-id style '{s}': {t}", .{ style, err }); - } else if (mem.eql(u8, arg, "--debug-rt")) { - graph.debug_compiler_runtime_libs = true; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { builder.debug_compile_errors = true; } else if (mem.eql(u8, arg, "--debug-incremental")) { @@ -204,10 +202,16 @@ pub fn main(init: process.Init.Minimal) !void { try builder.runBuild(root); + if (builder.validateUserInputDidItFail()) { + fatal(" access the help menu with 'zig build -h'", .{}); + } + var wc: Configuration.Wip = .init(gpa); defer wc.deinit(); assert(try wc.addString("") == .empty); + try serializeSystemIntegrationOptions(&graph, &wc); + var stdout_buffer: [1024]u8 = undefined; var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer); serialize(builder, &wc, &file_writer.interface) catch |err| switch (err) { @@ -367,7 +371,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .root_name = try wc.addString(c.name), })); - std.log.err("TODO serialize the trailing Compile step data", .{}); + log.err("TODO serialize the trailing Compile step data", .{}); break :e extra_index; }, @@ -441,7 +445,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .captured_stderr = captured_stderr, })); - std.log.err("TODO serialize the trailing Run step data", .{}); + log.err("TODO serialize the trailing Run step data", .{}); break :e extra_index; }, @@ -489,7 +493,7 @@ fn addModule( m.import_table.values(), @intFromEnum(import_table) + 1 + m.import_table.entries.len.., ) |dep, extra_index| { - // TODO module dependencies can be cyclic + log.err("TODO module dependencies can be cyclic", .{}); wc.extra.items[extra_index] = @intFromEnum(try addModule(wc, module_map, dep)); } @@ -530,7 +534,7 @@ fn addModule( .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target), }))); - std.log.err("TODO serialize the trailing Module data", .{}); + log.err("TODO serialize the trailing Module data", .{}); try module_map.putNoClobber(gpa, m, module_index); @@ -542,7 +546,7 @@ fn addOptionalResolvedTarget( optional_resolved_target: ?std.Build.ResolvedTarget, ) !Configuration.ResolvedTarget.OptionalIndex { const resolved_target = optional_resolved_target orelse return .none; - // TODO dedupe + log.debug("TODO deduplicate resolved targets", .{}); return @enumFromInt(try wc.addExtra(@as(Configuration.ResolvedTarget, .{ .query = try wc.addTargetQuery(resolved_target.query), .result = try wc.addTarget(resolved_target.result), @@ -698,3 +702,30 @@ const Summary = enum { all, new, failures, line, none }; fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { fatal(f ++ "\n access the help menu with \"zig build -h\"", args); } + +fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void { + const gpa = wc.gpa; + + var bad = false; + try wc.system_integrations.ensureTotalCapacityPrecise(gpa, graph.system_integration_options.entries.len); + for (graph.system_integration_options.keys(), graph.system_integration_options.values()) |k, v| { + wc.system_integrations.appendAssumeCapacity(.{ + .name = try wc.addString(k), + .status = switch (v) { + .user_disabled, .user_enabled => x: { + // The user tried to enable or disable a system library integration, but + // the configure script did not recognize that option. + log.err("system integration name not recognized by configure script: {s}", .{k}); + bad = true; + break :x .disabled; + }, + .declared_disabled => .disabled, + .declared_enabled => .enabled, + }, + }); + } + if (bad) { + log.info("access the help menu with \"zig build -h\"", .{}); + process.exit(1); + } +} diff --git a/lib/compiler/maker.zig b/lib/compiler/maker.zig index 44fe7170ab3b6043303880acb05b129c168d63d1..030bbdfe20d9a8bf606cc4631b3c13acfaa20541 100644 --- a/lib/compiler/maker.zig +++ b/lib/compiler/maker.zig @@ -1,21 +1,23 @@ const builtin = @import("builtin"); const std = @import("std"); +const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; +const Configuration = std.Build.Configuration; +const File = std.Io.File; const Io = std.Io; +const Path = std.Build.Cache.Path; +const Writer = std.Io.Writer; const assert = std.debug.assert; +const fatal = std.process.fatal; const fmt = std.fmt; +const log = std.log; const mem = std.mem; const process = std.process; -const File = std.Io.File; -const Allocator = std.mem.Allocator; -const fatal = std.process.fatal; -const Writer = std.Io.Writer; -const Cache = std.Build.Cache; -const Configuration = std.Build.Configuration; const Fuzz = @import("maker/Fuzz.zig"); const Graph = @import("maker/Graph.zig"); -const Step = @import("maker/Step.zig"); +const Step = void; // @import("maker/Step.zig"); const Watch = @import("maker/Watch.zig"); const WebServer = @import("maker/WebServer.zig"); @@ -48,12 +50,12 @@ pub fn main(init: process.Init.Minimal) !void { // skip my own exe name var arg_idx: usize = 1; - const zig_exe = cutArgPrefixOrFatal(args, &arg_idx, "--zig="); - const zig_lib_dir = cutArgPrefixOrFatal(args, &arg_idx, "--lib="); - const build_root = cutArgPrefixOrFatal(args, &arg_idx, "--build-root="); - const local_cache_root = cutArgPrefixOrFatal(args, &arg_idx, "--local-cache="); - const global_cache_root = cutArgPrefixOrFatal(args, &arg_idx, "--global-cache="); - const configure_path = cutArgPrefixOrFatal(args, &arg_idx, "--configure="); + const zig_exe = expectArgOrFatal(args, &arg_idx, "--zig"); + const zig_lib_dir = expectArgOrFatal(args, &arg_idx, "--zig-lib-dir"); + const build_root = expectArgOrFatal(args, &arg_idx, "--build-root"); + const local_cache_root = expectArgOrFatal(args, &arg_idx, "--local-cache"); + const global_cache_root = expectArgOrFatal(args, &arg_idx, "--global-cache"); + const configure_path = expectArgOrFatal(args, &arg_idx, "--configuration"); const cwd: Io.Dir = .cwd(); @@ -90,11 +92,6 @@ pub fn main(init: process.Init.Minimal) !void { .environ_map = try init.environ.createMap(arena), .global_cache_root = global_cache_directory, .zig_lib_directory = zig_lib_directory, - .host = .{ - .query = .{}, - .result = try std.zig.system.resolveTargetQuery(io, .{}), - }, - .time_report = false, }; graph.cache.addPrefix(.{ .path = null, .handle = cwd }); @@ -105,9 +102,13 @@ pub fn main(init: process.Init.Minimal) !void { var targets = std.array_list.Managed([]const u8).init(arena); var debug_log_scopes = std.array_list.Managed([]const u8).init(arena); - - var install_prefix: ?[]const u8 = null; - var dir_list: std.Build.DirList = .{}; + var help_menu = false; + var steps_menu = false; + var print_configuration = false; + var override_install_prefix: ?[]const u8 = null; + var override_lib_dir: ?[]const u8 = null; + var override_bin_dir: ?[]const u8 = null; + var override_include_dir: ?[]const u8 = null; var error_style: ErrorStyle = .verbose; var multiline_errors: MultilineErrors = .indent; var summary: ?Summary = null; @@ -115,8 +116,6 @@ pub fn main(init: process.Init.Minimal) !void { var skip_oom_steps = false; var test_timeout_ns: ?u64 = null; var color: Color = .auto; - var help_menu = false; - var steps_menu = false; var watch = false; var fuzz: ?Fuzz.Mode = null; var debounce_interval_ms: u16 = 50; @@ -152,51 +151,53 @@ pub fn main(init: process.Init.Minimal) !void { } } - var configuration: Configuration = undefined; - { - var file = cwd.openFile(io, configure_path, .{}) catch |err| - fatal("failed to open configuration file {f}: {t}", .{ configure_path, err }); - defer file.close(io); - configuration = Configuration.load(arena, io, file) catch |err| - fatal("failed to load configuration file {f}: {t}", .{ configure_path, err }); - } - graph.configuration = &configuration; - graph.scanConfiguration(); + const scanned_config: ScannedConfig = sc: { + const configuration = c: { + var file = cwd.openFile(io, configure_path, .{}) catch |err| + fatal("failed to open configuration file {s}: {t}", .{ configure_path, err }); + defer file.close(io); + break :c Configuration.loadFile(arena, io, file) catch |err| + fatal("failed to load configuration file {s}: {t}", .{ configure_path, err }); + }; + var top_level_steps: std.ArrayList(Configuration.Step.Index) = .empty; + for (configuration.steps, 0..) |*conf_step, step_index| { + const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]); + if (flags.tag == .top_level) { + try top_level_steps.append(arena, @enumFromInt(step_index)); + } + } + break :sc .{ + .configuration = configuration, + .top_level_steps = top_level_steps.items, + }; + }; - std.log.err("TODO handle user -D options", .{}); + log.err("TODO handle user -D options", .{}); while (nextArg(args, &arg_idx)) |arg| { if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "--verbose")) { - verbose = true; - } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { help_menu = true; - } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { - install_prefix = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { steps_menu = true; - } else if (mem.startsWith(u8, arg, "-fsys=")) { - const name = arg["-fsys=".len..]; - graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM"); - } else if (mem.startsWith(u8, arg, "-fno-sys=")) { - const name = arg["-fno-sys=".len..]; - graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM"); + } else if (mem.eql(u8, arg, "--print-configuration")) { + print_configuration = true; + } else if (mem.eql(u8, arg, "--verbose")) { + verbose = true; + } else if (mem.eql(u8, arg, "-p") or mem.eql(u8, arg, "--prefix")) { + override_install_prefix = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { - dir_list.lib_dir = nextArgOrFatal(args, &arg_idx); + override_lib_dir = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "--prefix-exe-dir")) { - dir_list.exe_dir = nextArgOrFatal(args, &arg_idx); + override_bin_dir = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "--prefix-include-dir")) { - dir_list.include_dir = nextArgOrFatal(args, &arg_idx); + override_include_dir = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "--sysroot")) { sysroot = nextArgOrFatal(args, &arg_idx); } else if (mem.eql(u8, arg, "--maxrss")) { const max_rss_text = nextArgOrFatal(args, &arg_idx); - max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err| { - std.debug.print("invalid byte size: '{s}': {s}\n", .{ - max_rss_text, @errorName(err), - }); - process.exit(1); - }; + max_rss = std.fmt.parseIntSizeSuffix(max_rss_text, 10) catch |err| + fatal("invalid byte size: '{s}': {t}", .{ max_rss_text, err }); } else if (mem.eql(u8, arg, "--skip-oom-steps")) { skip_oom_steps = true; } else if (mem.eql(u8, arg, "--test-timeout")) { @@ -270,9 +271,7 @@ pub fn main(init: process.Init.Minimal) !void { const next_arg = nextArg(args, &arg_idx) orelse fatalWithHint("expected u32 after '{s}'", .{arg}); graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| { - fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{ - next_arg, @errorName(err), - }); + fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {t}", .{ next_arg, err }); }; } else if (mem.eql(u8, arg, "--debounce")) { const next_arg = nextArg(args, &arg_idx) orelse @@ -288,7 +287,7 @@ pub fn main(init: process.Init.Minimal) !void { const addr_str = arg["--webui=".len..]; if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{}); webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| { - fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) }); + fatal("invalid web UI address '{s}': {t}", .{ addr_str, err }); }; } else if (mem.eql(u8, arg, "--debug-log")) { const next_arg = nextArgOrFatal(args, &arg_idx); @@ -300,11 +299,6 @@ pub fn main(init: process.Init.Minimal) !void { } else if (mem.cutPrefix(u8, arg, "--debug-rt=")) |rest| { graph.debug_compiler_runtime_libs = std.meta.stringToEnum(std.builtin.OptimizeMode, rest) orelse fatal("unrecognized optimization mode: {s}", .{rest}); - } else if (mem.eql(u8, arg, "--system")) { - // The usage text shows another argument after this parameter - // but it is handled by the parent process. The build runner - // only sees this flag. - graph.system_package_mode = true; } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) { // --glibc-runtimes was the old name of the flag; kept for compatibility for now. libc_runtimes_dir = nextArgOrFatal(args, &arg_idx); @@ -383,7 +377,7 @@ pub fn main(init: process.Init.Minimal) !void { } else if (mem.startsWith(u8, arg, "-freference-trace=")) { const num = arg["-freference-trace=".len..]; reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { - std.debug.print("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) }); + std.debug.print("unable to parse reference_trace count '{s}': {t}", .{ num, err }); process.exit(1); }; } else if (mem.eql(u8, arg, "-fno-reference-trace")) { @@ -414,6 +408,29 @@ pub fn main(init: process.Init.Minimal) !void { .off => .no_color, }; + if (help_menu) { + var w = initStdoutWriter(io); + scanned_config.printUsage(&graph, w) catch |err| switch (err) { + error.WriteFailed => return stdout_writer_allocation.err.?, + else => |e| return e, + }; + w.flush() catch return stdout_writer_allocation.err.?; + return; + } else if (steps_menu) { + var w = initStdoutWriter(io); + scanned_config.printSteps(&graph, w) catch |err| switch (err) { + error.WriteFailed => return stdout_writer_allocation.err.?, + else => |e| return e, + }; + w.flush() catch return stdout_writer_allocation.err.?; + return; + } else if (print_configuration) { + var w = initStdoutWriter(io); + scanned_config.print(w) catch return stdout_writer_allocation.err.?; + w.flush() catch return stdout_writer_allocation.err.?; + return; + } + if (webui_listen != null) { if (watch) fatal("using '--webui' and '--watch' together is not yet supported; consider omitting '--watch' in favour of the web UI \"Rebuild\" button", .{}); if (builtin.single_threaded) fatal("'--webui' is not yet supported on single-threaded hosts", .{}); @@ -424,27 +441,33 @@ pub fn main(init: process.Init.Minimal) !void { }); defer main_progress_node.end(); - graph.resolveInstallPrefix(install_prefix, dir_list); + const install_prefix_path: Path = if (graph.environ_map.get("DESTDIR")) |dest_dir| .{ + .root_dir = .cwd(), + .sub_path = try Io.Dir.path.join(arena, &.{ dest_dir, override_install_prefix orelse "/usr" }), + } else if (override_install_prefix) |cwd_relative| .{ + .root_dir = .cwd(), + .sub_path = cwd_relative, + } else .{ + .root_dir = build_root_directory, + .sub_path = "zig-out", + }; - if (graph.validateUserInputDidItFail()) { - fatal(" access the help menu with 'zig build -h'", .{}); - } + const install_lib_path: Path = if (override_lib_dir) |cwd_relative| .{ + .root_dir = .cwd(), + .sub_path = cwd_relative, + } else try install_prefix_path.join(arena, "lib"); - validateSystemLibraryOptions(&graph); + const install_bin_path: Path = if (override_bin_dir) |cwd_relative| .{ + .root_dir = .cwd(), + .sub_path = cwd_relative, + } else try install_prefix_path.join(arena, "bin"); - if (help_menu) { - var w = initStdoutWriter(io); - printUsage(&graph, w) catch return stdout_writer_allocation.err.?; - w.flush() catch return stdout_writer_allocation.err.?; - return; - } + const install_include_path: Path = if (override_include_dir) |cwd_relative| .{ + .root_dir = .cwd(), + .sub_path = cwd_relative, + } else try install_prefix_path.join(arena, "include"); - if (steps_menu) { - var w = initStdoutWriter(io); - printSteps(&graph, w) catch return stdout_writer_allocation.err.?; - w.flush() catch return stdout_writer_allocation.err.?; - return; - } + if (true) @panic("TODO"); var run: Run = .{ .gpa = gpa, @@ -463,6 +486,13 @@ pub fn main(init: process.Init.Minimal) !void { .error_style = error_style, .multiline_errors = multiline_errors, .summary = summary orelse if (watch or webui_listen != null) .line else .failures, + + .install_paths = .{ + .prefix = install_prefix_path, + .lib = install_lib_path, + .bin = install_bin_path, + .include = install_include_path, + }, }; defer { run.memory_blocked_steps.deinit(gpa); @@ -628,9 +658,9 @@ fn prepare(graph: *Graph, step_names: []const []const u8, run: *Run) !void { try step_stack.ensureUnusedCapacity(gpa, step_names.len); for (0..step_names.len) |i| { const step_name = step_names[step_names.len - i - 1]; - const s = graph.top_level_steps.get(step_name) orelse { - std.log.info("access the help menu with \"zig build -h\"", .{}); - fatal("no step named '{s}'", .{step_name}); + const s = run.top_level_steps.get(step_name) orelse { + log.info("access the help menu with 'zig build -h'", .{}); + fatal("no such step: {s}", .{step_name}); }; step_stack.putAssumeCapacity(&s.step, {}); } @@ -873,11 +903,11 @@ fn runStepNames( print_node.last = true; printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {}; } else { - const last_index = if (run.summary == .all) graph.top_level_steps.count() else blk: { + const last_index = if (run.summary == .all) run.top_level_steps.count() else blk: { var i: usize = step_names.len; while (i > 0) { i -= 1; - const step = graph.top_level_steps.get(step_names[i]).?.step; + const step = run.top_level_steps.get(step_names[i]).?.step; const found = switch (run.summary) { .all, .line, .none => unreachable, .failures => step.state != .success, @@ -885,10 +915,10 @@ fn runStepNames( }; if (found) break :blk i; } - break :blk graph.top_level_steps.count(); + break :blk run.top_level_steps.count(); }; for (step_names, 0..) |step_name, i| { - const tls = graph.top_level_steps.get(step_name).?; + const tls = run.top_level_steps.get(step_name).?; print_node.last = i + 1 == last_index; printTreeStep(graph, &tls.step, run, t, &print_node, &step_stack_copy) catch {}; } @@ -1207,7 +1237,7 @@ fn constructGraphAndCheckForDependencyLoop( // We dupe to avoid shuffling the steps in the summary, it depends // on s.dependencies' order. - const deps = gpa.dupe(*Step, s.dependencies.items) catch @panic("OOM"); + const deps = try gpa.dupe(*Step, s.dependencies.items); defer gpa.free(deps); rand.shuffle(*Step, deps); @@ -1327,7 +1357,7 @@ fn makeStep( try run.max_rss_mutex.lock(io); defer run.max_rss_mutex.unlock(io); run.available_rss += s.max_rss; - dispatch_set.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len) catch @panic("OOM"); + try dispatch_set.ensureUnusedCapacity(gpa, run.memory_blocked_steps.items.len); while (run.memory_blocked_steps.getLast()) |candidate| { if (run.available_rss < candidate.max_rss) break; assert(run.memory_blocked_steps.pop() == candidate); @@ -1360,7 +1390,7 @@ fn stepReady( defer run.max_rss_mutex.unlock(io); if (run.available_rss < s.max_rss) { // Running this step right now could possibly exceed the allotted RSS. - run.memory_blocked_steps.append(run.gpa, s) catch @panic("OOM"); + try run.memory_blocked_steps.append(run.gpa, s); return; } run.available_rss -= s.max_rss; @@ -1454,178 +1484,6 @@ pub fn printErrorMessages( try writer.writeByte('\n'); } -fn printSteps(graph: *Graph, w: *Writer) !void { - const arena = graph.arena; - for (graph.top_level_steps.values()) |top_level_step| { - const name = if (&top_level_step.step == graph.default_step) - try fmt.allocPrint(arena, "{s} (default)", .{top_level_step.step.name}) - else - top_level_step.step.name; - try w.print(" {s:<28} {s}\n", .{ name, top_level_step.description }); - } -} - -fn printUsage(graph: *Graph, w: *Writer) !void { - const arena = graph.arena; - - try w.print( - \\Usage: {s} build [steps] [options] - \\ - \\Steps: - \\ - , .{graph.zig_exe}); - try printSteps(graph, w); - try w.writeAll( - \\ - \\Project-Specific Options: - \\ - ); - - if (graph.available_options_list.items.len == 0) { - try w.print(" (none)\n", .{}); - } else { - for (graph.available_options_list.items) |option| { - const name = try fmt.allocPrint(arena, " -D{s}=[{t}]", .{ option.name, option.type_id }); - try w.print("{s:<30} {s}\n", .{ name, option.description }); - if (option.enum_options) |enum_options| { - const padding: [33]u8 = @splat(' '); - try w.writeAll(padding ++ "Supported Values:\n"); - for (enum_options) |enum_option| { - try w.print(padding ++ " {s}\n", .{enum_option}); - } - } - } - } - - try w.writeAll( - \\ - \\System Integration Options: - \\ --search-prefix [path] Add a path to look for binaries, libraries, headers - \\ --sysroot [path] Set the system root directory (usually /) - \\ --libc [file] Provide a file which specifies libc paths - \\ - \\ --system [pkgdir] Disable package fetching; enable all integrations - \\ -fsys=[name] Enable a system integration - \\ -fno-sys=[name] Disable a system integration - \\ - \\ -fdarling, -fno-darling Integration with system-installed Darling to - \\ execute macOS programs on Linux hosts - \\ (default: no) - \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute - \\ foreign-architecture programs on Linux hosts - \\ (default: no) - \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc - \\ (e.g. glibc or musl) built for multiple foreign - \\ architectures, allowing execution of non-native - \\ programs that link with libc. - \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on - \\ ARM64 macOS hosts. (default: no) - \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to - \\ execute WASI binaries. (default: no) - \\ -fwine, -fno-wine Integration with system-installed Wine to execute - \\ Windows programs on Linux hosts. (default: no) - \\ - \\ Available System Integrations: Enabled: - \\ - ); - if (graph.system_library_options.entries.len == 0) { - try w.writeAll(" (none) -\n"); - } else { - for (graph.system_library_options.keys(), graph.system_library_options.values()) |k, v| { - const status = switch (v) { - .declared_enabled => "yes", - .declared_disabled => "no", - .user_enabled, .user_disabled => unreachable, // already emitted error - }; - try w.print(" {s:<43} {s}\n", .{ k, status }); - } - } - - try w.writeAll( - \\ - \\General Options: - \\ -h, --help Print this help and exit - \\ -l, --list-steps Print available steps - \\ - \\ -p, --prefix [path] Where to install files (default: zig-out) - \\ --prefix-lib-dir [path] Where to install libraries - \\ --prefix-exe-dir [path] Where to install executables - \\ --prefix-include-dir [path] Where to install C header files - \\ --release[=mode] Request release mode, optionally specifying a - \\ preferred optimization mode: fast, safe, small - \\ - \\ --verbose Print commands before executing them - \\ --color [auto|off|on] Enable or disable colored error messages - \\ --error-style [style] Control how build errors are printed - \\ verbose (Default) Report errors with full context - \\ minimal Report errors after summary, excluding context like command lines - \\ verbose_clear Like 'verbose', but clear the terminal at the start of each update - \\ minimal_clear Like 'minimal', but clear the terminal at the start of each update - \\ --multiline-errors [style] Control how multi-line error messages are printed - \\ indent (Default) Indent non-initial lines to align with initial line - \\ newline Include a leading newline so that the error message is on its own lines - \\ none Print as usual so the first line is misaligned - \\ --summary [mode] Control the printing of the build summary - \\ all Print the build summary in its entirety - \\ new Omit cached steps - \\ failures (Default if short-lived) Only print failed steps - \\ line (Default if long-lived) Only print the single-line summary - \\ none Do not print the build summary - \\ -j Limit concurrent jobs (default is to use all CPU cores) - \\ --maxrss Limit memory usage (default is to use available memory) - \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss - \\ --test-timeout Limit execution time of unit tests, terminating if exceeded. - \\ The timeout must include a unit: ns, us, ms, s, m, h - \\ --watch Continuously rebuild when source files are modified - \\ --debounce Delay before rebuilding after changed file detected - \\ --webui[=ip] Enable the web interface on the given IP address - \\ --fuzz[=limit] Continuously search for unit test failures with an optional - \\ limit to the max number of iterations. The argument supports - \\ an optional 'K', 'M', or 'G' suffix (e.g. '10K'). Implies - \\ '--webui' when no limit is specified. - \\ --time-report Force full rebuild and provide detailed information on - \\ compilation time of Zig source code (implies '--webui') - \\ -fincremental Enable incremental compilation - \\ -fno-incremental Disable incremental compilation - \\ - \\Package Management Options: - \\ --fetch[=mode] Fetch dependency tree (optionally choose laziness) and exit - \\ needed (Default) Lazy dependencies are fetched as needed - \\ all Lazy dependencies are always fetched - \\ --fork=[path] Override one or more projects from dependency tree - \\ - \\Advanced Options: - \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error - \\ -fno-reference-trace Disable reference trace - \\ -fallow-so-scripts Allows .so files to be GNU ld scripts - \\ -fno-allow-so-scripts (default) .so files must be ELF files - \\ --build-file [file] Override path to build.zig - \\ --cache-dir [path] Override path to local Zig cache directory - \\ --global-cache-dir [path] Override path to global Zig cache directory - \\ --zig-lib-dir [arg] Override path to Zig lib directory - \\ --build-runner [file] Override path to build runner - \\ --seed [integer] For shuffling dependency traversal order (default: random) - \\ --build-id[=style] At a minor link-time expense, embeds a build ID in binaries - \\ fast 8-byte non-cryptographic hash (COFF, ELF, WASM) - \\ sha1, tree 20-byte cryptographic hash (ELF, WASM) - \\ md5 16-byte cryptographic hash (ELF) - \\ uuid 16-byte random UUID (ELF, WASM) - \\ 0x[hexstring] Constant ID, maximum 32 bytes (ELF, WASM) - \\ none (default) No build ID - \\ --debug-log [scope] Enable debugging the compiler - \\ --debug-pkg-config Fail if unknown pkg-config flags encountered - \\ --debug-rt Debug compiler runtime libraries - \\ --verbose-link Enable compiler debug output for linking - \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR - \\ --verbose-llvm-bc=[file] Enable compiler debug output for LLVM BC - \\ --verbose-cimport Enable compiler debug output for C imports - \\ --verbose-cc Enable compiler debug output for C compilation - \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features - \\ - ); -} - fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { if (idx.* >= args.len) return null; defer idx.* += 1; @@ -1633,17 +1491,17 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 { } fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 { - return nextArg(args, idx) orelse - fatal("expected argument after {q}\n access the help menu with \"zig build -h\"", .{args[idx.* - 1]}); + return nextArg(args, idx) orelse { + log.info("access the help menu with \"zig build -h\"", .{}); + fatal("expected argument after {q}", .{args[idx.* - 1]}); + }; } -fn cutArgPrefixOrFatal(args: []const [:0]const u8, idx: *usize, prefix: []const u8) []const u8 { - if (nextArg(args, idx)) |next_arg| { - if (mem.cutPrefix(u8, next_arg, prefix)) |arg| { - return arg; - } - } - fatal("expected argument after {q} to start with {q}", .{ args[idx.* - 1], prefix }); +fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 { + const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first}); + if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg }); + const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first}); + return arg; } fn argsRest(args: []const [:0]const u8, idx: usize) ?[]const [:0]const u8 { @@ -1674,35 +1532,8 @@ const MultilineErrors = enum { indent, newline, none }; const Summary = enum { all, new, failures, line, none }; fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn { - std.debug.print(f ++ "\n access the help menu with 'zig build -h'\n", args); - process.exit(1); -} - -fn validateSystemLibraryOptions(graph: *Graph) void { - var bad = false; - for (graph.system_library_options.keys(), graph.system_library_options.values()) |k, v| { - switch (v) { - .user_disabled, .user_enabled => { - // The user tried to enable or disable a system library integration, but - // the build script did not recognize that option. - std.debug.print("system library name not recognized by build script: '{s}'\n", .{k}); - bad = true; - }, - .declared_disabled, .declared_enabled => {}, - } - } - if (bad) { - std.debug.print(" access the help menu with 'zig build -h'\n", .{}); - process.exit(1); - } -} - -var stdio_buffer_allocation: [256]u8 = undefined; -var stdout_writer_allocation: Io.File.Writer = undefined; - -fn initStdoutWriter(io: Io) *Writer { - stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation); - return &stdout_writer_allocation.interface; + log.info("access the help menu with 'zig build -h'", .{}); + fatal(f, args); } fn cleanTmpFiles(io: Io, steps: []const *Step) void { @@ -1711,7 +1542,222 @@ fn cleanTmpFiles(io: Io, steps: []const *Step) void { if (wf.mode != .tmp) continue; const path = wf.generated_directory.path orelse continue; Io.Dir.cwd().deleteTree(io, path) catch |err| { - std.log.warn("failed to delete {s}: {t}", .{ path, err }); + log.warn("failed to delete {s}: {t}", .{ path, err }); }; } } + +const InstallPaths = struct { + prefix: Path, + lib: Path, + bin: Path, + include: Path, +}; + +var stdio_buffer_allocation: [256]u8 = undefined; +var stdout_writer_allocation: Io.File.Writer = undefined; + +fn initStdoutWriter(io: Io) *Writer { + stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation); + return &stdout_writer_allocation.interface; +} + +const ScannedConfig = struct { + configuration: Configuration, + top_level_steps: []const Configuration.Step.Index, + + fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void { + var serializer: std.zon.Serializer = .{ .writer = w }; + var s = try serializer.beginStruct(.{}); + + try s.field("default_step", @intFromEnum(sc.configuration.default_step), .{}); + { + var tuple = try s.beginTupleField("top_level_steps", .{}); + for (sc.top_level_steps) |step| try tuple.field(@intFromEnum(step), .{}); + try tuple.end(); + } + + try s.end(); + } + + fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void { + const arena = graph.arena; + const c = &sc.configuration; + for (sc.top_level_steps) |step_index| { + const step = step_index.ptr(c); + const name = step.name.slice(c); + const decorated_name = if (step_index == c.default_step) + try fmt.allocPrint(arena, "{s} (default)", .{name}) + else + name; + const top_level = c.extraData(Configuration.Step.TopLevel, step.extra_index); + const description = top_level.description.slice(c); + try w.print(" {s:<28} {s}\n", .{ decorated_name, description }); + } + } + + fn printUsage(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void { + const arena = graph.arena; + + try w.print( + \\Usage: {s} build [steps] [options] + \\ + \\Steps: + \\ + , .{graph.zig_exe}); + try printSteps(sc, graph, w); + try w.writeAll( + \\ + \\Project-Specific Options: + \\ + ); + + const available_options = sc.configuration.available_options; + if (available_options.len == 0) { + try w.print(" (none)\n", .{}); + } else { + for (available_options) |option| { + const name = option.name.slice(&sc.configuration); + const description = option.description.slice(&sc.configuration); + const help = try fmt.allocPrint(arena, " -D{s}=[{t}]", .{ name, option.type }); + try w.print("{s:<30} {s}\n", .{ help, description }); + if (option.enum_options.slice(&sc.configuration)) |enum_options| { + const padding: [33]u8 = @splat(' '); + try w.writeAll(padding ++ "Supported Values:\n"); + for (enum_options) |enum_option_index| { + const enum_option = enum_option_index.slice(&sc.configuration); + try w.print(padding ++ " {s}\n", .{enum_option}); + } + } + } + } + + try w.writeAll( + \\ + \\System Integration Options: + \\ --search-prefix [path] Add a path to look for binaries, libraries, headers + \\ --sysroot [path] Set the system root directory (usually /) + \\ --libc [file] Provide a file which specifies libc paths + \\ + \\ --system [pkgdir] Disable package fetching; enable all integrations + \\ -fsys=[name] Enable a system integration + \\ -fno-sys=[name] Disable a system integration + \\ + \\ -fdarling, -fno-darling Integration with system-installed Darling to + \\ execute macOS programs on Linux hosts + \\ (default: no) + \\ -fqemu, -fno-qemu Integration with system-installed QEMU to execute + \\ foreign-architecture programs on Linux hosts + \\ (default: no) + \\ --libc-runtimes [path] Enhances QEMU integration by providing dynamic libc + \\ (e.g. glibc or musl) built for multiple foreign + \\ architectures, allowing execution of non-native + \\ programs that link with libc. + \\ -frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on + \\ ARM64 macOS hosts. (default: no) + \\ -fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to + \\ execute WASI binaries. (default: no) + \\ -fwine, -fno-wine Integration with system-installed Wine to execute + \\ Windows programs on Linux hosts. (default: no) + \\ + \\ Available System Integrations: Enabled: + \\ + ); + if (sc.configuration.system_integrations.len == 0) { + try w.writeAll(" (none) -\n"); + } else { + for (sc.configuration.system_integrations) |system_integration| { + const name = system_integration.name.slice(&sc.configuration); + const status = switch (system_integration.status) { + .disabled => "no", + .enabled => "yes", + }; + try w.print(" {s:<43} {s}\n", .{ name, status }); + } + } + + try w.writeAll( + \\ + \\General Options: + \\ -h, --help Print this help and exit + \\ -l, --list-steps Print available steps + \\ + \\ -p, --prefix [path] Where to install files (default: zig-out) + \\ --prefix-lib-dir [path] Where to install libraries + \\ --prefix-exe-dir [path] Where to install executables + \\ --prefix-include-dir [path] Where to install C header files + \\ --release[=mode] Request release mode, optionally specifying a + \\ preferred optimization mode: fast, safe, small + \\ + \\ --verbose Print commands before executing them + \\ --color [auto|off|on] Enable or disable colored error messages + \\ --error-style [style] Control how build errors are printed + \\ verbose (Default) Report errors with full context + \\ minimal Report errors after summary, excluding context like command lines + \\ verbose_clear Like 'verbose', but clear the terminal at the start of each update + \\ minimal_clear Like 'minimal', but clear the terminal at the start of each update + \\ --multiline-errors [style] Control how multi-line error messages are printed + \\ indent (Default) Indent non-initial lines to align with initial line + \\ newline Include a leading newline so that the error message is on its own lines + \\ none Print as usual so the first line is misaligned + \\ --summary [mode] Control the printing of the build summary + \\ all Print the build summary in its entirety + \\ new Omit cached steps + \\ failures (Default if short-lived) Only print failed steps + \\ line (Default if long-lived) Only print the single-line summary + \\ none Do not print the build summary + \\ -j Limit concurrent jobs (default is to use all CPU cores) + \\ --maxrss Limit memory usage (default is to use available memory) + \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss + \\ --test-timeout Limit execution time of unit tests, terminating if exceeded. + \\ The timeout must include a unit: ns, us, ms, s, m, h + \\ --watch Continuously rebuild when source files are modified + \\ --debounce Delay before rebuilding after changed file detected + \\ --webui[=ip] Enable the web interface on the given IP address + \\ --fuzz[=limit] Continuously search for unit test failures with an optional + \\ limit to the max number of iterations. The argument supports + \\ an optional 'K', 'M', or 'G' suffix (e.g. '10K'). Implies + \\ '--webui' when no limit is specified. + \\ --time-report Force full rebuild and provide detailed information on + \\ compilation time of Zig source code (implies '--webui') + \\ -fincremental Enable incremental compilation + \\ -fno-incremental Disable incremental compilation + \\ + \\Package Management Options: + \\ --fetch[=mode] Fetch dependency tree (optionally choose laziness) and exit + \\ needed (Default) Lazy dependencies are fetched as needed + \\ all Lazy dependencies are always fetched + \\ --fork=[path] Override one or more projects from dependency tree + \\ + \\Advanced Options: + \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error + \\ -fno-reference-trace Disable reference trace + \\ -fallow-so-scripts Allows .so files to be GNU ld scripts + \\ -fno-allow-so-scripts (default) .so files must be ELF files + \\ --build-file [file] Override path to build.zig + \\ --cache-dir [path] Override path to local Zig cache directory + \\ --global-cache-dir [path] Override path to global Zig cache directory + \\ --zig-lib-dir [arg] Override path to Zig lib directory + \\ --build-runner [file] Override path to build runner + \\ --seed [integer] For shuffling dependency traversal order (default: random) + \\ --build-id[=style] At a minor link-time expense, embeds a build ID in binaries + \\ fast 8-byte non-cryptographic hash (COFF, ELF, WASM) + \\ sha1, tree 20-byte cryptographic hash (ELF, WASM) + \\ md5 16-byte cryptographic hash (ELF) + \\ uuid 16-byte random UUID (ELF, WASM) + \\ 0x[hexstring] Constant ID, maximum 32 bytes (ELF, WASM) + \\ none (default) No build ID + \\ --debug-log [scope] Enable debugging the compiler + \\ --debug-pkg-config Fail if unknown pkg-config flags encountered + \\ --debug-rt Debug compiler runtime libraries + \\ --verbose-link Enable compiler debug output for linking + \\ --verbose-air Enable compiler debug output for Zig AIR + \\ --verbose-llvm-ir[=file] Enable compiler debug output for LLVM IR + \\ --verbose-llvm-bc=[file] Enable compiler debug output for LLVM BC + \\ --verbose-cimport Enable compiler debug output for C imports + \\ --verbose-cc Enable compiler debug output for C compilation + \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features + \\ + ); + } +}; diff --git a/lib/compiler/maker/Fuzz.zig b/lib/compiler/maker/Fuzz.zig index a9b01e8111c0bf6034d740461d654f455e557813..b0bcbf8621e05cb5416f4f1240e4960b8f23e76a 100644 --- a/lib/compiler/maker/Fuzz.zig +++ b/lib/compiler/maker/Fuzz.zig @@ -1,17 +1,19 @@ -const std = @import("Std"); +const Fuzz = @This(); + +const std = @import("std"); const Io = std.Io; const Build = std.Build; -const Cache = Build.Cache; +const Cache = std.Build.Cache; const Step = std.Build.Step; const assert = std.debug.assert; const fatal = std.process.fatal; const Allocator = std.mem.Allocator; const log = std.log; const Coverage = std.debug.Coverage; -const abi = Build.abi.fuzz; +const abi = std.Build.abi.fuzz; -const Fuzz = @This(); -const build_runner = @import("root"); +const maker = @import("../maker.zig"); +const WebServer = @import("WebServer.zig"); gpa: Allocator, io: Io, @@ -33,7 +35,7 @@ queue_cond: Io.Condition, msg_queue: std.ArrayList(Msg), pub const Mode = union(enum) { - forever: struct { ws: *Build.WebServer }, + forever: struct { ws: *WebServer }, limit: Limited, pub const Limited = struct { @@ -173,7 +175,7 @@ fn rebuildTestsWorkerRunFallible(run: *Step.Run, gpa: Allocator, parent_prog_nod var buf: [256]u8 = undefined; const stderr = try io.lockStderr(&buf, graph.stderr_mode); defer io.unlockStderr(); - build_runner.printErrorMessages(gpa, &compile.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; + maker.printErrorMessages(gpa, &compile.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; } const rebuilt_bin_path = result catch |err| switch (err) { @@ -196,7 +198,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: *Step.Run) void { error.Canceled => return, }; defer io.unlockStderr(); - build_runner.printErrorMessages(gpa, &run.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; + maker.printErrorMessages(gpa, &run.step, .{}, stderr.terminal(), .verbose, .indent) catch {}; return; }, else => { diff --git a/lib/compiler/maker/Graph.zig b/lib/compiler/maker/Graph.zig index ed5e297b59339ce09271cf7a4ec49b4df4046a97..ba9cad0ee17b713cd1731aff04e22a132d94afdd 100644 --- a/lib/compiler/maker/Graph.zig +++ b/lib/compiler/maker/Graph.zig @@ -6,87 +6,20 @@ const Io = std.Io; const Allocator = std.mem.Allocator; const Configuration = std.Build.Configuration; -const Step = @import("Step.zig"); -const Package = @import("Package.zig"); - io: Io, /// Process lifetime. arena: Allocator, -system_library_options: std.StringArrayHashMapUnmanaged(std.Build.SystemLibraryMode), -system_package_mode: bool, -debug_compiler_runtime_libs: ?std.builtin.OptimizeMode = null, cache: std.Build.Cache, -zig_exe: [:0]const u8, +zig_exe: []const u8, environ_map: std.process.Environ.Map, global_cache_root: std.Build.Cache.Directory, zig_lib_directory: std.Build.Cache.Directory, -incremental: ?bool, -random_seed: u32, -allow_so_scripts: ?bool, -time_report: bool, + +debug_compiler_runtime_libs: ?std.builtin.OptimizeMode = null, +incremental: ?bool = null, +random_seed: u32 = 0, +allow_so_scripts: ?bool = null, +time_report: bool = false, /// Similar to the `Io.Terminal.Mode` returned by `Io.lockStderr`, but also /// respects the '--color' flag. -stderr_mode: ?Io.Terminal.Mode, - -configuration: *const Configuration, -top_level_steps: std.AutoArrayHashMapUnmanaged(Configuration.String, Configuration.Step.Index), - -pub const DirList = struct { - lib_dir: ?[]const u8 = null, - exe_dir: ?[]const u8 = null, - include_dir: ?[]const u8 = null, -}; - -/// This function is intended to be called by lib/build_runner.zig, not a build.zig file. -pub fn resolveInstallPrefix(graph: *Graph, p: *Package, install_prefix: ?[]const u8, dir_list: DirList) !void { - if (p.dest_dir) |dest_dir| { - p.install_prefix = install_prefix orelse "/usr"; - p.install_path = b.pathJoin(&.{ dest_dir, p.install_prefix }); - } else { - p.install_prefix = install_prefix orelse - (p.build_root.join(b.allocator, &.{"zig-out"}) catch @panic("unhandled error")); - b.install_path = b.install_prefix; - } - - var lib_list = [_][]const u8{ b.install_path, "lib" }; - var exe_list = [_][]const u8{ b.install_path, "bin" }; - var h_list = [_][]const u8{ b.install_path, "include" }; - - if (dir_list.lib_dir) |dir| { - if (fs.path.isAbsolute(dir)) lib_list[0] = b.dest_dir orelse ""; - lib_list[1] = dir; - } - - if (dir_list.exe_dir) |dir| { - if (fs.path.isAbsolute(dir)) exe_list[0] = b.dest_dir orelse ""; - exe_list[1] = dir; - } - - if (dir_list.include_dir) |dir| { - if (fs.path.isAbsolute(dir)) h_list[0] = b.dest_dir orelse ""; - h_list[1] = dir; - } - - b.lib_dir = b.pathJoin(&lib_list); - b.exe_dir = b.pathJoin(&exe_list); - b.h_dir = b.pathJoin(&h_list); -} - -fn determineAndApplyInstallPrefix(b: *Build) error{OutOfMemory}!void { - // Create an installation directory local to this package. This will be used when - // dependant packages require a standard prefix, such as include directories for C headers. - var hash = b.graph.cache.hash; - // Random bytes to make unique. Refresh this with new random bytes when - // implementation is modified in a non-backwards-compatible way. - hash.add(@as(u32, 0xd8cb0056)); - hash.addBytes(b.dep_prefix); - - var wyhash = std.hash.Wyhash.init(0); - hashUserInputOptionsMap(b.allocator, b.user_input_options, &wyhash); - hash.add(wyhash.final()); - - const digest = hash.final(); - const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &digest }); - b.resolveInstallPrefix(install_prefix, .{}); -} - +stderr_mode: ?Io.Terminal.Mode = null, diff --git a/lib/compiler/maker/Package.zig b/lib/compiler/maker/Package.zig index e3ad442f69d6fa215b7dde6394211bc9cda283bf..42e3b47d29cf46cae53f592f0e884ef8c931487f 100644 --- a/lib/compiler/maker/Package.zig +++ b/lib/compiler/maker/Package.zig @@ -10,3 +10,21 @@ exe_dir: []const u8, h_dir: []const u8, /// Path to the directory containing build.zig. build_root: std.Build.Cache.Path, + +fn determineAndApplyInstallPrefix(p: *Package) error{OutOfMemory}!void { + // Create an installation directory local to this package. This will be used when + // dependant packages require a standard prefix, such as include directories for C headers. + var hash = p.graph.cache.hash; + // Random bytes to make unique. Refresh this with new random bytes when + // implementation is modified in a non-backwards-compatible way. + hash.add(@as(u32, 0xd8cb0056)); + hash.addBytes(p.dep_prefix); + + var wyhash = std.hash.Wyhash.init(0); + hashUserInputOptionsMap(p.allocator, p.user_input_options, &wyhash); + hash.add(wyhash.final()); + + const digest = hash.final(); + const install_prefix = try p.cache_root.join(p.allocator, &.{ "i", &digest }); + p.resolveInstallPrefix(install_prefix, .{}); +} diff --git a/lib/compiler/maker/Step.zig b/lib/compiler/maker/Step.zig index 295993ec245d79d5edde3444f8ed7aec7e2c6b5b..164510f3dd8d56d5477585c0226fc543940deb42 100644 --- a/lib/compiler/maker/Step.zig +++ b/lib/compiler/maker/Step.zig @@ -848,3 +848,15 @@ pub fn allocPrintCmd( return aw.toOwnedSlice(); } +pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 { + assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix + const base_dir = switch (dir) { + .prefix => b.install_path, + .bin => b.exe_dir, + .lib => b.lib_dir, + .header => b.h_dir, + .custom => |p| b.pathJoin(&.{ b.install_path, p }), + }; + return b.pathResolve(&.{ base_dir, dest_rel_path }); +} + diff --git a/lib/compiler/maker/WebServer.zig b/lib/compiler/maker/WebServer.zig index f860b9f58f965ecdfdf260a40455a71dd518169e..9a81d6e5712c74104d40bfe0d9e7de909477187f 100644 --- a/lib/compiler/maker/WebServer.zig +++ b/lib/compiler/maker/WebServer.zig @@ -1,3 +1,21 @@ +const WebServer = @This(); + +const builtin = @import("builtin"); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Build = std.Build; +const Cache = std.Build.Cache; +const Io = std.Io; +const abi = std.Build.abi; +const assert = std.debug.assert; +const http = std.http; +const log = std.log.scoped(.web_server); +const mem = std.mem; +const net = std.Io.net; + +const Fuzz = @import("Fuzz.zig"); + gpa: Allocator, graph: *const Build.Graph, all_steps: []const *Build.Step, @@ -907,20 +925,3 @@ const cache_control_header: http.Header = .{ .name = "Cache-Control", .value = "max-age=0, must-revalidate", }; - -const builtin = @import("builtin"); - -const std = @import("std"); -const Io = std.Io; -const net = std.Io.net; -const assert = std.debug.assert; -const mem = std.mem; -const log = std.log.scoped(.web_server); -const Allocator = std.mem.Allocator; -const Build = std.Build; -const Cache = Build.Cache; -const Fuzz = Build.Fuzz; -const abi = Build.abi; -const http = std.http; - -const WebServer = @This(); diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 6fe78cb1d6c7ad681379413d204275b1446d6d70..c36e93e8118c67e7f74c314290cb47df09fc5778 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -92,9 +92,8 @@ pub const Graph = struct { io: Io, /// Process lifetime. arena: Allocator, - system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty, + system_integration_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty, system_package_mode: bool = false, - debug_compiler_runtime_libs: ?std.builtin.OptimizeMode = null, cache: Cache, zig_exe: []const u8, environ_map: process.Environ.Map, @@ -108,7 +107,7 @@ pub const Graph = struct { /// Steps should use `io` to limit the number of jobs, however in the case of /// a single step spawning a fixed number of processes this can be used. max_jobs: ?u32 = null, - time_report: bool, + time_report: bool = false, /// Similar to the `Io.Terminal.Mode` returned by `Io.lockStderr`, but also /// respects the '--color' flag. stderr_mode: ?Io.Terminal.Mode = null, @@ -322,11 +321,6 @@ fn createChild( .invalid_user_input = false, .default_step = undefined, .top_level_steps = .{}, - .install_prefix = undefined, - .lib_dir = parent.lib_dir, - .exe_dir = parent.exe_dir, - .h_dir = parent.h_dir, - .install_path = parent.install_path, .sysroot = parent.sysroot, .build_root = build_root, .cache_root = parent.cache_root, @@ -1769,18 +1763,6 @@ pub fn run(b: *Build, argv: []const []const u8) []u8 { ); } -pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 { - assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix - const base_dir = switch (dir) { - .prefix => b.install_path, - .bin => b.exe_dir, - .lib => b.lib_dir, - .header => b.h_dir, - .custom => |p| b.pathJoin(&.{ b.install_path, p }), - }; - return b.pathResolve(&.{ base_dir, dest_rel_path }); -} - pub const Dependency = struct { builder: *Build, @@ -2572,7 +2554,7 @@ pub fn systemIntegrationOption( name: []const u8, config: SystemIntegrationOptionConfig, ) bool { - const gop = b.graph.system_library_options.getOrPut(b.allocator, name) catch @panic("OOM"); + const gop = b.graph.system_integration_options.getOrPut(b.allocator, name) catch @panic("OOM"); if (gop.found_existing) switch (gop.value_ptr.*) { .user_disabled => { gop.value_ptr.* = .declared_disabled; diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig index 0dee682bafe4946209e550e78038c4eed2ccbccd..0c4d45b5f6f6ca166609d69006677484ba5502bf 100644 --- a/lib/std/zig/Configuration.zig +++ b/lib/std/zig/Configuration.zig @@ -11,7 +11,10 @@ steps: []Step, path_deps_base: []Path.Base, path_deps_sub: []String, unlazy_deps: []String, +system_integrations: []SystemIntegration, +available_options: []AvailableOption, extra: []u32, +default_step: Step.Index, /// The field order here matches `Configuration` which documents the order in /// the serialized format. @@ -20,6 +23,8 @@ pub const Header = extern struct { steps_len: u32, path_deps_len: u32, unlazy_deps_len: u32, + system_integrations_len: u32, + available_options_len: u32, extra_len: u32, default_step: Step.Index, @@ -33,6 +38,8 @@ pub const Wip = struct { string_bytes: std.ArrayList(u8) = .empty, unlazy_deps: std.ArrayList(String) = .empty, + system_integrations: std.ArrayList(SystemIntegration) = .empty, + available_options: std.ArrayList(AvailableOption) = .empty, steps: std.ArrayList(Step) = .empty, path_deps: std.MultiArrayList(Path) = .empty, extra: std.ArrayList(u32) = .empty, @@ -107,6 +114,8 @@ pub const Wip = struct { const gpa = wip.gpa; wip.string_bytes.deinit(gpa); wip.unlazy_deps.deinit(gpa); + wip.system_integrations.deinit(gpa); + wip.available_options.deinit(gpa); wip.steps.deinit(gpa); wip.path_deps.deinit(gpa); wip.extra.deinit(gpa); @@ -123,6 +132,8 @@ pub const Wip = struct { .steps_len = @intCast(wip.steps.items.len), .path_deps_len = @intCast(wip.path_deps.len), .unlazy_deps_len = @intCast(wip.unlazy_deps.items.len), + .system_integrations_len = @intCast(wip.system_integrations.items.len), + .available_options_len = @intCast(wip.available_options.items.len), .extra_len = @intCast(wip.extra.items.len), .default_step = static.default_step, @@ -134,6 +145,8 @@ pub const Wip = struct { @ptrCast(wip.path_deps.items(.base)), @ptrCast(wip.path_deps.items(.sub)), @ptrCast(wip.unlazy_deps.items), + @ptrCast(wip.system_integrations.items), + @ptrCast(wip.available_options.items), @ptrCast(wip.extra.items), }; try w.writeVecAll(&buffers); @@ -367,6 +380,37 @@ pub const Wip = struct { } }; +pub const SystemIntegration = extern struct { + name: String, + status: Status, + + pub const Status = enum(u32) { + disabled = 0, + enabled = 1, + }; +}; + +pub const AvailableOption = extern struct { + name: String, + description: String, + type: Type, + /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options + enum_options: OptionalStringList, + + pub const Type = enum(u8) { + bool, + int, + float, + @"enum", + enum_list, + string, + list, + build_id, + lazy_path, + lazy_path_list, + }; +}; + pub const Step = extern struct { name: String, deps: Deps, @@ -375,8 +419,19 @@ pub const Step = extern struct { /// with `Tag`. extra_index: u32, + /// Points into `steps`. pub const Index = enum(u32) { _, + + pub fn ptr(i: Index, c: *const Configuration) *const Step { + return &c.steps[@intFromEnum(i)]; + } + }; + + /// Shared by all steps. + pub const Flags = packed struct(u32) { + tag: Tag, + _: u27 = 0, }; pub const Tag = enum(u5) { @@ -400,7 +455,7 @@ pub const Step = extern struct { }; pub const TopLevel = struct { - flags: Flags = .{}, + flags: @This().Flags = .{}, description: String, pub const Flags = packed struct(u32) { @@ -410,7 +465,7 @@ pub const Step = extern struct { }; pub const InstallArtifact = struct { - flags: Flags, + flags: @This().Flags, dest_dir: InstallDir, dest_sub_path: String, @@ -445,7 +500,7 @@ pub const Step = extern struct { /// * stdio_limit: u64, // if stdio_limit is set /// * producer: Step.Index, // if producer is set. always compile step pub const Run = struct { - flags: Flags, + flags: @This().Flags, file_inputs_len: u32, args_len: u32, cwd: OptionalLazyPath, @@ -554,7 +609,7 @@ pub const Step = extern struct { /// * error_limit if flag is set /// * Hexstring if build_id is hexstring pub const Compile = struct { - flags: Flags, + flags: @This().Flags, flags2: Flags2, flags3: Flags3, flags4: Flags4, @@ -989,12 +1044,26 @@ pub const ImportTable = enum(u32) { _, }; -/// Points into `extra`, where the first element is number of deps, -/// following elements is `Step.Index` per dep. +/// Points into `extra`, where the first element is count of deps, following +/// elements is `Step.Index` per count. pub const Deps = enum(u32) { _, }; +/// Points into `extra`, where the first element is count of strings, following +/// elements is `String` per count. +/// +/// Stored identically to `Deps`. +pub const OptionalStringList = enum(u32) { + none = maxInt(u32), + _, + + pub fn slice(osl: OptionalStringList, c: *const Configuration) ?[]const String { + const len = c.extra[@intFromEnum(osl)]; + return @ptrCast(c.extra[@intFromEnum(osl) + 1 ..][0..len]); + } +}; + pub const Path = extern struct { base: Base, sub: String, @@ -1444,6 +1513,23 @@ pub const TargetQuery = struct { }; }; +pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T { + const extra = c.extra; + var i: usize = index; + var result: T = undefined; + inline for (@typeInfo(T).@"struct".fields) |field| { + comptime assert(@sizeOf(field.type) == @sizeOf(u32)); + @field(result, field.name) = switch (@typeInfo(field.type)) { + .int => extra[i], + .@"enum" => @enumFromInt(extra[i]), + .@"struct" => @bitCast(extra[i]), + else => comptime unreachable, + }; + i += 1; + } + return result; +} + pub const LoadFileError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream}; pub fn loadFile(arena: Allocator, io: Io, file: Io.File) LoadFileError!Configuration { @@ -1465,7 +1551,10 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { .path_deps_sub = try arena.alloc(String, header.path_deps_len), .path_deps_base = try arena.alloc(Path.Base, header.path_deps_len), .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len), + .system_integrations = try arena.alloc(SystemIntegration, header.system_integrations_len), + .available_options = try arena.alloc(AvailableOption, header.available_options_len), .extra = try arena.alloc(u32, header.extra_len), + .default_step = header.default_step, }; var vecs = [_][]u8{ result.string_bytes, @@ -1473,6 +1562,9 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { @ptrCast(result.path_deps_base), @ptrCast(result.path_deps_sub), @ptrCast(result.unlazy_deps), + @ptrCast(result.system_integrations), + @ptrCast(result.available_options), + @ptrCast(result.extra), }; try reader.readVecAll(&vecs); return result; diff --git a/src/Compilation.zig b/src/Compilation.zig index 9e9b66f03bb9c81dab21b305901efaee158fb8b9..870ac3f79893b9301c8daa075276c10237bce75f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3233,9 +3233,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE } // Failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); - }; + man.writeManifest() catch |err| log.warn("failed to write cache manifest: {t}", .{err}); assert(whole.lock == null); whole.lock = man.toOwnedLock(); diff --git a/src/main.zig b/src/main.zig index a0296f8a79e30d3ec7f018f8695873867aba25ce..a24acdc14ec48af7be44fe003704912c12533649 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3682,26 +3682,16 @@ fn buildOutputType( if (t.arch == target.cpu.arch and t.os == target.os.tag) { // If there's a `glibc_min`, there's also an `os_ver`. if (t.glibc_min) |glibc_min| { - std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{ - @tagName(t.arch), - @tagName(t.os), - t.os_ver.?, - @tagName(t.abi), - glibc_min.major, - glibc_min.minor, + std.log.info("zig can provide libc for related target {t}-{t}.{f}-{t}.{d}.{d}", .{ + t.arch, t.os, t.os_ver.?, t.abi, glibc_min.major, glibc_min.minor, }); } else if (t.os_ver) |os_ver| { - std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{ - @tagName(t.arch), - @tagName(t.os), - os_ver, - @tagName(t.abi), + std.log.info("zig can provide libc for related target {t}-{t}.{f}-{t}", .{ + t.arch, t.os, os_ver, t.abi, }); } else { - std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{ - @tagName(t.arch), - @tagName(t.os), - @tagName(t.abi), + std.log.info("zig can provide libc for related target {t}-{t}-{t}", .{ + t.arch, t.os, t.abi, }); } } @@ -3710,7 +3700,7 @@ fn buildOutputType( }, else => fatal("{f}", .{create_diag}), }, - else => fatal("failed to create compilation: {s}", .{@errorName(err)}), + else => fatal("failed to create compilation: {t}", .{err}), }; var comp_destroyed = false; defer if (!comp_destroyed) comp.destroy(); @@ -4984,7 +4974,6 @@ fn cmdBuild( try configure_argv.ensureUnusedCapacity(arena, 16); try make_argv.ensureUnusedCapacity(arena, 16); - const argv_index_exe = configure_argv.items.len; _ = configure_argv.addOneAssumeCapacity(); _ = make_argv.addOneAssumeCapacity(); @@ -5069,9 +5058,7 @@ fn cmdBuild( } else if (mem.cutPrefix(u8, arg, "--fetch=")) |sub_arg| { fetch_only = true; fetch_mode = std.meta.stringToEnum(Package.Fetch.JobQueue.Mode, sub_arg) orelse - fatal("expected [needed|all] after '--fetch=', found '{s}'", .{ - sub_arg, - }); + fatal("expected [needed|all] after '--fetch=', found '{s}'", .{sub_arg}); } else if (mem.cutPrefix(u8, arg, "--fork=")) |sub_arg| { try forks.append(arena, .{ .manifest_ast = undefined, @@ -5093,7 +5080,7 @@ fn cmdBuild( continue; } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| { reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { - fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) }); + fatal("unable to parse reference_trace count '{s}': {t}", .{ num, err }); }; } else if (mem.eql(u8, arg, "-fno-reference-trace")) { reference_trace = null; @@ -5169,7 +5156,8 @@ fn cmdBuild( } else if (mem.eql(u8, arg, "--")) { // The rest of the args are supposed to get passed onto // build runner's `build.args` - try configure_argv.appendSlice(arena, args[i..]); + try configure_argv.append(arena, "--have-run-args"); + try make_argv.appendSlice(arena, args[i..]); break; } } @@ -5273,7 +5261,12 @@ fn cmdBuild( // Kick off an optimized compilation of the make runner. var make_runner_task = io.async(compileMakeRunner, .{ gpa, arena, io, .{ - .dirs = &dirs, + .dirs = .{ + .cwd = dirs.cwd, + .zig_lib = dirs.zig_lib, + .global_cache = dirs.global_cache, + .local_cache = dirs.global_cache, + }, .environ_map = environ_map, .parent_prog_node = root_prog_node, .resolved_target = resolved_target, @@ -5281,6 +5274,7 @@ fn cmdBuild( .thread_limit = thread_limit, .self_exe_path = self_exe_path, .color = color, + .reference_trace = reference_trace, } }); defer _ = make_runner_task.cancel(io) catch {}; @@ -5289,6 +5283,11 @@ fn cmdBuild( configure_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; configure_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; + make_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path; + make_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path; + make_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path; + make_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path; + // Dummy http client that is not actually used when fetch_command is unsupported. // Prevents bootstrap from depending on a bunch of unnecessary stuff. var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct { @@ -5601,7 +5600,7 @@ fn cmdBuild( .sub_path = try std.fmt.allocPrint(arena, "o/{s}/{s}", .{ hex_digest, comp.emit_bin.? }), }; _ = try config_man.addFilePath(exe_path, null); - configure_argv.items[argv_index_exe] = try exe_path.toString(arena); + configure_argv.items[0] = try exe_path.toString(arena); if (try config_man.hit()) { const digest = config_man.final(); @@ -5758,15 +5757,15 @@ fn cmdBuild( }) { .exited => |code| { if (code == 0) return cleanExit(io); - const cmd = try std.mem.join(arena, " ", configure_argv.items); + const cmd = try std.mem.join(arena, " ", make_argv.items); fatal("the following maker command failed with exit code {d}:\n{s}", .{ code, cmd }); }, .signal => |sig| { - const cmd = try std.mem.join(arena, " ", configure_argv.items); + const cmd = try std.mem.join(arena, " ", make_argv.items); fatal("the following maker command terminated with signal {t}:\n{s}", .{ sig, cmd }); }, else => { - const cmd = try std.mem.join(arena, " ", configure_argv.items); + const cmd = try std.mem.join(arena, " ", make_argv.items); fatal("the following maker command crashed:\n{s}", .{cmd}); }, } @@ -5777,13 +5776,14 @@ const MakeRunner = struct { const Options = struct { environ_map: *const process.Environ.Map, - dirs: *Compilation.Directories, + dirs: Compilation.Directories, parent_prog_node: std.Progress.Node, resolved_target: Package.Module.ResolvedTarget, libc_installation: ?*const LibCInstallation, self_exe_path: []const u8, thread_limit: usize, color: Color, + reference_trace: ?u32, }; }; @@ -5798,7 +5798,7 @@ fn compileMakeRunner(gpa: Allocator, arena: Allocator, io: Io, options: MakeRunn const strip = optimize_mode != .Debug; const main_mod_paths: Package.Module.CreateOptions.Paths = .{ - .root = try .fromRoot(arena, options.dirs.*, .zig_lib, "compiler"), + .root = try .fromRoot(arena, options.dirs, .zig_lib, "compiler"), .root_src_path = "maker.zig", }; @@ -5827,7 +5827,7 @@ fn compileMakeRunner(gpa: Allocator, arena: Allocator, io: Io, options: MakeRunn var create_diag: Compilation.CreateDiagnostic = undefined; const comp = Compilation.create(gpa, arena, io, &create_diag, .{ - .dirs = options.dirs.*, + .dirs = options.dirs, .root_name = "maker", .config = config, .root_mod = root_mod, @@ -5837,6 +5837,7 @@ fn compileMakeRunner(gpa: Allocator, arena: Allocator, io: Io, options: MakeRunn .thread_limit = options.thread_limit, .cache_mode = .whole, .environ_map = options.environ_map, + .reference_trace = options.reference_trace, }) catch |err| switch (err) { error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}), error.Canceled => |e| return e, -- 2.54.0