commit afd7507a197d516c7240f92fc19e4f43adb0b79c (tree)
parent 0505318efe0d2757a344dded9ae1607f948f7511
Author: Andrew Kelley <andrew@ziglang.org>
Date: Tue, 17 Feb 2026 20:36:45 -0800
make runner: prepare steps for execution
Diffstat:
7 files changed, 506 insertions(+), 504 deletions(-)
diff --git a/lib/compiler/configure_runner.zig b/lib/compiler/configure_runner.zig
@@ -186,6 +186,8 @@ pub fn main(init: process.Init.Minimal) !void {
// but it is handled by the parent process. The build runner
// only sees this flag.
graph.system_package_mode = true;
+ } else if (mem.eql(u8, arg, "--have-run-args")) {
+ graph.have_run_args = true;
} else {
fatalWithHint("unrecognized argument: '{s}'", .{arg});
}
@@ -226,13 +228,69 @@ pub fn main(init: process.Init.Minimal) !void {
process.exit(0);
}
+const Serialize = struct {
+ arena: Allocator,
+ wc: *Configuration.Wip,
+ module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty,
+ package_map: std.AutoArrayHashMapUnmanaged(*std.Build, Configuration.Package.Index) = .empty,
+
+ fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
+ if (b.pkg_hash.len == 0) return .root;
+ const arena = s.arena;
+ const wc = s.wc;
+ const gop = try s.package_map.getOrPut(arena, b);
+ if (!gop.found_existing) {
+ gop.value_ptr.* = @enumFromInt(try wc.addExtra(@as(Configuration.Package, .{
+ .hash = try wc.addString(b.pkg_hash),
+ .dep_prefix = try wc.addString(b.dep_prefix),
+ })));
+ }
+ return gop.value_ptr.*;
+ }
+
+ fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
+ const wc = s.wc;
+ return @enumFromInt(switch (lp orelse return .none) {
+ .src_path => |src_path| i: {
+ const sub_path = try wc.addString(src_path.sub_path);
+ break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
+ .flags = .{},
+ .owner = try s.builderToPackage(src_path.owner),
+ .sub_path = sub_path,
+ }));
+ },
+ .generated => |generated| i: {
+ const sub_path = try wc.addString(generated.sub_path);
+ break :i try wc.addExtra(@as(Configuration.LazyPath.Generated, .{
+ .flags = .{ .up = @intCast(generated.up) },
+ .sub_path = sub_path,
+ }));
+ },
+ .cwd_relative => |cwd_relative_sub_path| i: {
+ const sub_path = try wc.addString(cwd_relative_sub_path);
+ break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{
+ .flags = .{ .base = .cwd },
+ .sub_path = sub_path,
+ }));
+ },
+ .dependency => |dependency| i: {
+ const sub_path = try wc.addString(dependency.sub_path);
+ break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
+ .flags = .{},
+ .owner = try s.builderToPackage(dependency.dependency.builder),
+ .sub_path = sub_path,
+ }));
+ },
+ });
+ }
+};
+
fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
const graph = b.graph;
const arena = graph.arena;
const gpa = wc.gpa;
- var module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty;
- defer module_map.deinit(gpa);
+ var s: Serialize = .{ .wc = wc, .arena = arena };
// Starting from all top-level steps in `b`, traverse the entire step graph
// and add all step dependencies implied by module graphs.
@@ -267,6 +325,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity);
wc.steps.appendAssumeCapacity(.{
.name = try wc.addString(step.name),
+ .owner = try s.builderToPackage(step.owner),
.deps = deps,
.max_rss = .fromBytes(step.max_rss),
.extra_index = switch (step.tag) {
@@ -367,7 +426,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.install_name = c.install_name != null,
.entitlements = c.entitlements != null,
},
- .root_module = try addModule(wc, &module_map, c.root_module),
+ .root_module = try addModule(&s, c.root_module),
.root_name = try wc.addString(c.name),
}));
@@ -383,13 +442,13 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
},
.dest_dir = try addInstallDir(wc, ia.dest_dir),
.dest_sub_path = try wc.addString(ia.dest_sub_path),
- .emitted_bin = try addOptionalLazyPath(wc, ia.emitted_bin),
+ .emitted_bin = try s.addOptionalLazyPath(ia.emitted_bin),
.implib_dir = try addInstallDir(wc, ia.implib_dir),
- .emitted_implib = try addOptionalLazyPath(wc, ia.emitted_implib),
+ .emitted_implib = try s.addOptionalLazyPath(ia.emitted_implib),
.pdb_dir = try addInstallDir(wc, ia.pdb_dir),
- .emitted_pdb = try addOptionalLazyPath(wc, ia.emitted_pdb),
+ .emitted_pdb = try s.addOptionalLazyPath(ia.emitted_pdb),
.h_dir = try addInstallDir(wc, ia.h_dir),
- .emitted_h = try addOptionalLazyPath(wc, ia.emitted_h),
+ .emitted_h = try s.addOptionalLazyPath(ia.emitted_h),
.artifact = stepIndex(&step_map, &ia.artifact.step),
}));
},
@@ -440,7 +499,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
},
.file_inputs_len = @intCast(run.file_inputs.items.len),
.args_len = @intCast(run.argv.items.len),
- .cwd = try addOptionalLazyPath(wc, run.cwd),
+ .cwd = try s.addOptionalLazyPath(run.cwd),
.captured_stdout = captured_stdout,
.captured_stderr = captured_stderr,
}));
@@ -469,13 +528,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
});
}
-fn addModule(
- wc: *Configuration.Wip,
- module_map: *std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index),
- m: *std.Build.Module,
-) !Configuration.Module.Index {
- if (module_map.get(m)) |index| return index;
+fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
+ if (s.module_map.get(m)) |index| return index;
+ const wc = s.wc;
+ const arena = s.arena;
const gpa = wc.gpa;
const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len);
const import_table_extra_len = 1 + 2 * m.import_table.entries.len;
@@ -494,7 +551,7 @@ fn addModule(
@intFromEnum(import_table) + 1 + m.import_table.entries.len..,
) |dep, extra_index| {
log.err("TODO module dependencies can be cyclic", .{});
- wc.extra.items[extra_index] = @intFromEnum(try addModule(wc, module_map, dep));
+ wc.extra.items[extra_index] = @intFromEnum(try addModule(s, dep));
}
const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{
@@ -528,15 +585,15 @@ fn addModule(
.link_libcpp = .init(m.strip),
.no_builtin = .init(m.strip),
},
- .owner = try builderToPackage(wc, m.owner),
- .root_source_file = try addOptionalLazyPath(wc, m.root_source_file),
+ .owner = try s.builderToPackage(m.owner),
+ .root_source_file = try s.addOptionalLazyPath(m.root_source_file),
.import_table = import_table,
.resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
})));
log.err("TODO serialize the trailing Module data", .{});
- try module_map.putNoClobber(gpa, m, module_index);
+ try s.module_map.putNoClobber(arena, m, module_index);
return module_index;
}
@@ -553,46 +610,6 @@ fn addOptionalResolvedTarget(
})));
}
-fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
- return @enumFromInt(switch (lp orelse return .none) {
- .src_path => |src_path| i: {
- const sub_path = try wc.addString(src_path.sub_path);
- break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
- .flags = .{},
- .owner = try builderToPackage(wc, src_path.owner),
- .sub_path = sub_path,
- }));
- },
- .generated => |generated| i: {
- const sub_path = try wc.addString(generated.sub_path);
- break :i try wc.addExtra(@as(Configuration.LazyPath.Generated, .{
- .flags = .{ .up = @intCast(generated.up) },
- .sub_path = sub_path,
- }));
- },
- .cwd_relative => |cwd_relative_sub_path| i: {
- const sub_path = try wc.addString(cwd_relative_sub_path);
- break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{
- .flags = .{ .base = .cwd },
- .sub_path = sub_path,
- }));
- },
- .dependency => |dependency| i: {
- const sub_path = try wc.addString(dependency.sub_path);
- break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
- .flags = .{},
- .owner = try builderToPackage(wc, dependency.dependency.builder),
- .sub_path = sub_path,
- }));
- },
- });
-}
-
-fn builderToPackage(wc: *Configuration.Wip, b: *std.Build) !Configuration.Package {
- if (b.pkg_hash.len == 0) return .root;
- return .fromHash(try wc.addString(b.pkg_hash));
-}
-
fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
switch (install_dir orelse return .none) {
.prefix => return .prefix,
@@ -665,9 +682,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
return nextArg(args, idx) orelse {
- fatal("expected argument after {q}\n access the help menu with \"zig build -h\"", .{
- args[idx.* - 1],
- });
+ fatalWithHint("expected argument after: {s}", .{args[idx.* - 1]});
};
}
@@ -700,7 +715,8 @@ const MultilineErrors = enum { indent, newline, none };
const Summary = enum { all, new, failures, line, none };
fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
- fatal(f ++ "\n access the help menu with \"zig build -h\"", args);
+ log.info("to access the help menu: zig build -h", .{});
+ fatal(f, args);
}
fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
@@ -725,7 +741,7 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration
});
}
if (bad) {
- log.info("access the help menu with \"zig build -h\"", .{});
+ log.info("help menu contains available options: zig build -h", .{});
process.exit(1);
}
}
diff --git a/lib/compiler/maker.zig b/lib/compiler/maker.zig
@@ -17,7 +17,7 @@ const process = std.process;
const Fuzz = @import("maker/Fuzz.zig");
const Graph = @import("maker/Graph.zig");
-const Step = void; // @import("maker/Step.zig");
+const Step = @import("maker/Step.zig");
const Watch = @import("maker/Watch.zig");
const WebServer = @import("maker/WebServer.zig");
@@ -100,8 +100,8 @@ pub fn main(init: process.Init.Minimal) !void {
graph.cache.addPrefix(global_cache_directory);
graph.cache.hash.addBytes(builtin.zig_version_string);
- var targets = std.array_list.Managed([]const u8).init(arena);
- var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);
+ var step_names: std.ArrayList([]const u8) = .empty;
+ var debug_log_scopes: std.ArrayList([]const u8) = .empty;
var help_menu = false;
var steps_menu = false;
var print_configuration = false;
@@ -151,29 +151,6 @@ pub fn main(init: process.Init.Minimal) !void {
}
}
- const scanned_config: ScannedConfig = sc: {
- const configuration = c: {
- var file = cwd.openFile(io, configure_path, .{}) catch |err|
- fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });
- defer file.close(io);
- break :c Configuration.loadFile(arena, io, file) catch |err|
- fatal("failed to load configuration file {s}: {t}", .{ configure_path, err });
- };
- var top_level_steps: std.ArrayList(Configuration.Step.Index) = .empty;
- for (configuration.steps, 0..) |*conf_step, step_index| {
- const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]);
- if (flags.tag == .top_level) {
- try top_level_steps.append(arena, @enumFromInt(step_index));
- }
- }
- break :sc .{
- .configuration = configuration,
- .top_level_steps = top_level_steps.items,
- };
- };
-
- log.err("TODO handle user -D options", .{});
-
while (nextArg(args, &arg_idx)) |arg| {
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
@@ -291,7 +268,7 @@ pub fn main(init: process.Init.Minimal) !void {
};
} else if (mem.eql(u8, arg, "--debug-log")) {
const next_arg = nextArgOrFatal(args, &arg_idx);
- try debug_log_scopes.append(next_arg);
+ try debug_log_scopes.append(arena, next_arg);
} else if (mem.eql(u8, arg, "--debug-pkg-config")) {
debug_pkg_config = true;
} else if (mem.eql(u8, arg, "--debug-rt")) {
@@ -395,7 +372,7 @@ pub fn main(init: process.Init.Minimal) !void {
fatalWithHint("unrecognized argument: '{s}'", .{arg});
}
} else {
- try targets.append(arg);
+ try step_names.append(arena, arg);
}
}
@@ -408,6 +385,29 @@ pub fn main(init: process.Init.Minimal) !void {
.off => .no_color,
};
+ const scanned_config: ScannedConfig = sc: {
+ const configuration = c: {
+ var file = cwd.openFile(io, configure_path, .{}) catch |err|
+ fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });
+ defer file.close(io);
+ break :c Configuration.loadFile(arena, io, file) catch |err|
+ fatal("failed to load configuration file {s}: {t}", .{ configure_path, err });
+ };
+ var top_level_steps: std.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]);
+ if (flags.tag == .top_level) {
+ const name = step_index.ptr(&configuration).name.slice(&configuration);
+ try top_level_steps.put(arena, name, step_index);
+ }
+ }
+ break :sc .{
+ .configuration = configuration,
+ .top_level_steps = top_level_steps,
+ };
+ };
+
if (help_menu) {
var w = initStdoutWriter(io);
scanned_config.printUsage(&graph, w) catch |err| switch (err) {
@@ -467,10 +467,17 @@ pub fn main(init: process.Init.Minimal) !void {
.sub_path = cwd_relative,
} else try install_prefix_path.join(arena, "include");
- if (true) @panic("TODO");
-
var run: Run = .{
.gpa = gpa,
+ .graph = &graph,
+ .scanned_config = &scanned_config,
+ .install_paths = .{
+ .prefix = install_prefix_path,
+ .lib = install_lib_path,
+ .bin = install_bin_path,
+ .include = install_include_path,
+ },
+ .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
.available_rss = max_rss,
.max_rss_is_default = false,
@@ -486,13 +493,6 @@ pub fn main(init: process.Init.Minimal) !void {
.error_style = error_style,
.multiline_errors = multiline_errors,
.summary = summary orelse if (watch or webui_listen != null) .line else .failures,
-
- .install_paths = .{
- .prefix = install_prefix_path,
- .lib = install_lib_path,
- .bin = install_bin_path,
- .include = install_include_path,
- },
};
defer {
run.memory_blocked_steps.deinit(gpa);
@@ -504,17 +504,16 @@ pub fn main(init: process.Init.Minimal) !void {
run.max_rss_is_default = true;
}
- prepare(arena, &graph, targets.items, &run) catch |err| switch (err) {
+ run.prepare(step_names.items) catch |err| switch (err) {
error.DependencyLoopDetected, error.InsufficientMemory => {
- // Perhaps in the future there could be an Advanced Options flag
- // such as --debug-build-runner-leaks which would make this code
- // return instead of calling exit.
_ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
process.exit(1);
},
else => |e| return e,
};
+ if (true) @panic("TODO");
+
var w: Watch = w: {
if (!watch) break :w undefined;
if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
@@ -547,7 +546,7 @@ pub fn main(init: process.Init.Minimal) !void {
}) {
if (run.web_server) |*ws| ws.startBuild();
- try runStepNames(graph, targets.items, main_progress_node, &run, fuzz);
+ try run.makeStepNames(step_names, main_progress_node, fuzz);
if (run.web_server) |*web_server| {
if (fuzz) |mode| if (mode != .forever) fatal(
@@ -628,6 +627,10 @@ fn countSubProcesses(all_steps: []const *Step) usize {
const Run = struct {
gpa: Allocator,
+ graph: *Graph,
+ install_paths: InstallPaths,
+ scanned_config: *const ScannedConfig,
+ steps: []Step,
available_rss: usize,
max_rss_is_default: bool,
@@ -637,309 +640,331 @@ const Run = struct {
watch: bool,
web_server: if (!builtin.single_threaded) ?WebServer else ?noreturn,
/// Allocated into `gpa`.
- memory_blocked_steps: std.ArrayList(*Step),
+ memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
/// Allocated into `gpa`.
- step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
+ step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
error_style: ErrorStyle,
multiline_errors: MultilineErrors,
summary: Summary,
-};
-fn prepare(graph: *Graph, step_names: []const []const u8, run: *Run) !void {
- const arena = graph.arena;
- const seed: u32 = graph.random_seed;
- const gpa = run.gpa;
- const step_stack = &run.step_stack;
+ const InstallPaths = struct {
+ prefix: Path,
+ lib: Path,
+ bin: Path,
+ include: Path,
+ };
- if (step_names.len == 0) {
- try step_stack.put(gpa, graph.configuration.default_step, {});
- } else {
- try step_stack.ensureUnusedCapacity(gpa, step_names.len);
- for (0..step_names.len) |i| {
- const step_name = step_names[step_names.len - i - 1];
- const s = run.top_level_steps.get(step_name) orelse {
- log.info("access the help menu with 'zig build -h'", .{});
- fatal("no such step: {s}", .{step_name});
- };
- step_stack.putAssumeCapacity(&s.step, {});
- }
+ fn stepByIndex(run: *const Run, i: Configuration.Step.Index) *Step {
+ return &run.steps[@intFromEnum(i)];
}
- const starting_steps = try arena.dupe(*Step, step_stack.keys());
+ fn prepare(run: *Run, step_names: []const []const u8) !void {
+ const gpa = run.gpa;
+ const graph = run.graph;
+ const arena = graph.arena;
+ const seed: u32 = graph.random_seed;
+ const step_stack = &run.step_stack;
+ const c = &run.scanned_config.configuration;
- var rng = std.Random.DefaultPrng.init(seed);
- const rand = rng.random();
- rand.shuffle(*Step, starting_steps);
+ @memset(run.steps, .{});
- for (starting_steps) |s| {
- try constructGraphAndCheckForDependencyLoop(gpa, s, &run.step_stack, rand);
- }
+ if (step_names.len == 0) {
+ try step_stack.put(gpa, c.default_step, {});
+ } else {
+ try step_stack.ensureUnusedCapacity(gpa, step_names.len);
+ for (0..step_names.len) |i| {
+ const step_name = step_names[step_names.len - i - 1];
+ const s = run.scanned_config.top_level_steps.get(step_name) orelse {
+ log.info("to list available steps: zig build -l", .{});
+ fatal("no such step: {s}", .{step_name});
+ };
+ step_stack.putAssumeCapacity(s, {});
+ }
+ }
- {
- // Check that we have enough memory to complete the build.
- var any_problems = false;
- var max_needed: usize = 0;
- for (step_stack.keys()) |s| {
- if (s.max_rss == 0) continue;
- max_needed = @max(max_needed, s.max_rss);
- if (s.max_rss > run.available_rss) {
- if (run.skip_oom_steps) {
- s.state = .skipped_oom;
- for (s.dependants.items) |dependant| {
- dependant.pending_deps -= 1;
+ const starting_steps = try arena.dupe(Configuration.Step.Index, step_stack.keys());
+
+ var rng = std.Random.DefaultPrng.init(seed);
+ const rand = rng.random();
+ rand.shuffle(Configuration.Step.Index, starting_steps);
+
+ for (starting_steps) |s| {
+ try constructGraphAndCheckForDependencyLoop(gpa, c, run.steps, s, &run.step_stack, rand);
+ }
+
+ {
+ // Check that we have enough memory to complete the build.
+ var any_problems = false;
+ var max_needed: usize = 0;
+ for (step_stack.keys()) |step_index| {
+ const make_step = run.stepByIndex(step_index);
+ const conf_step = step_index.ptr(c);
+ const max_rss = conf_step.max_rss.toBytes();
+ if (max_rss == 0) continue;
+ max_needed = @max(max_needed, max_rss);
+ if (max_rss > run.available_rss) {
+ if (run.skip_oom_steps) {
+ make_step.state = .skipped_oom;
+ for (make_step.dependants.items) |dependant| {
+ dependant.pending_deps -= 1;
+ }
+ } else {
+ log.err("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory", .{
+ conf_step.owner.depPrefixSlice(c),
+ conf_step.name.slice(c),
+ max_rss,
+ run.available_rss,
+ });
+ any_problems = true;
}
- } else {
- std.log.err("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory", .{
- s.owner.dep_prefix, s.name, s.max_rss, run.available_rss,
- });
- any_problems = true;
}
}
- }
- if (any_problems) {
- if (run.max_rss_is_default) {
- std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
- max_needed,
- });
+ if (any_problems) {
+ if (run.max_rss_is_default) {
+ std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
+ max_needed,
+ });
+ }
+ return error.InsufficientMemory;
}
- return error.InsufficientMemory;
}
}
-}
-fn runStepNames(
- graph: *Graph,
- step_names: []const []const u8,
- parent_prog_node: std.Progress.Node,
- run: *Run,
- fuzz: ?Fuzz.Mode,
-) !void {
- const gpa = run.gpa;
- const io = graph.io;
- const step_stack = &run.step_stack;
+ fn makeStepNames(
+ run: *Run,
+ step_names: []const []const u8,
+ parent_prog_node: std.Progress.Node,
+ fuzz: ?Fuzz.Mode,
+ ) !void {
+ const graph = run.graph;
+ const gpa = run.gpa;
+ const io = graph.io;
+ const step_stack = &run.step_stack;
+ const top_level_steps = &run.scanned_config.top_level_steps;
- {
- // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
- // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
- // a step is initial when it actually became ready due to an earlier initial step.
- var initial_set: std.ArrayList(*Step) = .empty;
- defer initial_set.deinit(gpa);
- try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
- for (step_stack.keys()) |s| {
- if (s.state == .precheck_done and s.pending_deps == 0) {
- initial_set.appendAssumeCapacity(s);
+ {
+ // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
+ // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
+ // a step is initial when it actually became ready due to an earlier initial step.
+ var initial_set: std.ArrayList(*Step) = .empty;
+ defer initial_set.deinit(gpa);
+ try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
+ for (step_stack.keys()) |s| {
+ if (s.state == .precheck_done and s.pending_deps == 0) {
+ initial_set.appendAssumeCapacity(s);
+ }
}
- }
- const step_prog = parent_prog_node.start("steps", step_stack.count());
- defer step_prog.end();
+ const step_prog = parent_prog_node.start("steps", step_stack.count());
+ defer step_prog.end();
- var group: Io.Group = .init;
- defer group.cancel(io);
- // Start working on all of the initial steps...
- for (initial_set.items) |s| try stepReady(&group, s, step_prog, run);
- // ...and `makeStep` will trigger every other step when their last dependency finishes.
- try group.await(io);
- }
+ var group: Io.Group = .init;
+ defer group.cancel(io);
+ // Start working on all of the initial steps...
+ for (initial_set.items) |s| try stepReady(&group, s, step_prog, run);
+ // ...and `makeStep` will trigger every other step when their last dependency finishes.
+ try group.await(io);
+ }
- assert(run.memory_blocked_steps.items.len == 0);
+ assert(run.memory_blocked_steps.items.len == 0);
- var test_pass_count: usize = 0;
- var test_skip_count: usize = 0;
- var test_fail_count: usize = 0;
- var test_crash_count: usize = 0;
- var test_timeout_count: usize = 0;
+ var test_pass_count: usize = 0;
+ var test_skip_count: usize = 0;
+ var test_fail_count: usize = 0;
+ var test_crash_count: usize = 0;
+ var test_timeout_count: usize = 0;
- var test_count: usize = 0;
+ var test_count: usize = 0;
- var success_count: usize = 0;
- var skipped_count: usize = 0;
- var failure_count: usize = 0;
- var pending_count: usize = 0;
- var total_compile_errors: usize = 0;
+ var success_count: usize = 0;
+ var skipped_count: usize = 0;
+ var failure_count: usize = 0;
+ var pending_count: usize = 0;
+ var total_compile_errors: usize = 0;
- var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
- defer cleanup_task.await(io);
+ var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
+ defer cleanup_task.await(io);
- for (step_stack.keys()) |s| {
- test_pass_count += s.test_results.passCount();
- test_skip_count += s.test_results.skip_count;
- test_fail_count += s.test_results.fail_count;
- test_crash_count += s.test_results.crash_count;
- test_timeout_count += s.test_results.timeout_count;
+ for (step_stack.keys()) |s| {
+ test_pass_count += s.test_results.passCount();
+ test_skip_count += s.test_results.skip_count;
+ test_fail_count += s.test_results.fail_count;
+ test_crash_count += s.test_results.crash_count;
+ test_timeout_count += s.test_results.timeout_count;
- test_count += s.test_results.test_count;
+ test_count += s.test_results.test_count;
- switch (s.state) {
- .precheck_unstarted => unreachable,
- .precheck_started => unreachable,
- .precheck_done => unreachable,
- .dependency_failure => pending_count += 1,
- .success => success_count += 1,
- .skipped, .skipped_oom => skipped_count += 1,
- .failure => {
- failure_count += 1;
- const compile_errors_len = s.result_error_bundle.errorMessageCount();
- if (compile_errors_len > 0) {
- total_compile_errors += compile_errors_len;
- }
- },
- }
- }
-
- if (fuzz) |mode| blk: {
- 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 => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
- else => {},
- }
- if (@bitSizeOf(usize) != 64) {
- // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
- // being compatible with file system's u64 return value. This is not the case
- // on 32-bit platforms.
- // Affects or affected by issues #5185, #22523, and #22464.
- fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
+ switch (s.state) {
+ .precheck_unstarted => unreachable,
+ .precheck_started => unreachable,
+ .precheck_done => unreachable,
+ .dependency_failure => pending_count += 1,
+ .success => success_count += 1,
+ .skipped, .skipped_oom => skipped_count += 1,
+ .failure => {
+ failure_count += 1;
+ const compile_errors_len = s.result_error_bundle.errorMessageCount();
+ if (compile_errors_len > 0) {
+ total_compile_errors += compile_errors_len;
+ }
+ },
+ }
}
- switch (mode) {
- .forever => break :blk,
- .limit => {},
- }
+ if (fuzz) |mode| blk: {
+ 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 => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
+ else => {},
+ }
+ if (@bitSizeOf(usize) != 64) {
+ // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
+ // being compatible with file system's u64 return value. This is not the case
+ // on 32-bit platforms.
+ // Affects or affected by issues #5185, #22523, and #22464.
+ fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
+ }
- 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});
- defer f.deinit();
-
- f.start();
- try f.waitAndPrintReport();
- }
+ switch (mode) {
+ .forever => break :blk,
+ .limit => {},
+ }
- // Every test has a state
- assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
+ 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});
+ defer f.deinit();
+
+ f.start();
+ try f.waitAndPrintReport();
+ }
- if (failure_count == 0) {
- std.Progress.setStatus(.success);
- } else {
- std.Progress.setStatus(.failure);
- }
+ // Every test has a state
+ assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
- summary: {
- switch (run.summary) {
- .all, .new, .line => {},
- .failures => if (failure_count == 0) break :summary,
- .none => break :summary,
+ if (failure_count == 0) {
+ std.Progress.setStatus(.success);
+ } else {
+ std.Progress.setStatus(.failure);
}
- const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
- defer io.unlockStderr();
- const t = stderr.terminal();
- const w = &stderr.file_writer.interface;
-
- const total_count = success_count + failure_count + pending_count + skipped_count;
- t.setColor(.cyan) catch {};
- t.setColor(.bold) catch {};
- w.writeAll("Build Summary: ") catch {};
- t.setColor(.reset) catch {};
- w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
- {
- t.setColor(.dim) catch {};
- var first = true;
- if (skipped_count > 0) {
- w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {};
- first = false;
- }
- if (failure_count > 0) {
- w.print("{s}{d} failed", .{ if (first) " (" else ", ", failure_count }) catch {};
- first = false;
+ summary: {
+ switch (run.summary) {
+ .all, .new, .line => {},
+ .failures => if (failure_count == 0) break :summary,
+ .none => break :summary,
}
- if (!first) w.writeByte(')') catch {};
- t.setColor(.reset) catch {};
- }
- if (test_count > 0) {
- w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
- t.setColor(.dim) catch {};
- var first = true;
- if (test_skip_count > 0) {
- w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {};
- first = false;
- }
- if (test_fail_count > 0) {
- w.print("{s}{d} failed", .{ if (first) " (" else ", ", test_fail_count }) catch {};
- first = false;
- }
- if (test_crash_count > 0) {
- w.print("{s}{d} crashed", .{ if (first) " (" else ", ", test_crash_count }) catch {};
- first = false;
+ const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
+ defer io.unlockStderr();
+ const t = stderr.terminal();
+ const w = &stderr.file_writer.interface;
+
+ const total_count = success_count + failure_count + pending_count + skipped_count;
+ t.setColor(.cyan) catch {};
+ t.setColor(.bold) catch {};
+ w.writeAll("Build Summary: ") catch {};
+ t.setColor(.reset) catch {};
+ w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
+ {
+ t.setColor(.dim) catch {};
+ var first = true;
+ if (skipped_count > 0) {
+ w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {};
+ first = false;
+ }
+ if (failure_count > 0) {
+ w.print("{s}{d} failed", .{ if (first) " (" else ", ", failure_count }) catch {};
+ first = false;
+ }
+ if (!first) w.writeByte(')') catch {};
+ t.setColor(.reset) catch {};
}
- if (test_timeout_count > 0) {
- w.print("{s}{d} timed out", .{ if (first) " (" else ", ", test_timeout_count }) catch {};
- first = false;
+
+ if (test_count > 0) {
+ w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
+ t.setColor(.dim) catch {};
+ var first = true;
+ if (test_skip_count > 0) {
+ w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {};
+ first = false;
+ }
+ if (test_fail_count > 0) {
+ w.print("{s}{d} failed", .{ if (first) " (" else ", ", test_fail_count }) catch {};
+ first = false;
+ }
+ if (test_crash_count > 0) {
+ w.print("{s}{d} crashed", .{ if (first) " (" else ", ", test_crash_count }) catch {};
+ first = false;
+ }
+ if (test_timeout_count > 0) {
+ w.print("{s}{d} timed out", .{ if (first) " (" else ", ", test_timeout_count }) catch {};
+ first = false;
+ }
+ if (!first) w.writeByte(')') catch {};
+ t.setColor(.reset) catch {};
}
- if (!first) w.writeByte(')') catch {};
- t.setColor(.reset) catch {};
- }
- w.writeAll("\n") catch {};
+ w.writeAll("\n") catch {};
- if (run.summary == .line) break :summary;
+ if (run.summary == .line) break :summary;
- // Print a fancy tree with build results.
- var step_stack_copy = try step_stack.clone(gpa);
- defer step_stack_copy.deinit(gpa);
+ // Print a fancy tree with build results.
+ var step_stack_copy = try step_stack.clone(gpa);
+ defer step_stack_copy.deinit(gpa);
- var print_node: PrintNode = .{ .parent = null };
- if (step_names.len == 0) {
- print_node.last = true;
- printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};
- } else {
- const last_index = if (run.summary == .all) run.top_level_steps.count() else blk: {
- var i: usize = step_names.len;
- while (i > 0) {
- i -= 1;
- const step = run.top_level_steps.get(step_names[i]).?.step;
- const found = switch (run.summary) {
- .all, .line, .none => unreachable,
- .failures => step.state != .success,
- .new => !step.result_cached,
- };
- if (found) break :blk i;
+ var print_node: PrintNode = .{ .parent = null };
+ if (step_names.len == 0) {
+ print_node.last = true;
+ printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};
+ } else {
+ const last_index = if (run.summary == .all) top_level_steps.count() else blk: {
+ var i: usize = step_names.len;
+ while (i > 0) {
+ i -= 1;
+ const step = top_level_steps.get(step_names[i]).?.step;
+ const found = switch (run.summary) {
+ .all, .line, .none => unreachable,
+ .failures => step.state != .success,
+ .new => !step.result_cached,
+ };
+ if (found) break :blk i;
+ }
+ break :blk top_level_steps.count();
+ };
+ for (step_names, 0..) |step_name, i| {
+ const tls = top_level_steps.get(step_name).?;
+ print_node.last = i + 1 == last_index;
+ printTreeStep(graph, &tls.step, run, t, &print_node, &step_stack_copy) catch {};
}
- break :blk run.top_level_steps.count();
- };
- for (step_names, 0..) |step_name, i| {
- const tls = run.top_level_steps.get(step_name).?;
- print_node.last = i + 1 == last_index;
- printTreeStep(graph, &tls.step, run, t, &print_node, &step_stack_copy) catch {};
}
+ w.writeByte('\n') catch {};
}
- w.writeByte('\n') catch {};
- }
- if (run.watch or run.web_server != null) return;
+ if (run.watch or run.web_server != null) return;
- // Perhaps in the future there could be an Advanced Options flag such as
- // --debug-build-runner-leaks which would make this code return instead of
- // calling exit.
+ // Perhaps in the future there could be an Advanced Options flag such as
+ // --debug-build-runner-leaks which would make this code return instead of
+ // calling exit.
- const code: u8 = code: {
- if (failure_count == 0) break :code 0; // success
- if (run.error_style.verboseContext()) break :code 1; // failure; print build command
- break :code 2; // failure; do not print build command
- };
- _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
- process.exit(code);
-}
+ const code: u8 = code: {
+ if (failure_count == 0) break :code 0; // success
+ if (run.error_style.verboseContext()) break :code 1; // failure; print build command
+ break :code 2; // failure; do not print build command
+ };
+ _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
+ process.exit(code);
+ }
+};
const PrintNode = struct {
parent: ?*PrintNode,
@@ -1221,40 +1246,47 @@ fn printTreeStep(
/// random order
fn constructGraphAndCheckForDependencyLoop(
gpa: Allocator,
- s: *Step,
- step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
+ c: *const Configuration,
+ steps: []Step,
+ step_index: Configuration.Step.Index,
+ step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
rand: std.Random,
-) !void {
+) error{ DependencyLoopDetected, OutOfMemory }!void {
+ const s: *Step = &steps[@intFromEnum(step_index)];
switch (s.state) {
.precheck_started => {
- std.debug.print("dependency loop detected:\n {s}\n", .{s.name});
+ log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)});
return error.DependencyLoopDetected;
},
.precheck_unstarted => {
s.state = .precheck_started;
- try step_stack.ensureUnusedCapacity(gpa, s.dependencies.items.len);
+ const step = step_index.ptr(c);
+ const dependencies = step.deps.slice(c);
+ try step_stack.ensureUnusedCapacity(gpa, dependencies.len);
// We dupe to avoid shuffling the steps in the summary, it depends
- // on s.dependencies' order.
- const deps = try gpa.dupe(*Step, s.dependencies.items);
+ // on dependencies' order.
+ const deps = try gpa.dupe(Configuration.Step.Index, dependencies);
defer gpa.free(deps);
- rand.shuffle(*Step, deps);
+ rand.shuffle(Configuration.Step.Index, deps);
for (deps) |dep| {
+ const dep_step: *Step = &steps[@intFromEnum(dep)];
try step_stack.put(gpa, dep, {});
- try dep.dependants.append(gpa, s);
- constructGraphAndCheckForDependencyLoop(gpa, dep, step_stack, rand) catch |err| {
- if (err == error.DependencyLoopDetected) {
- std.debug.print(" {s}\n", .{s.name});
- }
- return err;
+ try dep_step.dependants.append(gpa, s);
+ constructGraphAndCheckForDependencyLoop(gpa, c, steps, dep, step_stack, rand) catch |err| switch (err) {
+ error.DependencyLoopDetected => {
+ log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
+ return err;
+ },
+ else => return err,
};
}
s.state = .precheck_done;
- s.pending_deps = @intCast(s.dependencies.items.len);
+ s.pending_deps = @intCast(dependencies.len);
},
.precheck_done => {},
@@ -1492,8 +1524,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
return nextArg(args, idx) orelse {
- log.info("access the help menu with \"zig build -h\"", .{});
- fatal("expected argument after {q}", .{args[idx.* - 1]});
+ fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});
};
}
@@ -1532,7 +1563,7 @@ const MultilineErrors = enum { indent, newline, none };
const Summary = enum { all, new, failures, line, none };
fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
- log.info("access the help menu with 'zig build -h'", .{});
+ log.info("to access the help menu: zig build -h", .{});
fatal(f, args);
}
@@ -1547,13 +1578,6 @@ fn cleanTmpFiles(io: Io, steps: []const *Step) void {
}
}
-const InstallPaths = struct {
- prefix: Path,
- lib: Path,
- bin: Path,
- include: Path,
-};
-
var stdio_buffer_allocation: [256]u8 = undefined;
var stdout_writer_allocation: Io.File.Writer = undefined;
@@ -1564,17 +1588,20 @@ fn initStdoutWriter(io: Io) *Writer {
const ScannedConfig = struct {
configuration: Configuration,
- top_level_steps: []const Configuration.Step.Index,
+ top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index),
fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
+ const c = &sc.configuration;
var serializer: std.zon.Serializer = .{ .writer = w };
var s = try serializer.beginStruct(.{});
- try s.field("default_step", @intFromEnum(sc.configuration.default_step), .{});
+ try s.field("default_step", @intFromEnum(c.default_step), .{});
{
- var tuple = try s.beginTupleField("top_level_steps", .{});
- for (sc.top_level_steps) |step| try tuple.field(@intFromEnum(step), .{});
- try tuple.end();
+ var ss = try s.beginStructField("top_level_steps", .{});
+ for (sc.top_level_steps.keys(), sc.top_level_steps.values()) |name, step| {
+ try ss.field(name, @intFromEnum(step), .{});
+ }
+ try ss.end();
}
try s.end();
@@ -1583,9 +1610,8 @@ const ScannedConfig = struct {
fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
const arena = graph.arena;
const c = &sc.configuration;
- for (sc.top_level_steps) |step_index| {
+ for (sc.top_level_steps.keys(), sc.top_level_steps.values()) |name, step_index| {
const step = step_index.ptr(c);
- const name = step.name.slice(c);
const decorated_name = if (step_index == c.default_step)
try fmt.allocPrint(arena, "{s} (default)", .{name})
else
@@ -1679,8 +1705,8 @@ const ScannedConfig = struct {
try w.writeAll(
\\
\\General Options:
- \\ -h, --help Print this help and exit
- \\ -l, --list-steps Print available steps
+ \\ -h, --help Print this help to stdout and exit
+ \\ -l, --list-steps Print available steps to stdout and exit
\\
\\ -p, --prefix [path] Where to install files (default: zig-out)
\\ --prefix-lib-dir [path] Where to install libraries
diff --git a/lib/compiler/maker/Package.zig b/lib/compiler/maker/Package.zig
@@ -1,30 +0,0 @@
-const Package = @This();
-
-const std = @import("std");
-
-install_prefix: []const u8,
-install_path: []const u8,
-dest_dir: ?[]const u8,
-lib_dir: []const u8,
-exe_dir: []const u8,
-h_dir: []const u8,
-/// Path to the directory containing build.zig.
-build_root: std.Build.Cache.Path,
-
-fn determineAndApplyInstallPrefix(p: *Package) error{OutOfMemory}!void {
- // Create an installation directory local to this package. This will be used when
- // dependant packages require a standard prefix, such as include directories for C headers.
- var hash = p.graph.cache.hash;
- // Random bytes to make unique. Refresh this with new random bytes when
- // implementation is modified in a non-backwards-compatible way.
- hash.add(@as(u32, 0xd8cb0056));
- hash.addBytes(p.dep_prefix);
-
- var wyhash = std.hash.Wyhash.init(0);
- hashUserInputOptionsMap(p.allocator, p.user_input_options, &wyhash);
- hash.add(wyhash.final());
-
- const digest = hash.final();
- const install_prefix = try p.cache_root.join(p.allocator, &.{ "i", &digest });
- p.resolveInstallPrefix(install_prefix, .{});
-}
diff --git a/lib/compiler/maker/Step.zig b/lib/compiler/maker/Step.zig
@@ -1,19 +1,27 @@
+//! The state that maker needs in order to process a step.
const Step = @This();
+const builtin = @import("builtin");
+
const std = @import("std");
-const Io = std.Io;
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;
+const Io = std.Io;
+const LazyPath = std.Build.Configuration.LazyPath;
+const Package = std.Build.Configuration.Package;
+const Path = std.Build.Cache.Path;
const assert = std.debug.assert;
const WebServer = @import("WebServer.zig");
-pub const Compile = @import("Step/Compile.zig");
-pub const Run = @import("Step/Run.zig");
+pub const Compile = void; // @import("Step/Compile.zig");
+pub const Run = void; // @import("Step/Run.zig");
-state: State,
-makeFn: MakeFn,
-dependants: std.ArrayList(*Step),
+/// Avoid false sharing.
+_: void align(std.atomic.cache_line) = {},
+
+state: State = .precheck_unstarted,
+dependants: std.ArrayList(*Step) = .empty,
/// Collects the set of files that retrigger this step to run.
///
/// This is used by the build system's implementation of `--watch` but it can
@@ -23,20 +31,19 @@ dependants: std.ArrayList(*Step),
/// Populated within `make`. Implementation may choose to clear and repopulate,
/// retain previous value, or update.
inputs: Inputs = .init,
-pending_deps: u32,
+pending_deps: u32 = undefined,
-result_error_msgs: std.ArrayList([]const u8),
-result_error_bundle: std.zig.ErrorBundle,
-result_stderr: []const u8,
-result_cached: bool,
-result_duration_ns: ?u64,
+result_error_msgs: std.ArrayList([]const u8) = .empty,
+result_error_bundle: std.zig.ErrorBundle = .empty,
+result_stderr: []const u8 = "",
+result_cached: bool = false,
+result_duration_ns: ?u64 = null,
/// 0 means unavailable or not reported.
-result_peak_rss: usize,
+result_peak_rss: usize = 0,
/// If the step is failed and this field is populated, this is the command which failed.
/// This field may be populated even if the step succeeded.
-result_failed_command: ?[]const u8,
-test_results: TestResults,
-
+result_failed_command: ?[]const u8 = null,
+test_results: TestResults = .{},
pub const State = enum {
precheck_unstarted,
@@ -172,18 +179,6 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi
}
}
-fn makeNoOp(step: *Step, options: MakeOptions) anyerror!void {
- _ = options;
-
- var all_cached = true;
-
- for (step.dependencies.items) |dep| {
- all_cached = all_cached and dep.result_cached;
- }
-
- step.result_cached = all_cached;
-}
-
/// 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 {
@@ -233,7 +228,7 @@ pub fn captureChildProcess(
s.result_failed_command = try allocPrintCmd(gpa, .inherit, null, argv);
try handleChildProcUnsupported(s);
- try handleVerbose(s.owner, .inherit, argv);
+ try handleVerbose(s, .inherit, argv);
const result = std.process.run(arena, io, .{
.argv = argv,
@@ -340,7 +335,7 @@ pub fn evalZigProcess(
assert(argv.len != 0);
try handleChildProcUnsupported(s);
- try handleVerbose(s.owner, .inherit, argv);
+ try handleVerbose(s, .inherit, argv);
const zp = try gpa.create(ZigProcess);
defer if (!watch) gpa.destroy(zp);
@@ -399,11 +394,11 @@ pub fn evalZigProcess(
}
/// Wrapper around `Io.Dir.updateFile` that handles verbose and error output.
-pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {
+pub fn installFile(s: *Step, src_lazy_path: LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {
const b = s.owner;
const io = b.graph.io;
const src_path = src_lazy_path.getPath3(b, s);
- try handleVerbose(b, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
+ try handleVerbose(s, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|
return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
}
@@ -412,7 +407,7 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus {
const b = s.owner;
const io = b.graph.io;
- try handleVerbose(b, .inherit, &.{ "install", "-d", dest_path });
+ try handleVerbose(s, .inherit, &.{ "install", "-d", dest_path });
return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err|
return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
}
@@ -567,29 +562,21 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
}
pub fn handleVerbose(
- b: *Build,
- cwd: std.process.Child.Cwd,
- argv: []const []const u8,
-) error{OutOfMemory}!void {
- return handleVerbose2(b, cwd, null, argv);
-}
-
-pub fn handleVerbose2(
- b: *Build,
+ s: *Step,
+ arena: Allocator,
cwd: std.process.Child.Cwd,
opt_env: ?*const std.process.Environ.Map,
argv: []const []const u8,
) error{OutOfMemory}!void {
- if (b.verbose) {
- const graph = b.graph;
- // Intention of verbose is to print all sub-process command lines to
- // stderr before spawning them.
- const text = try allocPrintCmd(b.allocator, cwd, if (opt_env) |env| .{
- .child = env,
- .parent = &graph.environ_map,
- } else null, argv);
- std.debug.print("{s}\n", .{text});
- }
+ if (!s.verbose) return;
+ const graph = s.graph;
+ // Intention of verbose is to print all sub-process command lines to
+ // stderr before spawning them.
+ const text = try allocPrintCmd(arena, cwd, if (opt_env) |env| .{
+ .child = env,
+ .parent = &graph.environ_map,
+ } else null, argv);
+ std.log.scoped(.verbose).info("{s}", .{text});
}
/// Asserts that the caller has already populated `s.result_failed_command`.
@@ -688,7 +675,7 @@ fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void {
}
/// For steps that have a single input that never changes when re-running `make`.
-pub fn singleUnchangingWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!void {
+pub fn singleUnchangingWatchInput(step: *Step, lazy_path: LazyPath) Allocator.Error!void {
if (!step.inputs.populated()) try step.addWatchInput(lazy_path);
}
@@ -698,7 +685,7 @@ pub fn clearWatchInputs(step: *Step) void {
}
/// Places a *file* dependency on the path.
-pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!void {
+pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void {
switch (lazy_file) {
.src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
.dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
@@ -723,7 +710,7 @@ pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!voi
/// Paths derived from this directory should also be manually added via
/// `addDirectoryWatchInputFromPath` if and only if this function returns
/// `true`.
-pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Allocator.Error!bool {
+pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.Error!bool {
switch (lazy_directory) {
.src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
.dependency => |d| try addDirectoryWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
@@ -744,26 +731,26 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Alloc
/// Any changes inside the directory will trigger invalidation.
///
-/// See also `addDirectoryWatchInput` which takes a `Build.LazyPath` instead.
+/// See also `addDirectoryWatchInput` which takes a `LazyPath` instead.
///
/// This function should only be called when it has been verified that the
/// dependency on `path` is not already accounted for by a `Step` dependency.
/// In other words, before calling this function, first check that the
-/// `Build.LazyPath` which this `path` is derived from is not `generated`.
+/// `LazyPath` which this `path` is derived from is not `generated`.
pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void {
return addWatchInputFromPath(step, path, ".");
}
-fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {
+fn addWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
return addWatchInputFromPath(step, .{
- .root_dir = builder.build_root,
+ .root_dir = package.build_root,
.sub_path = std.fs.path.dirname(sub_path) orelse "",
}, std.fs.path.basename(sub_path));
}
-fn addDirectoryWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {
+fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
return addDirectoryWatchInputFromPath(step, .{
- .root_dir = builder.build_root,
+ .root_dir = package.build_root,
.sub_path = sub_path,
});
}
@@ -847,16 +834,3 @@ pub fn allocPrintCmd(
}
return aw.toOwnedSlice();
}
-
-pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
- assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
- const base_dir = switch (dir) {
- .prefix => b.install_path,
- .bin => b.exe_dir,
- .lib => b.lib_dir,
- .header => b.h_dir,
- .custom => |p| b.pathJoin(&.{ b.install_path, p }),
- };
- return b.pathResolve(&.{ base_dir, dest_rel_path });
-}
-
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
@@ -112,6 +112,10 @@ pub const Graph = struct {
/// respects the '--color' flag.
stderr_mode: ?Io.Terminal.Mode = null,
release_mode: ReleaseMode = .off,
+ /// Whether the user passed in "--" arguments. They can be added to a child
+ /// process via `Step.Run` API but cannot be observed in the configure
+ /// phase.
+ have_run_args: bool = false,
};
const AvailableDeps = []const struct { []const u8, []const u8 };
diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig
@@ -141,6 +141,8 @@ pub const Arg = union(enum) {
bytes: []u8,
output_file: *Output,
output_directory: *Output,
+ /// The arguments passed after "--" on the "zig build" CLI.
+ cli_rest_positionals,
};
pub const PrefixedArtifact = struct {
diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig
@@ -413,6 +413,7 @@ pub const AvailableOption = extern struct {
pub const Step = extern struct {
name: String,
+ owner: Package.Index,
deps: Deps,
max_rss: MaxRss,
/// Points into `extra` for step-specific data. First element has flags
@@ -534,6 +535,7 @@ pub const Step = extern struct {
bytes,
output_file,
output_directory,
+ cli_rest_positionals,
};
};
@@ -841,7 +843,7 @@ pub const LazyPath = enum(u32) {
pub const SourcePath = struct {
flags: Flags,
- owner: Package,
+ owner: Package.Index,
sub_path: String,
pub const Flags = packed struct(u32) {
@@ -877,16 +879,19 @@ pub const LazyPath = enum(u32) {
};
};
-/// It's an OptionalString which points to the package hash.
-pub const Package = enum(u32) {
- root = maxInt(u32),
- _,
+pub const Package = struct {
+ dep_prefix: String,
+ hash: String,
- pub fn fromHash(hash: String) Package {
- const result: Package = @enumFromInt(@intFromEnum(hash));
- assert(result != .root);
- return result;
- }
+ pub const Index = enum(u32) {
+ root = maxInt(u32),
+ _,
+
+ pub fn depPrefixSlice(i: Index, c: *const Configuration) [:0]const u8 {
+ if (i == .root) return "";
+ return extraData(c, Package, @intFromEnum(i)).dep_prefix.slice(c);
+ }
+ };
};
/// Trailing:
@@ -900,7 +905,7 @@ pub const Package = enum(u32) {
pub const Module = struct {
flags: Flags,
flags2: Flags2,
- owner: Package,
+ owner: Package.Index,
root_source_file: OptionalLazyPath,
import_table: ImportTable,
resolved_target: ResolvedTarget.OptionalIndex,
@@ -1048,6 +1053,11 @@ pub const ImportTable = enum(u32) {
/// elements is `Step.Index` per count.
pub const Deps = enum(u32) {
_,
+
+ pub fn slice(deps: Deps, c: *const Configuration) []Step.Index {
+ const len = c.extra[@intFromEnum(deps)];
+ return @ptrCast(c.extra[@intFromEnum(deps) + 1 ..][0..len]);
+ }
};
/// Points into `extra`, where the first element is count of strings, following