aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-03-02 22:38:07 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-03-15 10:48:13 -0700
commitdcec4d55e36f48e459f4e8f218b8619d9be925db (patch)
tree0064e09c25715650b4e1ac641d5a33ec91245be1 /lib/std
parent9bf63b09963ca6ea1179dfaa9142498556bfac9d (diff)
downloadzig-dcec4d55e36f48e459f4e8f218b8619d9be925db.tar.gz
zig-dcec4d55e36f48e459f4e8f218b8619d9be925db.zip
eliminate stderr usage in std.Build make() functions
* Eliminate all uses of `std.debug.print` in make() functions, instead properly using the step failure reporting mechanism. * Introduce the concept of skipped build steps. These do not cause the build to fail, and they do allow their dependants to run. * RunStep gains a new flag, `skip_foreign_checks` which causes the RunStep to be skipped if stdio mode is `check` and the binary cannot be executed due to it being a foreign executable. - RunStep is improved to automatically use known interpreters to execute binaries if possible (integrating with flags such as -fqemu and -fwasmtime). It only does this after attempting a native execution and receiving a "exec file format" error. - Update RunStep to use an ArrayList for the checks rather than this ad-hoc reallocation/copying mechanism. - `expectStdOutEqual` now also implicitly adds an exit_code==0 check if there is not already an expected termination. This matches previously expected behavior from older API and can be overridden by directly setting the checks array. * Add `dest_sub_path` to `InstallArtifactStep` which allows choosing an arbitrary subdirectory relative to the prefix, as well as overriding the basename. - Delete the custom InstallWithRename step that I found deep in the test/ directory. * WriteFileStep will now update its step display name after the first file is added. * Add missing stdout checks to various standalone test case build scripts.
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Build/CheckFileStep.zig7
-rw-r--r--lib/std/Build/CheckObjectStep.zig138
-rw-r--r--lib/std/Build/CompileStep.zig3
-rw-r--r--lib/std/Build/ConfigHeaderStep.zig23
-rw-r--r--lib/std/Build/InstallArtifactStep.zig8
-rw-r--r--lib/std/Build/ObjCopyStep.zig3
-rw-r--r--lib/std/Build/RunStep.zig372
-rw-r--r--lib/std/Build/Step.zig23
-rw-r--r--lib/std/Build/WriteFileStep.zig114
-rw-r--r--lib/std/child_process.zig1
10 files changed, 459 insertions, 233 deletions
diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig
index f70a29840e..03b23d0b03 100644
--- a/lib/std/Build/CheckFileStep.zig
+++ b/lib/std/Build/CheckFileStep.zig
@@ -42,15 +42,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
for (self.expected_matches) |expected_match| {
if (mem.indexOf(u8, contents, expected_match) == null) {
- std.debug.print(
+ return step.fail(
\\
- \\========= Expected to find: ===================
+ \\========= expected to find: ===================
\\{s}
- \\========= But file does not contain it: =======
+ \\========= but file does not contain it: =======
\\{s}
\\
, .{ expected_match, contents });
- return error.TestFailed;
}
}
}
diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig
index 57d280da0e..2a58850fab 100644
--- a/lib/std/Build/CheckObjectStep.zig
+++ b/lib/std/Build/CheckObjectStep.zig
@@ -133,7 +133,8 @@ const Action = struct {
/// Will return true if the `phrase` is correctly parsed into an RPN program and
/// its reduced, computed value compares using `op` with the expected value, either
/// a literal or another extracted variable.
- fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
+ fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool {
+ const gpa = step.owner.allocator;
var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
var values = std.ArrayList(u64).init(gpa);
@@ -150,11 +151,11 @@ const Action = struct {
} else {
const val = std.fmt.parseInt(u64, next, 0) catch blk: {
break :blk global_vars.get(next) orelse {
- std.debug.print(
+ try step.addError(
\\
- \\========= Variable was not extracted: ===========
+ \\========= variable was not extracted: ===========
\\{s}
- \\
+ \\=================================================
, .{next});
return error.UnknownVariable;
};
@@ -186,11 +187,11 @@ const Action = struct {
const exp_value = switch (act.expected.?.value) {
.variable => |name| global_vars.get(name) orelse {
- std.debug.print(
+ try step.addError(
\\
- \\========= Variable was not extracted: ===========
+ \\========= variable was not extracted: ===========
\\{s}
- \\
+ \\=================================================
, .{name});
return error.UnknownVariable;
},
@@ -323,14 +324,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
);
const output = switch (self.obj_format) {
- .macho => try MachODumper.parseAndDump(contents, .{
- .gpa = gpa,
+ .macho => try MachODumper.parseAndDump(step, contents, .{
.dump_symtab = self.dump_symtab,
}),
.elf => @panic("TODO elf parser"),
.coff => @panic("TODO coff parser"),
- .wasm => try WasmDumper.parseAndDump(contents, .{
- .gpa = gpa,
+ .wasm => try WasmDumper.parseAndDump(step, contents, .{
.dump_symtab = self.dump_symtab,
}),
else => unreachable,
@@ -346,54 +345,50 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
while (it.next()) |line| {
if (try act.match(line, &vars)) break;
} else {
- std.debug.print(
+ return step.fail(
\\
- \\========= Expected to find: ==========================
+ \\========= expected to find: ==========================
\\{s}
- \\========= But parsed file does not contain it: =======
+ \\========= but parsed file does not contain it: =======
\\{s}
- \\
+ \\======================================================
, .{ act.phrase, output });
- return error.TestFailed;
}
},
.not_present => {
while (it.next()) |line| {
if (try act.match(line, &vars)) {
- std.debug.print(
+ return step.fail(
\\
- \\========= Expected not to find: ===================
+ \\========= expected not to find: ===================
\\{s}
- \\========= But parsed file does contain it: ========
+ \\========= but parsed file does contain it: ========
\\{s}
- \\
+ \\===================================================
, .{ act.phrase, output });
- return error.TestFailed;
}
}
},
.compute_cmp => {
- const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
+ const res = act.computeCmp(step, vars) catch |err| switch (err) {
error.UnknownVariable => {
- std.debug.print(
- \\========= From parsed file: =====================
+ return step.fail(
+ \\========= from parsed file: =====================
\\{s}
- \\
+ \\=================================================
, .{output});
- return error.TestFailed;
},
else => |e| return e,
};
if (!res) {
- std.debug.print(
+ return step.fail(
\\
- \\========= Comparison failed for action: ===========
+ \\========= comparison failed for action: ===========
\\{s} {}
- \\========= From parsed file: =======================
+ \\========= from parsed file: =======================
\\{s}
- \\
+ \\===================================================
, .{ act.phrase, act.expected.?, output });
- return error.TestFailed;
}
},
}
@@ -402,7 +397,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
const Opts = struct {
- gpa: ?Allocator = null,
dump_symtab: bool = false,
};
@@ -410,8 +404,8 @@ const MachODumper = struct {
const LoadCommandIterator = macho.LoadCommandIterator;
const symtab_label = "symtab";
- fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
- const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
+ fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
+ const gpa = step.owner.allocator;
var stream = std.io.fixedBufferStream(bytes);
const reader = stream.reader();
@@ -693,8 +687,8 @@ const MachODumper = struct {
const WasmDumper = struct {
const symtab_label = "symbols";
- fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
- const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
+ fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 {
+ const gpa = step.owner.allocator;
if (opts.dump_symtab) {
@panic("TODO: Implement symbol table parsing and dumping");
}
@@ -715,20 +709,24 @@ const WasmDumper = struct {
const writer = output.writer();
while (reader.readByte()) |current_byte| {
- const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
- std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
- return err;
+ const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch {
+ return step.fail("Found invalid section id '{d}'", .{current_byte});
};
const section_length = try std.leb.readULEB128(u32, reader);
- try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
+ try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer);
fbs.pos += section_length;
} else |_| {} // reached end of stream
return output.toOwnedSlice();
}
- fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
+ fn parseAndDumpSection(
+ step: *Step,
+ section: std.wasm.Section,
+ data: []const u8,
+ writer: anytype,
+ ) !void {
var fbs = std.io.fixedBufferStream(data);
const reader = fbs.reader();
@@ -751,7 +749,7 @@ const WasmDumper = struct {
=> {
const entries = try std.leb.readULEB128(u32, reader);
try writer.print("\nentries {d}\n", .{entries});
- try dumpSection(section, data[fbs.pos..], entries, writer);
+ try dumpSection(step, section, data[fbs.pos..], entries, writer);
},
.custom => {
const name_length = try std.leb.readULEB128(u32, reader);
@@ -760,7 +758,7 @@ const WasmDumper = struct {
try writer.print("\nname {s}\n", .{name});
if (mem.eql(u8, name, "name")) {
- try parseDumpNames(reader, writer, data);
+ try parseDumpNames(step, reader, writer, data);
} else if (mem.eql(u8, name, "producers")) {
try parseDumpProducers(reader, writer, data);
} else if (mem.eql(u8, name, "target_features")) {
@@ -776,7 +774,7 @@ const WasmDumper = struct {
}
}
- fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
+ fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
var fbs = std.io.fixedBufferStream(data);
const reader = fbs.reader();
@@ -786,19 +784,18 @@ const WasmDumper = struct {
while (i < entries) : (i += 1) {
const func_type = try reader.readByte();
if (func_type != std.wasm.function_type) {
- std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
- return error.UnexpectedByte;
+ return step.fail("expected function type, found byte '{d}'", .{func_type});
}
const params = try std.leb.readULEB128(u32, reader);
try writer.print("params {d}\n", .{params});
var index: u32 = 0;
while (index < params) : (index += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
+ try parseDumpType(step, std.wasm.Valtype, reader, writer);
} else index = 0;
const returns = try std.leb.readULEB128(u32, reader);
try writer.print("returns {d}\n", .{returns});
while (index < returns) : (index += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
+ try parseDumpType(step, std.wasm.Valtype, reader, writer);
}
}
},
@@ -812,9 +809,8 @@ const WasmDumper = struct {
const name = data[fbs.pos..][0..name_len];
fbs.pos += name_len;
- const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
- std.debug.print("Invalid import kind\n", .{});
- return err;
+ const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch {
+ return step.fail("invalid import kind", .{});
};
try writer.print(
@@ -831,11 +827,11 @@ const WasmDumper = struct {
try parseDumpLimits(reader, writer);
},
.global => {
- try parseDumpType(std.wasm.Valtype, reader, writer);
+ try parseDumpType(step, std.wasm.Valtype, reader, writer);
try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
},
.table => {
- try parseDumpType(std.wasm.RefType, reader, writer);
+ try parseDumpType(step, std.wasm.RefType, reader, writer);
try parseDumpLimits(reader, writer);
},
}
@@ -850,7 +846,7 @@ const WasmDumper = struct {
.table => {
var i: u32 = 0;
while (i < entries) : (i += 1) {
- try parseDumpType(std.wasm.RefType, reader, writer);
+ try parseDumpType(step, std.wasm.RefType, reader, writer);
try parseDumpLimits(reader, writer);
}
},
@@ -863,9 +859,9 @@ const WasmDumper = struct {
.global => {
var i: u32 = 0;
while (i < entries) : (i += 1) {
- try parseDumpType(std.wasm.Valtype, reader, writer);
+ try parseDumpType(step, std.wasm.Valtype, reader, writer);
try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
- try parseDumpInit(reader, writer);
+ try parseDumpInit(step, reader, writer);
}
},
.@"export" => {
@@ -875,9 +871,8 @@ const WasmDumper = struct {
const name = data[fbs.pos..][0..name_len];
fbs.pos += name_len;
const kind_byte = try std.leb.readULEB128(u8, reader);
- const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
- std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
- return err;
+ const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch {
+ return step.fail("invalid export kind value '{d}'", .{kind_byte});
};
const index = try std.leb.readULEB128(u32, reader);
try writer.print(
@@ -892,7 +887,7 @@ const WasmDumper = struct {
var i: u32 = 0;
while (i < entries) : (i += 1) {
try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
- try parseDumpInit(reader, writer);
+ try parseDumpInit(step, reader, writer);
const function_indexes = try std.leb.readULEB128(u32, reader);
var function_index: u32 = 0;
@@ -908,7 +903,7 @@ const WasmDumper = struct {
while (i < entries) : (i += 1) {
const index = try std.leb.readULEB128(u32, reader);
try writer.print("memory index 0x{x}\n", .{index});
- try parseDumpInit(reader, writer);
+ try parseDumpInit(step, reader, writer);
const size = try std.leb.readULEB128(u32, reader);
try writer.print("size {d}\n", .{size});
try reader.skipBytes(size, .{}); // we do not care about the content of the segments
@@ -918,11 +913,10 @@ const WasmDumper = struct {
}
}
- fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
+ fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void {
const type_byte = try reader.readByte();
- const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
- std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
- return err;
+ const valtype = std.meta.intToEnum(WasmType, type_byte) catch {
+ return step.fail("Invalid wasm type value '{d}'", .{type_byte});
};
try writer.print("type {s}\n", .{@tagName(valtype)});
}
@@ -937,11 +931,10 @@ const WasmDumper = struct {
}
}
- fn parseDumpInit(reader: anytype, writer: anytype) !void {
+ fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void {
const byte = try std.leb.readULEB128(u8, reader);
- const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
- std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
- return err;
+ const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch {
+ return step.fail("invalid wasm opcode '{d}'", .{byte});
};
switch (opcode) {
.i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
@@ -953,14 +946,13 @@ const WasmDumper = struct {
}
const end_opcode = try std.leb.readULEB128(u8, reader);
if (end_opcode != std.wasm.opcode(.end)) {
- std.debug.print("expected 'end' opcode in init expression\n", .{});
- return error.MissingEndOpcode;
+ return step.fail("expected 'end' opcode in init expression", .{});
}
}
- fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
+ fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void {
while (reader.context.pos < data.len) {
- try parseDumpType(std.wasm.NameSubsection, reader, writer);
+ try parseDumpType(step, std.wasm.NameSubsection, reader, writer);
const size = try std.leb.readULEB128(u32, reader);
const entries = try std.leb.readULEB128(u32, reader);
try writer.print(
diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig
index f1a8f71334..99d99694c3 100644
--- a/lib/std/Build/CompileStep.zig
+++ b/lib/std/Build/CompileStep.zig
@@ -538,8 +538,7 @@ pub fn run(cs: *CompileStep) *RunStep {
}
pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
- const b = self.step.owner;
- return CheckObjectStep.create(b, self.getOutputSource(), obj_format);
+ return CheckObjectStep.create(self.step.owner, self.getOutputSource(), obj_format);
}
pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
diff --git a/lib/std/Build/ConfigHeaderStep.zig b/lib/std/Build/ConfigHeaderStep.zig
index 8b4c05bab7..37b04e75a4 100644
--- a/lib/std/Build/ConfigHeaderStep.zig
+++ b/lib/std/Build/ConfigHeaderStep.zig
@@ -192,13 +192,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try output.appendSlice(c_generated_line);
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);
+ try render_autoconf(step, contents, &output, self.values, src_path);
},
.cmake => |file_source| {
try output.appendSlice(c_generated_line);
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);
+ try render_cmake(step, contents, &output, self.values, src_path);
},
.blank => {
try output.appendSlice(c_generated_line);
@@ -234,8 +234,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
output_dir;
var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
- std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
- return err;
+ return step.fail("unable to make path '{s}': {s}", .{ output_dir, @errorName(err) });
};
defer dir.close();
@@ -247,6 +246,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}
fn render_autoconf(
+ step: *Step,
contents: []const u8,
output: *std.ArrayList(u8),
values: std.StringArrayHashMap(Value),
@@ -273,7 +273,7 @@ fn render_autoconf(
}
const name = it.rest();
const kv = values_copy.fetchSwapRemove(name) orelse {
- std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
+ try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
src_path, line_index + 1, name,
});
any_errors = true;
@@ -283,15 +283,17 @@ fn render_autoconf(
}
for (values_copy.keys()) |name| {
- std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
+ try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name });
+ any_errors = true;
}
if (any_errors) {
- return error.HeaderConfigFailed;
+ return error.MakeFailed;
}
}
fn render_cmake(
+ step: *Step,
contents: []const u8,
output: *std.ArrayList(u8),
values: std.StringArrayHashMap(Value),
@@ -317,14 +319,14 @@ fn render_cmake(
continue;
}
const name = it.next() orelse {
- std.debug.print("{s}:{d}: error: missing define name\n", .{
+ try step.addError("{s}:{d}: error: missing define name", .{
src_path, line_index + 1,
});
any_errors = true;
continue;
};
const kv = values_copy.fetchSwapRemove(name) orelse {
- std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
+ try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{
src_path, line_index + 1, name,
});
any_errors = true;
@@ -334,7 +336,8 @@ fn render_cmake(
}
for (values_copy.keys()) |name| {
- std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
+ try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name });
+ any_errors = true;
}
if (any_errors) {
diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig
index 7e35d0a5ee..377cba301c 100644
--- a/lib/std/Build/InstallArtifactStep.zig
+++ b/lib/std/Build/InstallArtifactStep.zig
@@ -12,6 +12,9 @@ artifact: *CompileStep,
dest_dir: InstallDir,
pdb_dir: ?InstallDir,
h_dir: ?InstallDir,
+/// If non-null, adds additional path components relative to dest_dir, and
+/// overrides the basename of the CompileStep.
+dest_sub_path: ?[]const u8,
pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
if (artifact.install_step) |s| return s;
@@ -40,6 +43,7 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
}
} else null,
.h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
+ .dest_sub_path = null,
};
self.step.dependOn(&artifact.step);
artifact.install_step = self;
@@ -71,7 +75,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const self = @fieldParentPtr(InstallArtifactStep, "step", step);
const dest_builder = self.dest_builder;
- const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
+ const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename;
+ const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path);
+
try src_builder.updateFile(
self.artifact.getOutputSource().getPath(src_builder),
full_dest_path,
diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig
index 7199431ee6..13046b3efe 100644
--- a/lib/std/Build/ObjCopyStep.zig
+++ b/lib/std/Build/ObjCopyStep.zig
@@ -95,8 +95,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });
const cache_path = "o" ++ fs.path.sep_str ++ digest;
b.cache_root.handle.makePath(cache_path) catch |err| {
- std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
- return err;
+ return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });
};
var argv = std.ArrayList([]const u8).init(b.allocator);
diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig
index 087483fea8..9304cc758e 100644
--- a/lib/std/Build/RunStep.zig
+++ b/lib/std/Build/RunStep.zig
@@ -10,6 +10,7 @@ const ArrayList = std.ArrayList;
const EnvMap = process.EnvMap;
const Allocator = mem.Allocator;
const ExecError = std.Build.ExecError;
+const assert = std.debug.assert;
const RunStep = @This();
@@ -54,6 +55,8 @@ rename_step_with_output_arg: bool = true,
/// 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).
+/// If this RunStep is considered to have side-effects, then this flag does
+/// nothing.
skip_foreign_checks: bool = false,
/// If stderr or stdout exceeds this amount, the child process is killed and
@@ -79,7 +82,7 @@ pub const StdIo = union(enum) {
/// 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,
+ check: std.ArrayList(Check),
pub const Check = union(enum) {
expect_stderr_exact: []const u8,
@@ -214,14 +217,20 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8
env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
}
+/// Adds a check for exact stderr match. Does not add any other checks.
pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) };
self.addCheck(new_check);
}
+/// Adds a check for exact stdout match as well as a check for exit code 0, if
+/// there is not already an expected termination check.
pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) };
self.addCheck(new_check);
+ if (!self.hasTermCheck()) {
+ self.expectExitCode(0);
+ }
}
pub fn expectExitCode(self: *RunStep, code: u8) void {
@@ -229,19 +238,21 @@ pub fn expectExitCode(self: *RunStep, code: u8) void {
self.addCheck(new_check);
}
+pub fn hasTermCheck(self: RunStep) bool {
+ for (self.stdio.check.items) |check| switch (check) {
+ .expect_term => return true,
+ else => continue,
+ };
+ return false;
+}
+
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;
+ self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) };
+ self.stdio.check.append(new_check) catch @panic("OOM");
},
+ .check => |*checks| checks.append(new_check) catch @panic("OOM"),
else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"),
}
}
@@ -298,14 +309,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
const b = step.owner;
+ const arena = b.allocator;
const self = @fieldParentPtr(RunStep, "step", step);
const has_side_effects = self.hasSideEffects();
- var argv_list = ArrayList([]const u8).init(b.allocator);
+ var argv_list = ArrayList([]const u8).init(arena);
var output_placeholders = ArrayList(struct {
index: usize,
output: Arg.Output,
- }).init(b.allocator);
+ }).init(arena);
var man = b.cache.obtain();
defer man.deinit();
@@ -357,7 +369,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const digest = man.final();
for (output_placeholders.items) |placeholder| {
placeholder.output.generated_file.path = try b.cache_root.join(
- b.allocator,
+ arena,
&.{ "o", &digest, placeholder.output.basename },
);
}
@@ -367,30 +379,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const digest = man.final();
for (output_placeholders.items) |placeholder| {
- const output_path = try b.cache_root.join(
- b.allocator,
- &.{ "o", &digest, placeholder.output.basename },
- );
- const output_dir = fs.path.dirname(output_path).?;
- fs.cwd().makePath(output_dir) catch |err| {
- std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
- return err;
+ const output_components = .{ "o", &digest, placeholder.output.basename };
+ const output_sub_path = try fs.path.join(arena, &output_components);
+ const output_sub_dir_path = fs.path.dirname(output_sub_path).?;
+ b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
+ return step.fail("unable to make path '{}{s}': {s}", .{
+ b.cache_root, output_sub_dir_path, @errorName(err),
+ });
};
-
+ const output_path = try b.cache_root.join(arena, &output_components);
placeholder.output.generated_file.path = output_path;
argv_list.items[placeholder.index] = output_path;
}
}
- try runCommand(
- step,
- self.cwd,
- argv_list.items,
- self.env_map,
- self.stdio,
- has_side_effects,
- self.max_stdio_size,
- );
+ try runCommand(self, argv_list.items, has_side_effects);
if (!has_side_effects) {
try man.writeManifest();
@@ -442,92 +445,150 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)
};
}
-fn runCommand(
- step: *Step,
- opt_cwd: ?[]const u8,
- argv: []const []const u8,
- env_map: ?*EnvMap,
- stdio: StdIo,
- has_side_effects: bool,
- max_stdio_size: usize,
-) !void {
+fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) !void {
+ const step = &self.step;
const b = step.owner;
const arena = b.allocator;
- const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path;
- try step.handleChildProcUnsupported(opt_cwd, argv);
- try Step.handleVerbose(step.owner, opt_cwd, argv);
-
- var child = std.ChildProcess.init(argv, arena);
- child.cwd = cwd;
- child.env_map = env_map orelse b.env_map;
-
- 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,
- };
-
- child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{
- argv[0], @errorName(err),
- });
+ try step.handleChildProcUnsupported(self.cwd, argv);
+ try Step.handleVerbose(step.owner, self.cwd, argv);
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();
+ const term = spawnChildAndCollect(self, argv, &stdout_bytes, &stderr_bytes, has_side_effects) catch |err| term: {
+ if (err == error.InvalidExe) interpret: {
+ // TODO: learn the target from the binary directly rather than from
+ // relying on it being a CompileStep. This will make this logic
+ // work even for the edge case that the binary was produced by a
+ // third party.
+ const exe = switch (self.argv.items[0]) {
+ .artifact => |exe| exe,
+ else => break :interpret,
+ };
+ if (exe.kind != .exe) break :interpret;
+
+ var interp_argv = std.ArrayList([]const u8).init(b.allocator);
+ defer interp_argv.deinit();
+
+ const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc;
+ switch (b.host.getExternalExecutor(exe.target_info, .{
+ .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
+ .link_libc = exe.is_linking_libc,
+ })) {
+ .native, .rosetta => {
+ if (self.stdio == .check and self.skip_foreign_checks)
+ return error.MakeSkipped;
+
+ break :interpret;
+ },
+ .wine => |bin_name| {
+ if (b.enable_wine) {
+ try interp_argv.append(bin_name);
+ } else {
+ return failForeign(self, "-fwine", argv[0], exe);
+ }
+ },
+ .qemu => |bin_name| {
+ if (b.enable_qemu) {
+ const glibc_dir_arg = if (need_cross_glibc)
+ b.glibc_runtimes_dir orelse return
+ else
+ null;
+
+ try interp_argv.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 = exe.target.getCpuArch();
+ const os_tag = exe.target.getOsTag();
+ const abi = 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(b.allocator, fmt_str, .{
+ dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
+ });
+
+ try interp_argv.append("-L");
+ try interp_argv.append(full_dir);
+ }
+ } else {
+ return failForeign(self, "-fqemu", argv[0], exe);
+ }
+ },
+ .darling => |bin_name| {
+ if (b.enable_darling) {
+ try interp_argv.append(bin_name);
+ } else {
+ return failForeign(self, "-fdarling", argv[0], exe);
+ }
+ },
+ .wasmtime => |bin_name| {
+ if (b.enable_wasmtime) {
+ try interp_argv.append(bin_name);
+ try interp_argv.append("--dir=.");
+ } else {
+ return failForeign(self, "-fwasmtime", argv[0], exe);
+ }
+ },
+ .bad_dl => |foreign_dl| {
+ if (self.stdio == .check and self.skip_foreign_checks)
+ return error.MakeSkipped;
+
+ const host_dl = b.host.dynamic_linker.get() orelse "(none)";
- 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;
+ return step.fail(
+ \\the host system is unable to execute binaries from the target
+ \\ because the host dynamic linker is '{s}',
+ \\ while the target dynamic linker is '{s}'.
+ \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step
+ , .{ host_dl, foreign_dl });
+ },
+ .bad_os_or_cpu => {
+ if (self.stdio == .check and self.skip_foreign_checks)
+ return error.MakeSkipped;
+
+ const host_name = try b.host.target.zigTriple(b.allocator);
+ const foreign_name = try exe.target.zigTriple(b.allocator);
+
+ return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
+ host_name, foreign_name,
+ });
+ },
}
- 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);
- }
+ if (exe.target.isWindows()) {
+ // On Windows we don't have rpaths so we have to add .dll search paths to PATH
+ RunStep.addPathForDynLibsInternal(&self.step, b, exe);
+ }
- 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);
+ try interp_argv.append(argv[0]);
+
+ try Step.handleVerbose(step.owner, self.cwd, interp_argv.items);
+
+ assert(stdout_bytes == null);
+ assert(stderr_bytes == null);
+ break :term spawnChildAndCollect(self, interp_argv.items, &stdout_bytes, &stderr_bytes, has_side_effects) catch |inner_err| {
+ return step.fail("unable to spawn {s}: {s}", .{
+ interp_argv.items[0], @errorName(inner_err),
+ });
+ };
}
- };
- const term = child.wait() catch |err| {
- return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
+ return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
};
- switch (stdio) {
- .check => |checks| for (checks) |check| switch (check) {
+ switch (self.stdio) {
+ .check => |checks| for (checks.items) |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: ====================
@@ -537,13 +598,14 @@ fn runCommand(
, .{
expected_bytes,
stderr_bytes.?,
- try Step.allocPrintCmd(arena, opt_cwd, argv),
+ try Step.allocPrintCmd(arena, self.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: =====
@@ -553,13 +615,14 @@ fn runCommand(
, .{
match,
stderr_bytes.?,
- try Step.allocPrintCmd(arena, opt_cwd, argv),
+ try Step.allocPrintCmd(arena, self.cwd, argv),
});
}
},
.expect_stdout_exact => |expected_bytes| {
if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) {
return step.fail(
+ \\
\\========= expected this stdout: =========
\\{s}
\\========= but found: ====================
@@ -569,13 +632,14 @@ fn runCommand(
, .{
expected_bytes,
stdout_bytes.?,
- try Step.allocPrintCmd(arena, opt_cwd, argv),
+ try Step.allocPrintCmd(arena, self.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: =====
@@ -585,7 +649,7 @@ fn runCommand(
, .{
match,
stdout_bytes.?,
- try Step.allocPrintCmd(arena, opt_cwd, argv),
+ try Step.allocPrintCmd(arena, self.cwd, argv),
});
}
},
@@ -594,17 +658,89 @@ fn runCommand(
return step.fail("the following command {} (expected {}):\n{s}", .{
fmtTerm(term),
fmtTerm(expected_term),
- try Step.allocPrintCmd(arena, opt_cwd, argv),
+ try Step.allocPrintCmd(arena, self.cwd, argv),
});
}
},
},
else => {
- try step.handleChildProcessTerm(term, opt_cwd, argv);
+ try step.handleChildProcessTerm(term, self.cwd, argv);
},
}
}
+fn spawnChildAndCollect(
+ self: *RunStep,
+ argv: []const []const u8,
+ stdout_bytes: *?[]const u8,
+ stderr_bytes: *?[]const u8,
+ has_side_effects: bool,
+) !std.ChildProcess.Term {
+ const b = self.step.owner;
+ const arena = b.allocator;
+ const cwd = if (self.cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path;
+
+ var child = std.ChildProcess.init(argv, arena);
+ child.cwd = cwd;
+ child.env_map = self.env_map orelse b.env_map;
+
+ child.stdin_behavior = switch (self.stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
+ .inherit => .Inherit,
+ .check => .Close,
+ };
+ child.stdout_behavior = switch (self.stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
+ .inherit => .Inherit,
+ .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore,
+ };
+ child.stderr_behavior = switch (self.stdio) {
+ .infer_from_args => if (has_side_effects) .Inherit else .Pipe,
+ .inherit => .Inherit,
+ .check => .Pipe,
+ };
+
+ child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{
+ argv[0], @errorName(err),
+ });
+
+ 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 > self.max_stdio_size)
+ return error.StdoutStreamTooLong;
+ if (poller.fifo(.stderr).count > self.max_stdio_size)
+ return error.StderrStreamTooLong;
+ }
+
+ stdout_bytes.* = try poller.fifo(.stdout).toOwnedSlice();
+ stderr_bytes.* = try poller.fifo(.stderr).toOwnedSlice();
+ } else {
+ stdout_bytes.* = try stdout.reader().readAllAlloc(arena, self.max_stdio_size);
+ }
+ } else if (child.stderr) |stderr| {
+ stderr_bytes.* = try stderr.reader().readAllAlloc(arena, self.max_stdio_size);
+ }
+
+ if (stderr_bytes.*) |stderr| if (stderr.len > 0) {
+ const stderr_is_diagnostic = switch (self.stdio) {
+ .check => |checks| !checksContainStderr(checks.items),
+ else => true,
+ };
+ if (stderr_is_diagnostic) {
+ try self.step.result_error_msgs.append(arena, stderr);
+ }
+ };
+
+ return child.wait();
+}
+
fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
addPathForDynLibsInternal(&self.step, self.step.owner, artifact);
}
@@ -624,3 +760,29 @@ pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *Co
}
}
}
+
+fn failForeign(
+ self: *RunStep,
+ suggested_flag: []const u8,
+ argv0: []const u8,
+ exe: *CompileStep,
+) error{ MakeFailed, MakeSkipped, OutOfMemory } {
+ switch (self.stdio) {
+ .check => {
+ if (self.skip_foreign_checks)
+ return error.MakeSkipped;
+
+ const b = self.step.owner;
+ const host_name = try b.host.target.zigTriple(b.allocator);
+ const foreign_name = try exe.target.zigTriple(b.allocator);
+
+ return self.step.fail(
+ \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
+ \\ consider using {s} or enabling skip_foreign_checks in the Run step
+ , .{ argv0, foreign_name, host_name, suggested_flag });
+ },
+ else => {
+ return self.step.fail("unable to spawn foreign binary '{s}'", .{argv0});
+ },
+ }
+}
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
index 3219588050..928e7e8735 100644
--- a/lib/std/Build/Step.zig
+++ b/lib/std/Build/Step.zig
@@ -26,6 +26,9 @@ pub const State = enum {
dependency_failure,
success,
failure,
+ /// This state indicates that the step did not complete, however, it also did not fail,
+ /// and it is safe to continue executing its dependencies.
+ skipped,
};
pub const Id = enum {
@@ -106,13 +109,15 @@ pub fn init(options: Options) Step {
/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
/// have already reported the error. Otherwise, we add a simple error report
/// here.
-pub fn make(s: *Step, prog_node: *std.Progress.Node) error{MakeFailed}!void {
- return s.makeFn(s, prog_node) catch |err| {
- if (err != error.MakeFailed) {
+pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void {
+ return s.makeFn(s, prog_node) catch |err| switch (err) {
+ error.MakeFailed => return error.MakeFailed,
+ error.MakeSkipped => return error.MakeSkipped,
+ else => {
const gpa = s.dependencies.allocator;
s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM");
- }
- return error.MakeFailed;
+ return error.MakeFailed;
+ },
};
}
@@ -192,10 +197,14 @@ pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void {
}
pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } {
+ try step.addError(fmt, args);
+ return error.MakeFailed;
+}
+
+pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
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
@@ -398,5 +407,5 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer
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 });
+ return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path });
}
diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig
index d554d6efc1..b5164d5730 100644
--- a/lib/std/Build/WriteFileStep.zig
+++ b/lib/std/Build/WriteFileStep.zig
@@ -37,7 +37,7 @@ pub fn init(owner: *std.Build) WriteFileStep {
return .{
.step = Step.init(.{
.id = .write_file,
- .name = "writefile",
+ .name = "WriteFile",
.owner = owner,
.makeFn = make,
}),
@@ -56,6 +56,8 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
.contents = .{ .bytes = b.dupe(bytes) },
};
wf.files.append(gpa, file) catch @panic("OOM");
+
+ wf.maybeUpdateName();
}
/// Place the file into the generated directory within the local cache,
@@ -75,6 +77,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [
.contents = .{ .copy = source },
};
wf.files.append(gpa, file) catch @panic("OOM");
+
+ wf.maybeUpdateName();
}
/// A path relative to the package root.
@@ -101,6 +105,15 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo
return null;
}
+fn maybeUpdateName(wf: *WriteFileStep) void {
+ if (wf.files.items.len == 1) {
+ // First time adding a file; update name.
+ if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
+ wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path});
+ }
+ }
+}
+
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = prog_node;
const b = step.owner;
@@ -110,14 +123,39 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// WriteFileStep - arguably it should be a different step. But anyway here
// it is, it happens unconditionally and does not interact with the other
// files here.
+ var any_miss = false;
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 b.build_root.handle.makeOpenPath(dirname, .{});
- defer dir.close();
- try writeFile(wf, dir, output_source_file.contents, basename);
- } else {
- try writeFile(wf, b.build_root.handle, output_source_file.contents, basename);
+ b.build_root.handle.makePath(dirname) catch |err| {
+ return step.fail("unable to make path '{}{s}': {s}", .{
+ b.build_root, dirname, @errorName(err),
+ });
+ };
+ }
+ switch (output_source_file.contents) {
+ .bytes => |bytes| {
+ b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| {
+ return step.fail("unable to write file '{}{s}': {s}", .{
+ b.build_root, output_source_file.sub_path, @errorName(err),
+ });
+ };
+ any_miss = true;
+ },
+ .copy => |file_source| {
+ const source_path = file_source.getPath(b);
+ const prev_status = fs.Dir.updateFile(
+ fs.cwd(),
+ source_path,
+ b.build_root.handle,
+ output_source_file.sub_path,
+ .{},
+ ) catch |err| {
+ return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{
+ source_path, b.build_root, output_source_file.sub_path, @errorName(err),
+ });
+ };
+ any_miss = any_miss or prev_status == .stale;
+ },
}
}
@@ -164,19 +202,52 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const cache_path = "o" ++ fs.path.sep_str ++ digest;
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;
+ return step.fail("unable to make path '{}{s}': {s}", .{
+ b.cache_root, cache_path, @errorName(err),
+ });
};
defer cache_dir.close();
for (wf.files.items) |file| {
- const basename = fs.path.basename(file.sub_path);
if (fs.path.dirname(file.sub_path)) |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);
+ cache_dir.makePath(dirname) catch |err| {
+ return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{
+ b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),
+ });
+ };
+ }
+ switch (file.contents) {
+ .bytes => |bytes| {
+ cache_dir.writeFile(file.sub_path, bytes) catch |err| {
+ return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{
+ b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err),
+ });
+ };
+ },
+ .copy => |file_source| {
+ const source_path = file_source.getPath(b);
+ const prev_status = fs.Dir.updateFile(
+ fs.cwd(),
+ source_path,
+ cache_dir,
+ file.sub_path,
+ .{},
+ ) catch |err| {
+ return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{
+ source_path,
+ b.cache_root,
+ cache_path,
+ fs.path.sep,
+ file.sub_path,
+ @errorName(err),
+ });
+ };
+ // At this point we already will mark the step as a cache miss.
+ // But this is kind of a partial cache hit since individual
+ // file copies may be avoided. Oh well, this information is
+ // discarded.
+ _ = prev_status;
+ },
}
file.generated_file.path = try b.cache_root.join(
@@ -188,19 +259,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
try man.writeManifest();
}
-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(b);
- const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{});
- _ = prev_status; // TODO logging (affected by open PR regarding concurrency)
- },
- }
-}
-
const std = @import("../std.zig");
const Step = std.Build.Step;
const fs = std.fs;
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index f8cba85874..3bdef3177a 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -185,7 +185,6 @@ pub const ChildProcess = struct {
}
/// Blocks until child process terminates and then cleans up all resources.
- /// TODO: set the pid to undefined in this function.
pub fn wait(self: *ChildProcess) !Term {
const term = if (builtin.os.tag == .windows)
try self.waitWindows()