Commit 58edefc6d1
Changed files (23)
lib
std
src
test
lib/std/Build/CheckFileStep.zig
@@ -8,26 +8,25 @@ const CheckFileStep = @This();
pub const base_id = .check_file;
step: Step,
-builder: *std.Build,
expected_matches: []const []const u8,
source: std.Build.FileSource,
max_bytes: usize = 20 * 1024 * 1024,
pub fn create(
- builder: *std.Build,
+ owner: *std.Build,
source: std.Build.FileSource,
expected_matches: []const []const u8,
) *CheckFileStep {
- const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");
+ const self = owner.allocator.create(CheckFileStep) catch @panic("OOM");
self.* = CheckFileStep{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = .check_file,
.name = "CheckFile",
+ .owner = owner,
.makeFn = make,
}),
- .source = source.dupe(builder),
- .expected_matches = builder.dupeStrings(expected_matches),
+ .source = source.dupe(owner),
+ .expected_matches = owner.dupeStrings(expected_matches),
};
self.source.addStepDependencies(&self.step);
return self;
@@ -35,10 +34,11 @@ pub fn create(
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const b = step.owner;
const self = @fieldParentPtr(CheckFileStep, "step", step);
- const src_path = self.source.getPath(self.builder);
- const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
+ const src_path = self.source.getPath(b);
+ const contents = try fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes);
for (self.expected_matches) |expected_match| {
if (mem.indexOf(u8, contents, expected_match) == null) {
lib/std/Build/CheckObjectStep.zig
@@ -10,29 +10,31 @@ const CheckObjectStep = @This();
const Allocator = mem.Allocator;
const Step = std.Build.Step;
-const EmulatableRunStep = std.Build.EmulatableRunStep;
pub const base_id = .check_object;
step: Step,
-builder: *std.Build,
source: std.Build.FileSource,
max_bytes: usize = 20 * 1024 * 1024,
checks: std.ArrayList(Check),
dump_symtab: bool = false,
obj_format: std.Target.ObjectFormat,
-pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- const gpa = builder.allocator;
+pub fn create(
+ owner: *std.Build,
+ source: std.Build.FileSource,
+ obj_format: std.Target.ObjectFormat,
+) *CheckObjectStep {
+ const gpa = owner.allocator;
const self = gpa.create(CheckObjectStep) catch @panic("OOM");
self.* = .{
- .builder = builder,
- .step = Step.init(gpa, .{
+ .step = Step.init(.{
.id = .check_file,
.name = "CheckObject",
+ .owner = owner,
.makeFn = make,
}),
- .source = source.dupe(builder),
+ .source = source.dupe(owner),
.checks = std.ArrayList(Check).init(gpa),
.obj_format = obj_format,
};
@@ -42,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std
/// Runs and (optionally) compares the output of a binary.
/// Asserts `self` was generated from an executable step.
-pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
+/// TODO this doesn't actually compare, and there's no apparent reason for it
+/// to depend on the check object step. I don't see why this function should exist,
+/// the caller could just add the run step directly.
+pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
const dependencies_len = self.step.dependencies.items.len;
assert(dependencies_len > 0);
const exe_step = self.step.dependencies.items[dependencies_len - 1];
const exe = exe_step.cast(std.Build.CompileStep).?;
- const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
- emulatable_step.step.dependOn(&self.step);
- return emulatable_step;
+ const run = self.step.owner.addRunArtifact(exe);
+ run.skip_foreign_checks = true;
+ run.step.dependOn(&self.step);
+ return run;
}
/// There two types of actions currently suported:
@@ -253,7 +259,7 @@ const Check = struct {
/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
- var new_check = Check.create(self.builder);
+ var new_check = Check.create(self.step.owner);
new_check.match(phrase);
self.checks.append(new_check) catch @panic("OOM");
}
@@ -295,17 +301,18 @@ pub fn checkComputeCompare(
program: []const u8,
expected: ComputeCompareExpected,
) void {
- var new_check = Check.create(self.builder);
+ var new_check = Check.create(self.step.owner);
new_check.computeCmp(program, expected);
self.checks.append(new_check) catch @panic("OOM");
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const b = step.owner;
+ const gpa = b.allocator;
const self = @fieldParentPtr(CheckObjectStep, "step", step);
- const gpa = self.builder.allocator;
- const src_path = self.source.getPath(self.builder);
+ const src_path = self.source.getPath(b);
const contents = try fs.cwd().readFileAllocOptions(
gpa,
src_path,
lib/std/Build/CompileStep.zig
@@ -22,7 +22,6 @@ const InstallDir = std.Build.InstallDir;
const InstallArtifactStep = std.Build.InstallArtifactStep;
const GeneratedFile = std.Build.GeneratedFile;
const ObjCopyStep = std.Build.ObjCopyStep;
-const EmulatableRunStep = std.Build.EmulatableRunStep;
const CheckObjectStep = std.Build.CheckObjectStep;
const RunStep = std.Build.RunStep;
const OptionsStep = std.Build.OptionsStep;
@@ -32,7 +31,6 @@ const CompileStep = @This();
pub const base_id: Step.Id = .compile;
step: Step,
-builder: *std.Build,
name: []const u8,
target: CrossTarget,
target_info: NativeTargetInfo,
@@ -305,24 +303,23 @@ pub const EmitOption = union(enum) {
}
};
-pub fn create(builder: *std.Build, options: Options) *CompileStep {
- const name = builder.dupe(options.name);
- const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
+pub fn create(owner: *std.Build, options: Options) *CompileStep {
+ const name = owner.dupe(options.name);
+ const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null;
if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
}
- const step_name = builder.fmt("compile {s} {s} {s}", .{
+ const step_name = owner.fmt("compile {s} {s} {s}", .{
name,
@tagName(options.optimize),
- options.target.zigTriple(builder.allocator) catch @panic("OOM"),
+ options.target.zigTriple(owner.allocator) catch @panic("OOM"),
});
- const self = builder.allocator.create(CompileStep) catch @panic("OOM");
+ const self = owner.allocator.create(CompileStep) catch @panic("OOM");
self.* = CompileStep{
.strip = null,
.unwind_tables = null,
- .builder = builder,
.verbose_link = false,
.verbose_cc = false,
.optimize = options.optimize,
@@ -331,27 +328,28 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
.kind = options.kind,
.root_src = root_src,
.name = name,
- .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
- .step = Step.init(builder.allocator, .{
+ .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator),
+ .step = Step.init(.{
.id = base_id,
.name = step_name,
+ .owner = owner,
.makeFn = make,
}),
.version = options.version,
.out_filename = undefined,
- .out_h_filename = builder.fmt("{s}.h", .{name}),
+ .out_h_filename = owner.fmt("{s}.h", .{name}),
.out_lib_filename = undefined,
- .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
+ .out_pdb_filename = owner.fmt("{s}.pdb", .{name}),
.major_only_filename = null,
.name_only_filename = null,
- .modules = std.StringArrayHashMap(*Module).init(builder.allocator),
- .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .c_macros = ArrayList([]const u8).init(builder.allocator),
- .lib_paths = ArrayList([]const u8).init(builder.allocator),
- .rpaths = ArrayList([]const u8).init(builder.allocator),
- .framework_dirs = ArrayList([]const u8).init(builder.allocator),
- .installed_headers = ArrayList(*Step).init(builder.allocator),
+ .modules = std.StringArrayHashMap(*Module).init(owner.allocator),
+ .include_dirs = ArrayList(IncludeDir).init(owner.allocator),
+ .link_objects = ArrayList(LinkObject).init(owner.allocator),
+ .c_macros = ArrayList([]const u8).init(owner.allocator),
+ .lib_paths = ArrayList([]const u8).init(owner.allocator),
+ .rpaths = ArrayList([]const u8).init(owner.allocator),
+ .framework_dirs = ArrayList([]const u8).init(owner.allocator),
+ .installed_headers = ArrayList(*Step).init(owner.allocator),
.object_src = undefined,
.c_std = std.Build.CStd.C99,
.zig_lib_dir = null,
@@ -382,9 +380,10 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
}
fn computeOutFileNames(self: *CompileStep) void {
+ const b = self.step.owner;
const target = self.target_info.target;
- self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
+ self.out_filename = std.zig.binNameAlloc(b.allocator, .{
.root_name = self.name,
.target = target,
.output_mode = switch (self.kind) {
@@ -404,30 +403,30 @@ fn computeOutFileNames(self: *CompileStep) void {
self.out_lib_filename = self.out_filename;
} else if (self.version) |version| {
if (target.isDarwin()) {
- self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
+ self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{
self.name,
version.major,
});
- self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
+ self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name});
self.out_lib_filename = self.out_filename;
} else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
} else {
- self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
- self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
+ self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major });
+ self.name_only_filename = b.fmt("lib{s}.so", .{self.name});
self.out_lib_filename = self.out_filename;
}
} else {
if (target.isDarwin()) {
self.out_lib_filename = self.out_filename;
} else if (target.os.tag == .windows) {
- self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
+ self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
} else {
self.out_lib_filename = self.out_filename;
}
}
if (self.output_dir != null) {
- self.output_lib_path_source.path = self.builder.pathJoin(
+ self.output_lib_path_source.path = b.pathJoin(
&.{ self.output_dir.?, self.out_lib_filename },
);
}
@@ -435,17 +434,20 @@ fn computeOutFileNames(self: *CompileStep) void {
}
pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
- self.output_dir = self.builder.dupePath(dir);
+ const b = self.step.owner;
+ self.output_dir = b.dupePath(dir);
}
pub fn install(self: *CompileStep) void {
- self.builder.installArtifact(self);
+ const b = self.step.owner;
+ b.installArtifact(self);
}
-pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
- const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
- a.builder.getInstallStep().dependOn(&install_file.step);
- a.installed_headers.append(&install_file.step) catch @panic("OOM");
+pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
+ const b = cs.step.owner;
+ const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
+ b.getInstallStep().dependOn(&install_file.step);
+ cs.installed_headers.append(&install_file.step) catch @panic("OOM");
}
pub const InstallConfigHeaderOptions = struct {
@@ -459,13 +461,14 @@ pub fn installConfigHeader(
options: InstallConfigHeaderOptions,
) void {
const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
- const install_file = cs.builder.addInstallFileWithDir(
+ const b = cs.step.owner;
+ const install_file = b.addInstallFileWithDir(
.{ .generated = &config_header.output_file },
options.install_dir,
dest_rel_path,
);
install_file.step.dependOn(&config_header.step);
- cs.builder.getInstallStep().dependOn(&install_file.step);
+ b.getInstallStep().dependOn(&install_file.step);
cs.installed_headers.append(&install_file.step) catch @panic("OOM");
}
@@ -482,91 +485,84 @@ pub fn installHeadersDirectory(
}
pub fn installHeadersDirectoryOptions(
- a: *CompileStep,
+ cs: *CompileStep,
options: std.Build.InstallDirStep.Options,
) void {
- const install_dir = a.builder.addInstallDirectory(options);
- a.builder.getInstallStep().dependOn(&install_dir.step);
- a.installed_headers.append(&install_dir.step) catch @panic("OOM");
+ const b = cs.step.owner;
+ const install_dir = b.addInstallDirectory(options);
+ b.getInstallStep().dependOn(&install_dir.step);
+ cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
}
-pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
+pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void {
assert(l.kind == .lib);
- const install_step = a.builder.getInstallStep();
+ const b = cs.step.owner;
+ const install_step = b.getInstallStep();
// Copy each element from installed_headers, modifying the builder
// to be the new parent's builder.
for (l.installed_headers.items) |step| {
const step_copy = switch (step.id) {
inline .install_file, .install_dir => |id| blk: {
const T = id.Type();
- const ptr = a.builder.allocator.create(T) catch @panic("OOM");
+ const ptr = b.allocator.create(T) catch @panic("OOM");
ptr.* = step.cast(T).?.*;
- ptr.override_source_builder = ptr.builder;
- ptr.builder = a.builder;
+ ptr.dest_builder = b;
break :blk &ptr.step;
},
else => unreachable,
};
- a.installed_headers.append(step_copy) catch @panic("OOM");
+ cs.installed_headers.append(step_copy) catch @panic("OOM");
install_step.dependOn(step_copy);
}
- a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
+ cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
}
pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {
+ const b = cs.step.owner;
var copy = options;
if (copy.basename == null) {
if (options.format) |f| {
- copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
+ copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
} else {
copy.basename = cs.name;
}
}
- return cs.builder.addObjCopy(cs.getOutputSource(), copy);
+ return b.addObjCopy(cs.getOutputSource(), copy);
}
/// Deprecated: use `std.Build.addRunArtifact`
/// This function will run in the context of the package that created the executable,
/// which is undesirable when running an executable provided by a dependency package.
-pub fn run(exe: *CompileStep) *RunStep {
- return exe.builder.addRunArtifact(exe);
-}
-
-/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
-/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
-/// When a binary cannot be ran through emulation or the option is disabled, a warning
-/// will be printed and the binary will *NOT* be ran.
-pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
- assert(exe.kind == .exe or exe.kind == .test_exe);
-
- const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
- if (exe.vcpkg_bin_path) |path| {
- RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
- }
- return run_step;
+pub fn run(cs: *CompileStep) *RunStep {
+ return cs.step.owner.addRunArtifact(cs);
}
pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
+ const b = self.step.owner;
+ return CheckObjectStep.create(b, self.getOutputSource(), obj_format);
}
pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
- self.linker_script = source.dupe(self.builder);
+ const b = self.step.owner;
+ self.linker_script = source.dupe(b);
source.addStepDependencies(&self.step);
}
pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
+ const b = self.step.owner;
+ self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
}
pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
+ const b = self.step.owner;
+ self.frameworks.put(b.dupe(framework_name), .{
.needed = true,
}) catch @panic("OOM");
}
pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
- self.frameworks.put(self.builder.dupe(framework_name), .{
+ const b = self.step.owner;
+ self.frameworks.put(b.dupe(framework_name), .{
.weak = true,
}) catch @panic("OOM");
}
@@ -619,21 +615,24 @@ pub fn linkLibCpp(self: *CompileStep) void {
/// If the value is omitted, it is set to 1.
/// `name` and `value` need not live longer than the function call.
pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
- const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
+ const b = self.step.owner;
+ const macro = std.Build.constructCMacro(b.allocator, name, value);
self.c_macros.append(macro) catch @panic("OOM");
}
/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
- self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
+ const b = self.step.owner;
+ self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
}
/// This one has no integration with anything, it just puts -lname on the command line.
/// Prefer to use `linkSystemLibrary` instead.
pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(name),
+ .name = b.dupe(name),
.needed = false,
.weak = false,
.use_pkg_config = .no,
@@ -644,9 +643,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
/// This one has no integration with anything, it just puts -needed-lname on the command line.
/// Prefer to use `linkSystemLibraryNeeded` instead.
pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(name),
+ .name = b.dupe(name),
.needed = true,
.weak = false,
.use_pkg_config = .no,
@@ -657,9 +657,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
/// command line. Prefer to use `linkSystemLibraryWeak` instead.
pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(name),
+ .name = b.dupe(name),
.needed = false,
.weak = true,
.use_pkg_config = .no,
@@ -670,9 +671,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
/// This links against a system library, exclusively using pkg-config to find the library.
/// Prefer to use `linkSystemLibrary` instead.
pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(lib_name),
+ .name = b.dupe(lib_name),
.needed = false,
.weak = false,
.use_pkg_config = .force,
@@ -683,9 +685,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)
/// This links against a system library, exclusively using pkg-config to find the library.
/// Prefer to use `linkSystemLibraryNeeded` instead.
pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(lib_name),
+ .name = b.dupe(lib_name),
.needed = true,
.weak = false,
.use_pkg_config = .force,
@@ -696,13 +699,14 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons
/// Run pkg-config for the given library name and parse the output, returning the arguments
/// that should be passed to zig to link the given library.
pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
+ const b = self.step.owner;
const pkg_name = match: {
// First we have to map the library name to pkg config name. Unfortunately,
// there are several examples where this is not straightforward:
// -lSDL2 -> pkg-config sdl2
// -lgdk-3 -> pkg-config gdk-3.0
// -latk-1.0 -> pkg-config atk
- const pkgs = try getPkgConfigList(self.builder);
+ const pkgs = try getPkgConfigList(b);
// Exact match means instant winner.
for (pkgs) |pkg| {
@@ -742,7 +746,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
};
var code: u8 = undefined;
- const stdout = if (self.builder.execAllowFail(&[_][]const u8{
+ const stdout = if (b.execAllowFail(&[_][]const u8{
"pkg-config",
pkg_name,
"--cflags",
@@ -755,7 +759,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
else => return err,
};
- var zig_args = ArrayList([]const u8).init(self.builder.allocator);
+ var zig_args = ArrayList([]const u8).init(b.allocator);
defer zig_args.deinit();
var it = mem.tokenize(u8, stdout, " \r\n\t");
@@ -780,7 +784,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
} else if (mem.startsWith(u8, tok, "-D")) {
try zig_args.append(tok);
- } else if (self.builder.verbose) {
+ } else if (b.verbose) {
log.warn("Ignoring pkg-config flag '{s}'", .{tok});
}
}
@@ -804,6 +808,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
needed: bool = false,
weak: bool = false,
}) void {
+ const b = self.step.owner;
if (isLibCLibrary(name)) {
self.linkLibC();
return;
@@ -815,7 +820,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
self.link_objects.append(.{
.system_lib = .{
- .name = self.builder.dupe(name),
+ .name = b.dupe(name),
.needed = opts.needed,
.weak = opts.weak,
.use_pkg_config = .yes,
@@ -824,26 +829,30 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
}
pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
+ const b = self.step.owner;
assert(self.kind == .@"test" or self.kind == .test_exe);
- self.name_prefix = self.builder.dupe(text);
+ self.name_prefix = b.dupe(text);
}
pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
+ const b = self.step.owner;
assert(self.kind == .@"test" or self.kind == .test_exe);
- self.filter = if (text) |t| self.builder.dupe(t) else null;
+ self.filter = if (text) |t| b.dupe(t) else null;
}
pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
+ const b = self.step.owner;
assert(self.kind == .@"test" or self.kind == .test_exe);
- self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
+ self.test_runner = if (path) |p| b.dupePath(p) else null;
}
/// Handy when you have many C/C++ source files and want them all to have the same flags.
pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
- const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");
+ const b = self.step.owner;
+ const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM");
- const files_copy = self.builder.dupeStrings(files);
- const flags_copy = self.builder.dupeStrings(flags);
+ const files_copy = b.dupeStrings(files);
+ const flags_copy = b.dupeStrings(flags);
c_source_files.* = .{
.files = files_copy,
@@ -860,8 +869,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con
}
pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
- const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
- c_source_file.* = source.dupe(self.builder);
+ const b = self.step.owner;
+ const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM");
+ c_source_file.* = source.dupe(b);
self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
source.source.addStepDependencies(&self.step);
}
@@ -875,15 +885,18 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
}
pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
- self.zig_lib_dir = self.builder.dupePath(dir_path);
+ const b = self.step.owner;
+ self.zig_lib_dir = b.dupePath(dir_path);
}
pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
- self.main_pkg_path = self.builder.dupePath(dir_path);
+ const b = self.step.owner;
+ self.main_pkg_path = b.dupePath(dir_path);
}
pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
- self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
+ const b = self.step.owner;
+ self.libc_file = if (libc_file) |f| f.dupe(b) else null;
}
/// Returns the generated executable, library or object file.
@@ -914,13 +927,15 @@ pub fn getOutputPdbSource(self: *CompileStep) FileSource {
}
pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
+ const b = self.step.owner;
self.link_objects.append(.{
- .assembly_file = .{ .path = self.builder.dupe(path) },
+ .assembly_file = .{ .path = b.dupe(path) },
}) catch @panic("OOM");
}
pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
- const source_duped = source.dupe(self.builder);
+ const b = self.step.owner;
+ const source_duped = source.dupe(b);
self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
source_duped.addStepDependencies(&self.step);
}
@@ -930,7 +945,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
}
pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
- self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");
+ const b = self.step.owner;
+ self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM");
source.addStepDependencies(&self.step);
}
@@ -945,11 +961,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");
pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");
+ const b = self.step.owner;
+ self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM");
}
pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
- self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");
+ const b = self.step.owner;
+ self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM");
}
pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
@@ -958,23 +976,27 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi
}
pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
- self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
+ const b = self.step.owner;
+ self.lib_paths.append(b.dupe(path)) catch @panic("OOM");
}
pub fn addRPath(self: *CompileStep, path: []const u8) void {
- self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
+ const b = self.step.owner;
+ self.rpaths.append(b.dupe(path)) catch @panic("OOM");
}
pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
- self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
+ const b = self.step.owner;
+ self.framework_dirs.append(b.dupe(dir_path)) catch @panic("OOM");
}
/// Adds a module to be used with `@import` and exposing it in the current
/// package's module table using `name`.
pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
- cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");
+ const b = cs.step.owner;
+ cs.modules.put(b.dupe(name), module) catch @panic("OOM");
- var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator);
+ var done = std.AutoHashMap(*Module, void).init(b.allocator);
defer done.deinit();
cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");
}
@@ -982,7 +1004,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
/// Adds a module to be used with `@import` without exposing it in the current
/// package's module table.
pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
- const module = cs.builder.createModule(options);
+ const b = cs.step.owner;
+ const module = b.createModule(options);
return addModule(cs, name, module);
}
@@ -1002,12 +1025,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM
/// If Vcpkg was found on the system, it will be added to include and lib
/// paths for the specified target.
pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
+ const b = self.step.owner;
// Ideally in the Unattempted case we would call the function recursively
// after findVcpkgRoot and have only one switch statement, but the compiler
// cannot resolve the error set.
- switch (self.builder.vcpkg_root) {
+ switch (b.vcpkg_root) {
.unattempted => {
- self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
+ b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root|
VcpkgRoot{ .found = root }
else
.not_found;
@@ -1016,31 +1040,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
.found => {},
}
- switch (self.builder.vcpkg_root) {
+ switch (b.vcpkg_root) {
.unattempted => unreachable,
.not_found => return error.VcpkgNotFound,
.found => |root| {
- const allocator = self.builder.allocator;
+ const allocator = b.allocator;
const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
- defer self.builder.allocator.free(triplet);
+ defer b.allocator.free(triplet);
- const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
+ const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" });
errdefer allocator.free(include_path);
try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
- const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
+ const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" });
try self.lib_paths.append(lib_path);
- self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
+ self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" });
},
}
}
pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
+ const b = self.step.owner;
assert(self.kind == .@"test");
- const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
+ const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
for (args, 0..) |arg, i| {
- duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
+ duped_args[i] = if (arg) |a| b.dupe(a) else null;
}
self.exec_cmd_args = duped_args;
}
@@ -1055,16 +1080,17 @@ fn appendModuleArgs(
cs: *CompileStep,
zig_args: *ArrayList([]const u8),
) error{OutOfMemory}!void {
+ const b = cs.step.owner;
// First, traverse the whole dependency graph and give every module a unique name, ideally one
// named after what it's called somewhere in the graph. It will help here to have both a mapping
// from module to name and a set of all the currently-used names.
- var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator);
- var names = std.StringHashMap(void).init(cs.builder.allocator);
+ var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator);
+ var names = std.StringHashMap(void).init(b.allocator);
var to_name = std.ArrayList(struct {
name: []const u8,
mod: *Module,
- }).init(cs.builder.allocator);
+ }).init(b.allocator);
{
var it = cs.modules.iterator();
while (it.next()) |kv| {
@@ -1085,7 +1111,7 @@ fn appendModuleArgs(
if (mod_names.contains(dep.mod)) continue;
// We'll use this buffer to store the name we decide on
- var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32);
+ var buf = try b.allocator.alloc(u8, dep.name.len + 32);
// First, try just the exposed dependency name
std.mem.copy(u8, buf, dep.name);
var name = buf[0..dep.name.len];
@@ -1122,15 +1148,15 @@ fn appendModuleArgs(
const mod = kv.key_ptr.*;
const name = kv.value_ptr.*;
- const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies);
+ const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies);
const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));
try zig_args.append("--mod");
- try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));
+ try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));
}
}
// Lastly, output the root dependencies
- const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules);
+ const deps_str = try constructDepString(b.allocator, mod_names, cs.modules);
if (deps_str.len > 0) {
try zig_args.append("--deps");
try zig_args.append(deps_str);
@@ -1161,18 +1187,18 @@ fn constructDepString(
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ const b = step.owner;
const self = @fieldParentPtr(CompileStep, "step", step);
- const builder = self.builder;
if (self.root_src == null and self.link_objects.items.len == 0) {
log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
return error.NeedAnObject;
}
- var zig_args = ArrayList([]const u8).init(builder.allocator);
+ var zig_args = ArrayList([]const u8).init(b.allocator);
defer zig_args.deinit();
- try zig_args.append(builder.zig_exe);
+ try zig_args.append(b.zig_exe);
const cmd = switch (self.kind) {
.lib => "build-lib",
@@ -1183,15 +1209,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
};
try zig_args.append(cmd);
- if (builder.reference_trace) |some| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
+ if (b.reference_trace) |some| {
+ try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some}));
}
try addFlag(&zig_args, "LLVM", self.use_llvm);
try addFlag(&zig_args, "LLD", self.use_lld);
if (self.target.ofmt) |ofmt| {
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
+ try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
}
if (self.entry_symbol_name) |entry| {
@@ -1201,18 +1227,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.stack_size) |stack_size| {
try zig_args.append("--stack");
- try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
+ try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));
}
- if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
+ if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b));
// We will add link objects from transitive dependencies, but we want to keep
// all link objects in the same order provided.
// This array is used to keep self.link_objects immutable.
var transitive_deps: TransitiveDeps = .{
- .link_objects = ArrayList(LinkObject).init(builder.allocator),
- .seen_system_libs = StringHashMap(void).init(builder.allocator),
- .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
+ .link_objects = ArrayList(LinkObject).init(b.allocator),
+ .seen_system_libs = StringHashMap(void).init(b.allocator),
+ .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator),
.is_linking_libcpp = self.is_linking_libcpp,
.is_linking_libc = self.is_linking_libc,
.frameworks = &self.frameworks,
@@ -1225,14 +1251,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
for (transitive_deps.link_objects.items) |link_object| {
switch (link_object) {
- .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
+ .static_path => |static_path| try zig_args.append(static_path.getPath(b)),
.other_step => |other| switch (other.kind) {
.exe => @panic("Cannot link with an executable build artifact"),
.test_exe => @panic("Cannot link with an executable build artifact"),
.@"test" => @panic("Cannot link with a test"),
.obj => {
- try zig_args.append(other.getOutputSource().getPath(builder));
+ try zig_args.append(other.getOutputSource().getPath(b));
},
.lib => l: {
if (self.isStaticLibrary() and other.isStaticLibrary()) {
@@ -1240,7 +1266,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
break :l;
}
- const full_path_lib = other.getOutputLibSource().getPath(builder);
+ const full_path_lib = other.getOutputLibSource().getPath(b);
try zig_args.append(full_path_lib);
if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
@@ -1262,7 +1288,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
break :prefix "-l";
};
switch (system_lib.use_pkg_config) {
- .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
+ .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
.yes, .force => {
if (self.runPkgConfig(system_lib.name)) |args| {
try zig_args.appendSlice(args);
@@ -1276,7 +1302,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
.yes => {
// pkg-config failed, so fall back to linking the library
// by name directly.
- try zig_args.append(builder.fmt("{s}{s}", .{
+ try zig_args.append(b.fmt("{s}{s}", .{
prefix,
system_lib.name,
}));
@@ -1299,7 +1325,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try zig_args.append("--");
prev_has_extra_flags = false;
}
- try zig_args.append(asm_file.getPath(builder));
+ try zig_args.append(asm_file.getPath(b));
},
.c_source_file => |c_source_file| {
@@ -1316,7 +1342,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
try zig_args.append("--");
}
- try zig_args.append(c_source_file.source.getPath(builder));
+ try zig_args.append(c_source_file.source.getPath(b));
},
.c_source_files => |c_source_files| {
@@ -1334,7 +1360,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try zig_args.append("--");
}
for (c_source_files.files) |file| {
- try zig_args.append(builder.pathFromRoot(file));
+ try zig_args.append(b.pathFromRoot(file));
}
},
}
@@ -1350,7 +1376,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.image_base) |image_base| {
try zig_args.append("--image-base");
- try zig_args.append(builder.fmt("0x{x}", .{image_base}));
+ try zig_args.append(b.fmt("0x{x}", .{image_base}));
}
if (self.filter) |filter| {
@@ -1369,32 +1395,32 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.test_runner) |test_runner| {
try zig_args.append("--test-runner");
- try zig_args.append(builder.pathFromRoot(test_runner));
+ try zig_args.append(b.pathFromRoot(test_runner));
}
- for (builder.debug_log_scopes) |log_scope| {
+ for (b.debug_log_scopes) |log_scope| {
try zig_args.append("--debug-log");
try zig_args.append(log_scope);
}
- if (builder.debug_compile_errors) {
+ if (b.debug_compile_errors) {
try zig_args.append("--debug-compile-errors");
}
- if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
- if (builder.verbose_air) try zig_args.append("--verbose-air");
- if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
- if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
- if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
- if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
+ if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
+ if (b.verbose_air) try zig_args.append("--verbose-air");
+ if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
+ if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
+ if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
+ if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
- if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
- if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
- if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
- if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
- if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
- if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
+ if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg);
+ if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg);
+ if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg);
+ if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg);
+ if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg);
+ if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg);
if (self.emit_h) try zig_args.append("-femit-h");
@@ -1435,31 +1461,31 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
if (self.link_z_common_page_size) |size| {
try zig_args.append("-z");
- try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
+ try zig_args.append(b.fmt("common-page-size={d}", .{size}));
}
if (self.link_z_max_page_size) |size| {
try zig_args.append("-z");
- try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
+ try zig_args.append(b.fmt("max-page-size={d}", .{size}));
}
if (self.libc_file) |libc_file| {
try zig_args.append("--libc");
- try zig_args.append(libc_file.getPath(builder));
- } else if (builder.libc_file) |libc_file| {
+ try zig_args.append(libc_file.getPath(b));
+ } else if (b.libc_file) |libc_file| {
try zig_args.append("--libc");
try zig_args.append(libc_file);
}
switch (self.optimize) {
.Debug => {}, // Skip since it's the default.
- else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),
+ else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
}
try zig_args.append("--cache-dir");
- try zig_args.append(builder.cache_root.path orelse ".");
+ try zig_args.append(b.cache_root.path orelse ".");
try zig_args.append("--global-cache-dir");
- try zig_args.append(builder.global_cache_root.path orelse ".");
+ try zig_args.append(b.global_cache_root.path orelse ".");
try zig_args.append("--name");
try zig_args.append(self.name);
@@ -1471,11 +1497,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
if (self.version) |version| {
try zig_args.append("--version");
- try zig_args.append(builder.fmt("{}", .{version}));
+ try zig_args.append(b.fmt("{}", .{version}));
}
if (self.target.isDarwin()) {
- const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
+ const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
self.target.libPrefix(),
self.name,
self.target.dynamicLibSuffix(),
@@ -1489,7 +1515,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
}
if (self.pagezero_size) |pagezero_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
+ const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size});
try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
}
if (self.search_strategy) |strat| switch (strat) {
@@ -1497,7 +1523,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
.dylibs_first => try zig_args.append("-search_dylibs_first"),
};
if (self.headerpad_size) |headerpad_size| {
- const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
+ const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size});
try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
}
if (self.headerpad_max_install_names) {
@@ -1545,16 +1571,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try zig_args.append("--export-table");
}
if (self.initial_memory) |initial_memory| {
- try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
+ try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory}));
}
if (self.max_memory) |max_memory| {
- try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
+ try zig_args.append(b.fmt("--max-memory={d}", .{max_memory}));
}
if (self.shared_memory) {
try zig_args.append("--shared-memory");
}
if (self.global_base) |global_base| {
- try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
+ try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
}
if (self.code_model != .default) {
@@ -1562,16 +1588,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try zig_args.append(@tagName(self.code_model));
}
if (self.wasi_exec_model) |model| {
- try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
+ try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)}));
}
for (self.export_symbol_names) |symbol_name| {
- try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
+ try zig_args.append(b.fmt("--export={s}", .{symbol_name}));
}
if (!self.target.isNative()) {
try zig_args.appendSlice(&.{
- "-target", try self.target.zigTriple(builder.allocator),
- "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
+ "-target", try self.target.zigTriple(b.allocator),
+ "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()),
});
if (self.target.dynamic_linker.get()) |dynamic_linker| {
@@ -1582,12 +1608,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.linker_script) |linker_script| {
try zig_args.append("--script");
- try zig_args.append(linker_script.getPath(builder));
+ try zig_args.append(linker_script.getPath(b));
}
if (self.version_script) |version_script| {
try zig_args.append("--version-script");
- try zig_args.append(builder.pathFromRoot(version_script));
+ try zig_args.append(b.pathFromRoot(version_script));
}
if (self.kind == .@"test") {
@@ -1603,23 +1629,23 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
} else {
const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
- switch (builder.host.getExternalExecutor(self.target_info, .{
- .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
+ switch (b.host.getExternalExecutor(self.target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
.link_libc = transitive_deps.is_linking_libc,
})) {
.native => {},
.bad_dl, .bad_os_or_cpu => {
try zig_args.append("--test-no-exec");
},
- .rosetta => if (builder.enable_rosetta) {
+ .rosetta => if (b.enable_rosetta) {
try zig_args.append("--test-cmd-bin");
} else {
try zig_args.append("--test-no-exec");
},
.qemu => |bin_name| ok: {
- if (builder.enable_qemu) qemu: {
+ if (b.enable_qemu) qemu: {
const glibc_dir_arg = if (need_cross_glibc)
- builder.glibc_runtimes_dir orelse break :qemu
+ b.glibc_runtimes_dir orelse break :qemu
else
null;
try zig_args.append("--test-cmd");
@@ -1636,7 +1662,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
"i686"
else
@tagName(cpu_arch);
- const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
+ const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{
dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
});
@@ -1650,14 +1676,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
try zig_args.append("--test-no-exec");
},
- .wine => |bin_name| if (builder.enable_wine) {
+ .wine => |bin_name| if (b.enable_wine) {
try zig_args.append("--test-cmd");
try zig_args.append(bin_name);
try zig_args.append("--test-cmd-bin");
} else {
try zig_args.append("--test-no-exec");
},
- .wasmtime => |bin_name| if (builder.enable_wasmtime) {
+ .wasmtime => |bin_name| if (b.enable_wasmtime) {
try zig_args.append("--test-cmd");
try zig_args.append(bin_name);
try zig_args.append("--test-cmd");
@@ -1666,7 +1692,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
} else {
try zig_args.append("--test-no-exec");
},
- .darling => |bin_name| if (builder.enable_darling) {
+ .darling => |bin_name| if (b.enable_darling) {
try zig_args.append("--test-cmd");
try zig_args.append(bin_name);
try zig_args.append("--test-cmd-bin");
@@ -1685,18 +1711,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
switch (include_dir) {
.raw_path => |include_path| {
try zig_args.append("-I");
- try zig_args.append(builder.pathFromRoot(include_path));
+ try zig_args.append(b.pathFromRoot(include_path));
},
.raw_path_system => |include_path| {
- if (builder.sysroot != null) {
+ if (b.sysroot != null) {
try zig_args.append("-iwithsysroot");
} else {
try zig_args.append("-isystem");
}
- const resolved_include_path = builder.pathFromRoot(include_path);
+ const resolved_include_path = b.pathFromRoot(include_path);
- const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
+ const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
// We need to check for disk designator and strip it out from dir path so
// that zig/clang can concat resolved_include_path with sysroot.
const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
@@ -1712,7 +1738,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
},
.other_step => |other| {
if (other.emit_h) {
- const h_path = other.getOutputHSource().getPath(builder);
+ const h_path = other.getOutputHSource().getPath(b);
try zig_args.append("-isystem");
try zig_args.append(fs.path.dirname(h_path).?);
}
@@ -1721,8 +1747,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try install_step.make(prog_node);
}
try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
- other.builder.install_prefix, "include",
+ try zig_args.append(b.pathJoin(&.{
+ other.step.owner.install_prefix, "include",
}));
}
},
@@ -1751,7 +1777,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.target.isDarwin()) {
for (self.framework_dirs.items) |dir| {
- if (builder.sysroot != null) {
+ if (b.sysroot != null) {
try zig_args.append("-iframeworkwithsysroot");
} else {
try zig_args.append("-iframework");
@@ -1784,17 +1810,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
}
- if (builder.sysroot) |sysroot| {
+ if (b.sysroot) |sysroot| {
try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
}
- for (builder.search_prefixes.items) |search_prefix| {
+ for (b.search_prefixes.items) |search_prefix| {
try zig_args.append("-L");
- try zig_args.append(builder.pathJoin(&.{
+ try zig_args.append(b.pathJoin(&.{
search_prefix, "lib",
}));
try zig_args.append("-I");
- try zig_args.append(builder.pathJoin(&.{
+ try zig_args.append(b.pathJoin(&.{
search_prefix, "include",
}));
}
@@ -1805,15 +1831,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (self.zig_lib_dir) |dir| {
try zig_args.append("--zig-lib-dir");
- try zig_args.append(builder.pathFromRoot(dir));
- } else if (builder.zig_lib_dir) |dir| {
+ try zig_args.append(b.pathFromRoot(dir));
+ } else if (b.zig_lib_dir) |dir| {
try zig_args.append("--zig-lib-dir");
try zig_args.append(dir);
}
if (self.main_pkg_path) |dir| {
try zig_args.append("--main-pkg-path");
- try zig_args.append(builder.pathFromRoot(dir));
+ try zig_args.append(b.pathFromRoot(dir));
}
try addFlag(&zig_args, "PIC", self.force_pic);
@@ -1846,15 +1872,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
args_length += arg.len + 1; // +1 to account for null terminator
}
if (args_length >= 30 * 1024) {
- try builder.cache_root.handle.makePath("args");
+ try b.cache_root.handle.makePath("args");
const args_to_escape = zig_args.items[2..];
- var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len);
+ var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len);
arg_blk: for (args_to_escape) |arg| {
for (arg, 0..) |c, arg_idx| {
if (c == '\\' or c == '"') {
// Slow path for arguments that need to be escaped. We'll need to allocate and copy
- var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1);
+ var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1);
const writer = escaped.writer();
try writer.writeAll(arg[0..arg_idx]);
for (arg[arg_idx..]) |to_escape| {
@@ -1870,8 +1896,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
// other zig build commands running in parallel.
- const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
- const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
+ const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items);
+ const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
var args_hash: [Sha256.digest_length]u8 = undefined;
Sha256.hash(args, &args_hash, .{});
@@ -1883,18 +1909,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
);
const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
- try builder.cache_root.handle.writeFile(args_file, args);
+ try b.cache_root.handle.writeFile(args_file, args);
- const resolved_args_file = try mem.concat(builder.allocator, u8, &.{
+ const resolved_args_file = try mem.concat(b.allocator, u8, &.{
"@",
- try builder.cache_root.join(builder.allocator, &.{args_file}),
+ try b.cache_root.join(b.allocator, &.{args_file}),
});
zig_args.shrinkRetainingCapacity(2);
try zig_args.append(resolved_args_file);
}
- const output_bin_path = try builder.execFromStep(zig_args.items, &self.step, prog_node);
+ const output_bin_path = try step.evalZigProcess(zig_args.items, prog_node);
const build_output_dir = fs.path.dirname(output_bin_path).?;
if (self.output_dir) |output_dir| {
@@ -1928,25 +1954,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// Update generated files
if (self.output_dir != null) {
- self.output_path_source.path = builder.pathJoin(
+ self.output_path_source.path = b.pathJoin(
&.{ self.output_dir.?, self.out_filename },
);
if (self.emit_h) {
- self.output_h_path_source.path = builder.pathJoin(
+ self.output_h_path_source.path = b.pathJoin(
&.{ self.output_dir.?, self.out_h_filename },
);
}
if (self.target.isWindows() or self.target.isUefi()) {
- self.output_pdb_path_source.path = builder.pathJoin(
+ self.output_pdb_path_source.path = b.pathJoin(
&.{ self.output_dir.?, self.out_pdb_filename },
);
}
}
if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
- try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
+ try doAtomicSymLinks(b.allocator, self.getOutputSource().getPath(b), self.major_only_filename.?, self.name_only_filename.?);
}
}
lib/std/Build/ConfigHeaderStep.zig
@@ -34,7 +34,6 @@ pub const Value = union(enum) {
};
step: Step,
-builder: *std.Build,
values: std.StringArrayHashMap(Value),
output_file: std.Build.GeneratedFile,
@@ -49,8 +48,8 @@ pub const Options = struct {
first_ret_addr: ?usize = null,
};
-pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
- const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
+pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep {
+ const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM");
var include_path: []const u8 = "config.h";
@@ -69,29 +68,28 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
}
const name = if (options.style.getFileSource()) |s|
- builder.fmt("configure {s} header {s} to {s}", .{
+ owner.fmt("configure {s} header {s} to {s}", .{
@tagName(options.style), s.getDisplayName(), include_path,
})
else
- builder.fmt("configure {s} header to {s}", .{@tagName(options.style), include_path});
+ owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path });
self.* = .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = base_id,
.name = name,
+ .owner = owner,
.makeFn = make,
.first_ret_addr = options.first_ret_addr orelse @returnAddress(),
}),
.style = options.style,
- .values = std.StringArrayHashMap(Value).init(builder.allocator),
+ .values = std.StringArrayHashMap(Value).init(owner.allocator),
.max_bytes = options.max_bytes,
.include_path = include_path,
.output_file = .{ .step = &self.step },
};
-
return self;
}
@@ -161,8 +159,9 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const b = step.owner;
const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
- const gpa = self.builder.allocator;
+ const gpa = b.allocator;
// The cache is used here not really as a way to speed things up - because writing
// the data to a file would probably be very fast - but as a way to find a canonical
@@ -191,13 +190,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
switch (self.style) {
.autoconf => |file_source| {
try output.appendSlice(c_generated_line);
- const src_path = file_source.getPath(self.builder);
+ const src_path = file_source.getPath(b);
const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
try render_autoconf(contents, &output, self.values, src_path);
},
.cmake => |file_source| {
try output.appendSlice(c_generated_line);
- const src_path = file_source.getPath(self.builder);
+ const src_path = file_source.getPath(b);
const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
try render_cmake(contents, &output, self.values, src_path);
},
@@ -222,7 +221,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
.{std.fmt.fmtSliceHexLower(&digest)},
) catch unreachable;
- const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });
+ const output_dir = try b.cache_root.join(gpa, &.{ "o", &hash_basename });
// If output_path has directory parts, deal with them. Example:
// output_dir is zig-cache/o/HASH
@@ -242,7 +241,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
- self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{
+ self.output_file.path = try std.fs.path.join(b.allocator, &.{
output_dir, self.include_path,
});
}
lib/std/Build/EmulatableRunStep.zig
@@ -1,218 +0,0 @@
-//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
-//! When a binary is foreign, but emulation for the target is disabled, the specified binary
-//! will not be run and therefore also not validated against its output.
-//! This step can be useful when wishing to run a built binary on multiple platforms,
-//! without having to verify if it's possible to be ran against.
-
-const std = @import("../std.zig");
-const Step = std.Build.Step;
-const CompileStep = std.Build.CompileStep;
-const RunStep = std.Build.RunStep;
-
-const fs = std.fs;
-const process = std.process;
-const EnvMap = process.EnvMap;
-
-const EmulatableRunStep = @This();
-
-pub const base_id = .emulatable_run;
-
-const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
-
-step: Step,
-builder: *std.Build,
-
-/// The artifact (executable) to be run by this step
-exe: *CompileStep,
-
-/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
-expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },
-
-/// Override this field to modify the environment
-env_map: ?*EnvMap,
-
-/// Set this to modify the current working directory
-cwd: ?[]const u8,
-
-stdout_action: RunStep.StdIoAction = .inherit,
-stderr_action: RunStep.StdIoAction = .inherit,
-
-/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
-/// or through emulation.
-hide_foreign_binaries_warning: bool,
-
-/// Creates a step that will execute the given artifact. This step will allow running the
-/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
-/// When set to false, and the binary is foreign, running the executable is skipped.
-/// Asserts given artifact is an executable.
-pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
- std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
- const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
-
- const option_name = "hide-foreign-warnings";
- const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
- break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
- } else false;
-
- self.* = .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
- .id = .emulatable_run,
- .name = name,
- .makeFn = make,
- }),
- .exe = artifact,
- .env_map = null,
- .cwd = null,
- .hide_foreign_binaries_warning = hide_warnings,
- };
- self.step.dependOn(&artifact.step);
-
- return self;
-}
-
-fn make(step: *Step, prog_node: *std.Progress.Node) !void {
- _ = prog_node;
- const self = @fieldParentPtr(EmulatableRunStep, "step", step);
- const host_info = self.builder.host;
-
- var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
- defer argv_list.deinit();
-
- const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
- switch (host_info.getExternalExecutor(self.exe.target_info, .{
- .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
- .link_libc = self.exe.is_linking_libc,
- })) {
- .native => {},
- .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
- .wine => |bin_name| if (self.builder.enable_wine) {
- try argv_list.append(bin_name);
- } else return,
- .qemu => |bin_name| if (self.builder.enable_qemu) {
- const glibc_dir_arg = if (need_cross_glibc)
- self.builder.glibc_runtimes_dir orelse return
- else
- null;
- try argv_list.append(bin_name);
- if (glibc_dir_arg) |dir| {
- // TODO look into making this a call to `linuxTriple`. This
- // needs the directory to be called "i686" rather than
- // "x86" which is why we do it manually here.
- const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
- const cpu_arch = self.exe.target.getCpuArch();
- const os_tag = self.exe.target.getOsTag();
- const abi = self.exe.target.getAbi();
- const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
- "i686"
- else
- @tagName(cpu_arch);
- const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
- dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
- });
-
- try argv_list.append("-L");
- try argv_list.append(full_dir);
- }
- } else return warnAboutForeignBinaries(self),
- .darling => |bin_name| if (self.builder.enable_darling) {
- try argv_list.append(bin_name);
- } else return warnAboutForeignBinaries(self),
- .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
- try argv_list.append(bin_name);
- try argv_list.append("--dir=.");
- } else return warnAboutForeignBinaries(self),
- else => return warnAboutForeignBinaries(self),
- }
-
- if (self.exe.target.isWindows()) {
- // On Windows we don't have rpaths so we have to add .dll search paths to PATH
- RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
- }
-
- const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
- try argv_list.append(executable_path);
-
- try RunStep.runCommand(
- argv_list.items,
- self.builder,
- self.expected_term,
- self.stdout_action,
- self.stderr_action,
- .Inherit,
- self.env_map,
- self.cwd,
- false,
- );
-}
-
-pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
- self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
- self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
-}
-
-fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
- if (step.hide_foreign_binaries_warning) return;
- const builder = step.builder;
- const artifact = step.exe;
-
- const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
- const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
- const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
- const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
- switch (builder.host.getExternalExecutor(target_info, .{
- .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
- .link_libc = artifact.is_linking_libc,
- })) {
- .native => unreachable,
- .bad_dl => |foreign_dl| {
- const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
- std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
- host_dl, foreign_dl, host_dl,
- });
- },
- .bad_os_or_cpu => {
- std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
- host_name, foreign_name,
- });
- },
- .darling => if (!builder.enable_darling) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling darling.\n",
- .{ host_name, foreign_name },
- );
- },
- .rosetta => if (!builder.enable_rosetta) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling rosetta.\n",
- .{ host_name, foreign_name },
- );
- },
- .wine => if (!builder.enable_wine) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling wine.\n",
- .{ host_name, foreign_name },
- );
- },
- .qemu => if (!builder.enable_qemu) {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling qemu.\n",
- .{ host_name, foreign_name },
- );
- },
- .wasmtime => {
- std.debug.print(
- "the host system ({s}) does not appear to be capable of executing binaries " ++
- "from the target ({s}). Consider enabling wasmtime.\n",
- .{ host_name, foreign_name },
- );
- },
- }
-}
lib/std/Build/FmtStep.zig
@@ -1,37 +1,73 @@
-const std = @import("../std.zig");
-const Step = std.Build.Step;
-const FmtStep = @This();
+//! This step has two modes:
+//! * Modify mode: directly modify source files, formatting them in place.
+//! * Check mode: fail the step if a non-conforming file is found.
+
+step: Step,
+paths: []const []const u8,
+exclude_paths: []const []const u8,
+check: bool,
pub const base_id = .fmt;
-step: Step,
-builder: *std.Build,
-argv: [][]const u8,
-
-pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
- const self = builder.allocator.create(FmtStep) catch @panic("OOM");
- const name = "zig fmt";
- self.* = FmtStep{
- .step = Step.init(builder.allocator, .{
- .id = .fmt,
+pub const Options = struct {
+ paths: []const []const u8 = &.{},
+ exclude_paths: []const []const u8 = &.{},
+ /// If true, fails the build step when any non-conforming files are encountered.
+ check: bool = false,
+};
+
+pub fn create(owner: *std.Build, options: Options) *FmtStep {
+ const self = owner.allocator.create(FmtStep) catch @panic("OOM");
+ const name = if (options.check) "zig fmt --check" else "zig fmt";
+ self.* = .{
+ .step = Step.init(.{
+ .id = base_id,
.name = name,
+ .owner = owner,
.makeFn = make,
}),
- .builder = builder,
- .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),
+ .paths = options.paths,
+ .exclude_paths = options.exclude_paths,
+ .check = options.check,
};
-
- self.argv[0] = builder.zig_exe;
- self.argv[1] = "fmt";
- for (paths, 0..) |path, i| {
- self.argv[2 + i] = builder.pathFromRoot(path);
- }
return self;
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ // zig fmt is fast enough that no progress is needed.
_ = prog_node;
+
+ // TODO: if check=false, this means we are modifying source files in place, which
+ // is an operation that could race against other operations also modifying source files
+ // in place. In this case, this step should obtain a write lock while making those
+ // modifications.
+
+ const b = step.owner;
+ const arena = b.allocator;
const self = @fieldParentPtr(FmtStep, "step", step);
- return self.builder.spawnChild(self.argv);
+ var argv: std.ArrayListUnmanaged([]const u8) = .{};
+ try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len);
+
+ argv.appendAssumeCapacity(b.zig_exe);
+ argv.appendAssumeCapacity("fmt");
+
+ if (self.check) {
+ argv.appendAssumeCapacity("--check");
+ }
+
+ for (self.paths) |p| {
+ argv.appendAssumeCapacity(b.pathFromRoot(p));
+ }
+
+ for (self.exclude_paths) |p| {
+ argv.appendAssumeCapacity("--exclude");
+ argv.appendAssumeCapacity(b.pathFromRoot(p));
+ }
+
+ return step.evalChildProcess(argv.items);
}
+
+const std = @import("../std.zig");
+const Step = std.Build.Step;
+const FmtStep = @This();
lib/std/Build/InstallArtifactStep.zig
@@ -7,23 +7,24 @@ const InstallArtifactStep = @This();
pub const base_id = .install_artifact;
step: Step,
-builder: *std.Build,
+dest_builder: *std.Build,
artifact: *CompileStep,
dest_dir: InstallDir,
pdb_dir: ?InstallDir,
h_dir: ?InstallDir,
-pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
+pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
if (artifact.install_step) |s| return s;
- const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");
+ const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM");
self.* = InstallArtifactStep{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = base_id,
- .name = builder.fmt("install {s}", .{artifact.name}),
+ .name = owner.fmt("install {s}", .{artifact.name}),
+ .owner = owner,
.makeFn = make,
}),
+ .dest_builder = owner,
.artifact = artifact,
.dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
.obj => @panic("Cannot install a .obj build artifact."),
@@ -43,48 +44,52 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep
self.step.dependOn(&artifact.step);
artifact.install_step = self;
- builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
+ owner.pushInstalledFile(self.dest_dir, artifact.out_filename);
if (self.artifact.isDynamicLibrary()) {
if (artifact.major_only_filename) |name| {
- builder.pushInstalledFile(.lib, name);
+ owner.pushInstalledFile(.lib, name);
}
if (artifact.name_only_filename) |name| {
- builder.pushInstalledFile(.lib, name);
+ owner.pushInstalledFile(.lib, name);
}
if (self.artifact.target.isWindows()) {
- builder.pushInstalledFile(.lib, artifact.out_lib_filename);
+ owner.pushInstalledFile(.lib, artifact.out_lib_filename);
}
}
if (self.pdb_dir) |pdb_dir| {
- builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
+ owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
}
if (self.h_dir) |h_dir| {
- builder.pushInstalledFile(h_dir, artifact.out_h_filename);
+ owner.pushInstalledFile(h_dir, artifact.out_h_filename);
}
return self;
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const src_builder = step.owner;
const self = @fieldParentPtr(InstallArtifactStep, "step", step);
- const builder = self.builder;
+ const dest_builder = self.dest_builder;
- const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
- try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
+ const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
+ try src_builder.updateFile(
+ self.artifact.getOutputSource().getPath(src_builder),
+ full_dest_path,
+ );
if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
- try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
+ try CompileStep.doAtomicSymLinks(src_builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
}
if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
- const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
- try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
+ const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
+ try src_builder.updateFile(self.artifact.getOutputLibSource().getPath(src_builder), full_implib_path);
}
if (self.pdb_dir) |pdb_dir| {
- const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
- try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
+ const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
+ try src_builder.updateFile(self.artifact.getOutputPdbSource().getPath(src_builder), full_pdb_path);
}
if (self.h_dir) |h_dir| {
- const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
- try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
+ const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename);
+ try src_builder.updateFile(self.artifact.getOutputHSource().getPath(src_builder), full_h_path);
}
self.artifact.installed_path = full_dest_path;
}
lib/std/Build/InstallDirStep.zig
@@ -7,11 +7,10 @@ const InstallDirStep = @This();
const log = std.log;
step: Step,
-builder: *std.Build,
options: Options,
/// This is used by the build system when a file being installed comes from one
/// package but is being installed by another.
-override_source_builder: ?*std.Build = null,
+dest_builder: *std.Build,
pub const base_id = .install_dir;
@@ -40,27 +39,26 @@ pub const Options = struct {
}
};
-pub fn init(
- builder: *std.Build,
- options: Options,
-) InstallDirStep {
- builder.pushInstalledFile(options.install_dir, options.install_subdir);
+pub fn init(owner: *std.Build, options: Options) InstallDirStep {
+ owner.pushInstalledFile(options.install_dir, options.install_subdir);
return .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = .install_dir,
- .name = builder.fmt("install {s}/", .{options.source_dir}),
+ .name = owner.fmt("install {s}/", .{options.source_dir}),
+ .owner = owner,
.makeFn = make,
}),
- .options = options.dupe(builder),
+ .options = options.dupe(owner),
+ .dest_builder = owner,
};
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
const self = @fieldParentPtr(InstallDirStep, "step", step);
- const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
- const src_builder = self.override_source_builder orelse self.builder;
+ const dest_builder = self.dest_builder;
+ const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
+ const src_builder = self.step.owner;
const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
@@ -69,7 +67,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
return error.StepFailed;
};
defer src_dir.close();
- var it = try src_dir.walk(self.builder.allocator);
+ var it = try src_dir.walk(dest_builder.allocator);
next_entry: while (try it.next()) |entry| {
for (self.options.exclude_extensions) |ext| {
if (mem.endsWith(u8, entry.path, ext)) {
@@ -77,20 +75,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
}
- const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
- const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
+ const full_path = dest_builder.pathJoin(&.{ full_src_dir, entry.path });
+ const dest_path = dest_builder.pathJoin(&.{ dest_prefix, entry.path });
switch (entry.kind) {
.Directory => try fs.cwd().makePath(dest_path),
.File => {
for (self.options.blank_extensions) |ext| {
if (mem.endsWith(u8, entry.path, ext)) {
- try self.builder.truncateFile(dest_path);
+ try dest_builder.truncateFile(dest_path);
continue :next_entry;
}
}
- try self.builder.updateFile(full_path, dest_path);
+ try dest_builder.updateFile(full_path, dest_path);
},
else => continue,
}
lib/std/Build/InstallFileStep.zig
@@ -7,39 +7,40 @@ const InstallFileStep = @This();
pub const base_id = .install_file;
step: Step,
-builder: *std.Build,
source: FileSource,
dir: InstallDir,
dest_rel_path: []const u8,
/// This is used by the build system when a file being installed comes from one
/// package but is being installed by another.
-override_source_builder: ?*std.Build = null,
+dest_builder: *std.Build,
pub fn init(
- builder: *std.Build,
+ owner: *std.Build,
source: FileSource,
dir: InstallDir,
dest_rel_path: []const u8,
) InstallFileStep {
- builder.pushInstalledFile(dir, dest_rel_path);
+ owner.pushInstalledFile(dir, dest_rel_path);
return InstallFileStep{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
- .id = .install_file,
- .name = builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),
+ .step = Step.init(.{
+ .id = base_id,
+ .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),
+ .owner = owner,
.makeFn = make,
}),
- .source = source.dupe(builder),
- .dir = dir.dupe(builder),
- .dest_rel_path = builder.dupePath(dest_rel_path),
+ .source = source.dupe(owner),
+ .dir = dir.dupe(owner),
+ .dest_rel_path = owner.dupePath(dest_rel_path),
+ .dest_builder = owner,
};
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const src_builder = step.owner;
const self = @fieldParentPtr(InstallFileStep, "step", step);
- const src_builder = self.override_source_builder orelse self.builder;
+ const dest_builder = self.dest_builder;
const full_src_path = self.source.getPath2(src_builder, step);
- const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
- try self.builder.updateFile(full_src_path, full_dest_path);
+ const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path);
+ try dest_builder.updateFile(full_src_path, full_dest_path);
}
lib/std/Build/LogStep.zig
@@ -1,28 +0,0 @@
-const std = @import("../std.zig");
-const log = std.log;
-const Step = std.Build.Step;
-const LogStep = @This();
-
-pub const base_id = .log;
-
-step: Step,
-builder: *std.Build,
-data: []const u8,
-
-pub fn init(builder: *std.Build, data: []const u8) LogStep {
- return LogStep{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
- .id = .log,
- .name = builder.fmt("log {s}", .{data}),
- .makeFn = make,
- }),
- .data = builder.dupe(data),
- };
-}
-
-fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void {
- _ = prog_node;
- const self = @fieldParentPtr(LogStep, "step", step);
- log.info("{s}", .{self.data});
-}
lib/std/Build/ObjCopyStep.zig
@@ -21,7 +21,6 @@ pub const RawFormat = enum {
};
step: Step,
-builder: *std.Build,
file_source: std.Build.FileSource,
basename: []const u8,
output_file: std.Build.GeneratedFile,
@@ -38,18 +37,18 @@ pub const Options = struct {
};
pub fn create(
- builder: *std.Build,
+ owner: *std.Build,
file_source: std.Build.FileSource,
options: Options,
) *ObjCopyStep {
- const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM");
+ const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM");
self.* = ObjCopyStep{
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = base_id,
- .name = builder.fmt("objcopy {s}", .{file_source.getDisplayName()}),
+ .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}),
+ .owner = owner,
.makeFn = make,
}),
- .builder = builder,
.file_source = file_source,
.basename = options.basename orelse file_source.getDisplayName(),
.output_file = std.Build.GeneratedFile{ .step = &self.step },
@@ -67,9 +66,8 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
- _ = prog_node;
+ const b = step.owner;
const self = @fieldParentPtr(ObjCopyStep, "step", step);
- const b = self.builder;
var man = b.cache.obtain();
defer man.deinit();
@@ -84,7 +82,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
man.hash.addOptional(self.pad_to);
man.hash.addOptional(self.format);
- if (man.hit() catch |err| failWithCacheError(man, err)) {
+ if (try step.cacheHit(&man)) {
// Cache hit, skip subprocess execution.
const digest = man.final();
self.output_file.path = try b.cache_root.join(b.allocator, &.{
@@ -116,23 +114,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
};
try argv.appendSlice(&.{ full_src_path, full_dest_path });
- _ = try self.builder.execFromStep(argv.items, &self.step);
+ _ = try step.spawnZigProcess(argv.items, prog_node);
self.output_file.path = full_dest_path;
try man.writeManifest();
}
-
-/// TODO consolidate this with the same function in RunStep?
-/// Also properly deal with concurrency (see open PR)
-fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
- const i = man.failed_file_index orelse failWithSimpleError(err);
- const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
- const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
- std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
- std.process.exit(1);
-}
-
-fn failWithSimpleError(err: anyerror) noreturn {
- std.debug.print("{s}\n", .{@errorName(err)});
- std.process.exit(1);
-}
lib/std/Build/OptionsStep.zig
@@ -12,25 +12,24 @@ pub const base_id = .options;
step: Step,
generated_file: GeneratedFile,
-builder: *std.Build,
contents: std.ArrayList(u8),
artifact_args: std.ArrayList(OptionArtifactArg),
file_source_args: std.ArrayList(OptionFileSourceArg),
-pub fn create(builder: *std.Build) *OptionsStep {
- const self = builder.allocator.create(OptionsStep) catch @panic("OOM");
+pub fn create(owner: *std.Build) *OptionsStep {
+ const self = owner.allocator.create(OptionsStep) catch @panic("OOM");
self.* = .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = base_id,
.name = "options",
+ .owner = owner,
.makeFn = make,
}),
.generated_file = undefined,
- .contents = std.ArrayList(u8).init(builder.allocator),
- .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
- .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
+ .contents = std.ArrayList(u8).init(owner.allocator),
+ .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator),
+ .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator),
};
self.generated_file = .{ .step = &self.step };
@@ -196,7 +195,7 @@ pub fn addOptionFileSource(
) void {
self.file_source_args.append(.{
.name = name,
- .source = source.dupe(self.builder),
+ .source = source.dupe(self.step.owner),
}) catch @panic("OOM");
source.addStepDependencies(&self.step);
}
@@ -204,12 +203,12 @@ pub fn addOptionFileSource(
/// The value is the path in the cache dir.
/// Adds a dependency automatically.
pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
- self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");
+ self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM");
self.step.dependOn(&artifact.step);
}
pub fn createModule(self: *OptionsStep) *std.Build.Module {
- return self.builder.createModule(.{
+ return self.step.owner.createModule(.{
.source_file = self.getSource(),
.dependencies = &.{},
});
@@ -220,14 +219,17 @@ pub fn getSource(self: *OptionsStep) FileSource {
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ // This step completes so quickly that no progress is necessary.
_ = prog_node;
+
+ const b = step.owner;
const self = @fieldParentPtr(OptionsStep, "step", step);
for (self.artifact_args.items) |item| {
self.addOption(
[]const u8,
item.name,
- self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
+ b.pathFromRoot(item.artifact.getOutputSource().getPath(b)),
);
}
@@ -235,20 +237,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
self.addOption(
[]const u8,
item.name,
- item.source.getPath(self.builder),
+ item.source.getPath(b),
);
}
- var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});
+ var options_dir = try b.cache_root.handle.makeOpenPath("options", .{});
defer options_dir.close();
const basename = self.hashContentsToFileName();
try options_dir.writeFile(&basename, self.contents.items);
- self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{
- "options", &basename,
- });
+ self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename });
}
fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
lib/std/Build/RemoveDirStep.zig
@@ -7,28 +7,37 @@ const RemoveDirStep = @This();
pub const base_id = .remove_dir;
step: Step,
-builder: *std.Build,
dir_path: []const u8,
-pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {
+pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep {
return RemoveDirStep{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = .remove_dir,
- .name = builder.fmt("RemoveDir {s}", .{dir_path}),
+ .name = owner.fmt("RemoveDir {s}", .{dir_path}),
+ .owner = owner,
.makeFn = make,
}),
- .dir_path = builder.dupePath(dir_path),
+ .dir_path = owner.dupePath(dir_path),
};
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ // TODO update progress node while walking file system.
+ // Should the standard library support this use case??
_ = prog_node;
+
+ const b = step.owner;
const self = @fieldParentPtr(RemoveDirStep, "step", step);
- const full_path = self.builder.pathFromRoot(self.dir_path);
- fs.cwd().deleteTree(full_path) catch |err| {
- log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
- return err;
+ b.build_root.handle.deleteTree(self.dir_path) catch |err| {
+ if (b.build_root.path) |base| {
+ return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{
+ base, self.dir_path, @errorName(err),
+ });
+ } else {
+ return step.fail("unable to recursively delete path '{s}': {s}", .{
+ self.dir_path, @errorName(err),
+ });
+ }
};
}
lib/std/Build/RunStep.zig
@@ -11,14 +11,11 @@ const EnvMap = process.EnvMap;
const Allocator = mem.Allocator;
const ExecError = std.Build.ExecError;
-const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
-
const RunStep = @This();
pub const base_id: Step.Id = .run;
step: Step,
-builder: *std.Build,
/// See also addArg and addArgs to modifying this directly
argv: ArrayList(Arg),
@@ -29,35 +26,68 @@ cwd: ?[]const u8,
/// Override this field to modify the environment, or use setEnvironmentVariable
env_map: ?*EnvMap,
-stdout_action: StdIoAction = .inherit,
-stderr_action: StdIoAction = .inherit,
-
-stdin_behavior: std.ChildProcess.StdIo = .Inherit,
-
-/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
-expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },
-
-/// Print the command before running it
-print: bool,
-/// Controls whether execution is skipped if the output file is up-to-date.
-/// The default is to always run if there is no output file, and to skip
-/// running if all output files are up-to-date.
-condition: enum { output_outdated, always } = .output_outdated,
+/// Configures whether the RunStep is considered to have side-effects, and also
+/// whether the RunStep will inherit stdio streams, forwarding them to the
+/// parent process, in which case will require a global lock to prevent other
+/// steps from interfering with stdio while the subprocess associated with this
+/// RunStep is running.
+/// If the RunStep is determined to not have side-effects, then execution will
+/// be skipped if all output files are up-to-date and input files are
+/// unchanged.
+stdio: StdIo = .infer_from_args,
/// Additional file paths relative to build.zig that, when modified, indicate
/// that the RunStep should be re-executed.
+/// If the RunStep is determined to have side-effects, this field is ignored
+/// and the RunStep is always executed when it appears in the build graph.
extra_file_dependencies: []const []const u8 = &.{},
/// After adding an output argument, this step will by default rename itself
/// for a better display name in the build summary.
/// This can be disabled by setting this to false.
-rename_step_with_output_arg: bool,
-
-pub const StdIoAction = union(enum) {
+rename_step_with_output_arg: bool = true,
+
+/// If this is true, a RunStep which is configured to check the output of the
+/// executed binary will not fail the build if the binary cannot be executed
+/// due to being for a foreign binary to the host system which is running the
+/// build graph.
+/// Command-line arguments such as -fqemu and -fwasmtime may affect whether a
+/// binary is detected as foreign, as well as system configuration such as
+/// Rosetta (macOS) and binfmt_misc (Linux).
+skip_foreign_checks: bool = false,
+
+/// If stderr or stdout exceeds this amount, the child process is killed and
+/// the step fails.
+max_stdio_size: usize = 10 * 1024 * 1024,
+
+pub const StdIo = union(enum) {
+ /// Whether the RunStep has side-effects will be determined by whether or not one
+ /// of the args is an output file (added with `addOutputFileArg`).
+ /// If the RunStep is determined to have side-effects, this is the same as `inherit`.
+ /// The step will fail if the subprocess crashes or returns a non-zero exit code.
+ infer_from_args,
+ /// Causes the RunStep to be considered to have side-effects, and therefore
+ /// always execute when it appears in the build graph.
+ /// It also means that this step will obtain a global lock to prevent other
+ /// steps from running in the meantime.
+ /// The step will fail if the subprocess crashes or returns a non-zero exit code.
inherit,
- ignore,
- expect_exact: []const u8,
- expect_matches: []const []const u8,
+ /// Causes the RunStep to be considered to *not* have side-effects. The
+ /// process will be re-executed if any of the input dependencies are
+ /// modified. The exit code and standard I/O streams will be checked for
+ /// certain conditions, and the step will succeed or fail based on these
+ /// conditions.
+ /// Note that an explicit check for exit code 0 needs to be added to this
+ /// list if such a check is desireable.
+ check: []const Check,
+
+ pub const Check = union(enum) {
+ expect_stderr_exact: []const u8,
+ expect_stderr_match: []const u8,
+ expect_stdout_exact: []const u8,
+ expect_stdout_match: []const u8,
+ expect_term: std.ChildProcess.Term,
+ };
};
pub const Arg = union(enum) {
@@ -72,20 +102,20 @@ pub const Arg = union(enum) {
};
};
-pub fn create(builder: *std.Build, name: []const u8) *RunStep {
- const self = builder.allocator.create(RunStep) catch @panic("OOM");
+pub fn create(owner: *std.Build, name: []const u8) *RunStep {
+ const self = owner.allocator.create(RunStep) catch @panic("OOM");
self.* = .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = base_id,
.name = name,
+ .owner = owner,
.makeFn = make,
}),
- .argv = ArrayList(Arg).init(builder.allocator),
+ .argv = ArrayList(Arg).init(owner.allocator),
.cwd = null,
.env_map = null,
- .print = builder.verbose,
.rename_step_with_output_arg = true,
+ .max_stdio_size = 10 * 1024 * 1024,
};
return self;
}
@@ -99,16 +129,17 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
/// run, and returns a FileSource which can be used as inputs to other APIs
/// throughout the build system.
pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
- const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
+ const b = rs.step.owner;
+ const generated_file = b.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
generated_file.* = .{ .step = &rs.step };
rs.argv.append(.{ .output = .{
.generated_file = generated_file,
- .basename = rs.builder.dupe(basename),
+ .basename = b.dupe(basename),
} }) catch @panic("OOM");
if (rs.rename_step_with_output_arg) {
rs.rename_step_with_output_arg = false;
- rs.step.name = rs.builder.fmt("{s} ({s})", .{ rs.step.name, basename });
+ rs.step.name = b.fmt("{s} ({s})", .{ rs.step.name, basename });
}
return .{ .generated = generated_file };
@@ -116,13 +147,13 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource
pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
self.argv.append(Arg{
- .file_source = file_source.dupe(self.builder),
+ .file_source = file_source.dupe(self.step.owner),
}) catch @panic("OOM");
file_source.addStepDependencies(&self.step);
}
pub fn addArg(self: *RunStep, arg: []const u8) void {
- self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");
+ self.argv.append(Arg{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM");
}
pub fn addArgs(self: *RunStep, args: []const []const u8) void {
@@ -132,13 +163,14 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {
}
pub fn clearEnvironment(self: *RunStep) void {
- const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");
- new_env_map.* = EnvMap.init(self.builder.allocator);
+ const b = self.step.owner;
+ const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
+ new_env_map.* = EnvMap.init(b.allocator);
self.env_map = new_env_map;
}
pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
- addPathDirInternal(&self.step, self.builder, search_path);
+ addPathDirInternal(&self.step, self.step.owner, search_path);
}
/// For internal use only, users of `RunStep` should use `addPathDir` directly.
@@ -157,13 +189,12 @@ pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const
}
pub fn getEnvMap(self: *RunStep) *EnvMap {
- return getEnvMapInternal(&self.step, self.builder.allocator);
+ return getEnvMapInternal(&self.step, self.step.owner.allocator);
}
fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
const maybe_env_map = switch (step.id) {
.run => step.cast(RunStep).?.env_map,
- .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
else => unreachable,
};
return maybe_env_map orelse {
@@ -171,7 +202,6 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
switch (step.id) {
.run => step.cast(RunStep).?.env_map = env_map,
- .emulatable_run => step.cast(RunStep).?.env_map = env_map,
else => unreachable,
}
return env_map;
@@ -179,41 +209,85 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
}
pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
+ const b = self.step.owner;
const env_map = self.getEnvMap();
- env_map.put(
- self.builder.dupe(key),
- self.builder.dupe(value),
- ) catch @panic("unhandled error");
+ env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
}
pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
- self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
+ const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) };
+ self.addCheck(new_check);
}
pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
- self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
+ const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) };
+ self.addCheck(new_check);
}
-fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
- return switch (action) {
- .ignore => .Ignore,
- .inherit => .Inherit,
- .expect_exact, .expect_matches => .Pipe,
- };
+pub fn expectExitCode(self: *RunStep, code: u8) void {
+ const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } };
+ self.addCheck(new_check);
}
-fn needOutputCheck(self: RunStep) bool {
- switch (self.condition) {
- .always => return false,
- .output_outdated => {},
+pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void {
+ const arena = self.step.owner.allocator;
+ switch (self.stdio) {
+ .infer_from_args => {
+ const list = arena.create([1]StdIo.Check) catch @panic("OOM");
+ list.* = .{new_check};
+ self.stdio = .{ .check = list };
+ },
+ .check => |checks| {
+ const new_list = arena.alloc(StdIo.Check, checks.len + 1) catch @panic("OOM");
+ std.mem.copy(StdIo.Check, new_list, checks);
+ new_list[checks.len] = new_check;
+ },
+ else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"),
}
- if (self.extra_file_dependencies.len > 0) return true;
+}
+
+/// Returns whether the RunStep has side effects *other than* updating the output arguments.
+fn hasSideEffects(self: RunStep) bool {
+ return switch (self.stdio) {
+ .infer_from_args => !self.hasAnyOutputArgs(),
+ .inherit => true,
+ .check => false,
+ };
+}
+fn hasAnyOutputArgs(self: RunStep) bool {
for (self.argv.items) |arg| switch (arg) {
.output => return true,
else => continue,
};
+ return false;
+}
+fn checksContainStdout(checks: []const StdIo.Check) bool {
+ for (checks) |check| switch (check) {
+ .expect_stderr_exact,
+ .expect_stderr_match,
+ .expect_term,
+ => continue,
+
+ .expect_stdout_exact,
+ .expect_stdout_match,
+ => return true,
+ };
+ return false;
+}
+
+fn checksContainStderr(checks: []const StdIo.Check) bool {
+ for (checks) |check| switch (check) {
+ .expect_stdout_exact,
+ .expect_stdout_match,
+ .expect_term,
+ => continue,
+
+ .expect_stderr_exact,
+ .expect_stderr_match,
+ => return true,
+ };
return false;
}
@@ -223,16 +297,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// processes could use to supply progress updates.
_ = prog_node;
+ const b = step.owner;
const self = @fieldParentPtr(RunStep, "step", step);
- const need_output_check = self.needOutputCheck();
+ const has_side_effects = self.hasSideEffects();
- var argv_list = ArrayList([]const u8).init(self.builder.allocator);
+ var argv_list = ArrayList([]const u8).init(b.allocator);
var output_placeholders = ArrayList(struct {
index: usize,
output: Arg.Output,
- }).init(self.builder.allocator);
+ }).init(b.allocator);
- var man = self.builder.cache.obtain();
+ var man = b.cache.obtain();
defer man.deinit();
for (self.argv.items) |arg| {
@@ -242,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
man.hash.addBytes(bytes);
},
.file_source => |file| {
- const file_path = file.getPath(self.builder);
+ const file_path = file.getPath(b);
try argv_list.append(file_path);
_ = try man.addFile(file_path, null);
},
@@ -252,7 +327,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
self.addPathForDynLibs(artifact);
}
const file_path = artifact.installed_path orelse
- artifact.getOutputSource().getPath(self.builder);
+ artifact.getOutputSource().getPath(b);
try argv_list.append(file_path);
@@ -272,17 +347,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
}
- if (need_output_check) {
+ if (!has_side_effects) {
for (self.extra_file_dependencies) |file_path| {
- _ = try man.addFile(self.builder.pathFromRoot(file_path), null);
+ _ = try man.addFile(b.pathFromRoot(file_path), null);
}
- if (man.hit() catch |err| failWithCacheError(man, err)) {
+ if (try step.cacheHit(&man)) {
// cache hit, skip running command
const digest = man.final();
for (output_placeholders.items) |placeholder| {
- placeholder.output.generated_file.path = try self.builder.cache_root.join(
- self.builder.allocator,
+ placeholder.output.generated_file.path = try b.cache_root.join(
+ b.allocator,
&.{ "o", &digest, placeholder.output.basename },
);
}
@@ -292,8 +367,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const digest = man.final();
for (output_placeholders.items) |placeholder| {
- const output_path = try self.builder.cache_root.join(
- self.builder.allocator,
+ const output_path = try b.cache_root.join(
+ b.allocator,
&.{ "o", &digest, placeholder.output.basename },
);
const output_dir = fs.path.dirname(output_path).?;
@@ -308,18 +383,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
try runCommand(
+ step,
+ self.cwd,
argv_list.items,
- self.builder,
- self.expected_term,
- self.stdout_action,
- self.stderr_action,
- self.stdin_behavior,
self.env_map,
- self.cwd,
- self.print,
+ self.stdio,
+ has_side_effects,
+ self.max_stdio_size,
);
- if (need_output_check) {
+ if (!has_side_effects) {
try man.writeManifest();
}
}
@@ -369,165 +442,171 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)
};
}
-pub fn runCommand(
+fn runCommand(
+ step: *Step,
+ opt_cwd: ?[]const u8,
argv: []const []const u8,
- builder: *std.Build,
- expected_term: ?std.ChildProcess.Term,
- stdout_action: StdIoAction,
- stderr_action: StdIoAction,
- stdin_behavior: std.ChildProcess.StdIo,
env_map: ?*EnvMap,
- maybe_cwd: ?[]const u8,
- print: bool,
+ stdio: StdIo,
+ has_side_effects: bool,
+ max_stdio_size: usize,
) !void {
- const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;
-
- if (!std.process.can_spawn) {
- const cmd = try std.mem.join(builder.allocator, " ", argv);
- std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
- @tagName(builtin.os.tag), cmd,
- });
- builder.allocator.free(cmd);
- return ExecError.ExecNotSupported;
- }
+ const b = step.owner;
+ const arena = b.allocator;
+ const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path;
- var child = std.ChildProcess.init(argv, builder.allocator);
- child.cwd = cwd;
- child.env_map = env_map orelse builder.env_map;
+ try step.handleChildProcUnsupported(opt_cwd, argv);
+ try Step.handleVerbose(step.owner, opt_cwd, argv);
- child.stdin_behavior = stdin_behavior;
- child.stdout_behavior = stdIoActionToBehavior(stdout_action);
- child.stderr_behavior = stdIoActionToBehavior(stderr_action);
-
- if (print)
- printCmd(cwd, argv);
+ var child = std.ChildProcess.init(argv, arena);
+ child.cwd = cwd;
+ child.env_map = env_map orelse b.env_map;
- child.spawn() catch |err| {
- std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
- return err;
+ child.stdin_behavior = switch (stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
+ .inherit => .Inherit,
+ .check => .Close,
+ };
+ child.stdout_behavior = switch (stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
+ .inherit => .Inherit,
+ .check => |checks| if (checksContainStdout(checks)) .Pipe else .Ignore,
+ };
+ child.stderr_behavior = switch (stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Pipe,
+ .inherit => .Inherit,
+ .check => .Pipe,
};
- // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
-
- var stdout: ?[]const u8 = null;
- defer if (stdout) |s| builder.allocator.free(s);
+ child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{
+ argv[0], @errorName(err),
+ });
+
+ var stdout_bytes: ?[]const u8 = null;
+ var stderr_bytes: ?[]const u8 = null;
+
+ if (child.stdout) |stdout| {
+ if (child.stderr) |stderr| {
+ var poller = std.io.poll(arena, enum { stdout, stderr }, .{
+ .stdout = stdout,
+ .stderr = stderr,
+ });
+ defer poller.deinit();
+
+ while (try poller.poll()) {
+ if (poller.fifo(.stdout).count > max_stdio_size)
+ return error.StdoutStreamTooLong;
+ if (poller.fifo(.stderr).count > max_stdio_size)
+ return error.StderrStreamTooLong;
+ }
- switch (stdout_action) {
- .expect_exact, .expect_matches => {
- stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
- },
- .inherit, .ignore => {},
+ stdout_bytes = try poller.fifo(.stdout).toOwnedSlice();
+ stderr_bytes = try poller.fifo(.stderr).toOwnedSlice();
+ } else {
+ stdout_bytes = try stdout.reader().readAllAlloc(arena, max_stdio_size);
+ }
+ } else if (child.stderr) |stderr| {
+ stderr_bytes = try stderr.reader().readAllAlloc(arena, max_stdio_size);
}
- var stderr: ?[]const u8 = null;
- defer if (stderr) |s| builder.allocator.free(s);
-
- switch (stderr_action) {
- .expect_exact, .expect_matches => {
- stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
- },
- .inherit, .ignore => {},
- }
+ if (stderr_bytes) |stderr| if (stderr.len > 0) {
+ const stderr_is_diagnostic = switch (stdio) {
+ .check => |checks| !checksContainStderr(checks),
+ else => true,
+ };
+ if (stderr_is_diagnostic) {
+ try step.result_error_msgs.append(arena, stderr);
+ }
+ };
const term = child.wait() catch |err| {
- std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
- return err;
+ return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
};
- if (!termMatches(expected_term, term)) {
- std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) });
- printCmd(cwd, argv);
- return error.UnexpectedExit;
- }
-
- switch (stderr_action) {
- .inherit, .ignore => {},
- .expect_exact => |expected_bytes| {
- if (!mem.eql(u8, expected_bytes, stderr.?)) {
- std.debug.print(
- \\
- \\========= Expected this stderr: =========
- \\{s}
- \\========= But found: ====================
- \\{s}
- \\
- , .{ expected_bytes, stderr.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- .expect_matches => |matches| for (matches) |match| {
- if (mem.indexOf(u8, stderr.?, match) == null) {
- std.debug.print(
- \\
- \\========= Expected to find in stderr: =========
- \\{s}
- \\========= But stderr does not contain it: =====
- \\{s}
- \\
- , .{ match, stderr.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
- },
- }
-
- switch (stdout_action) {
- .inherit, .ignore => {},
- .expect_exact => |expected_bytes| {
- if (!mem.eql(u8, expected_bytes, stdout.?)) {
- std.debug.print(
- \\
- \\========= Expected this stdout: =========
- \\{s}
- \\========= But found: ====================
- \\{s}
- \\
- , .{ expected_bytes, stdout.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
+ switch (stdio) {
+ .check => |checks| for (checks) |check| switch (check) {
+ .expect_stderr_exact => |expected_bytes| {
+ if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) {
+ return step.fail(
+ \\========= expected this stderr: =========
+ \\{s}
+ \\========= but found: ====================
+ \\{s}
+ \\========= from the following command: ===
+ \\{s}
+ , .{
+ expected_bytes,
+ stderr_bytes.?,
+ try Step.allocPrintCmd(arena, opt_cwd, argv),
+ });
+ }
+ },
+ .expect_stderr_match => |match| {
+ if (mem.indexOf(u8, stderr_bytes.?, match) == null) {
+ return step.fail(
+ \\========= expected to find in stderr: =========
+ \\{s}
+ \\========= but stderr does not contain it: =====
+ \\{s}
+ \\========= from the following command: =========
+ \\{s}
+ , .{
+ match,
+ stderr_bytes.?,
+ try Step.allocPrintCmd(arena, opt_cwd, argv),
+ });
+ }
+ },
+ .expect_stdout_exact => |expected_bytes| {
+ if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) {
+ return step.fail(
+ \\========= expected this stdout: =========
+ \\{s}
+ \\========= but found: ====================
+ \\{s}
+ \\========= from the following command: ===
+ \\{s}
+ , .{
+ expected_bytes,
+ stdout_bytes.?,
+ try Step.allocPrintCmd(arena, opt_cwd, argv),
+ });
+ }
+ },
+ .expect_stdout_match => |match| {
+ if (mem.indexOf(u8, stdout_bytes.?, match) == null) {
+ return step.fail(
+ \\========= expected to find in stdout: =========
+ \\{s}
+ \\========= but stdout does not contain it: =====
+ \\{s}
+ \\========= from the following command: =========
+ \\{s}
+ , .{
+ match,
+ stdout_bytes.?,
+ try Step.allocPrintCmd(arena, opt_cwd, argv),
+ });
+ }
+ },
+ .expect_term => |expected_term| {
+ if (!termMatches(expected_term, term)) {
+ return step.fail("the following command {} (expected {}):\n{s}", .{
+ fmtTerm(term),
+ fmtTerm(expected_term),
+ try Step.allocPrintCmd(arena, opt_cwd, argv),
+ });
+ }
+ },
},
- .expect_matches => |matches| for (matches) |match| {
- if (mem.indexOf(u8, stdout.?, match) == null) {
- std.debug.print(
- \\
- \\========= Expected to find in stdout: =========
- \\{s}
- \\========= But stdout does not contain it: =====
- \\{s}
- \\
- , .{ match, stdout.? });
- printCmd(cwd, argv);
- return error.TestFailed;
- }
+ else => {
+ try step.handleChildProcessTerm(term, opt_cwd, argv);
},
}
}
-fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
- const i = man.failed_file_index orelse failWithSimpleError(err);
- const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
- const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
- std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
- std.process.exit(1);
-}
-
-fn failWithSimpleError(err: anyerror) noreturn {
- std.debug.print("{s}\n", .{@errorName(err)});
- std.process.exit(1);
-}
-
-fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
- if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
- for (argv) |arg| {
- std.debug.print("{s} ", .{arg});
- }
- std.debug.print("\n", .{});
-}
-
fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
- addPathForDynLibsInternal(&self.step, self.builder, artifact);
+ addPathForDynLibsInternal(&self.step, self.step.owner, artifact);
}
/// This should only be used for internal usage, this is called automatically
lib/std/Build/Step.zig
@@ -1,5 +1,6 @@
id: Id,
name: []const u8,
+owner: *Build,
makeFn: MakeFn,
dependencies: std.ArrayList(*Step),
/// This field is empty during execution of the user's build script, and
@@ -39,7 +40,6 @@ pub const Id = enum {
translate_c,
write_file,
run,
- emulatable_run,
check_file,
check_object,
config_header,
@@ -60,7 +60,6 @@ pub const Id = enum {
.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,
@@ -74,11 +73,14 @@ pub const Id = enum {
pub const Options = struct {
id: Id,
name: []const u8,
+ owner: *Build,
makeFn: MakeFn = makeNoOp,
first_ret_addr: ?usize = null,
};
-pub fn init(allocator: Allocator, options: Options) Step {
+pub fn init(options: Options) Step {
+ const arena = options.owner.allocator;
+
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{
@@ -89,9 +91,10 @@ pub fn init(allocator: Allocator, options: Options) Step {
return .{
.id = options.id,
- .name = allocator.dupe(u8, options.name) catch @panic("OOM"),
+ .name = arena.dupe(u8, options.name) catch @panic("OOM"),
+ .owner = options.owner,
.makeFn = options.makeFn,
- .dependencies = std.ArrayList(*Step).init(allocator),
+ .dependencies = std.ArrayList(*Step).init(arena),
.dependants = .{},
.state = .precheck_unstarted,
.debug_stack_trace = addresses,
@@ -168,3 +171,231 @@ const std = @import("../std.zig");
const Build = std.Build;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
+const builtin = @import("builtin");
+
+pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void {
+ const arena = s.owner.allocator;
+
+ try handleChildProcUnsupported(s, null, argv);
+ try handleVerbose(s.owner, null, argv);
+
+ const result = std.ChildProcess.exec(.{
+ .allocator = arena,
+ .argv = argv,
+ }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
+
+ if (result.stderr.len > 0) {
+ try s.result_error_msgs.append(arena, result.stderr);
+ }
+
+ try handleChildProcessTerm(s, result.term, null, argv);
+}
+
+pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } {
+ const arena = step.owner.allocator;
+ const msg = try std.fmt.allocPrint(arena, fmt, args);
+ try step.result_error_msgs.append(arena, msg);
+ return error.MakeFailed;
+}
+
+/// Assumes that argv contains `--listen=-` and that the process being spawned
+/// is the zig compiler - the same version that compiled the build runner.
+pub fn evalZigProcess(
+ s: *Step,
+ argv: []const []const u8,
+ prog_node: *std.Progress.Node,
+) ![]const u8 {
+ assert(argv.len != 0);
+ const b = s.owner;
+ const arena = b.allocator;
+ const gpa = arena;
+
+ try handleChildProcUnsupported(s, null, argv);
+ try handleVerbose(s.owner, null, argv);
+
+ var child = std.ChildProcess.init(argv, arena);
+ child.env_map = b.env_map;
+ child.stdin_behavior = .Pipe;
+ child.stdout_behavior = .Pipe;
+ child.stderr_behavior = .Pipe;
+
+ child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{
+ argv[0], @errorName(err),
+ });
+
+ var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
+ .stdout = child.stdout.?,
+ .stderr = child.stderr.?,
+ });
+ defer poller.deinit();
+
+ try sendMessage(child.stdin.?, .update);
+ try sendMessage(child.stdin.?, .exit);
+
+ const Header = std.zig.Server.Message.Header;
+ var result: ?[]const u8 = null;
+
+ var node_name: std.ArrayListUnmanaged(u8) = .{};
+ defer node_name.deinit(gpa);
+ var sub_prog_node: ?std.Progress.Node = null;
+ defer if (sub_prog_node) |*n| n.end();
+
+ while (try poller.poll()) {
+ const stdout = poller.fifo(.stdout);
+ const buf = stdout.readableSlice(0);
+ assert(stdout.readableLength() == buf.len);
+ if (buf.len >= @sizeOf(Header)) {
+ const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
+ const header_and_msg_len = header.bytes_len + @sizeOf(Header);
+ if (buf.len >= header_and_msg_len) {
+ const body = buf[@sizeOf(Header)..][0..header.bytes_len];
+ switch (header.tag) {
+ .zig_version => {
+ if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
+ return s.fail(
+ "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
+ .{ builtin.zig_version_string, body },
+ );
+ }
+ },
+ .error_bundle => {
+ const EbHdr = std.zig.Server.Message.ErrorBundle;
+ const eb_hdr = @ptrCast(*align(1) const EbHdr, body);
+ const extra_bytes =
+ body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
+ const string_bytes =
+ body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
+ // TODO: use @ptrCast when the compiler supports it
+ const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
+ const extra_array = try arena.alloc(u32, unaligned_extra.len);
+ // TODO: use @memcpy when it supports slices
+ for (extra_array, unaligned_extra) |*dst, src| dst.* = src;
+ s.result_error_bundle = .{
+ .string_bytes = try arena.dupe(u8, string_bytes),
+ .extra = extra_array,
+ };
+ },
+ .progress => {
+ if (sub_prog_node) |*n| n.end();
+ node_name.clearRetainingCapacity();
+ try node_name.appendSlice(gpa, body);
+ sub_prog_node = prog_node.start(node_name.items, 0);
+ sub_prog_node.?.activate();
+ },
+ .emit_bin_path => {
+ result = try arena.dupe(u8, body);
+ },
+ _ => {
+ // Unrecognized message.
+ },
+ }
+ stdout.discard(header_and_msg_len);
+ }
+ }
+ }
+
+ const stderr = poller.fifo(.stderr);
+ if (stderr.readableLength() > 0) {
+ try s.result_error_msgs.append(arena, try stderr.toOwnedSlice());
+ }
+
+ // Send EOF to stdin.
+ child.stdin.?.close();
+ child.stdin = null;
+
+ const term = child.wait() catch |err| {
+ return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
+ };
+ try handleChildProcessTerm(s, term, null, argv);
+
+ if (s.result_error_bundle.errorMessageCount() > 0) {
+ return s.fail("the following command failed with {d} compilation errors:\n{s}", .{
+ s.result_error_bundle.errorMessageCount(),
+ try allocPrintCmd(arena, null, argv),
+ });
+ }
+
+ return result orelse return s.fail(
+ "the following command failed to communicate the compilation result:\n{s}",
+ .{try allocPrintCmd(arena, null, argv)},
+ );
+}
+
+fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
+ const header: std.zig.Client.Message.Header = .{
+ .tag = tag,
+ .bytes_len = 0,
+ };
+ try file.writeAll(std.mem.asBytes(&header));
+}
+
+pub fn handleVerbose(
+ b: *Build,
+ opt_cwd: ?[]const u8,
+ argv: []const []const u8,
+) error{OutOfMemory}!void {
+ if (b.verbose) {
+ // Intention of verbose is to print all sub-process command lines to
+ // stderr before spawning them.
+ const text = try allocPrintCmd(b.allocator, opt_cwd, argv);
+ std.debug.print("{s}\n", .{text});
+ }
+}
+
+pub inline fn handleChildProcUnsupported(
+ s: *Step,
+ opt_cwd: ?[]const u8,
+ argv: []const []const u8,
+) error{ OutOfMemory, MakeFailed }!void {
+ if (!std.process.can_spawn) {
+ return s.fail(
+ "unable to execute the following command: host cannot spawn child processes\n{s}",
+ .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)},
+ );
+ }
+}
+
+pub fn handleChildProcessTerm(
+ s: *Step,
+ term: std.ChildProcess.Term,
+ opt_cwd: ?[]const u8,
+ argv: []const []const u8,
+) error{ MakeFailed, OutOfMemory }!void {
+ const arena = s.owner.allocator;
+ switch (term) {
+ .Exited => |code| {
+ if (code != 0) {
+ return s.fail(
+ "the following command exited with error code {d}:\n{s}",
+ .{ code, try allocPrintCmd(arena, opt_cwd, argv) },
+ );
+ }
+ },
+ .Signal, .Stopped, .Unknown => {
+ return s.fail(
+ "the following command terminated unexpectedly:\n{s}",
+ .{try allocPrintCmd(arena, opt_cwd, argv)},
+ );
+ },
+ }
+}
+
+pub fn allocPrintCmd(arena: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
+ var buf: std.ArrayListUnmanaged(u8) = .{};
+ if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd});
+ for (argv) |arg| {
+ try buf.writer(arena).print("{s} ", .{arg});
+ }
+ return buf.toOwnedSlice(arena);
+}
+
+pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool {
+ return man.hit() catch |err| return failWithCacheError(s, man, err);
+}
+
+fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror {
+ const i = man.failed_file_index orelse return err;
+ const pp = man.files.items[i].prefixed_path orelse return err;
+ const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
+ return s.fail("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
+}
lib/std/Build/TranslateCStep.zig
@@ -11,7 +11,6 @@ const TranslateCStep = @This();
pub const base_id = .translate_c;
step: Step,
-builder: *std.Build,
source: std.Build.FileSource,
include_dirs: std.ArrayList([]const u8),
c_macros: std.ArrayList([]const u8),
@@ -26,19 +25,19 @@ pub const Options = struct {
optimize: std.builtin.OptimizeMode,
};
-pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
- const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
- const source = options.source_file.dupe(builder);
+pub fn create(owner: *std.Build, options: Options) *TranslateCStep {
+ const self = owner.allocator.create(TranslateCStep) catch @panic("OOM");
+ const source = options.source_file.dupe(owner);
self.* = TranslateCStep{
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = .translate_c,
.name = "translate-c",
+ .owner = owner,
.makeFn = make,
}),
- .builder = builder,
.source = source,
- .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
- .c_macros = std.ArrayList([]const u8).init(builder.allocator),
+ .include_dirs = std.ArrayList([]const u8).init(owner.allocator),
+ .c_macros = std.ArrayList([]const u8).init(owner.allocator),
.out_basename = undefined,
.target = options.target,
.optimize = options.optimize,
@@ -58,7 +57,7 @@ pub const AddExecutableOptions = struct {
/// Creates a step to build an executable from the translated source.
pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
- return self.builder.addExecutable(.{
+ return self.step.owner.addExecutable(.{
.root_source_file = .{ .generated = &self.output_file },
.name = options.name orelse "translated_c",
.version = options.version,
@@ -69,30 +68,31 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp
}
pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
- self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
+ self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM");
}
pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
- return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
+ return CheckFileStep.create(self.step.owner, .{ .generated = &self.output_file }, self.step.owner.dupeStrings(expected_matches));
}
/// If the value is omitted, it is set to 1.
/// `name` and `value` need not live longer than the function call.
pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
- const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
+ const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value);
self.c_macros.append(macro) catch @panic("OOM");
}
/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
- self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
+ self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM");
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ const b = step.owner;
const self = @fieldParentPtr(TranslateCStep, "step", step);
- var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
- try argv_list.append(self.builder.zig_exe);
+ var argv_list = std.ArrayList([]const u8).init(b.allocator);
+ try argv_list.append(b.zig_exe);
try argv_list.append("translate-c");
try argv_list.append("-lc");
@@ -101,12 +101,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
if (!self.target.isNative()) {
try argv_list.append("-target");
- try argv_list.append(try self.target.zigTriple(self.builder.allocator));
+ try argv_list.append(try self.target.zigTriple(b.allocator));
}
switch (self.optimize) {
.Debug => {}, // Skip since it's the default.
- else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
+ else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
}
for (self.include_dirs.items) |include_dir| {
@@ -119,15 +119,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try argv_list.append(c_macro);
}
- try argv_list.append(self.source.getPath(self.builder));
+ try argv_list.append(self.source.getPath(b));
- const output_path = try self.builder.execFromStep(argv_list.items, &self.step, prog_node);
+ const output_path = try step.evalZigProcess(argv_list.items, prog_node);
self.out_basename = fs.path.basename(output_path);
const output_dir = fs.path.dirname(output_path).?;
self.output_file.path = try fs.path.join(
- self.builder.allocator,
+ b.allocator,
&[_][]const u8{ output_dir, self.out_basename },
);
}
lib/std/Build/WriteFileStep.zig
@@ -10,7 +10,6 @@
//! control.
step: Step,
-builder: *std.Build,
/// The elements here are pointers because we need stable pointers for the
/// GeneratedFile field.
files: std.ArrayListUnmanaged(*File),
@@ -34,12 +33,12 @@ pub const Contents = union(enum) {
copy: std.Build.FileSource,
};
-pub fn init(builder: *std.Build) WriteFileStep {
+pub fn init(owner: *std.Build) WriteFileStep {
return .{
- .builder = builder,
- .step = Step.init(builder.allocator, .{
+ .step = Step.init(.{
.id = .write_file,
.name = "writefile",
+ .owner = owner,
.makeFn = make,
}),
.files = .{},
@@ -48,12 +47,13 @@ pub fn init(builder: *std.Build) WriteFileStep {
}
pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
- const gpa = wf.builder.allocator;
+ const b = wf.step.owner;
+ const gpa = b.allocator;
const file = gpa.create(File) catch @panic("OOM");
file.* = .{
.generated_file = .{ .step = &wf.step },
- .sub_path = wf.builder.dupePath(sub_path),
- .contents = .{ .bytes = wf.builder.dupe(bytes) },
+ .sub_path = b.dupePath(sub_path),
+ .contents = .{ .bytes = b.dupe(bytes) },
};
wf.files.append(gpa, file) catch @panic("OOM");
}
@@ -66,11 +66,12 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
/// required sub-path exists.
/// This is the option expected to be used most commonly with `addCopyFile`.
pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
- const gpa = wf.builder.allocator;
+ const b = wf.step.owner;
+ const gpa = b.allocator;
const file = gpa.create(File) catch @panic("OOM");
file.* = .{
.generated_file = .{ .step = &wf.step },
- .sub_path = wf.builder.dupePath(sub_path),
+ .sub_path = b.dupePath(sub_path),
.contents = .{ .copy = source },
};
wf.files.append(gpa, file) catch @panic("OOM");
@@ -83,7 +84,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [
/// those changes to version control.
/// A file added this way is not available with `getFileSource`.
pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
- wf.output_source_files.append(wf.builder.allocator, .{
+ const b = wf.step.owner;
+ wf.output_source_files.append(b.allocator, .{
.contents = .{ .copy = source },
.sub_path = sub_path,
}) catch @panic("OOM");
@@ -101,6 +103,7 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
+ const b = step.owner;
const wf = @fieldParentPtr(WriteFileStep, "step", step);
// Writing to source files is kind of an extra capability of this
@@ -110,11 +113,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
for (wf.output_source_files.items) |output_source_file| {
const basename = fs.path.basename(output_source_file.sub_path);
if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
- var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{});
+ var dir = try b.build_root.handle.makeOpenPath(dirname, .{});
defer dir.close();
try writeFile(wf, dir, output_source_file.contents, basename);
} else {
- try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename);
+ try writeFile(wf, b.build_root.handle, output_source_file.contents, basename);
}
}
@@ -125,7 +128,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// If, for example, a hard-coded path was used as the location to put WriteFileStep
// files, then two WriteFileSteps executing in parallel might clobber each other.
- var man = wf.builder.cache.obtain();
+ var man = b.cache.obtain();
defer man.deinit();
// Random bytes to make WriteFileStep unique. Refresh this with
@@ -140,17 +143,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
man.hash.addBytes(bytes);
},
.copy => |file_source| {
- _ = try man.addFile(file_source.getPath(wf.builder), null);
+ _ = try man.addFile(file_source.getPath(b), null);
},
}
}
- if (man.hit() catch |err| failWithCacheError(man, err)) {
+ if (try step.cacheHit(&man)) {
// Cache hit, skip writing file data.
const digest = man.final();
for (wf.files.items) |file| {
- file.generated_file.path = try wf.builder.cache_root.join(
- wf.builder.allocator,
+ file.generated_file.path = try b.cache_root.join(
+ b.allocator,
&.{ "o", &digest, file.sub_path },
);
}
@@ -160,7 +163,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const digest = man.final();
const cache_path = "o" ++ fs.path.sep_str ++ digest;
- var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
+ var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
return err;
};
@@ -169,15 +172,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
for (wf.files.items) |file| {
const basename = fs.path.basename(file.sub_path);
if (fs.path.dirname(file.sub_path)) |dirname| {
- var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{});
+ var dir = try b.cache_root.handle.makeOpenPath(dirname, .{});
defer dir.close();
try writeFile(wf, dir, file.contents, basename);
} else {
try writeFile(wf, cache_dir, file.contents, basename);
}
- file.generated_file.path = try wf.builder.cache_root.join(
- wf.builder.allocator,
+ file.generated_file.path = try b.cache_root.join(
+ b.allocator,
&.{ cache_path, file.sub_path },
);
}
@@ -186,32 +189,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void {
+ const b = wf.step.owner;
// TODO after landing concurrency PR, improve error reporting here
switch (contents) {
.bytes => |bytes| return dir.writeFile(basename, bytes),
.copy => |file_source| {
- const source_path = file_source.getPath(wf.builder);
+ const source_path = file_source.getPath(b);
const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{});
_ = prev_status; // TODO logging (affected by open PR regarding concurrency)
},
}
}
-/// TODO consolidate this with the same function in RunStep?
-/// Also properly deal with concurrency (see open PR)
-fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
- const i = man.failed_file_index orelse failWithSimpleError(err);
- const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
- const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
- std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
- std.process.exit(1);
-}
-
-fn failWithSimpleError(err: anyerror) noreturn {
- std.debug.print("{s}\n", .{@errorName(err)});
- std.process.exit(1);
-}
-
const std = @import("../std.zig");
const Step = std.Build.Step;
const fs = std.fs;
lib/std/Build.zig
@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");
pub const CheckFileStep = @import("Build/CheckFileStep.zig");
pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
-pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
pub const FmtStep = @import("Build/FmtStep.zig");
pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
pub const InstallDirStep = @import("Build/InstallDirStep.zig");
pub const InstallFileStep = @import("Build/InstallFileStep.zig");
pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");
pub const CompileStep = @import("Build/CompileStep.zig");
-pub const LogStep = @import("Build/LogStep.zig");
pub const OptionsStep = @import("Build/OptionsStep.zig");
pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
pub const RunStep = @import("Build/RunStep.zig");
@@ -195,7 +193,7 @@ pub fn create(
env_map.* = try process.getEnvMap(allocator);
const self = try allocator.create(Build);
- self.* = Build{
+ self.* = .{
.zig_exe = zig_exe,
.build_root = build_root,
.cache_root = cache_root,
@@ -224,16 +222,18 @@ pub fn create(
.dest_dir = env_map.get("DESTDIR"),
.installed_files = ArrayList(InstalledFile).init(allocator),
.install_tls = .{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .top_level,
.name = "install",
+ .owner = self,
}),
.description = "Copy build artifacts to prefix path",
},
.uninstall_tls = .{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .top_level,
.name = "uninstall",
+ .owner = self,
.makeFn = makeUninstall,
}),
.description = "Remove build artifacts from prefix path",
@@ -267,16 +267,18 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
child.* = .{
.allocator = allocator,
.install_tls = .{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .top_level,
.name = "install",
+ .owner = child,
}),
.description = "Copy build artifacts to prefix path",
},
.uninstall_tls = .{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .top_level,
.name = "uninstall",
+ .owner = child,
.makeFn = makeUninstall,
}),
.description = "Remove build artifacts from prefix path",
@@ -689,21 +691,14 @@ pub fn addWriteFiles(self: *Build) *WriteFileStep {
return write_file_step;
}
-pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
- const data = self.fmt(format, args);
- const log_step = self.allocator.create(LogStep) catch @panic("OOM");
- log_step.* = LogStep.init(self, data);
- return log_step;
-}
-
pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
remove_dir_step.* = RemoveDirStep.init(self, dir_path);
return remove_dir_step;
}
-pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
- return FmtStep.create(self, paths);
+pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep {
+ return FmtStep.create(b, options);
}
pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
@@ -870,10 +865,11 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
- step_info.* = TopLevelStep{
- .step = Step.init(self.allocator, .{
+ step_info.* = .{
+ .step = Step.init(.{
.id = .top_level,
.name = name,
+ .owner = self,
}),
.description = self.dupe(description),
};
@@ -1145,10 +1141,6 @@ pub fn validateUserInputDidItFail(self: *Build) bool {
return self.invalid_user_input;
}
-pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
- return self.spawnChildEnvMap(null, self.env_map, argv);
-}
-
fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
var buf = ArrayList(u8).init(ally);
if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
@@ -1163,40 +1155,6 @@ fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
std.debug.print("{s}\n", .{text});
}
-pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
- if (self.verbose) {
- printCmd(self.allocator, cwd, argv);
- }
-
- if (!process.can_spawn)
- return error.ExecNotSupported;
-
- var child = std.ChildProcess.init(argv, self.allocator);
- child.cwd = cwd;
- child.env_map = env_map;
-
- const term = child.spawnAndWait() catch |err| {
- log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
- return err;
- };
-
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- log.err("The following command exited with error code {}:", .{code});
- printCmd(self.allocator, cwd, argv);
- return error.UncleanExit;
- }
- },
- else => {
- log.err("The following command terminated unexpectedly:", .{});
- printCmd(self.allocator, cwd, argv);
-
- return error.UncleanExit;
- },
- }
-}
-
pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
}
@@ -1403,160 +1361,6 @@ pub fn execAllowFail(
}
}
-/// This function is used exclusively for spawning and communicating with the zig compiler.
-/// TODO: move to build_runner.zig
-pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step, prog_node: *std.Progress.Node) ![]const u8 {
- assert(argv.len != 0);
-
- if (b.verbose) {
- const text = try allocPrintCmd(b.allocator, null, argv);
- try s.result_error_msgs.append(b.allocator, text);
- }
-
- if (!process.can_spawn) {
- try s.result_error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{
- try allocPrintCmd(b.allocator, null, argv),
- }));
- return error.MakeFailed;
- }
-
- var child = std.ChildProcess.init(argv, b.allocator);
- child.env_map = b.env_map;
- child.stdin_behavior = .Pipe;
- child.stdout_behavior = .Pipe;
- child.stderr_behavior = .Pipe;
-
- try child.spawn();
-
- var poller = std.io.poll(b.allocator, enum { stdout, stderr }, .{
- .stdout = child.stdout.?,
- .stderr = child.stderr.?,
- });
- defer poller.deinit();
-
- try sendMessage(child.stdin.?, .update);
- try sendMessage(child.stdin.?, .exit);
-
- const Header = std.zig.Server.Message.Header;
- var result: ?[]const u8 = null;
-
- var node_name: std.ArrayListUnmanaged(u8) = .{};
- defer node_name.deinit(b.allocator);
- var sub_prog_node: ?std.Progress.Node = null;
- defer if (sub_prog_node) |*n| n.end();
-
- while (try poller.poll()) {
- const stdout = poller.fifo(.stdout);
- const buf = stdout.readableSlice(0);
- assert(stdout.readableLength() == buf.len);
- if (buf.len >= @sizeOf(Header)) {
- const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
- const header_and_msg_len = header.bytes_len + @sizeOf(Header);
- if (buf.len >= header_and_msg_len) {
- const body = buf[@sizeOf(Header)..][0..header.bytes_len];
- switch (header.tag) {
- .zig_version => {
- if (!mem.eql(u8, builtin.zig_version_string, body)) {
- try s.result_error_msgs.append(
- b.allocator,
- b.fmt("zig version mismatch build runner vs compiler: '{s}' vs '{s}'", .{
- builtin.zig_version_string, body,
- }),
- );
- return error.MakeFailed;
- }
- },
- .error_bundle => {
- const EbHdr = std.zig.Server.Message.ErrorBundle;
- const eb_hdr = @ptrCast(*align(1) const EbHdr, body);
- const extra_bytes =
- body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
- const string_bytes =
- body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
- // TODO: use @ptrCast when the compiler supports it
- const unaligned_extra = mem.bytesAsSlice(u32, extra_bytes);
- const extra_array = try b.allocator.alloc(u32, unaligned_extra.len);
- // TODO: use @memcpy when it supports slices
- for (extra_array, unaligned_extra) |*dst, src| dst.* = src;
- s.result_error_bundle = .{
- .string_bytes = try b.allocator.dupe(u8, string_bytes),
- .extra = extra_array,
- };
- },
- .progress => {
- if (sub_prog_node) |*n| n.end();
- node_name.clearRetainingCapacity();
- try node_name.appendSlice(b.allocator, body);
- sub_prog_node = prog_node.start(node_name.items, 0);
- sub_prog_node.?.activate();
- },
- .emit_bin_path => {
- result = try b.allocator.dupe(u8, body);
- },
- _ => {
- // Unrecognized message.
- },
- }
- stdout.discard(header_and_msg_len);
- }
- }
- }
-
- const stderr = poller.fifo(.stderr);
- if (stderr.readableLength() > 0) {
- try s.result_error_msgs.append(b.allocator, try stderr.toOwnedSlice());
- }
-
- // Send EOF to stdin.
- child.stdin.?.close();
- child.stdin = null;
-
- const term = try child.wait();
- switch (term) {
- .Exited => |code| {
- if (code != 0) {
- try s.result_error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{
- code, try allocPrintCmd(b.allocator, null, argv),
- }));
- return error.MakeFailed;
- }
- },
- .Signal, .Stopped, .Unknown => |code| {
- _ = code;
- try s.result_error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{
- try allocPrintCmd(b.allocator, null, argv),
- }));
- return error.MakeFailed;
- },
- }
-
- if (s.result_error_bundle.errorMessageCount() > 0) {
- try s.result_error_msgs.append(
- b.allocator,
- b.fmt("the following command failed with {d} compilation errors:\n{s}", .{
- s.result_error_bundle.errorMessageCount(),
- try allocPrintCmd(b.allocator, null, argv),
- }),
- );
- return error.MakeFailed;
- }
-
- return result orelse {
- try s.result_error_msgs.append(b.allocator, b.fmt("the following command failed to communicate the compilation result:\n{s}", .{
- try allocPrintCmd(b.allocator, null, argv),
- }));
- return error.MakeFailed;
- };
-}
-
-fn sendMessage(file: fs.File, tag: std.zig.Client.Message.Tag) !void {
- const header: std.zig.Client.Message.Header = .{
- .tag = tag,
- .bytes_len = 0,
- };
- try file.writeAll(std.mem.asBytes(&header));
-}
-
/// This is a helper function to be called from build.zig scripts, *not* from
/// inside step make() functions. If any errors occur, it fails the build with
/// a helpful message.
@@ -1910,14 +1714,12 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
test {
_ = CheckFileStep;
_ = CheckObjectStep;
- _ = EmulatableRunStep;
_ = FmtStep;
_ = InstallArtifactStep;
_ = InstallDirStep;
_ = InstallFileStep;
_ = ObjCopyStep;
_ = CompileStep;
- _ = LogStep;
_ = OptionsStep;
_ = RemoveDirStep;
_ = RunStep;
lib/build_runner.zig
@@ -93,6 +93,7 @@ pub fn main() !void {
var install_prefix: ?[]const u8 = null;
var dir_list = std.Build.DirList{};
+ var enable_summary: ?bool = null;
const Color = enum { auto, off, on };
var color: Color = .auto;
@@ -217,6 +218,10 @@ pub fn main() !void {
builder.enable_darling = true;
} else if (mem.eql(u8, arg, "-fno-darling")) {
builder.enable_darling = false;
+ } else if (mem.eql(u8, arg, "-fsummary")) {
+ enable_summary = true;
+ } else if (mem.eql(u8, arg, "-fno-summary")) {
+ enable_summary = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
builder.reference_trace = 256;
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
@@ -252,8 +257,9 @@ pub fn main() !void {
}
}
+ const stderr = std.io.getStdErr();
const ttyconf: std.debug.TTY.Config = switch (color) {
- .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
+ .auto => std.debug.detectTTYConfig(stderr),
.on => .escape_codes,
.off => .no_color,
};
@@ -279,6 +285,8 @@ pub fn main() !void {
main_progress_node,
thread_pool_options,
ttyconf,
+ stderr,
+ enable_summary,
) catch |err| switch (err) {
error.UncleanExit => process.exit(1),
else => return err,
@@ -292,6 +300,8 @@ fn runStepNames(
parent_prog_node: *std.Progress.Node,
thread_pool_options: std.Thread.Pool.Options,
ttyconf: std.debug.TTY.Config,
+ stderr: std.fs.File,
+ enable_summary: ?bool,
) !void {
const gpa = b.allocator;
var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{};
@@ -382,28 +392,35 @@ fn runStepNames(
// A proper command line application defaults to silently succeeding.
// The user may request verbose mode if they have a different preference.
- if (failure_count == 0 and !b.verbose) return cleanExit();
-
- const stderr = std.io.getStdErr();
-
- const total_count = success_count + failure_count + pending_count;
- ttyconf.setColor(stderr, .Cyan) catch {};
- stderr.writeAll("Build Summary: ") catch {};
- ttyconf.setColor(stderr, .Reset) catch {};
- stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{
- success_count, total_count, failure_count, total_compile_errors,
- }) catch {};
+ if (failure_count == 0 and enable_summary != true) return cleanExit();
+
+ if (enable_summary != false) {
+ const total_count = success_count + failure_count + pending_count;
+ ttyconf.setColor(stderr, .Cyan) catch {};
+ stderr.writeAll("Build Summary:") catch {};
+ ttyconf.setColor(stderr, .Reset) catch {};
+ stderr.writer().print(" {d}/{d} steps succeeded; {d} failed", .{
+ success_count, total_count, failure_count,
+ }) catch {};
+
+ if (enable_summary == null) {
+ ttyconf.setColor(stderr, .Dim) catch {};
+ stderr.writeAll(" (disable with -fno-summary)") catch {};
+ ttyconf.setColor(stderr, .Reset) catch {};
+ }
+ stderr.writeAll("\n") catch {};
- // Print a fancy tree with build results.
- var print_node: PrintNode = .{ .parent = null };
- if (step_names.len == 0) {
- print_node.last = true;
- printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {};
- } else {
- for (step_names, 0..) |step_name, i| {
- const tls = b.top_level_steps.get(step_name).?;
- print_node.last = i + 1 == b.top_level_steps.count();
- printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {};
+ // Print a fancy tree with build results.
+ var print_node: PrintNode = .{ .parent = null };
+ if (step_names.len == 0) {
+ print_node.last = true;
+ printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {};
+ } else {
+ for (step_names, 0..) |step_name, i| {
+ const tls = b.top_level_steps.get(step_name).?;
+ print_node.last = i + 1 == b.top_level_steps.count();
+ printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {};
+ }
}
}
@@ -453,9 +470,9 @@ fn printTreeStep(
step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
) !void {
const first = step_stack.swapRemove(s);
- if (!first) try ttyconf.setColor(stderr, .Dim);
try printPrefix(parent_node, stderr);
+ if (!first) try ttyconf.setColor(stderr, .Dim);
if (parent_node.parent != null) {
if (parent_node.last) {
try stderr.writeAll("└─ ");
@@ -464,7 +481,7 @@ fn printTreeStep(
}
}
- // TODO print the dep prefix too?
+ // dep_prefix omitted here because it is redundant with the tree.
try stderr.writeAll(s.name);
if (first) {
@@ -608,8 +625,10 @@ fn workerMakeOneStep(
const stderr = std.io.getStdErr();
for (s.result_error_msgs.items) |msg| {
- // TODO print the dep prefix too
+ // Sometimes it feels like you just can't catch a break. Finally,
+ // with Zig, you can.
ttyconf.setColor(stderr, .Bold) catch break;
+ stderr.writeAll(s.owner.dep_prefix) catch break;
stderr.writeAll(s.name) catch break;
stderr.writeAll(": ") catch break;
ttyconf.setColor(stderr, .Red) catch break;
@@ -735,6 +754,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
\\Advanced Options:
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
+ \\ -fsummary Print the build summary, even on success
+ \\ -fno-summary Omit the build summary, even on failure
\\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory
src/main.zig
@@ -4419,6 +4419,8 @@ pub const usage_build =
\\Options:
\\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
\\ -fno-reference-trace Disable reference trace
+ \\ -fsummary Print the build summary, even on success
+ \\ -fno-summary Omit the build summary, even on failure
\\ --build-file [file] Override path to build.zig
\\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory
@@ -4920,8 +4922,6 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
};
defer tree.deinit(gpa);
- try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
- var has_ast_error = false;
if (check_ast_flag) {
var file: Module.File = .{
.status = .never_loaded,
@@ -4957,11 +4957,11 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
var error_bundle = try wip_errors.toOwnedBundle();
defer error_bundle.deinit(gpa);
error_bundle.renderToStdErr(ttyconf);
- has_ast_error = true;
+ process.exit(2);
}
- }
- if (tree.errors.len != 0 or has_ast_error) {
- process.exit(1);
+ } else if (tree.errors.len != 0) {
+ try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
+ process.exit(2);
}
const formatted = try tree.render(gpa);
defer gpa.free(formatted);
test/src/compare_output.zig
@@ -166,9 +166,7 @@ pub const CompareOutputContext = struct {
const run = exe.run();
run.addArgs(case.cli_args);
- run.stderr_action = .ignore;
- run.stdout_action = .ignore;
- run.expected_term = .{ .Exited = 126 };
+ run.expectExitCode(126);
self.step.dependOn(&run.step);
},
test/tests.zig
@@ -858,10 +858,11 @@ pub const StackTracesContext = struct {
const allocator = context.b.allocator;
const ptr = allocator.create(RunAndCompareStep) catch unreachable;
ptr.* = RunAndCompareStep{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .custom,
.name = "StackTraceCompareOutputStep",
.makeFn = make,
+ .owner = context.b,
}),
.context = context,
.exe = exe,
@@ -1121,10 +1122,7 @@ pub const StandaloneContext = struct {
defer zig_args.resize(zig_args_base_len) catch unreachable;
const run_cmd = b.addSystemCommand(zig_args.items);
- const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) });
- log_step.step.dependOn(&run_cmd.step);
-
- self.step.dependOn(&log_step.step);
+ self.step.dependOn(&run_cmd.step);
}
}
@@ -1150,10 +1148,7 @@ pub const StandaloneContext = struct {
exe.linkSystemLibrary("c");
}
- const log_step = b.addLog("PASS {s}", .{annotated_case_name});
- log_step.step.dependOn(&exe.step);
-
- self.step.dependOn(&log_step.step);
+ self.step.dependOn(&exe.step);
}
}
};
@@ -1203,9 +1198,10 @@ pub const GenHContext = struct {
const allocator = context.b.allocator;
const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
ptr.* = GenHCmpOutputStep{
- .step = Step.init(allocator, .{
+ .step = Step.init(.{
.id = .custom,
.name = "ParseCCmpOutput",
+ .owner = context.b,
.makeFn = make,
}),
.context = context,
build.zig
@@ -61,8 +61,6 @@ pub fn build(b: *std.Build) !void {
test_cases.stack_size = stack_size;
test_cases.single_threaded = single_threaded;
- const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
-
const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
@@ -386,10 +384,24 @@ pub fn build(b: *std.Build) !void {
}
const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
- // run stage1 `zig fmt` on this build.zig file just to make sure it works
- test_step.dependOn(&fmt_build_zig.step);
- const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");
- fmt_step.dependOn(&fmt_build_zig.step);
+ const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" };
+ const fmt_exclude_paths = &.{ "test/cases" };
+ const check_fmt = b.addFmt(.{
+ .paths = fmt_include_paths,
+ .exclude_paths = fmt_exclude_paths,
+ .check = true,
+ });
+ const do_fmt = b.addFmt(.{
+ .paths = fmt_include_paths,
+ .exclude_paths = fmt_exclude_paths,
+ });
+
+ const test_fmt_step = b.step("test-fmt", "Check whether source files have conforming formatting");
+ test_fmt_step.dependOn(&check_fmt.step);
+
+ const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting");
+ do_fmt_step.dependOn(&do_fmt.step);
+
test_step.dependOn(tests.addPkgTests(
b,