id: Id, name: []const u8, makeFn: *const fn (self: *Step) anyerror!void, dependencies: std.ArrayList(*Step), /// This field is empty during execution of the user's build script, and /// then populated during dependency loop checking in the build runner. dependants: std.ArrayListUnmanaged(*Step), state: State, /// Populated only if state is success. result: struct { err_code: anyerror, stderr: []u8, }, /// The return addresss associated with creation of this step that can be useful /// to print along with debugging messages. debug_stack_trace: [n_debug_stack_frames]usize, const n_debug_stack_frames = 4; pub const State = enum { precheck_unstarted, precheck_started, precheck_done, running, dependency_failure, success, failure, }; pub const Id = enum { top_level, compile, install_artifact, install_file, install_dir, log, remove_dir, fmt, translate_c, write_file, run, emulatable_run, check_file, check_object, config_header, objcopy, options, custom, pub fn Type(comptime id: Id) type { return switch (id) { .top_level => Build.TopLevelStep, .compile => Build.CompileStep, .install_artifact => Build.InstallArtifactStep, .install_file => Build.InstallFileStep, .install_dir => Build.InstallDirStep, .log => Build.LogStep, .remove_dir => Build.RemoveDirStep, .fmt => Build.FmtStep, .translate_c => Build.TranslateCStep, .write_file => Build.WriteFileStep, .run => Build.RunStep, .emulatable_run => Build.EmulatableRunStep, .check_file => Build.CheckFileStep, .check_object => Build.CheckObjectStep, .config_header => Build.ConfigHeaderStep, .objcopy => Build.ObjCopyStep, .options => Build.OptionsStep, .custom => @compileError("no type available for custom step"), }; } }; pub const Options = struct { id: Id, name: []const u8, makeFn: *const fn (self: *Step) anyerror!void = makeNoOp, first_ret_addr: ?usize = null, }; pub fn init(allocator: Allocator, options: Options) Step { var addresses = [1]usize{0} ** n_debug_stack_frames; const first_ret_addr = options.first_ret_addr orelse @returnAddress(); var stack_trace = std.builtin.StackTrace{ .instruction_addresses = &addresses, .index = 0, }; std.debug.captureStackTrace(first_ret_addr, &stack_trace); return .{ .id = options.id, .name = allocator.dupe(u8, options.name) catch @panic("OOM"), .makeFn = options.makeFn, .dependencies = std.ArrayList(*Step).init(allocator), .dependants = .{}, .state = .precheck_unstarted, .result = .{ .err_code = undefined, .stderr = &.{}, }, .debug_stack_trace = addresses, }; } pub fn make(self: *Step) !void { try self.makeFn(self); } pub fn dependOn(self: *Step, other: *Step) void { self.dependencies.append(other) catch @panic("OOM"); } pub fn getStackTrace(s: *Step) std.builtin.StackTrace { const stack_addresses = &s.debug_stack_trace; var len: usize = 0; while (len < n_debug_stack_frames and stack_addresses[len] != 0) { len += 1; } return .{ .instruction_addresses = stack_addresses, .index = len, }; } fn makeNoOp(self: *Step) anyerror!void { _ = self; } pub fn cast(step: *Step, comptime T: type) ?*T { if (step.id == T.base_id) { return @fieldParentPtr(T, "step", step); } return null; } const Step = @This(); const std = @import("../std.zig"); const Build = std.Build; const Allocator = std.mem.Allocator; const assert = std.debug.assert;