diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 15c2799185bb5610206e2dfda5ab61ee215898da..8101449cb4ea5f1d2442ab1bdcf0c5c36dbbbeb3 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -419,7 +419,7 @@ pub fn main(init: process.Init.Minimal) !void { var top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index) = .empty; for (configuration.steps, 0..) |*conf_step, step_index_usize| { const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize); - const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]); + const flags = conf_step.flags(&configuration); if (flags.tag == .top_level) { const name = step_index.ptr(&configuration).name.slice(&configuration); try top_level_steps.put(arena, name, step_index); @@ -538,7 +538,7 @@ pub fn main(init: process.Init.Minimal) !void { var w: Watch = w: { if (!watch) break :w undefined; if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag}); - break :w try .init(graph.cache.cwd, &scanned_config.configuration, maker.steps); + break :w try .init(&maker); }; const now = Io.Clock.Timestamp.now(io, .awake); @@ -546,14 +546,10 @@ pub fn main(init: process.Init.Minimal) !void { maker.web_server = if (webui_listen) |listen_address| ws: { if (builtin.single_threaded) unreachable; // `fatal` above break :ws .init(.{ - .gpa = gpa, - .graph = &graph, - .all_steps = maker.step_stack.keys(), + .maker = &maker, .root_prog_node = main_progress_node, - .watch = watch, .listen_address = listen_address, .base_timestamp = now, - .configuration = &scanned_config.configuration, }); } else null; @@ -564,7 +560,9 @@ pub fn main(init: process.Init.Minimal) !void { rebuild: while (true) : (if (maker.error_style.clearOnUpdate()) { const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); defer io.unlockStderr(); - try stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H"); + stderr.file_writer.interface.writeAll("\x1B[2J\x1B[3J\x1B[H") catch |err| switch (err) { + error.WriteFailed => return stderr.file_writer.err.?, + }; }) { if (maker.web_server) |*ws| ws.startBuild(); @@ -608,15 +606,15 @@ pub fn main(init: process.Init.Minimal) !void { // recursive dependants. var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined; const caption = std.fmt.bufPrint(&caption_buf, "watching {d} directories, {d} processes", .{ - w.dir_count, countSubProcesses(maker.steps, maker.step_stack.keys()), + w.dir_count, countSubProcesses(&maker), }) catch &caption_buf; var debouncing_node = main_progress_node.start(caption, 0); var in_debounce = false; - while (true) switch (try w.wait(gpa, io, if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) { + while (true) switch (try w.wait(if (in_debounce) .{ .ms = debounce_interval_ms } else .none)) { .timeout => { assert(in_debounce); debouncing_node.end(); - markFailedStepsDirty(gpa, maker.steps, maker.step_stack.keys()); + markFailedStepsDirty(&maker); continue :rebuild; }, .dirty => if (!in_debounce) { @@ -629,18 +627,20 @@ pub fn main(init: process.Init.Minimal) !void { } } -fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const Configuration.Step.Index) void { +fn markFailedStepsDirty(maker: *Maker) void { + const all_steps = maker.step_stack.keys(); + for (all_steps) |step_index| { - const step = &make_steps[@intFromEnum(step_index)]; + const step = maker.stepByIndex(step_index); switch (step.state) { - .dependency_failure, .failure, .skipped => _ = step.invalidateResult(gpa), + .dependency_failure, .failure, .skipped => _ = maker.invalidateResult(step), else => continue, } } // Now that all dirty steps have been found, the remaining steps that // succeeded from last run shall be marked "cached". for (all_steps) |step_index| { - const step = &make_steps[@intFromEnum(step_index)]; + const step = maker.stepByIndex(step_index); switch (step.state) { .success => step.result_cached = true, else => continue, @@ -648,10 +648,11 @@ fn markFailedStepsDirty(gpa: Allocator, make_steps: []Step, all_steps: []const C } } -fn countSubProcesses(make_steps: []Step, all_steps: []const Configuration.Step.Index) usize { +fn countSubProcesses(maker: *Maker) usize { + const all_steps = maker.step_stack.keys(); var count: usize = 0; for (all_steps) |step_index| { - const s = &make_steps[@intFromEnum(step_index)]; + const s = maker.stepByIndex(step_index); count += @intFromBool(s.getZigProcess() != null); } return count; @@ -664,7 +665,7 @@ const InstallPaths = struct { include: Path, }; -fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step { +pub fn stepByIndex(maker: *const Maker, i: Configuration.Step.Index) *Step { return &maker.steps[@intFromEnum(i)]; } @@ -676,7 +677,10 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { const step_stack = &maker.step_stack; const c = &maker.scanned_config.configuration; - @memset(maker.steps, .{}); + for (maker.steps, 0..) |*step, step_index_usize| { + const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize); + step.* = .{ .extended = .init(step_index.ptr(c).flags(c).tag) }; + } if (step_names.len == 0) { try step_stack.put(gpa, c.default_step, {}); @@ -699,7 +703,7 @@ fn prepare(maker: *Maker, step_names: []const []const u8) !void { rand.shuffle(Configuration.Step.Index, starting_steps); for (starting_steps) |s| { - try constructGraphAndCheckForDependencyLoop(gpa, c, maker.steps, s, &maker.step_stack, rand); + try constructGraphAndCheckForDependencyLoop(maker, s, &maker.step_stack, rand); } { @@ -847,13 +851,8 @@ fn makeStepNames( } assert(mode == .limit); - var f = Fuzz.init( - gpa, - io, - step_stack.keys(), - parent_prog_node, - mode, - ) catch |err| fatal("failed to start fuzzer: {t}", .{err}); + var f = Fuzz.init(maker, step_stack.keys(), parent_prog_node, mode) catch |err| + fatal("failed to start fuzzer: {t}", .{err}); defer f.deinit(); f.start(); @@ -1048,13 +1047,7 @@ fn makeStep( .success, .skipped => {}, } - } else if (make_step.make(.{ - .progress_node = step_prog_node, - .watch = maker.watch, - .web_server = if (maker.web_server) |*ws| ws else null, - .unit_test_timeout_ns = maker.unit_test_timeout_ns, - .gpa = gpa, - })) state: { + } else if (Step.make(step_index, maker, step_prog_node)) state: { break :state .success; } else |err| switch (err) { error.MakeFailed => .failure, @@ -1091,7 +1084,7 @@ fn makeStep( { const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode); defer io.unlockStderr(); - printErrorMessages(gpa, c, maker.steps, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) { + printErrorMessages(maker, step_index, .{}, stderr.terminal(), maker.error_style, maker.multiline_errors) catch |err| switch (err) { error.Canceled => |e| return e, error.WriteFailed => switch (stderr.file_writer.err.?) { error.Canceled => |e| return e, @@ -1136,7 +1129,7 @@ fn makeStep( } fn printTreeStep( - maker: *const Maker, + maker: *Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal, parent_node: *PrintNode, @@ -1211,7 +1204,7 @@ fn printTreeStep( } } -fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal) !void { +fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal) !void { const s = maker.stepByIndex(step_index); const writer = stderr.writer; switch (s.state) { @@ -1293,20 +1286,20 @@ fn printStepStatus(maker: *const Maker, step_index: Configuration.Step.Index, st try stderr.setColor(.reset); }, .failure => { - try printStepFailure(maker.steps, step_index, stderr, false); + try printStepFailure(maker, step_index, stderr, false); try stderr.setColor(.reset); }, } } fn printStepFailure( - make_steps: []Step, + maker: *Maker, step_index: Configuration.Step.Index, stderr: Io.Terminal, dim: bool, ) !void { const w = stderr.writer; - const s = &make_steps[@intFromEnum(step_index)]; + const s = maker.stepByIndex(step_index); if (s.result_error_bundle.errorMessageCount() > 0) { try stderr.setColor(.red); try w.print(" {d} errors\n", .{ @@ -1428,14 +1421,14 @@ fn printChildNodePrefix(stderr: Io.Terminal) !void { /// when it finishes executing in `makeStep`, it spawns next steps to run in /// random order fn constructGraphAndCheckForDependencyLoop( - gpa: Allocator, - c: *const Configuration, - steps: []Step, + maker: *Maker, step_index: Configuration.Step.Index, step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void), rand: std.Random, ) error{ DependencyLoopDetected, OutOfMemory }!void { - const make_step: *Step = &steps[@intFromEnum(step_index)]; + const c = &maker.scanned_config.configuration; + const gpa = maker.gpa; + const make_step = maker.stepByIndex(step_index); switch (make_step.state) { .precheck_started => { log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)}); @@ -1456,10 +1449,10 @@ fn constructGraphAndCheckForDependencyLoop( rand.shuffle(Configuration.Step.Index, deps); for (deps) |dep| { - const dep_step: *Step = &steps[@intFromEnum(dep)]; + const dep_step = maker.stepByIndex(dep); try step_stack.put(gpa, dep, {}); try dep_step.dependants.append(gpa, step_index); - constructGraphAndCheckForDependencyLoop(gpa, c, steps, dep, step_stack, rand) catch |err| switch (err) { + constructGraphAndCheckForDependencyLoop(maker, dep, step_stack, rand) catch |err| switch (err) { error.DependencyLoopDetected => { log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)}); return err; @@ -1482,16 +1475,34 @@ fn constructGraphAndCheckForDependencyLoop( } } +/// When file watching, prepares the step for being re-evaluated. Returns +/// `true` if the step was newly invalidated, `false` if it was already +/// invalidated. +pub fn invalidateResult(maker: *Maker, step: *Step) bool { + if (step.state == .precheck_done) return false; + const gpa = maker.gpa; + assert(step.pending_deps == 0); + step.state = .precheck_done; + step.reset(gpa); + for (step.dependants.items) |dependant_index| { + const dependant = maker.stepByIndex(dependant_index); + _ = invalidateResult(maker, dependant); + dependant.pending_deps += 1; + } + return true; +} + pub fn printErrorMessages( - gpa: Allocator, - c: *const Configuration, - make_steps: []Step, + maker: *Maker, failing_step_index: Configuration.Step.Index, options: std.zig.ErrorBundle.RenderOptions, stderr: Io.Terminal, error_style: ErrorStyle, multiline_errors: MultilineErrors, ) !void { + const c = &maker.scanned_config.configuration; + const gpa = maker.gpa; + log.err("TODO also report if result_oom flag is set", .{}); const writer = stderr.writer; if (error_style.verboseContext()) { // Provide context for where these error messages are coming from by @@ -1500,7 +1511,7 @@ pub fn printErrorMessages( defer step_stack.deinit(gpa); try step_stack.append(gpa, failing_step_index); while (true) { - const last_step = &make_steps[@intFromEnum(step_stack.items[step_stack.items.len - 1])]; + const last_step = maker.stepByIndex(step_stack.items[step_stack.items.len - 1]); if (last_step.dependants.items.len == 0) break; try step_stack.append(gpa, last_step.dependants.items[0]); } @@ -1517,7 +1528,7 @@ pub fn printErrorMessages( try writer.writeAll(step_index.ptr(c).name.slice(c)); if (step_index == failing_step_index) { - try printStepFailure(make_steps, step_index, stderr, true); + try printStepFailure(maker, step_index, stderr, true); } else { try writer.writeAll("\n"); } @@ -1527,11 +1538,11 @@ pub fn printErrorMessages( // Just print the failing step itself. try stderr.setColor(.dim); try writer.writeAll(failing_step_index.ptr(c).name.slice(c)); - try printStepFailure(make_steps, failing_step_index, stderr, true); + try printStepFailure(maker, failing_step_index, stderr, true); try stderr.setColor(.reset); } - const failing_step = &make_steps[@intFromEnum(failing_step_index)]; + const failing_step = maker.stepByIndex(failing_step_index); if (failing_step.result_stderr.len > 0) { try writer.writeAll(failing_step.result_stderr); diff --git a/lib/compiler/Maker/Fuzz.zig b/lib/compiler/Maker/Fuzz.zig index d3066d24afc31632e211e2883843adb497c83de7..77fab2b2df6dfe80bb4c4ae4dea33871992782fd 100644 --- a/lib/compiler/Maker/Fuzz.zig +++ b/lib/compiler/Maker/Fuzz.zig @@ -15,8 +15,7 @@ const log = std.log; const Maker = @import("../Maker.zig"); const WebServer = @import("WebServer.zig"); -gpa: Allocator, -io: Io, +maker: *Maker, mode: Mode, /// Allocated into `gpa`. @@ -76,12 +75,15 @@ const CoverageMap = struct { }; pub fn init( - gpa: Allocator, - io: Io, + maker: *Maker, all_steps: []const Configuration.Step.Index, root_prog_node: std.Progress.Node, mode: Mode, ) error{ OutOfMemory, Canceled }!Fuzz { + const graph = maker.graph; + const gpa = graph.cache.gpa; + const io = graph.io; + const run_steps: []const Configuration.Step.Index = steps: { var steps: std.ArrayList(Configuration.Step.Index) = .empty; defer steps.deinit(gpa); @@ -115,8 +117,7 @@ pub fn init( } return .{ - .gpa = gpa, - .io = io, + .maker = maker, .mode = mode, .run_steps = run_steps, .group = .init, @@ -131,7 +132,10 @@ pub fn init( } pub fn start(fuzz: *Fuzz) void { - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; + fuzz.prog_node = fuzz.root_prog_node.start("Fuzzing", 0); if (fuzz.mode == .forever) { @@ -149,10 +153,14 @@ pub fn start(fuzz: *Fuzz) void { } pub fn deinit(fuzz: *Fuzz) void { - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; + const gpa = maker.gpa; + fuzz.group.cancel(io); fuzz.prog_node.end(); - fuzz.gpa.free(fuzz.run_steps); + gpa.free(fuzz.run_steps); } fn rebuildTestsWorkerRun(run: Configuration.Step.Index, gpa: Allocator, parent_prog_node: std.Progress.Node) void { @@ -215,19 +223,20 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void { pub fn serveSourcesTar(fuzz: *Fuzz, req: *std.http.Server.Request) !void { if (true) @panic("TODO"); assert(fuzz.mode == .forever); + const gpa = fuzz.maker.gpa; - var arena_state: std.heap.ArenaAllocator = .init(fuzz.gpa); + var arena_state: std.heap.ArenaAllocator = .init(gpa); defer arena_state.deinit(); const arena = arena_state.allocator(); const DedupTable = std.ArrayHashMapUnmanaged(Build.Cache.Path, void, Build.Cache.Path.TableAdapter, false); var dedup_table: DedupTable = .empty; - defer dedup_table.deinit(fuzz.gpa); + defer dedup_table.deinit(gpa); for (fuzz.run_steps) |run_step| { const compile_inputs = run_step.producer.?.step.inputs.table; for (compile_inputs.keys(), compile_inputs.values()) |dir_path, *file_list| { - try dedup_table.ensureUnusedCapacity(fuzz.gpa, file_list.items.len); + try dedup_table.ensureUnusedCapacity(gpa, file_list.items.len); for (file_list.items) |sub_path| { if (!std.mem.endsWith(u8, sub_path, ".zig")) continue; const joined_path = try dir_path.join(arena, sub_path); @@ -266,7 +275,9 @@ pub fn sendUpdate( socket: *std.http.Server.WebSocket, prev: *Previous, ) !void { - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; try fuzz.coverage_mutex.lock(io); defer fuzz.coverage_mutex.unlock(io); @@ -337,7 +348,9 @@ fn coverageRun(fuzz: *Fuzz) void { } fn coverageRunCancelable(fuzz: *Fuzz) Io.Cancelable!void { - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; try fuzz.queue_mutex.lock(io); defer fuzz.queue_mutex.unlock(io); @@ -363,8 +376,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage if (true) @panic("TODO"); assert(fuzz.mode == .forever); const ws = fuzz.mode.forever.ws; - const gpa = fuzz.gpa; - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; + const gpa = maker.gpa; try fuzz.coverage_mutex.lock(io); defer fuzz.coverage_mutex.unlock(io); @@ -470,7 +485,10 @@ fn prepareTables(fuzz: *Fuzz, run_step_index: Configuration.Step.Index, coverage } fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReported, OutOfMemory, Canceled }!void { - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; + const gpa = maker.gpa; try fuzz.coverage_mutex.lock(io); defer fuzz.coverage_mutex.unlock(io); @@ -516,13 +534,15 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte }); } } - try coverage_map.entry_points.append(fuzz.gpa, @intCast(index)); + try coverage_map.entry_points.append(gpa, @intCast(index)); } pub fn waitAndPrintReport(fuzz: *Fuzz) Io.Cancelable!void { if (true) @panic("TODO"); assert(fuzz.mode == .limit); - const io = fuzz.io; + const maker = fuzz.maker; + const graph = maker.graph; + const io = graph.io; try fuzz.group.await(io); fuzz.group = .init; diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 845bc1e1f8ecb4191d945a5c3f86b390adc7290e..c43b502bc5e17b1dace1236ac5f5a8426fa72217 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -1,4 +1,5 @@ -//! The state that maker needs in order to process a step. +//! The *mutable* state that `Maker` needs in order to process one node from +//! the build graph. const Step = @This(); const builtin = @import("builtin"); @@ -14,14 +15,20 @@ const Configuration = std.Build.Configuration; const assert = std.debug.assert; const WebServer = @import("WebServer.zig"); +const Maker = @import("../Maker.zig"); -pub const Compile = void; // @import("Step/Compile.zig"); -pub const Run = void; // @import("Step/Run.zig"); +const Compile = @import("Step/Compile.zig"); +const Run = @import("Step/Run.zig"); /// Avoid false sharing. _: void align(std.atomic.cache_line) = {}, +/// Extra data for specific types of steps. +extended: Extended, + +/// This field is atomically accessed multi-threaded. state: State = .precheck_unstarted, + dependants: std.ArrayList(Configuration.Step.Index) = .empty, /// Collects the set of files that retrigger this step to run. /// @@ -38,6 +45,8 @@ result_error_msgs: std.ArrayList([]const u8) = .empty, result_error_bundle: std.zig.ErrorBundle = .empty, result_stderr: []const u8 = "", result_cached: bool = false, +/// Indicates error information is missing due to allocation failure. +result_oom: bool = false, result_duration_ns: ?u64 = null, /// 0 means unavailable or not reported. result_peak_rss: usize = 0, @@ -46,6 +55,70 @@ result_peak_rss: usize = 0, result_failed_command: ?[]const u8 = null, test_results: TestResults = .{}, +comptime { + // Common cache line size is 128. This check prevents accidentally crossing + // an additional cache line. In the future it might be nice to try to fit + // this struct in 128 bytes or less. + assert(@sizeOf(@This()) <= 128 * 3); +} + +pub const Extended = union(enum) { + check_file: Todo, + check_object: Todo, + compile: Compile, + config_header: Todo, + fail: Todo, + fmt: Todo, + install_artifact: Todo, + install_dir: Todo, + install_file: Todo, + objcopy: Todo, + options: Todo, + remove_dir: Todo, + run: Run, + top_level: Todo, + translate_c: Todo, + update_source_files: Todo, + write_file: Todo, + + pub fn init(tag: Configuration.Step.Tag) Extended { + return switch (tag) { + .check_file => .{ .check_file = .{} }, + .check_object => .{ .check_object = .{} }, + .compile => .{ .compile = .{} }, + .config_header => .{ .config_header = .{} }, + .fail => .{ .fail = .{} }, + .fmt => .{ .fmt = .{} }, + .install_artifact => .{ .install_artifact = .{} }, + .install_dir => .{ .install_dir = .{} }, + .install_file => .{ .install_file = .{} }, + .objcopy => .{ .objcopy = .{} }, + .options => .{ .options = .{} }, + .remove_dir => .{ .remove_dir = .{} }, + .run => .{ .run = .{} }, + .top_level => .{ .top_level = .{} }, + .translate_c => .{ .translate_c = .{} }, + .update_source_files => .{ .update_source_files = .{} }, + .write_file => .{ .write_file = .{} }, + }; + } + + pub const Todo = struct { + pub fn make( + todo: *Todo, + step_index: Configuration.Step.Index, + maker: *Maker, + progress_node: std.Progress.Node, + ) Step.ExtendedMakeError!void { + _ = todo; + _ = step_index; + _ = maker; + _ = progress_node; + @panic("TODO implement another step type"); + } + }; +}; + pub const State = enum { precheck_unstarted, precheck_started, @@ -128,43 +201,51 @@ pub const TestResults = struct { } }; -pub const MakeOptions = struct { - progress_node: std.Progress.Node, - watch: bool, - web_server: ?*WebServer, - /// If set, this is a timeout to enforce on all individual unit tests, in nanoseconds. - unit_test_timeout_ns: ?u64, - /// Not to be confused with `Build.allocator`, which is an alias of `Build.graph.arena`. - gpa: Allocator, +pub const MakeError = error{ + /// Indicates the error is already reported. + MakeFailed, + MakeSkipped, }; -pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void; +pub const ExtendedMakeError = MakeError || Allocator.Error; -/// If the Step's `make` function reports `error.MakeFailed`, it indicates they -/// have already reported the error. Otherwise, we add a simple error report -/// here. -pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!void { - if (true) @panic("TODO Step.make"); - const arena = s.owner.allocator; - const graph = s.owner.graph; +pub fn make( + step_index: Configuration.Step.Index, + maker: *Maker, + progress_node: std.Progress.Node, +) MakeError!void { + const graph = maker.graph; + const process_arena = graph.arena; // TODO don't leak into the process arena const io = graph.io; + const c = &maker.scanned_config.configuration; + const conf_step = step_index.ptr(c); + const s = maker.stepByIndex(step_index); var start_ts: ?Io.Timestamp = t: { if (!graph.time_report) break :t null; - if (s.id == .compile) break :t null; - if (s.id == .run and s.cast(Run).?.stdio == .zig_test) break :t null; + const flags = conf_step.flags(c); + switch (flags.tag) { + .compile => break :t null, + .run => { + const run_flags: Configuration.Step.Run.Flags = @bitCast(flags); + if (run_flags.stdio == .zig_test) break :t null; + }, + else => {}, + } break :t Io.Clock.awake.now(io); }; - const make_result = s.makeFn(s, options); + const make_result = switch (s.extended) { + inline else => |*extended| extended.make(step_index, maker, progress_node), + }; if (start_ts) |*ts| { const duration = ts.untilNow(io, .awake); - options.web_server.?.updateTimeReportGeneric(s, duration); + maker.web_server.?.updateTimeReportGeneric(step_index, duration); } make_result catch |err| switch (err) { error.MakeFailed, error.MakeSkipped => |e| return e, - else => { - s.result_error_msgs.append(arena, @errorName(err)) catch @panic("OOM"); + error.OutOfMemory => { + s.result_oom = true; return error.MakeFailed; }, }; @@ -173,30 +254,19 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi return error.MakeFailed; } - if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { - const msg = std.fmt.allocPrint(arena, "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)", .{ - s.result_peak_rss, s.max_rss, - }) catch @panic("OOM"); - s.result_error_msgs.append(arena, msg) catch @panic("OOM"); + const max_rss = conf_step.max_rss.toBytes(); + if (max_rss != 0 and s.result_peak_rss > max_rss) { + if (std.fmt.allocPrint( + process_arena, + "memory usage peaked at {0B:.2} ({0d} bytes), exceeding the declared upper bound of {1B:.2} ({1d} bytes)", + .{ s.result_peak_rss, max_rss }, + )) |msg| { + s.oomWrap(s.result_error_msgs.append(process_arena, msg)); + } else |_| s.result_oom = true; } } -/// Implementation detail of file watching. Prepares the step for being re-evaluated. -/// Returns `true` if the step was newly invalidated, `false` if it was already invalidated. -pub fn invalidateResult(step: *Step, gpa: Allocator) bool { - if (true) @panic("TODO Step.invalidateResult"); - if (step.state == .precheck_done) return false; - assert(step.pending_deps == 0); - step.state = .precheck_done; - step.reset(gpa); - for (step.dependants.items) |dependant| { - _ = dependant.invalidateResult(gpa); - dependant.pending_deps += 1; - } - return true; -} - -/// Implementation detail of file watching and forced rebuilds. Prepares the step for being re-evaluated. +/// Prepares the step for being re-evaluated. pub fn reset(step: *Step, gpa: Allocator) void { assert(step.state == .precheck_done); @@ -547,9 +617,8 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool, web_server: ?*WebSer } pub fn getZigProcess(s: *Step) ?*ZigProcess { - if (true) @panic("TODO getZigProcess"); - return switch (s.id) { - .compile => s.cast(Compile).?.zig_process, + return switch (s.extended) { + .compile => |*compile| compile.zig_process, else => null, }; } @@ -838,3 +907,9 @@ pub fn allocPrintCmd( } return aw.toOwnedSlice(); } + +fn oomWrap(s: *Step, result: error{OutOfMemory}!void) void { + result catch { + s.result_oom = true; + }; +} diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig index 5ffbc23cfc15d6d6de5df4f4585585364052da6d..5ef2ec9dacc021e2287af597107de953b88cb846 100644 --- a/lib/compiler/Maker/Step/Compile.zig +++ b/lib/compiler/Maker/Step/Compile.zig @@ -1,19 +1,40 @@ +const Compile = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Configuration = std.Build.Configuration; +const Dir = std.Io.Dir; +const Path = std.Build.Cache.Path; +const Module = std.Build.Configuration.Module; +const Io = std.Io; +const Sha256 = std.crypto.hash.sha2.Sha256; +const assert = std.debug.assert; +const mem = std.mem; + +const Step = @import("../Step.zig"); +const Maker = @import("../../Maker.zig"); + /// Populated during the make phase when there is a long-lived compiler process. /// Managed by the build runner, not user build script. -zig_process: ?*Step.ZigProcess, +zig_process: ?*Step.ZigProcess = null, -fn make(step: *Step, options: Step.MakeOptions) !void { - const b = step.owner; - const compile: *Compile = @fieldParentPtr("step", step); - - const zig_args = try getZigArgs(compile, false); +pub fn make( + compile: *Compile, + step_index: Configuration.Step.Index, + maker: *Maker, + progress_node: std.Progress.Node, +) Step.ExtendedMakeError!void { + if (true) @panic("TODO implement compile.make()"); + const graph = maker.graph; + const step = maker.stepByIndex(step_index); + const zig_args = try getZigArgs(compile, maker, false); + const process_arena = graph.arena; // TODO don't leak into the process_arena const maybe_output_dir = step.evalZigProcess( zig_args, - options.progress_node, - (b.graph.incremental == true) and (options.watch or options.web_server != null), - options.web_server, - options.gpa, + progress_node, + (graph.incremental == true) and (maker.watch or maker.web_server != null), + maker, ) catch |err| switch (err) { error.NeedCompileErrorCheck => { assert(compile.expect_errors != null); @@ -26,7 +47,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { // Update generated files if (maybe_output_dir) |output_dir| { if (compile.emit_directory) |lp| { - lp.path = b.fmt("{f}", .{output_dir}); + lp.path = try std.fmt.allocPrint(process_arena, "{f}", .{output_dir}); } // zig fmt: off @@ -49,22 +70,23 @@ fn make(step: *Step, options: Step.MakeOptions) !void { { try doAtomicSymLinks( step, - compile.getEmittedBin().getPath2(b, step), + compile.getEmittedBin().getPath2(step.owner, step), compile.major_only_filename.?, compile.name_only_filename.?, ); } } -fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { +fn getZigArgs(compile: *Compile, maker: *Maker, fuzz: bool) ![][]const u8 { const step = &compile.step; const b = step.owner; - const arena = b.allocator; + const graph = maker.graph; + const arena = graph.arena; // TODO don't leak into the process arena var zig_args = std.array_list.Managed([]const u8).init(arena); defer zig_args.deinit(); - try zig_args.append(b.graph.zig_exe); + try zig_args.append(graph.zig_exe); const cmd = switch (compile.kind) { .lib => "build-lib", @@ -78,7 +100,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { if (b.reference_trace) |some| { try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some})); } - try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts); + try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse graph.allow_so_scripts); try addFlag(&zig_args, "llvm", compile.use_llvm); try addFlag(&zig_args, "lld", compile.use_lld); @@ -118,7 +140,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { // module, along with any arguments that need to be passed to the // compiler for each module individually. var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty; - var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .empty; + var frameworks: std.StringArrayHashMapUnmanaged(Module.FrameworkFlags) = .empty; var prev_has_cflags = false; var prev_has_rcflags = false; @@ -130,7 +152,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { // Fully recursive iteration including dynamic libraries to detect // libc and libc++ linkage. - for (compile.getCompileDependencies(true)) |some_compile| { + for (getCompileDependencies(true)) |some_compile| { for (some_compile.root_module.getGraph().modules) |mod| { if (mod.link_libc == true) compile.is_linking_libc = true; if (mod.link_libcpp == true) compile.is_linking_libcpp = true; @@ -141,7 +163,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { // For this loop, don't chase dynamic libraries because their link // objects are already linked. - for (compile.getCompileDependencies(false)) |dep_compile| { + for (getCompileDependencies(false)) |dep_compile| { for (dep_compile.root_module.getGraph().modules) |mod| { // While walking transitive dependencies, if a given link object is // already included in a library, it should not redundantly be @@ -207,7 +229,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { switch (system_lib.use_pkg_config) { .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), .yes, .force => { - if (compile.runPkgConfig(system_lib.name)) |result| { + if (compile.runPkgConfig(maker, system_lib.name)) |result| { try zig_args.appendSlice(result.cflags); try zig_args.appendSlice(result.libs); try seen_system_libs.put(arena, system_lib.name, result.cflags); @@ -227,7 +249,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { })); }, .force => { - panic("pkg-config failed for library {s}", .{system_lib.name}); + return step.fail("pkg-config failed for library {s}", .{system_lib.name}); }, .no => unreachable, }, @@ -272,7 +294,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { if (other.linkage == .dynamic and compile.rootModuleTarget().os.tag != .windows) { - if (fs.path.dirname(full_path_lib)) |dirname| { + if (Dir.path.dirname(full_path_lib)) |dirname| { try zig_args.append("-rpath"); try zig_args.append(dirname); } @@ -479,7 +501,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link"); if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc"); if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); - if (b.graph.time_report) try zig_args.append("--time-report"); + if (graph.time_report) try zig_args.append("--time-report"); if (compile.generated_asm != null) try zig_args.append("-femit-asm"); if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin"); @@ -555,9 +577,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { try zig_args.append(b.cache_root.path orelse "."); try zig_args.append("--global-cache-dir"); - try zig_args.append(b.graph.global_cache_root.path orelse "."); + try zig_args.append(graph.global_cache_root.path orelse "."); - if (b.graph.debug_compiler_runtime_libs) |mode| + if (graph.debug_compiler_runtime_libs) |mode| try zig_args.append(b.fmt("--debug-rt={t}", .{mode})); try zig_args.append("--name"); @@ -681,7 +703,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { // -I and -L arguments that appear after the last --mod argument apply to all modules. const cwd: Io.Dir = .cwd(); - const io = b.graph.io; + const io = graph.io; for (b.search_prefixes.items) |search_prefix| { var prefix_dir = cwd.openDir(io, search_prefix, .{}) catch |err| { @@ -734,8 +756,8 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir| dir.getPath2(b, step) - else if (b.graph.zig_lib_directory.path) |_| - b.fmt("{f}", .{b.graph.zig_lib_directory}) + else if (graph.zig_lib_directory.path) |_| + b.fmt("{f}", .{graph.zig_lib_directory}) else null; @@ -769,7 +791,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { "--error-limit", b.fmt("{d}", .{err_limit}), }); - try addFlag(&zig_args, "incremental", b.graph.incremental); + try addFlag(&zig_args, "incremental", graph.incremental); try zig_args.append("--listen=-"); @@ -814,7 +836,7 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash}); - const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; + const args_file = "args" ++ Dir.path.sep_str ++ args_hex_hash; if (b.cache_root.handle.access(io, args_file, .{})) |_| { // The args file is already present from a previous run. } else |err| switch (err) { @@ -859,7 +881,9 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { return try zig_args.toOwnedSlice(); } -pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progress.Node) !Path { +pub fn rebuildInFuzzMode(c: *Compile, maker: *Maker, progress_node: std.Progress.Node) !Path { + const gpa = maker.graph.gpa; + c.step.result_error_msgs.clearRetainingCapacity(); c.step.result_stderr = ""; @@ -871,21 +895,23 @@ pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progres c.step.result_failed_command = null; } - const zig_args = try getZigArgs(c, true); + const zig_args = try getZigArgs(c, maker, true); const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa); return maybe_output_bin_path.?; } pub fn doAtomicSymLinks( step: *Step, + maker: *Maker, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8, ) !void { const b = step.owner; - const io = b.graph.io; - const out_dir = fs.path.dirname(output_path) orelse "."; - const out_basename = fs.path.basename(output_path); + const graph = maker.graph; + const io = graph.io; + const out_dir = Dir.path.dirname(output_path) orelse "."; + const out_basename = Dir.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only }); const cwd: Io.Dir = .cwd(); @@ -903,10 +929,24 @@ pub fn doAtomicSymLinks( }; } -fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg { - const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; - const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore); - var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator); +pub const PkgConfigError = error{ + PkgConfigCrashed, + PkgConfigFailed, + PkgConfigNotInstalled, + PkgConfigInvalidOutput, +}; + +pub const PkgConfigPkg = struct { + name: []const u8, + desc: []const u8, +}; + +fn execPkgConfigList(maker: *Maker, out_code: *u8) (PkgConfigError || Maker.RunError)![]const PkgConfigPkg { + const graph = maker.graph; + const process_arena = graph.arena; // TODO don't leak into process arena + const pkg_config_exe = graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; + const stdout = try maker.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .ignore); + var list = std.array_list.Managed(PkgConfigPkg).init(process_arena); errdefer list.deinit(); var line_it = mem.tokenizeAny(u8, stdout, "\r\n"); while (line_it.next()) |line| { @@ -960,7 +1000,8 @@ const PkgConfigResult = struct { /// Run pkg-config for the given library name and parse the output, returning the arguments /// that should be passed to zig to link the given library. -fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult { +fn runPkgConfig(compile: *Compile, maker: *Maker, lib_name: []const u8) !PkgConfigResult { + const graph = maker.graph; const wl_rpath_prefix = "-Wl,-rpath,"; const b = compile.step.owner; @@ -1013,7 +1054,7 @@ fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult { }; var code: u8 = undefined; - const pkg_config_exe = b.graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; + const pkg_config_exe = graph.environ_map.get("PKG_CONFIG") orelse "pkg-config"; const stdout = if (b.runAllowFail(&[_][]const u8{ pkg_config_exe, pkg_name, @@ -1198,3 +1239,43 @@ fn moduleNeedsCliArg(mod: *const Module) bool { } else false; } +const CliNamedModules = struct { + modules: std.AutoArrayHashMapUnmanaged(*Module, void), + names: std.StringArrayHashMapUnmanaged(void), + + /// Traverse the whole dependency graph and give every module a unique + /// name, ideally one named after what it's called somewhere in the graph. + /// It will help here to have both a mapping from module to name and a set + /// of all the currently-used names. + fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules { + var compile: CliNamedModules = .{ + .modules = .{}, + .names = .{}, + }; + const graph = root_module.getGraph(); + { + assert(graph.modules[0] == root_module); + try compile.modules.put(arena, root_module, {}); + try compile.names.put(arena, "root", {}); + } + for (graph.modules[1..], graph.names[1..]) |mod, orig_name| { + var name = orig_name; + var n: usize = 0; + while (true) { + const gop = try compile.names.getOrPut(arena, name); + if (!gop.found_existing) { + try compile.modules.putNoClobber(arena, mod, {}); + break; + } + name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n }); + n += 1; + } + } + return compile; + } +}; + +fn getCompileDependencies(chase_dynamic: bool) void { + _ = chase_dynamic; + @panic("TODO"); +} diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index 4ba04092e02221fffc55729a64b44aa41a5b9d91..122c394e0d93d6b293545e8677d16d142c9dd27c 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -3,38 +3,46 @@ const Run = @This(); const builtin = @import("builtin"); const std = @import("std"); -const Io = std.Io; +const Cache = std.Build.Cache; +const Configuration = std.Build.Configuration; const Dir = std.Io.Dir; -const mem = std.mem; -const process = std.process; const EnvMap = std.process.Environ.Map; -const assert = std.debug.assert; -const Cache = std.Build.Cache; +const Io = std.Io; const Path = std.Build.Cache.Path; +const assert = std.debug.assert; +const mem = std.mem; +const process = std.process; const Step = @import("../Step.zig"); +const Maker = @import("../../Maker.zig"); /// If this is a Zig unit test binary, this tracks the names of the unit /// tests that are also fuzz tests. Indexes cannot be used as they may /// change between reruns. -fuzz_tests: std.ArrayList([]const u8), +fuzz_tests: std.ArrayList([]const u8) = .empty, cached_test_metadata: ?CachedTestMetadata = null, /// Populated during the fuzz phase if this run step corresponds to a unit test /// executable that contains fuzz tests. -rebuilt_executable: ?Path, +rebuilt_executable: ?Path = null, -fn make(step: *Step, options: Step.MakeOptions) !void { - const b = step.owner; - const io = b.graph.io; - const arena = b.allocator; - const run: *Run = @fieldParentPtr("step", step); +pub fn make( + run: *Run, + step_index: Configuration.Step.Index, + maker: *Maker, + progress_node: std.Progress.Node, +) Step.ExtendedMakeError!void { + if (true) @panic("TODO implement run.make()"); + const graph = maker.graph; + const step = maker.stepByIndex(step_index); + const io = graph.io; + const arena = graph.arena; // TODO don't leak into the process arena const has_side_effects = run.hasSideEffects(); var argv_list = std.array_list.Managed([]const u8).init(arena); var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena); - var man = b.graph.cache.obtain(); + var man = graph.cache.obtain(); defer man.deinit(); if (run.environ_map) |environ_map| { @@ -54,19 +62,19 @@ fn make(step: *Step, options: Step.MakeOptions) !void { man.hash.addBytes(bytes); }, .lazy_path => |file| { - const file_path = file.lazy_path.getPath3(b, step); - try argv_list.append(b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) })); + const file_path = file.lazy_path.getPath3(graph, step); + try argv_list.append(graph.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) })); man.hash.addBytes(file.prefix); _ = try man.addFilePath(file_path, null); }, .decorated_directory => |dd| { - const file_path = dd.lazy_path.getPath3(b, step); - const resolved_arg = b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix }); + const file_path = dd.lazy_path.getPath3(graph, step); + const resolved_arg = graph.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix }); try argv_list.append(resolved_arg); man.hash.addBytes(resolved_arg); }, .file_content => |file_plp| { - const file_path = file_plp.lazy_path.getPath3(b, step); + const file_path = file_plp.lazy_path.getPath3(graph, step); var result: std.Io.Writer.Allocating = .init(arena); errdefer result.deinit(); @@ -99,13 +107,13 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (artifact.rootModuleTarget().os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH - run.addPathForDynLibs(artifact); + addPathForDynLibs(artifact); } const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?; - try argv_list.append(b.fmt("{s}{s}", .{ + try argv_list.append(graph.fmt("{s}{s}", .{ pa.prefix, - run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }), + run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }), })); _ = try man.addFile(file_path, null); @@ -131,7 +139,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { man.hash.addBytes(bytes); }, .lazy_path => |lazy_path| { - const file_path = lazy_path.getPath2(b, step); + const file_path = lazy_path.getPath2(graph, step); _ = try man.addFile(file_path, null); }, .none => {}, @@ -147,14 +155,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void { man.hash.add(captured.trim_whitespace); } - hashStdIo(&man.hash, run.stdio); + std.log.err("TODO hashStdIo", .{}); + //hashStdIo(&man.hash, run.stdio); for (run.file_inputs.items) |lazy_path| { - _ = try man.addFile(lazy_path.getPath2(b, step), null); + _ = try man.addFile(lazy_path.getPath2(graph, step), null); } if (run.cwd) |cwd| { - const cwd_path = cwd.getPath3(b, step); + const cwd_path = cwd.getPath3(graph, step); _ = man.hash.addBytes(try cwd_path.toString(arena)); } @@ -165,9 +174,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try populateGeneratedPaths( arena, output_placeholders.items, - run.captured_stdout, - run.captured_stderr, - b.cache_root, + graph.cache_root, &digest, ); @@ -185,36 +192,34 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try populateGeneratedPaths( arena, output_placeholders.items, - run.captured_stdout, - run.captured_stderr, - b.cache_root, + graph.cache_root, &digest, ); const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest; for (output_placeholders.items) |placeholder| { - const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename }); + const output_sub_path = graph.pathJoin(&.{ output_dir_path, placeholder.output.basename }); const output_sub_dir_path = switch (placeholder.tag) { .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; - b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| { - return step.fail("unable to make path '{f}{s}': {s}", .{ - b.cache_root, output_sub_dir_path, @errorName(err), + graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| { + return step.fail("unable to make path '{f}{s}': {t}", .{ + graph.cache_root, output_sub_dir_path, err, }); }; - const arg_output_path = run.convertPathArg(.{ + const arg_output_path = run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = placeholder.output.generated_file.getPath(), }); argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0) arg_output_path else - b.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path }); + graph.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path }); } - try runCommand(run, argv_list.items, has_side_effects, output_dir_path, options, null); + try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null); if (!has_side_effects) try step.writeManifestAndWatch(&man); return; }; @@ -226,32 +231,32 @@ fn make(step: *Step, options: Step.MakeOptions) !void { for (output_placeholders.items) |placeholder| { const output_components = .{ tmp_dir_path, placeholder.output.basename }; - const output_sub_path = b.pathJoin(&output_components); + const output_sub_path = graph.pathJoin(&output_components); const output_sub_dir_path = switch (placeholder.tag) { .output_file => Dir.path.dirname(output_sub_path).?, .output_directory => output_sub_path, else => unreachable, }; - b.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| { - return step.fail("unable to make path '{f}{s}': {s}", .{ - b.cache_root, output_sub_dir_path, @errorName(err), + graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| { + return step.fail("unable to make path '{f}{s}': {t}", .{ + graph.cache_root, output_sub_dir_path, err, }); }; - const raw_output_path: Cache.Path = .{ - .root_dir = b.cache_root, - .sub_path = b.pathJoin(&output_components), + const raw_output_path: Path = .{ + .root_dir = graph.cache_root, + .sub_path = graph.pathJoin(&output_components), }; - placeholder.output.generated_file.path = raw_output_path.toString(b.graph.arena) catch @panic("OOM"); - argv_list.items[placeholder.index] = b.fmt("{s}{s}", .{ + placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM"); + argv_list.items[placeholder.index] = graph.fmt("{s}{s}", .{ placeholder.output.prefix, - run.convertPathArg(raw_output_path), + run.convertPathArg(maker, raw_output_path), }); } - try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, options, null); + try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null); const dep_file_dir = Dir.cwd(); - const dep_file_basename = dep_output_file.generated_file.getPath2(b, step); + const dep_file_basename = dep_output_file.generated_file.getPath2(graph, step); if (has_side_effects) try man.addDepFile(dep_file_dir, dep_file_basename) else @@ -269,21 +274,21 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (any_output) { const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest; - b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |err| switch (err) { + graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |err| switch (err) { Dir.RenameError.DirNotEmpty => { - b.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| { + graph.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| { return step.fail("unable to remove dir '{f}'{s}: {t}", .{ - b.cache_root, tmp_dir_path, del_err, + graph.cache_root, tmp_dir_path, del_err, }); }; - b.cache_root.handle.rename(tmp_dir_path, b.cache_root.handle, o_sub_path, io) catch |retry_err| { + graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |retry_err| { return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{ - b.cache_root, tmp_dir_path, b.cache_root, o_sub_path, retry_err, + graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, retry_err, }); }; }, else => return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{ - b.cache_root, tmp_dir_path, b.cache_root, o_sub_path, err, + graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, err, }), }; } @@ -293,9 +298,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try populateGeneratedPaths( arena, output_placeholders.items, - run.captured_stdout, - run.captured_stderr, - b.cache_root, + graph.cache_root, &digest, ); } @@ -347,7 +350,6 @@ fn waitZigTest( // start and it acknowledging the test starting, we terminate the child and raise an error. This // *should* never happen, but could in theory be caused by some very unlucky IB in a test. const response_timeout: Io.Clock.Duration = t: { - if (fuzz_context != null) break :t null; // don't timeout fuzz tests const ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s); break :t .{ .clock = .awake, .raw = .fromNanoseconds(ns) }; }; @@ -773,8 +775,8 @@ const FuzzTestRunner = struct { try f.pending_broadcasts.ensureUnusedCapacity(gpa, size); f.pending_broadcasts.appendSliceAssumeCapacity(body); f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer)); - } - }, + } + }, else => {}, // ignore other messages } @@ -863,7 +865,7 @@ const FuzzTestRunner = struct { if (f.coverage_id == null) return; // Search for the input file corresponding to the instance - const InputHeader = Build.abi.fuzz.MmapInputHeader; + const InputHeader = std.Build.abi.fuzz.MmapInputHeader; var in_r_buf: [@sizeOf(InputHeader)]u8 = undefined; var in_r: Io.File.Reader = undefined; var in_f: Io.File = undefined; @@ -1299,11 +1301,11 @@ fn sendRunFuzzTestMessage( } } -fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResult { - const b = run.step.owner; - const io = b.graph.io; - const arena = b.allocator; - const gpa = b.allocator; +fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !EvalGenericResult { + const graph = maker.graph; + const io = graph.io; + const arena = graph.allocator; // TODO don't leak into the process arena + const gpa = maker.gpa; var child = try process.spawn(io, spawn_options); defer child.kill(io); @@ -1317,7 +1319,7 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul child.stdin = null; }, .lazy_path => |lazy_path| { - const path = lazy_path.getPath3(b, &run.step); + const path = lazy_path.getPath3(graph, &run.step); const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| { return run.step.fail("unable to open stdin file: {t}", .{err}); }; @@ -1417,18 +1419,22 @@ fn evalGeneric(run: *Run, spawn_options: process.SpawnOptions) !EvalGenericResul const IndexedOutput = struct { index: usize, - tag: @typeInfo(Arg).@"union".tag_type.?, + tag: Configuration.Step.Run.Arg.Tag, output: *Output, }; +const Output = void; // TODO + pub fn rerunInFuzzMode( run: *Run, fuzz: *std.Build.Fuzz, prog_node: std.Progress.Node, ) !void { + const maker = fuzz.maker; + const graph = maker.graph; const step = &run.step; const b = step.owner; - const io = b.graph.io; + const io = graph.io; const arena = b.allocator; var argv_list: std.ArrayList([]const u8) = .empty; for (run.argv.items) |arg| { @@ -1438,11 +1444,11 @@ pub fn rerunInFuzzMode( }, .lazy_path => |file| { const file_path = file.lazy_path.getPath3(b, step); - try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(file_path) })); + try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) })); }, .decorated_directory => |dd| { const file_path = dd.lazy_path.getPath3(b, step); - try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(file_path), dd.suffix })); + try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix })); }, .file_content => |file_plp| { const file_path = file_plp.lazy_path.getPath3(b, step); @@ -1471,7 +1477,7 @@ pub fn rerunInFuzzMode( }; try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, - run.convertPathArg(.{ .root_dir = .cwd(), .sub_path = file_path }), + run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }), })); }, .output_file, .output_directory => unreachable, @@ -1487,17 +1493,13 @@ pub fn rerunInFuzzMode( var rand_int: u64 = undefined; io.random(@ptrCast(&rand_int)); const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int); - try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, .{ - .progress_node = prog_node, - .watch = undefined, // not used by `runCommand` - .web_server = null, // only needed for time reports - .unit_test_timeout_ns = null, // don't time out fuzz tests for now - .gpa = fuzz.gpa, - }, .{ + try runCommand(run, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{ .fuzz = fuzz, }); } +const CapturedStdIo = void; // TODO get it from Configuration + fn populateGeneratedPaths( arena: std.mem.Allocator, output_placeholders: []const IndexedOutput, @@ -1545,17 +1547,19 @@ const FuzzContext = struct { fn runCommand( run: *Run, + maker: *Maker, + progress_node: std.Progress.Node, argv: []const []const u8, has_side_effects: bool, output_dir_path: []const u8, - options: Step.MakeOptions, fuzz_context: ?FuzzContext, ) !void { + const graph = maker.graph; + const arena = graph.arena; // TODO don't leak into process arena + const gpa = maker.gpa; const step = &run.step; const b = step.owner; - const arena = b.allocator; - const gpa = options.gpa; - const io = b.graph.io; + const io = graph.io; const cwd: process.Child.Cwd = if (run.cwd) |lazy_cwd| .{ .path = lazy_cwd.getPath2(b, step) } else .inherit; @@ -1571,12 +1575,12 @@ fn runCommand( defer interp_argv.deinit(); var environ_map: EnvMap = env: { - const orig = run.environ_map orelse &b.graph.environ_map; + const orig = run.environ_map orelse &graph.environ_map; break :env try orig.clone(gpa); }; defer environ_map.deinit(); - const opt_generic_result = spawnChildAndCollect(run, argv, &environ_map, has_side_effects, options, fuzz_context) catch |err| term: { + const opt_generic_result = spawnChildAndCollect(run, maker, progress_node, argv, &environ_map, has_side_effects, fuzz_context) catch |err| term: { // InvalidExe: cpu arch mismatch // FileNotFound: can happen with a wrong dynamic linker path if (err == error.InvalidExe or err == error.FileNotFound) interpret: { @@ -1597,7 +1601,7 @@ fn runCommand( const need_cross_libc = exe.is_linking_libc and (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic)); const other_target = exe.root_module.resolved_target.?.result; - switch (std.zig.system.getExternalExecutor(io, &b.graph.host.result, &other_target, .{ + switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{ .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null, .link_libc = exe.is_linking_libc, })) { @@ -1669,7 +1673,7 @@ fn runCommand( .bad_dl => |foreign_dl| { if (allow_skip) return error.MakeSkipped; - const host_dl = b.graph.host.result.dynamic_linker.get() orelse "(none)"; + const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)"; return step.fail( \\the host system is unable to execute binaries from the target @@ -1681,7 +1685,7 @@ fn runCommand( .bad_os_or_cpu => { if (allow_skip) return error.MakeSkipped; - const host_name = try b.graph.host.result.zigTriple(b.allocator); + const host_name = try graph.host.result.zigTriple(b.allocator); const foreign_name = try root_target.zigTriple(b.allocator); return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ @@ -1692,14 +1696,14 @@ fn runCommand( if (root_target.os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH - run.addPathForDynLibs(exe); + addPathForDynLibs(exe); } gpa.free(step.result_failed_command.?); step.result_failed_command = null; try Step.handleVerbose2(step.owner, cwd, run.environ_map, interp_argv.items); - break :term spawnChildAndCollect(run, interp_argv.items, &environ_map, has_side_effects, options, fuzz_context) catch |e| { + break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| { if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; if (e == error.MakeFailed) return error.MakeFailed; // error already reported return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e }); @@ -1851,14 +1855,16 @@ const EvalGenericResult = struct { fn spawnChildAndCollect( run: *Run, + maker: *Maker, + progress_node: std.Progress.Node, argv: []const []const u8, environ_map: *EnvMap, has_side_effects: bool, - options: Step.MakeOptions, fuzz_context: ?FuzzContext, ) !?EvalGenericResult { const b = run.step.owner; - const graph = b.graph; + const graph = maker.graph; + const gpa = maker.gpa; const io = graph.io; if (fuzz_context != null) { @@ -1870,7 +1876,7 @@ fn spawnChildAndCollect( // If an error occurs, it's caused by this command: assert(run.step.result_failed_command == null); - run.step.result_failed_command = try Step.allocPrintCmd(options.gpa, child_cwd, .{ + run.step.result_failed_command = try Step.allocPrintCmd(gpa, child_cwd, .{ .child = environ_map, .parent = &graph.environ_map, }, argv); @@ -1905,7 +1911,7 @@ fn spawnChildAndCollect( if (run.stdio == .zig_test) { const started: Io.Clock.Timestamp = .now(io, .awake); - const result = evalZigTest(run, spawn_options, options, fuzz_context) catch |err| switch (err) { + const result = evalZigTest(run, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }; @@ -1915,7 +1921,7 @@ fn spawnChildAndCollect( } else { const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit; if (!run.disable_zig_progress and !inherit) { - spawn_options.progress_node = options.progress_node; + spawn_options.progress_node = progress_node; } const terminal_mode: Io.Terminal.Mode = if (inherit) m: { const stderr = try io.lockStderr(&.{}, graph.stderr_mode); @@ -1925,7 +1931,7 @@ fn spawnChildAndCollect( try setColorEnvironmentVariables(run, environ_map, terminal_mode); const started: Io.Clock.Timestamp = .now(io, .awake); - const result = evalGeneric(run, spawn_options) catch |err| switch (err) { + const result = evalGeneric(run, maker, spawn_options) catch |err| switch (err) { error.Canceled => |e| return e, else => |e| e, }; @@ -1934,11 +1940,11 @@ fn spawnChildAndCollect( } } -fn hashStdIo(hh: *Cache.HashHelper, stdio: StdIo) void { +fn hashStdIo(hh: *Cache.HashHelper, stdio: void) void { switch (stdio) { .infer_from_args, .inherit, .zig_test => {}, .check => |checks| for (checks.items) |check| { - hh.add(@as(std.meta.Tag(StdIo.Check), check)); + hh.add(@as(std.meta.Tag(@This().StdIo.Check), check)); switch (check) { .expect_stderr_exact, .expect_stderr_match, @@ -2010,7 +2016,7 @@ fn setColorEnvironmentVariables(run: *Run, environ_map: *EnvMap, terminal_mode: } } -fn checksContainStdout(checks: []const StdIo.Check) bool { +fn checksContainStdout(checks: []const @This().StdIo.Check) bool { for (checks) |check| switch (check) { .expect_stderr_exact, .expect_stderr_match, @@ -2024,7 +2030,7 @@ fn checksContainStdout(checks: []const StdIo.Check) bool { return false; } -fn checksContainStderr(checks: []const StdIo.Check) bool { +fn checksContainStderr(checks: []const @This().StdIo.Check) bool { for (checks) |check| switch (check) { .expect_stdout_exact, .expect_stdout_match, @@ -2063,9 +2069,9 @@ fn hasAnyOutputArgs(run: Run) bool { /// /// Whenever a path is included in the argv of a child, it should be put through this function first /// to make sure the child doesn't see paths relative to a cwd other than its own. -fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { +fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 { const b = run.step.owner; - const graph = b.graph; + const graph = maker.graph; const arena = graph.arena; const path_str = path.toString(arena) catch @panic("OOM"); @@ -2091,40 +2097,43 @@ fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); } -fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void { - const b = run.step.owner; - const compiles = artifact.getCompileDependencies(true); - for (compiles) |compile| { +fn addPathForDynLibs(artifact: *Step.Compile) void { + if (true) @panic("TODO"); + for (artifact.getCompileDependencies(true)) |compile| { if (compile.root_module.resolved_target.?.result.os.tag == .windows and compile.isDynamicLibrary()) { - addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); + @panic("TODO"); + //addPathDir(run, Dir.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?); } } } fn failForeign( run: *Run, + maker: *Maker, + step_index: Configuration.Step.Index, suggested_flag: []const u8, argv0: []const u8, exe: *Step.Compile, -) error{ MakeFailed, MakeSkipped, OutOfMemory } { +) Step.ExtendedMakeError { + const step = maker.stepByIndex(step_index); switch (run.stdio) { .check, .zig_test => { - if (run.skip_foreign_checks) - return error.MakeSkipped; + if (run.skip_foreign_checks) return error.MakeSkipped; - const b = run.step.owner; - const host_name = try b.graph.host.result.zigTriple(b.allocator); - const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator); + const graph = maker.graph; + const process_arena = graph.arena; // TODO don't leak into process arena + const host_name = try graph.host.result.zigTriple(process_arena); + const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena); - return run.step.fail( + return step.fail( \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) \\ consider using {s} or enabling skip_foreign_checks in the Run step , .{ argv0, foreign_name, host_name, suggested_flag }); }, else => { - return run.step.fail("unable to spawn foreign binary '{s}'", .{argv0}); + return step.fail("unable to spawn foreign binary '{s}'", .{argv0}); }, } } diff --git a/lib/compiler/Maker/Watch.zig b/lib/compiler/Maker/Watch.zig index 907e6536a132863603d70e423041b41c5b677a5c..b59f616d995c1b7c77893714cdf441563ea7c1c6 100644 --- a/lib/compiler/Maker/Watch.zig +++ b/lib/compiler/Maker/Watch.zig @@ -10,6 +10,7 @@ const Configuration = std.Build.Configuration; const FsEvents = @import("Watch/FsEvents.zig"); const Step = @import("Step.zig"); +const Maker = @import("../Maker.zig"); os: Os, /// The number to show as the number of directories being watched. @@ -18,8 +19,7 @@ dir_count: usize, // They are `undefined` on implementations which do not utilize then. dir_table: DirTable, generation: Generation, -configuration: *const Configuration, -make_steps: []Step, +maker: *Maker, pub const have_impl = Os != void; @@ -105,8 +105,7 @@ const Os = switch (builtin.os.tag) { }; }; - fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch { - _ = cwd_path; + fn init(maker: *Maker) !Watch { return .{ .dir_table = .{}, .dir_count = 0, @@ -118,8 +117,7 @@ const Os = switch (builtin.os.tag) { else => {}, }, .generation = 0, - .make_steps = make_steps, - .configuration = configuration, + .maker = maker, }; } @@ -136,7 +134,8 @@ const Os = switch (builtin.os.tag) { return stack_lfh.clone(gpa); } - fn markDirtySteps(w: *Watch, gpa: Allocator, fan_fd: posix.fd_t) !bool { + fn markDirtySteps(w: *Watch, fan_fd: posix.fd_t) !bool { + const maker = w.maker; const fanotify = std.os.linux.fanotify; const M = fanotify.event_metadata; var events_buf: [256 + 4096]u8 = undefined; @@ -155,7 +154,7 @@ const Os = switch (builtin.os.tag) { if (meta[0].mask.Q_OVERFLOW) { any_dirty = true; std.log.warn("file system watch queue overflowed; falling back to fstat", .{}); - markAllFilesDirty(w, gpa); + markAllFilesDirty(w); return true; } const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1); @@ -167,9 +166,9 @@ const Os = switch (builtin.os.tag) { const lfh: FileHandle = .{ .handle = file_handle }; if (w.os.handle_table.getPtr(lfh)) |value| { if (value.reaction_set.getPtr(".")) |glob_set| - any_dirty = markStepSetDirty(gpa, w.make_steps, glob_set, any_dirty); + any_dirty = markStepSetDirty(maker, glob_set, any_dirty); if (value.reaction_set.getPtr(file_name)) |step_set| - any_dirty = markStepSetDirty(gpa, w.make_steps, step_set, any_dirty); + any_dirty = markStepSetDirty(maker, step_set, any_dirty); } }, else => |t| std.log.warn("unexpected fanotify event '{t}'", .{t}), @@ -179,9 +178,11 @@ const Os = switch (builtin.os.tag) { } fn update(w: *Watch, gpa: Allocator, steps: []const Configuration.Step.Index) !void { + const maker = w.maker; + // Add missing marks and note persisted ones. for (steps) |step_index| { - const step = &w.make_steps[@intFromEnum(step_index)]; + const step = maker.stepByIndex(step_index); for (step.inputs.table.keys(), step.inputs.table.values()) |path, *files| { const reaction_set = rs: { const gop = try w.dir_table.getOrPut(gpa, path); @@ -298,13 +299,12 @@ const Os = switch (builtin.os.tag) { w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { - _ = io; + fn wait(w: *Watch, timeout: Timeout) !WaitResult { const events_len = try std.posix.poll(w.os.poll_fds.values(), timeout.to_i32_ms()); if (events_len == 0) return .timeout; for (w.os.poll_fds.values()) |poll_fd| { - if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, gpa, poll_fd.fd)) + if (poll_fd.revents & std.posix.POLL.IN == std.posix.POLL.IN and try markDirtySteps(w, poll_fd.fd)) return .dirty; } return .clean; @@ -515,12 +515,14 @@ const Os = switch (builtin.os.tag) { return file_id; } - fn markDirtySteps(w: *Watch, gpa: Allocator, dir: *Directory) !bool { + fn markDirtySteps(w: *Watch, dir: *Directory) !bool { + const maker = w.maker; + var any_dirty = false; const bytes_returned = dir.iosb.Information; if (bytes_returned == 0) { std.log.warn("file system watch queue overflowed; falling back to fstat", .{}); - markAllFilesDirty(w, gpa); + markAllFilesDirty(w); try dir.startListening(w); return true; } @@ -530,9 +532,9 @@ const Os = switch (builtin.os.tag) { const notify: *windows.FILE.NOTIFY.INFORMATION = @ptrCast(@alignCast(&dir.buffer[offset])); const file_name = file_name_buf[0..std.unicode.wtf16LeToWtf8(&file_name_buf, notify.fileName())]; if (dir.reaction_set.getPtr(".")) |glob_set| - any_dirty = markStepSetDirty(gpa, glob_set, any_dirty); + any_dirty = markStepSetDirty(maker, glob_set, any_dirty); if (dir.reaction_set.getPtr(file_name)) |step_set| - any_dirty = markStepSetDirty(gpa, step_set, any_dirty); + any_dirty = markStepSetDirty(maker, step_set, any_dirty); if (notify.NextEntryOffset == 0) break; @@ -619,14 +621,17 @@ const Os = switch (builtin.os.tag) { w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { + fn wait(w: *Watch, timeout: Timeout) !WaitResult { + const maker = w.maker; + const io = maker.graph.io; + for (0..2) |attempt| { while (w.os.ready_dirs.popFirst()) |ready_node| { const dir: *Directory = @fieldParentPtr("ready_node", ready_node); assert(dir.state == .ready); dir.state = .idle; switch (dir.iosb.u.Status) { - .SUCCESS => return if (try markDirtySteps(w, gpa, dir)) .dirty else .clean, + .SUCCESS => return if (try markDirtySteps(w, dir)) .dirty else .clean, .PENDING => unreachable, .CANCELLED => {}, else => |status| return windows.unexpectedStatus(status), @@ -810,25 +815,25 @@ const Os = switch (builtin.os.tag) { w.dir_count = w.dir_table.count(); } - fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { - _ = io; + fn wait(w: *Watch, timeout: Timeout) !WaitResult { + const maker = w.maker; var timespec_buffer: posix.timespec = undefined; var event_buffer: [100]posix.Kevent = undefined; var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(×pec_buffer)); if (n == 0) return .timeout; const reaction_sets = w.os.handles.items(.rs); - var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false); + var any_dirty = markDirtySteps(maker, reaction_sets, event_buffer[0..n], false); timespec_buffer = .{ .sec = 0, .nsec = 0 }; while (n == event_buffer.len) { n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, ×pec_buffer); if (n == 0) break; - any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty); + any_dirty = markDirtySteps(maker, reaction_sets, event_buffer[0..n], any_dirty); } return if (any_dirty) .dirty else .clean; } fn markDirtySteps( - gpa: Allocator, + maker: *Maker, reaction_sets: []ReactionSet, events: []const std.c.Kevent, start_any_dirty: bool, @@ -840,13 +845,13 @@ const Os = switch (builtin.os.tag) { // If we knew the basename of the changed file, here we would // mark only the step set dirty, and possibly the glob set: //if (reaction_set.getPtr(".")) |glob_set| - // any_dirty = markStepSetDirty(gpa, glob_set, any_dirty); + // any_dirty = markStepSetDirty(maker, glob_set, any_dirty); //if (reaction_set.getPtr(file_name)) |step_set| - // any_dirty = markStepSetDirty(gpa, step_set, any_dirty); + // any_dirty = markStepSetDirty(maker, step_set, any_dirty); // However we don't know the file name so just mark all the // sets dirty for this directory. for (reaction_set.values()) |*step_set| { - any_dirty = markStepSetDirty(gpa, step_set, any_dirty); + any_dirty = markStepSetDirty(maker, step_set, any_dirty); } } return any_dirty; @@ -878,8 +883,8 @@ const Os = switch (builtin.os.tag) { else => void, }; -pub fn init(cwd_path: []const u8, configuration: *const Configuration, make_steps: []Step) !Watch { - return Os.init(cwd_path, configuration, make_steps); +pub fn init(maker: *Maker) !Watch { + return Os.init(maker); } pub const Match = struct { @@ -904,7 +909,9 @@ pub const Match = struct { }; }; -fn markAllFilesDirty(w: *Watch, gpa: Allocator) void { +fn markAllFilesDirty(w: *Watch) void { + const maker = w.maker; + for (switch (builtin.os.tag) { .windows => w.os.handle_table.keys(), else => w.os.handle_table.values(), @@ -915,18 +922,18 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void { }; for (reaction_set.values()) |step_set| { for (step_set.keys()) |step_index| { - const step = &w.make_steps[@intFromEnum(step_index)]; - _ = step.invalidateResult(gpa); + const step = maker.stepByIndex(step_index); + _ = maker.invalidateResult(step); } } } } -fn markStepSetDirty(gpa: Allocator, make_steps: []Step, step_set: *StepSet, any_dirty: bool) bool { +fn markStepSetDirty(maker: *Maker, step_set: *StepSet, any_dirty: bool) bool { var this_any_dirty = false; for (step_set.keys()) |step_index| { - const step = &make_steps[@intFromEnum(step_index)]; - if (step.invalidateResult(gpa)) this_any_dirty = true; + const step = maker.stepByIndex(step_index); + if (maker.invalidateResult(step)) this_any_dirty = true; } return any_dirty or this_any_dirty; } @@ -971,6 +978,6 @@ pub const WaitResult = enum { clean, }; -pub fn wait(w: *Watch, gpa: Allocator, io: Io, timeout: Timeout) !WaitResult { - return Os.wait(w, gpa, io, timeout); +pub fn wait(w: *Watch, timeout: Timeout) !WaitResult { + return Os.wait(w, timeout); } diff --git a/lib/compiler/Maker/WebServer.zig b/lib/compiler/Maker/WebServer.zig index fd3806e8b2b925f6badad2830ddd8d780abab2b4..1ea44e0ba0a47a2f2c607ca7f8d4c42bdba08c99 100644 --- a/lib/compiler/Maker/WebServer.zig +++ b/lib/compiler/Maker/WebServer.zig @@ -14,16 +14,14 @@ const log = std.log.scoped(.web_server); const mem = std.mem; const net = std.Io.net; +const Maker = @import("../Maker.zig"); const Fuzz = @import("Fuzz.zig"); const Graph = @import("Graph.zig"); const Step = @import("Step.zig"); -gpa: Allocator, -graph: *const Graph, -all_steps: []const Configuration.Step.Index, +maker: *Maker, listen_address: net.IpAddress, root_prog_node: std.Progress.Node, -watch: bool, tcp_server: ?net.Server, serve_task: ?Io.Future(Io.Cancelable!void), @@ -65,19 +63,16 @@ pub const base_clock: Io.Clock = .awake; /// Thread-safe. Triggers updates to be sent to connected WebSocket clients; see `update_id`. pub fn notifyUpdate(ws: *WebServer) void { + const io = ws.maker.graph.io; _ = ws.update_id.rmw(.Add, 1, .release); - ws.graph.io.futexWake(u32, &ws.update_id.raw, 16); + io.futexWake(u32, &ws.update_id.raw, 16); } pub const Options = struct { - gpa: Allocator, - graph: *const Graph, - all_steps: []const Configuration.Step.Index, + maker: *Maker, root_prog_node: std.Progress.Node, - watch: bool, listen_address: net.IpAddress, base_timestamp: Io.Clock.Timestamp, - configuration: *const Configuration, }; pub fn init(opts: Options) WebServer { // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent` @@ -85,10 +80,13 @@ pub fn init(opts: Options) WebServer { comptime assert(!builtin.single_threaded); assert(opts.base_timestamp.clock == base_clock); - const all_steps = opts.all_steps; - const c = opts.configuration; + const maker = opts.maker; + const all_steps = maker.step_stack.keys(); + const c = &maker.scanned_config.configuration; + const gpa = maker.gpa; + const graph = maker.graph; - const step_names_trailing = opts.gpa.alloc(u8, len: { + const step_names_trailing = gpa.alloc(u8, len: { var name_bytes: usize = 0; for (all_steps) |step_index| name_bytes += step_index.ptr(c).name.slice(c).len; break :len name_bytes + all_steps.len * 4; @@ -105,25 +103,22 @@ pub fn init(opts: Options) WebServer { assert(idx == step_names_trailing.len); } - const step_status_bits = opts.gpa.alloc( + const step_status_bits = gpa.alloc( u8, std.math.divCeil(usize, all_steps.len, 4) catch unreachable, ) catch @panic("out of memory"); @memset(step_status_bits, 0); - const time_reports_len: usize = if (opts.graph.time_report) all_steps.len else 0; - const time_report_msgs = opts.gpa.alloc([]u8, time_reports_len) catch @panic("out of memory"); - const time_report_update_times = opts.gpa.alloc(i64, time_reports_len) catch @panic("out of memory"); + const time_reports_len: usize = if (graph.time_report) all_steps.len else 0; + const time_report_msgs = gpa.alloc([]u8, time_reports_len) catch @panic("out of memory"); + const time_report_update_times = gpa.alloc(i64, time_reports_len) catch @panic("out of memory"); @memset(time_report_msgs, &.{}); @memset(time_report_update_times, std.math.minInt(i64)); return .{ - .gpa = opts.gpa, - .graph = opts.graph, - .all_steps = all_steps, + .maker = maker, .listen_address = opts.listen_address, .root_prog_node = opts.root_prog_node, - .watch = opts.watch, .tcp_server = null, .serve_task = null, @@ -148,8 +143,9 @@ pub fn init(opts: Options) WebServer { }; } pub fn deinit(ws: *WebServer) void { - const gpa = ws.gpa; - const io = ws.graph.io; + const maker = ws.maker; + const gpa = maker.gpa; + const io = maker.graph.io; gpa.free(ws.step_names_trailing); gpa.free(ws.step_status_bits); @@ -170,7 +166,8 @@ pub fn deinit(ws: *WebServer) void { pub fn start(ws: *WebServer) error{AlreadyReported}!void { assert(ws.tcp_server == null); assert(ws.serve_task == null); - const io = ws.graph.io; + const maker = ws.maker; + const io = maker.graph.io; ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| { log.err("failed to listen to port {d}: {t}", .{ ws.listen_address.getPort(), err }); @@ -189,9 +186,12 @@ pub fn start(ws: *WebServer) error{AlreadyReported}!void { } } fn serve(ws: *WebServer) Io.Cancelable!void { - const io = ws.graph.io; + const maker = ws.maker; + const io = maker.graph.io; + var group: Io.Group = .init; defer group.cancel(io); + while (true) { var stream = ws.tcp_server.?.accept(io) catch |err| switch (err) { error.Canceled => |e| return e, @@ -223,8 +223,10 @@ pub fn updateStepStatus( step_index: Configuration.Step.Index, new_status: abi.StepUpdate.Status, ) void { + const maker = ws.maker; + const all_steps = maker.step_stack.keys(); // TODO don't do linear search, especially in a hot loop like this - const step_idx: u32 = for (ws.all_steps, 0..) |s, i| { + const step_idx: u32 = for (all_steps, 0..) |s, i| { if (s == step_index) break @intCast(i); } else unreachable; const ptr = &ws.step_status_bits[step_idx / 4]; @@ -238,13 +240,16 @@ pub fn updateStepStatus( pub fn finishBuild(ws: *WebServer, opts: struct { fuzz: bool, }) void { + const maker = ws.maker; + const all_steps = maker.step_stack.keys(); + if (opts.fuzz) { switch (builtin.os.tag) { // Current implementation depends on two things that need to be ported to Windows: // * Memory-mapping to share data between the fuzzer and build runner. // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving // many addresses to source locations). - .windows => std.process.fatal("--fuzz not yet implemented for {s}", .{@tagName(builtin.os.tag)}), + .windows => std.process.fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}), else => {}, } if (@bitSizeOf(usize) != 64) { @@ -260,28 +265,26 @@ pub fn finishBuild(ws: *WebServer, opts: struct { ws.build_status.store(.fuzz_init, .monotonic); ws.notifyUpdate(); - ws.fuzz = Fuzz.init( - ws.gpa, - ws.graph.io, - ws.all_steps, - ws.root_prog_node, - .{ .forever = .{ .ws = ws } }, - ) catch |err| std.process.fatal("failed to start fuzzer: {s}", .{@errorName(err)}); + ws.fuzz = Fuzz.init(maker, all_steps, ws.root_prog_node, .{ .forever = .{ .ws = ws } }) catch |err| + std.process.fatal("failed to start fuzzer: {t}", .{err}); ws.fuzz.?.start(); } - ws.build_status.store(if (ws.watch) .watching else .idle, .monotonic); + ws.build_status.store(if (maker.watch) .watching else .idle, .monotonic); ws.notifyUpdate(); } -pub fn now(s: *const WebServer) i64 { - const io = s.graph.io; +pub fn now(ws: *const WebServer) i64 { + const maker = ws.maker; + const io = maker.graph.io; const ts = base_clock.now(io); - return @intCast(s.base_timestamp.durationTo(ts).toNanoseconds()); + return @intCast(ws.base_timestamp.durationTo(ts).toNanoseconds()); } fn accept(ws: *WebServer, stream: net.Stream) void { - const io = ws.graph.io; + const maker = ws.maker; + const io = maker.graph.io; + defer { // `net.Stream.close` wants to helpfully overwrite `stream` with // `undefined`, but it cannot do so since it is an immutable parameter. @@ -326,12 +329,16 @@ fn accept(ws: *WebServer, stream: net.Stream) void { } fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { - const io = ws.graph.io; + const maker = ws.maker; + const gpa = maker.gpa; + const graph = maker.graph; + const io = graph.io; + const all_steps = maker.step_stack.keys(); var prev_build_status = ws.build_status.load(.monotonic); - const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len); - defer ws.gpa.free(prev_step_status_bits); + const prev_step_status_bits = try gpa.alloc(u8, ws.step_status_bits.len); + defer gpa.free(prev_step_status_bits); for (prev_step_status_bits, ws.step_status_bits) |*copy, *shared| { copy.* = @atomicLoad(u8, shared, .monotonic); } @@ -343,10 +350,10 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { const hello_header: abi.Hello = .{ .status = prev_build_status, .flags = .{ - .time_report = ws.graph.time_report, + .time_report = graph.time_report, }, .timestamp = ws.now(), - .steps_len = @intCast(ws.all_steps.len), + .steps_len = @intCast(all_steps.len), }; var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits }; try sock.writeMessageVec(&bufs, .binary); @@ -369,8 +376,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { if (update_time <= prev_time) continue; // We want to send `msg`, but shouldn't block `ws.time_report_mutex` while we do, so // that we don't hold up the build system on the client accepting this packet. - const owned_msg = try ws.gpa.dupe(u8, msg); - defer ws.gpa.free(owned_msg); + const owned_msg = try gpa.dupe(u8, msg); + defer gpa.free(owned_msg); // Temporarily unlock, then re-lock after the message is sent. ws.time_report_mutex.unlock(io); defer ws.time_report_mutex.lockUncancelable(io); @@ -427,7 +434,8 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { } } fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void { - const io = ws.graph.io; + const maker = ws.maker; + const io = maker.graph.io; while (true) { const msg = sock.readSmallMessage() catch return; @@ -485,8 +493,11 @@ fn serveLibFile( sub_path: []const u8, content_type: []const u8, ) !void { + const maker = ws.maker; + const graph = maker.graph; + return serveFile(ws, request, .{ - .root_dir = ws.graph.zig_lib_directory, + .root_dir = graph.zig_lib_directory, .sub_path = sub_path, }, content_type); } @@ -495,7 +506,9 @@ fn serveClientWasm( req: *http.Server.Request, optimize_mode: std.builtin.OptimizeMode, ) !void { - var arena_state: std.heap.ArenaAllocator = .init(ws.gpa); + const gpa = ws.maker.gpa; + + var arena_state: std.heap.ArenaAllocator = .init(gpa); defer arena_state.deinit(); const arena = arena_state.allocator(); @@ -510,8 +523,10 @@ pub fn serveFile( path: Cache.Path, content_type: []const u8, ) !void { - const gpa = ws.gpa; - const io = ws.graph.io; + const maker = ws.maker; + const gpa = ws.maker.gpa; + const io = maker.graph.io; + // The desired API is actually sendfile, which will require enhancing http.Server. // We load the file with every request so that the user can make changes to the file // and refresh the HTML page without restarting this server. @@ -528,7 +543,8 @@ pub fn serveFile( }); } pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { - const graph = ws.graph; + const maker = ws.maker; + const graph = maker.graph; const io = graph.io; var send_buffer: [0x4000]u8 = undefined; @@ -576,8 +592,9 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim const arch_os_abi = "wasm32-freestanding"; const cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; - const gpa = ws.gpa; - const graph = ws.graph; + const maker = ws.maker; + const gpa = maker.gpa; + const graph = maker.graph; const io = graph.io; const main_src_path: Cache.Path = .{ @@ -697,7 +714,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim if (code != 0) { log.err( "the following command exited with error code {d}:\n{s}", - .{ code, try Step.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ code, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, ); return error.WasmCompilationFailed; } @@ -705,21 +722,21 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim .signal => |sig| { log.err( "the following command terminated with signal {t}:\n{s}", - .{ sig, try Step.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, ); return error.WasmCompilationFailed; }, .stopped => |sig| { log.err( "the following command stopped unexpectedly with signal {t}:\n{s}", - .{ sig, try Build.Step.allocPrintCmd(arena, .inherit, null, argv.items) }, + .{ sig, try std.zig.allocPrintCmd(arena, .inherit, null, argv.items) }, ); return error.WasmCompilationFailed; }, .unknown => { log.err( "the following command terminated unexpectedly:\n{s}", - .{try Step.allocPrintCmd(arena, .inherit, null, argv.items)}, + .{try std.zig.allocPrintCmd(arena, .inherit, null, argv.items)}, ); return error.WasmCompilationFailed; }, @@ -729,14 +746,14 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim try result_error_bundle.renderToStderr(io, .{}, .auto); log.err("the following command failed with {d} compilation errors:\n{s}", .{ result_error_bundle.errorMessageCount(), - try Step.allocPrintCmd(arena, .inherit, null, argv.items), + try std.zig.allocPrintCmd(arena, .inherit, null, argv.items), }); return error.WasmCompilationFailed; } const base_path = result orelse { log.err("child process failed to report result\n{s}", .{ - try Step.allocPrintCmd(arena, .inherit, null, argv.items), + try std.zig.allocPrintCmd(arena, .inherit, null, argv.items), }); return error.WasmCompilationFailed; }; @@ -773,11 +790,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { /// The trailing data of `abi.time_report.CompileResult`, except the step name. trailing: []const u8, }) void { - const gpa = ws.gpa; - const io = ws.graph.io; + const maker = ws.maker; + const gpa = maker.gpa; + const io = maker.graph.io; + const all_steps = maker.step_stack.keys(); // TODO don't do linear search - const step_idx: u32 = for (ws.all_steps, 0..) |s, i| { + const step_idx: u32 = for (all_steps, 0..) |s, i| { if (s == opts.compile_step) break @intCast(i); } else unreachable; @@ -815,11 +834,13 @@ pub fn updateTimeReportCompile(ws: *WebServer, opts: struct { } pub fn updateTimeReportGeneric(ws: *WebServer, step_index: Configuration.Step.Index, duration: Io.Duration) void { - const gpa = ws.gpa; - const io = ws.graph.io; + const maker = ws.maker; + const gpa = maker.gpa; + const io = maker.graph.io; + const all_steps = maker.step_stack.keys(); // TODO don't do linear search - const step_idx: u32 = for (ws.all_steps, 0..) |s, i| { + const step_idx: u32 = for (all_steps, 0..) |s, i| { if (s == step_index) break @intCast(i); } else unreachable; @@ -852,11 +873,13 @@ pub fn updateTimeReportRunTest( tests: *const Step.Run.CachedTestMetadata, ns_per_test: []const u64, ) void { - const gpa = ws.gpa; - const io = ws.graph.io; + const maker = ws.maker; + const gpa = maker.gpa; + const io = maker.graph.io; + const all_steps = maker.step_stack.keys(); // TODO don't do linear search - const step_idx: u32 = for (ws.all_steps, 0..) |s, i| { + const step_idx: u32 = for (all_steps, 0..) |s, i| { if (s == run_step_index) break @intCast(i); } else unreachable; @@ -910,7 +933,7 @@ const RunnerRequest = union(enum) { rebuild, }; pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest { - const io = ws.graph.io; + const io = ws.maker.graph.io; ws.runner_request_mutex.lock(io) catch return; defer ws.runner_request_mutex.unlock(io); if (ws.runner_request) |req| { @@ -921,7 +944,7 @@ pub fn getRunnerRequest(ws: *WebServer) ?RunnerRequest { return null; } pub fn wait(ws: *WebServer) Io.Cancelable!RunnerRequest { - const io = ws.graph.io; + const io = ws.maker.graph.io; try ws.runner_request_mutex.lock(io); defer ws.runner_request_mutex.unlock(io); while (true) { diff --git a/lib/std/Build.zig b/lib/std/Build.zig index a6668c5c1daa5fde7ea2c52ff4433023e74c60eb..e7e523e0c85a9e71e450608228e9a829a87ff503 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -45,7 +45,6 @@ install_prefix: []const u8, /// Path to the directory containing build.zig. build_root: Cache.Directory, cache_root: Cache.Directory, -pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, debug_log_scopes: []const []const u8 = &.{}, debug_compile_errors: bool = false, debug_incremental: bool = false, @@ -176,18 +175,6 @@ pub const RunError = error{ ExecNotSupported, } || std.process.SpawnError; -pub const PkgConfigError = error{ - PkgConfigCrashed, - PkgConfigFailed, - PkgConfigNotInstalled, - PkgConfigInvalidOutput, -}; - -pub const PkgConfigPkg = struct { - name: []const u8, - desc: []const u8, -}; - const UserInputOptionsMap = StringHashMap(UserInputOption); const AvailableOptionsMap = StringHashMap(AvailableOption); diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 8b580f3a5da9a11903540524f7b98a445b2d9573..46b9cd0a52beab8c20c94802fae244a4c906e78c 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -8,13 +8,9 @@ const fs = std.fs; const assert = std.debug.assert; const panic = std.debug.panic; const StringHashMap = std.StringHashMap; -const Sha256 = std.crypto.hash.sha2.Sha256; const Allocator = std.mem.Allocator; const Step = std.Build.Step; const LazyPath = std.Build.LazyPath; -const PkgConfigPkg = std.Build.PkgConfigPkg; -const PkgConfigError = std.Build.PkgConfigError; -const RunError = std.Build.RunError; const Module = std.Build.Module; const InstallDir = std.Build.InstallDir; const GeneratedFile = std.Build.GeneratedFile; @@ -777,42 +773,6 @@ pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void { compile.exec_cmd_args = duped_args; } -const CliNamedModules = struct { - modules: std.AutoArrayHashMapUnmanaged(*Module, void), - names: std.StringArrayHashMapUnmanaged(void), - - /// Traverse the whole dependency graph and give every module a unique - /// name, ideally one named after what it's called somewhere in the graph. - /// It will help here to have both a mapping from module to name and a set - /// of all the currently-used names. - fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules { - var compile: CliNamedModules = .{ - .modules = .{}, - .names = .{}, - }; - const graph = root_module.getGraph(); - { - assert(graph.modules[0] == root_module); - try compile.modules.put(arena, root_module, {}); - try compile.names.put(arena, "root", {}); - } - for (graph.modules[1..], graph.names[1..]) |mod, orig_name| { - var name = orig_name; - var n: usize = 0; - while (true) { - const gop = try compile.names.getOrPut(arena, name); - if (!gop.found_existing) { - try compile.modules.putNoClobber(arena, mod, {}); - break; - } - name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n }); - n += 1; - } - } - return compile; - } -}; - fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) ![]const u8 { const step = &compile.step; const b = step.owner; diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig index 69061bdb136f607c9186951e9ad61b00b51571bc..dffc69c70e597f2ea75efd2a70b7f218d6f6be21 100644 --- a/lib/std/zig/Configuration.zig +++ b/lib/std/zig/Configuration.zig @@ -436,23 +436,23 @@ pub const Step = extern struct { }; pub const Tag = enum(u5) { - top_level, + check_file, + check_object, compile, + config_header, + fail, + fmt, install_artifact, - install_file, install_dir, + install_file, + objcopy, + options, remove_dir, - fail, - fmt, + run, + top_level, translate_c, - write_file, update_source_files, - run, - check_file, - check_object, - config_header, - objcopy, - options, + write_file, }; pub const TopLevel = struct { @@ -808,6 +808,10 @@ pub const Step = extern struct { _: u23 = 0, }; }; + + pub fn flags(s: *const Step, c: *const Configuration) Flags { + return @bitCast(c.extra[s.extra_index]); + } }; pub const MaxRss = enum(u32) {