Skip to content

Commit

Permalink
std.Build: Add a finalize function to Build.Steps
Browse files Browse the repository at this point in the history
It is called after the user has fully configured the steps in the build() function.
  • Loading branch information
xxxbxxx committed Sep 23, 2024
1 parent 3207f7a commit b336b0a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 21 deletions.
36 changes: 36 additions & 0 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -780,6 +785,7 @@ fn printStepStatus(
run: *const Run,
) !void {
switch (s.state) {
.unfinalized => unreachable,
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
.precheck_done => unreachable,
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -1057,6 +1092,7 @@ fn workerMakeOneStep(
// dependency is not finished yet.
return;
},
.unfinalized => unreachable,
.precheck_unstarted => unreachable,
.precheck_started => unreachable,
}
Expand Down
17 changes: 16 additions & 1 deletion lib/std/Build/Step.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
id: Id,
name: []const u8,
owner: *Build,
finalizeFn: FinalizeFn,
makeFn: MakeFn,

dependencies: std.ArrayList(*Step),
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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");
Expand All @@ -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.
Expand Down Expand Up @@ -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;

Expand Down
45 changes: 25 additions & 20 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit b336b0a

Please sign in to comment.