From b336b0a5f898cdbcd7310a50324ce2ff0781958f Mon Sep 17 00:00:00 2001 From: Xavier Bouchoux Date: Tue, 23 Jul 2024 09:45:37 +0200 Subject: [PATCH] std.Build: Add a `finalize` function to Build.Steps It is called after the user has fully configured the steps in the build() function. --- lib/compiler/build_runner.zig | 36 +++++++++++++++++++++++++++ lib/std/Build/Step.zig | 17 ++++++++++++- lib/std/Build/Step/Compile.zig | 45 +++++++++++++++++++--------------- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig index 4d643222d723..6c3783ded622 100644 --- a/lib/compiler/build_runner.zig +++ b/lib/compiler/build_runner.zig @@ -335,6 +335,10 @@ pub fn main() !void { try builder.runBuild(root); } + for (builder.top_level_steps.values()) |s| { + try finalizeSteps(&s.step); + } + if (graph.needed_lazy_dependencies.entries.len != 0) { var buffer: std.ArrayListUnmanaged(u8) = .empty; for (graph.needed_lazy_dependencies.keys()) |k| { @@ -635,6 +639,7 @@ fn runStepNames( test_count += s.test_results.test_count; switch (s.state) { + .unfinalized => unreachable, .precheck_unstarted => unreachable, .precheck_started => unreachable, .running => unreachable, @@ -780,6 +785,7 @@ fn printStepStatus( run: *const Run, ) !void { switch (s.state) { + .unfinalized => unreachable, .precheck_unstarted => unreachable, .precheck_started => unreachable, .precheck_done => unreachable, @@ -977,6 +983,34 @@ fn printTreeStep( } } +/// Traverse the dependency graph after the user build() call, +/// this allows for checks and postprocessing after the steps are fully configured by the user. +fn finalizeSteps( + s: *Step, +) !void { + switch (s.state) { + .unfinalized => { + try s.finalize(); + s.state = .precheck_unstarted; + + for (s.dependencies.items) |dep| { + try finalizeSteps(dep); + } + }, + + .precheck_unstarted => {}, + + .precheck_started => unreachable, + .precheck_done => unreachable, + .dependency_failure => unreachable, + .running => unreachable, + .success => unreachable, + .failure => unreachable, + .skipped => unreachable, + .skipped_oom => unreachable, + } +} + /// Traverse the dependency graph depth-first and make it undirected by having /// steps know their dependants (they only know dependencies at start). /// Along the way, check that there is no dependency loop, and record the steps @@ -995,6 +1029,7 @@ fn constructGraphAndCheckForDependencyLoop( rand: std.Random, ) !void { switch (s.state) { + .unfinalized => unreachable, .precheck_started => { std.debug.print("dependency loop detected:\n {s}\n", .{s.name}); return error.DependencyLoopDetected; @@ -1057,6 +1092,7 @@ fn workerMakeOneStep( // dependency is not finished yet. return; }, + .unfinalized => unreachable, .precheck_unstarted => unreachable, .precheck_started => unreachable, } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 421c8fa579ac..797361566c5e 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -1,6 +1,7 @@ id: Id, name: []const u8, owner: *Build, +finalizeFn: FinalizeFn, makeFn: MakeFn, dependencies: std.ArrayList(*Step), @@ -68,6 +69,8 @@ pub const TestResults = struct { } }; +pub const FinalizeFn = *const fn (step: *Step) anyerror!void; + pub const MakeOptions = struct { progress_node: std.Progress.Node, thread_pool: *std.Thread.Pool, @@ -77,6 +80,7 @@ pub const MakeOptions = struct { pub const MakeFn = *const fn (step: *Step, options: MakeOptions) anyerror!void; pub const State = enum { + unfinalized, precheck_unstarted, precheck_started, /// This is also used to indicate "dirty" steps that have been modified @@ -183,6 +187,7 @@ pub const StepOptions = struct { id: Id, name: []const u8, owner: *Build, + finalizeFn: FinalizeFn = finalizeNoOp, makeFn: MakeFn = makeNoOp, first_ret_addr: ?usize = null, max_rss: usize = 0, @@ -195,11 +200,12 @@ pub fn init(options: StepOptions) Step { .id = options.id, .name = arena.dupe(u8, options.name) catch @panic("OOM"), .owner = options.owner, + .finalizeFn = options.finalizeFn, .makeFn = options.makeFn, .dependencies = std.ArrayList(*Step).init(arena), .dependants = .{}, .inputs = Inputs.init, - .state = .precheck_unstarted, + .state = .unfinalized, .max_rss = options.max_rss, .debug_stack_trace = blk: { const addresses = arena.alloc(usize, options.owner.debug_stack_frames_count) catch @panic("OOM"); @@ -222,6 +228,11 @@ pub fn init(options: StepOptions) Step { }; } +pub fn finalize(s: *Step) !void { + assert(s.state == .unfinalized); + try s.finalizeFn(s); +} + /// 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. @@ -266,6 +277,10 @@ pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace { }; } +fn finalizeNoOp(step: *Step) anyerror!void { + _ = step; +} + fn makeNoOp(step: *Step, options: MakeOptions) anyerror!void { _ = options; diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 7a45093bef53..e0bc2bf34cfe 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -207,9 +207,9 @@ generated_h: ?*GeneratedFile, /// Defaults to `std.math.maxInt(u16)` error_limit: ?u32 = null, -/// Computed during make(). +/// Computed during finalize(). is_linking_libc: bool = false, -/// Computed during make(). +/// Computed during finalize(). is_linking_libcpp: bool = false, no_builtin: bool = false, @@ -393,6 +393,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .id = base_id, .name = step_name, .owner = owner, + .finalizeFn = finalize, .makeFn = make, .max_rss = options.max_rss, }), @@ -1095,24 +1096,6 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { // emitted if there is nothing to link. var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null); - { - // Fully recursive iteration including dynamic libraries to detect - // libc and libc++ linkage. - var dep_it = compile.root_module.iterateDependencies(compile, true); - while (dep_it.next()) |key| { - if (key.module.link_libc == true) compile.is_linking_libc = true; - if (key.module.link_libcpp == true) compile.is_linking_libcpp = true; - } - } - - if (compile.kind == .pch) { - // precompiled headers must have a single input header file. - var it = compile.root_module.iterateDependencies(compile, false); - const link_objects = it.next().?.module.link_objects; - assert(link_objects.items.len == 1 and link_objects.items[0] == .c_source_file); - assert(it.next() == null); - } - var cli_named_modules = try CliNamedModules.init(arena, &compile.root_module); // For this loop, don't chase dynamic libraries because their link @@ -1839,6 +1822,28 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 { return try zig_args.toOwnedSlice(); } +fn finalize(step: *Step) !void { + const compile: *Compile = @fieldParentPtr("step", step); + + { + // Fully recursive iteration including dynamic libraries to detect + // libc and libc++ linkage. + var dep_it = compile.root_module.iterateDependencies(compile, true); + while (dep_it.next()) |key| { + if (key.module.link_libc == true) compile.is_linking_libc = true; + if (key.module.link_libcpp == true) compile.is_linking_libcpp = true; + } + } + + if (compile.kind == .pch) { + // precompiled headers must have a single input header file. + var it = compile.root_module.iterateDependencies(compile, false); + const link_objects = it.next().?.module.link_objects; + assert(link_objects.items.len == 1 and link_objects.items[0] == .c_source_file); + assert(it.next() == null); + } +} + fn make(step: *Step, options: Step.MakeOptions) !void { const b = step.owner; const compile: *Compile = @fieldParentPtr("step", step);