configure runner: implement serialization of InstallArtifact

This commit is contained in:
Andrew Kelley
2026-02-15 18:47:38 -08:00
parent cb966d14a2
commit c3168cf25a
6 changed files with 219 additions and 47 deletions
+79 -28
View File
@@ -140,16 +140,13 @@ pub fn main(init: process.Init.Minimal) !void {
if (try builder.addUserInputFlag(option_contents))
fatal(" access the help menu with 'zig build -h'", .{});
}
} else if (mem.startsWith(u8, arg, "-fsys=")) {
const name = arg["-fsys=".len..];
} else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {
graph.system_library_options.put(arena, name, .user_enabled) catch @panic("OOM");
} else if (mem.startsWith(u8, arg, "-fno-sys=")) {
const name = arg["-fno-sys=".len..];
} else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| {
graph.system_library_options.put(arena, name, .user_disabled) catch @panic("OOM");
} else if (mem.eql(u8, arg, "--release")) {
graph.release_mode = .any;
} else if (mem.startsWith(u8, arg, "--release=")) {
const text = arg["--release=".len..];
} else if (mem.cutPrefix(u8, arg, "--release=")) |text| {
graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {
fatalWithHint("expected [off|any|fast|safe|small] in '{s}', found '{s}'", .{
arg, text,
@@ -175,23 +172,11 @@ pub fn main(init: process.Init.Minimal) !void {
multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse {
fatalWithHint("expected style after '{s}', found '{s}'", .{ arg, next_arg });
};
} else if (mem.eql(u8, arg, "--seed")) {
const next_arg = nextArg(args, &arg_idx) orelse
fatalWithHint("expected u32 after '{s}'", .{arg});
graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{
next_arg, @errorName(err),
});
};
} else if (mem.eql(u8, arg, "--build-id")) {
builder.build_id = .fast;
} else if (mem.startsWith(u8, arg, "--build-id=")) {
const style = arg["--build-id=".len..];
builder.build_id = std.zig.BuildId.parse(style) catch |err| {
fatal("unable to parse --build-id style '{s}': {s}", .{
style, @errorName(err),
});
};
} else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| {
builder.build_id = std.zig.BuildId.parse(style) catch |err|
fatal("unable to parse --build-id style '{s}': {t}", .{ style, err });
} else if (mem.eql(u8, arg, "--debug-rt")) {
graph.debug_compiler_runtime_libs = true;
} else if (mem.eql(u8, arg, "--debug-compile-errors")) {
@@ -203,11 +188,6 @@ 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.cutPrefix(u8, arg, "-j")) |text| {
const n = std.fmt.parseUnsigned(u32, text, 10) catch |err|
fatal("unable to parse jobs count '{s}': {t}", .{ text, err });
if (n < 1) fatal("number of jobs must be at least 1", .{});
threaded.setAsyncLimit(.limited(n));
} else {
fatalWithHint("unrecognized argument: '{s}'", .{arg});
}
@@ -281,6 +261,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.name = try wc.addString(step.name),
.flags = .{ .tag = step.tag },
.deps = deps,
.max_rss = .fromBytes(step.max_rss),
.extra_index = switch (step.tag) {
.top_level => e: {
const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
@@ -289,7 +270,21 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
}));
},
.compile => @panic("TODO"),
.install_artifact => @panic("TODO"),
.install_artifact => e: {
const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
break :e try wc.addExtra(@as(Configuration.Step.InstallArtifact, .{
.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),
.implib_dir = try addInstallDir(wc, ia.implib_dir),
.emitted_implib = try addOptionalLazyPath(wc, ia.emitted_implib),
.pdb_dir = try addInstallDir(wc, ia.pdb_dir),
.emitted_pdb = try addOptionalLazyPath(wc, ia.emitted_pdb),
.h_dir = try addInstallDir(wc, ia.h_dir),
.emitted_h = try addOptionalLazyPath(wc, ia.emitted_h),
.artifact = stepIndex(&step_map, &ia.artifact.step),
}));
},
.install_file => @panic("TODO"),
.install_dir => @panic("TODO"),
.remove_dir => @panic("TODO"),
@@ -315,10 +310,66 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
}
try wc.write(writer, .{
.default_step = @intCast(step_map.getIndex(b.default_step).?),
.default_step = stepIndex(&step_map, b.default_step),
});
}
fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
return @enumFromInt(switch (lp orelse return .none) {
.src_path => |src_path| i: {
const owner = builderToPackage(src_path.owner);
const sub_path = try wc.addString(src_path.sub_path);
break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.SourcePath, .{
.flags = .{},
.owner = owner,
.sub_path = sub_path,
}));
},
.generated => |generated| i: {
const sub_path = try wc.addString(generated.sub_path);
break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.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.OptionalLazyPath.Relative, .{
.flags = .{ .base = .cwd },
.sub_path = sub_path,
}));
},
.dependency => |dependency| i: {
const owner = builderToPackage(dependency.dependency.builder);
const sub_path = try wc.addString(dependency.sub_path);
break :i try wc.addExtra(@as(Configuration.OptionalLazyPath.SourcePath, .{
.flags = .{},
.owner = owner,
.sub_path = sub_path,
}));
},
});
}
fn builderToPackage(b: *std.Build) Configuration.Package {
_ = b;
@panic("TODO");
}
fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
switch (install_dir orelse return .none) {
.prefix => return .prefix,
.lib => return .lib,
.bin => return .bin,
.header => return .header,
.custom => |sub_path| return .initCustom(try wc.addString(sub_path)),
}
}
fn stepIndex(step_map: *const std.AutoArrayHashMapUnmanaged(*Step, void), step: *Step) Configuration.Step.Index {
return @enumFromInt(step_map.getIndex(step).?);
}
/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
-1
View File
@@ -103,7 +103,6 @@ pub const Graph = struct {
needed_lazy_dependencies: std.StringArrayHashMapUnmanaged(void) = .empty,
/// Information about the native target. Computed before build() is invoked.
host: ResolvedTarget,
random_seed: u32 = 0,
dependency_cache: InitializedDepMap = .empty,
allow_so_scripts: ?bool = null,
/// Steps should use `io` to limit the number of jobs, however in the case of
+2 -2
View File
@@ -1,8 +1,8 @@
const InstallArtifact = @This();
const std = @import("std");
const Step = std.Build.Step;
const InstallDir = std.Build.InstallDir;
const InstallArtifact = @This();
const fs = std.fs;
const LazyPath = std.Build.LazyPath;
step: Step,
+3 -4
View File
@@ -87,6 +87,7 @@ captured_stderr: ?*CapturedStdIo,
dep_output_file: ?*Output,
has_side_effects: bool,
test_runner_mode: bool = false,
/// Populated during the fuzz phase if this run step corresponds to a unit test
/// executable that contains fuzz tests.
@@ -234,13 +235,11 @@ pub fn setName(run: *Run, name: []const u8) void {
}
pub fn enableTestRunnerMode(run: *Run) void {
if (run.test_runner_mode) return;
const b = run.step.owner;
run.stdio = .zig_test;
run.addPrefixedDirectoryArg("--cache-dir=", .{ .cwd_relative = b.cache_root.path orelse "." });
run.addArgs(&.{
b.fmt("--seed=0x{x}", .{b.graph.random_seed}),
"--listen=-",
});
run.test_runner_mode = true;
}
pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
+117 -7
View File
@@ -4,6 +4,7 @@ const std = @import("../std.zig");
const Io = std.Io;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const maxInt = std.math.maxInt;
string_bytes: []u8,
steps: []Step,
@@ -21,8 +22,7 @@ pub const Header = extern struct {
unlazy_deps_len: u32,
extra_len: u32,
/// Index into `steps`.
default_step: u32,
default_step: Step.Index,
};
pub const Wip = struct {
@@ -97,7 +97,7 @@ pub const Wip = struct {
}
pub const Static = struct {
default_step: u32,
default_step: Step.Index,
};
pub fn write(wip: *Wip, w: *Io.Writer, static: Static) Io.Writer.Error!void {
@@ -182,10 +182,12 @@ pub const Wip = struct {
const fields = @typeInfo(@TypeOf(extra)).@"struct".fields;
var i = index;
inline for (fields) |field| {
wip.extra.items[i] = switch (field.type) {
u32 => @field(extra, field.name),
String, Deps => @intFromEnum(@field(extra, field.name)),
else => @compileError("bad field type"),
comptime assert(@sizeOf(field.type) == @sizeOf(u32));
wip.extra.items[i] = switch (@typeInfo(field.type)) {
.int => @field(extra, field.name),
.@"enum" => @intFromEnum(@field(extra, field.name)),
.@"struct" => @bitCast(@field(extra, field.name)),
else => @compileError("bad field type: " ++ @typeName(field.type)),
};
i += 1;
}
@@ -196,6 +198,7 @@ pub const Step = extern struct {
name: String,
flags: Flags,
deps: Deps,
max_rss: MaxRss,
/// Points into `extra` for step-specific data.
extra_index: u32,
@@ -231,6 +234,98 @@ pub const Step = extern struct {
pub const TopLevel = struct {
description: String,
};
pub const InstallArtifact = struct {
dest_dir: InstallDir,
dest_sub_path: String,
emitted_bin: OptionalLazyPath,
implib_dir: InstallDir,
emitted_implib: OptionalLazyPath,
pdb_dir: InstallDir,
emitted_pdb: OptionalLazyPath,
h_dir: InstallDir,
emitted_h: OptionalLazyPath,
/// Always a compile step.
artifact: Step.Index,
const Flags = packed struct(u32) {
tag: Tag = .install_artifact,
dylib_symlinks: bool,
_: u23 = 0,
};
};
};
pub const MaxRss = enum(u32) {
none = 0,
_,
pub fn toBytes(mr: MaxRss) usize {
const x: usize = @intFromEnum(mr);
return x << 8;
}
pub fn fromBytes(bytes: usize) MaxRss {
return @enumFromInt(bytes >> 8);
}
};
/// An index into `extra`.
pub const OptionalLazyPath = enum(u32) {
none = maxInt(u32),
_,
pub const Tag = enum(u8) {
/// A source file path relative to build root.
source_path,
generated,
relative,
};
pub const SourcePath = struct {
flags: Flags,
owner: Package,
sub_path: String,
pub const Flags = packed struct(u32) {
tag: Tag = .source_path,
_: u24 = 0,
};
};
pub const Generated = struct {
flags: Flags,
/// Applied after `up`.
sub_path: String,
pub const Flags = packed struct(u32) {
tag: Tag = .generated,
/// The number of parent directories to go up.
/// 0 means the generated file itself.
/// 1 means the directory of the generated file.
/// 2 means the parent of that directory, and so on.
up: u24,
};
};
pub const Relative = struct {
flags: Flags,
sub_path: String,
pub const Flags = packed struct(u32) {
tag: Tag = .relative,
base: Path.Base,
_: u16 = 0,
};
};
};
pub const Package = enum(u32) {
_,
};
/// Points into `extra`, where the first element is number of deps,
@@ -258,6 +353,21 @@ pub const Path = extern struct {
}
};
pub const InstallDir = enum(u32) {
none = maxInt(u32) - 4,
prefix = maxInt(u32) - 3,
lib = maxInt(u32) - 2,
bin = maxInt(u32) - 1,
header = maxInt(u32),
/// A `String` path relative to the prefix.
_,
pub fn initCustom(sub_path: String) InstallDir {
assert(@intFromEnum(sub_path) < @intFromEnum(InstallDir.none));
return @enumFromInt(@intFromEnum(sub_path));
}
};
/// Points into `string_bytes`, null-terminated.
pub const String = enum(u32) {
_,
+18 -5
View File
@@ -4996,34 +4996,47 @@ fn cmdBuild(
const default_seed = try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)});
try configure_argv.ensureUnusedCapacity(arena, 16);
try make_argv.ensureUnusedCapacity(arena, 16);
const argv_index_exe = configure_argv.items.len;
_ = configure_argv.addOneAssumeCapacity();
_ = make_argv.addOneAssumeCapacity();
configure_argv.appendAssumeCapacity("--zig");
configure_argv.appendAssumeCapacity(self_exe_path);
make_argv.appendAssumeCapacity("--zig");
make_argv.appendAssumeCapacity(self_exe_path);
configure_argv.appendAssumeCapacity("--zig-lib-dir");
make_argv.appendAssumeCapacity("--zig-lib-dir");
const argv_index_zig_lib_dir = configure_argv.items.len;
_ = configure_argv.addOneAssumeCapacity();
_ = make_argv.addOneAssumeCapacity();
configure_argv.appendAssumeCapacity("--build-root");
make_argv.appendAssumeCapacity("--build-root");
const argv_index_build_file = configure_argv.items.len;
_ = configure_argv.addOneAssumeCapacity();
_ = make_argv.addOneAssumeCapacity();
configure_argv.appendAssumeCapacity("--local-cache");
make_argv.appendAssumeCapacity("--local-cache");
const argv_index_cache_dir = configure_argv.items.len;
_ = configure_argv.addOneAssumeCapacity();
_ = make_argv.addOneAssumeCapacity();
configure_argv.appendAssumeCapacity("--global-cache");
make_argv.appendAssumeCapacity("--global-cache");
const argv_index_global_cache_dir = configure_argv.items.len;
_ = configure_argv.addOneAssumeCapacity();
_ = make_argv.addOneAssumeCapacity();
configure_argv.appendSliceAssumeCapacity(&.{ "--seed", default_seed });
const argv_index_seed = configure_argv.items.len - 1;
make_argv.appendSliceAssumeCapacity(&.{ "--configuration", undefined });
const argv_index_configuration_file = make_argv.items.len - 1;
const argv_index_configuration_file = make_argv.items.len;
_ = try make_argv.addOne(arena);
make_argv.appendSliceAssumeCapacity(&.{ "--seed", default_seed });
const argv_index_seed = make_argv.items.len - 1;
var color: Color = .auto;
var n_jobs: ?u32 = null;
@@ -5164,7 +5177,7 @@ fn cmdBuild(
} else if (mem.eql(u8, arg, "--seed")) {
if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
i += 1;
configure_argv.items[argv_index_seed] = args[i];
make_argv.items[argv_index_seed] = args[i];
continue;
} else if (mem.eql(u8, arg, "--")) {
// The rest of the args are supposed to get passed onto