| author | |
| committer | |
| log | dcec4d55e36f48e459f4e8f218b8619d9be925db |
| tree | 0064e09c25715650b4e1ac641d5a33ec91245be1 |
| parent | 9bf63b09963ca6ea1179dfaa9142498556bfac9d |
* 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.20 files changed, 513 insertions(+), 312 deletions(-)
build.zig+1-2| ... | @@ -385,7 +385,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -385,7 +385,7 @@ pub fn build(b: *std.Build) !void { |
| 385 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; | 385 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; |
| 386 | 386 | ||
| 387 | const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; | 387 | const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" }; |
| 388 | const fmt_exclude_paths = &.{ "test/cases" }; | 388 | const fmt_exclude_paths = &.{"test/cases"}; |
| 389 | const check_fmt = b.addFmt(.{ | 389 | const check_fmt = b.addFmt(.{ |
| 390 | .paths = fmt_include_paths, | 390 | .paths = fmt_include_paths, |
| 391 | .exclude_paths = fmt_exclude_paths, | 391 | .exclude_paths = fmt_exclude_paths, |
| ... | @@ -402,7 +402,6 @@ pub fn build(b: *std.Build) !void { | ... | @@ -402,7 +402,6 @@ pub fn build(b: *std.Build) !void { |
| 402 | const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); | 402 | const do_fmt_step = b.step("fmt", "Modify source files in place to have conforming formatting"); |
| 403 | do_fmt_step.dependOn(&do_fmt.step); | 403 | do_fmt_step.dependOn(&do_fmt.step); |
| 404 | 404 | ||
| 405 | |||
| 406 | test_step.dependOn(tests.addPkgTests( | 405 | test_step.dependOn(tests.addPkgTests( |
| 407 | b, | 406 | b, |
| 408 | test_filter, | 407 | test_filter, |
lib/build_runner.zig+23-12| ... | @@ -357,6 +357,7 @@ fn runStepNames( | ... | @@ -357,6 +357,7 @@ fn runStepNames( |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | var success_count: usize = 0; | 359 | var success_count: usize = 0; |
| 360 | var skipped_count: usize = 0; | ||
| 360 | var failure_count: usize = 0; | 361 | var failure_count: usize = 0; |
| 361 | var pending_count: usize = 0; | 362 | var pending_count: usize = 0; |
| 362 | var total_compile_errors: usize = 0; | 363 | var total_compile_errors: usize = 0; |
| ... | @@ -379,6 +380,7 @@ fn runStepNames( | ... | @@ -379,6 +380,7 @@ fn runStepNames( |
| 379 | }, | 380 | }, |
| 380 | .dependency_failure => pending_count += 1, | 381 | .dependency_failure => pending_count += 1, |
| 381 | .success => success_count += 1, | 382 | .success => success_count += 1, |
| 383 | .skipped => skipped_count += 1, | ||
| 382 | .failure => { | 384 | .failure => { |
| 383 | failure_count += 1; | 385 | failure_count += 1; |
| 384 | const compile_errors_len = s.result_error_bundle.errorMessageCount(); | 386 | const compile_errors_len = s.result_error_bundle.errorMessageCount(); |
| ... | @@ -395,13 +397,13 @@ fn runStepNames( | ... | @@ -395,13 +397,13 @@ fn runStepNames( |
| 395 | if (failure_count == 0 and enable_summary != true) return cleanExit(); | 397 | if (failure_count == 0 and enable_summary != true) return cleanExit(); |
| 396 | 398 | ||
| 397 | if (enable_summary != false) { | 399 | if (enable_summary != false) { |
| 398 | const total_count = success_count + failure_count + pending_count; | 400 | const total_count = success_count + failure_count + pending_count + skipped_count; |
| 399 | ttyconf.setColor(stderr, .Cyan) catch {}; | 401 | ttyconf.setColor(stderr, .Cyan) catch {}; |
| 400 | stderr.writeAll("Build Summary:") catch {}; | 402 | stderr.writeAll("Build Summary:") catch {}; |
| 401 | ttyconf.setColor(stderr, .Reset) catch {}; | 403 | ttyconf.setColor(stderr, .Reset) catch {}; |
| 402 | stderr.writer().print(" {d}/{d} steps succeeded; {d} failed", .{ | 404 | stderr.writer().print(" {d}/{d} steps succeeded", .{ success_count, total_count }) catch {}; |
| 403 | success_count, total_count, failure_count, | 405 | if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; |
| 404 | }) catch {}; | 406 | if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; |
| 405 | 407 | ||
| 406 | if (enable_summary == null) { | 408 | if (enable_summary == null) { |
| 407 | ttyconf.setColor(stderr, .Dim) catch {}; | 409 | ttyconf.setColor(stderr, .Dim) catch {}; |
| ... | @@ -503,6 +505,12 @@ fn printTreeStep( | ... | @@ -503,6 +505,12 @@ fn printTreeStep( |
| 503 | try ttyconf.setColor(stderr, .Reset); | 505 | try ttyconf.setColor(stderr, .Reset); |
| 504 | }, | 506 | }, |
| 505 | 507 | ||
| 508 | .skipped => { | ||
| 509 | try ttyconf.setColor(stderr, .Yellow); | ||
| 510 | try stderr.writeAll(" skipped\n"); | ||
| 511 | try ttyconf.setColor(stderr, .Reset); | ||
| 512 | }, | ||
| 513 | |||
| 506 | .failure => { | 514 | .failure => { |
| 507 | try ttyconf.setColor(stderr, .Red); | 515 | try ttyconf.setColor(stderr, .Red); |
| 508 | if (s.result_error_bundle.errorMessageCount() > 0) { | 516 | if (s.result_error_bundle.errorMessageCount() > 0) { |
| ... | @@ -569,6 +577,7 @@ fn checkForDependencyLoop( | ... | @@ -569,6 +577,7 @@ fn checkForDependencyLoop( |
| 569 | .running => unreachable, | 577 | .running => unreachable, |
| 570 | .success => unreachable, | 578 | .success => unreachable, |
| 571 | .failure => unreachable, | 579 | .failure => unreachable, |
| 580 | .skipped => unreachable, | ||
| 572 | } | 581 | } |
| 573 | } | 582 | } |
| 574 | 583 | ||
| ... | @@ -587,7 +596,7 @@ fn workerMakeOneStep( | ... | @@ -587,7 +596,7 @@ fn workerMakeOneStep( |
| 587 | // queue this step up again when dependencies are met. | 596 | // queue this step up again when dependencies are met. |
| 588 | for (s.dependencies.items) |dep| { | 597 | for (s.dependencies.items) |dep| { |
| 589 | switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { | 598 | switch (@atomicLoad(Step.State, &dep.state, .SeqCst)) { |
| 590 | .success => continue, | 599 | .success, .skipped => continue, |
| 591 | .failure, .dependency_failure => { | 600 | .failure, .dependency_failure => { |
| 592 | @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); | 601 | @atomicStore(Step.State, &s.state, .dependency_failure, .SeqCst); |
| 593 | return; | 602 | return; |
| ... | @@ -639,13 +648,15 @@ fn workerMakeOneStep( | ... | @@ -639,13 +648,15 @@ fn workerMakeOneStep( |
| 639 | } | 648 | } |
| 640 | } | 649 | } |
| 641 | 650 | ||
| 642 | make_result catch |err| { | 651 | if (make_result) |_| { |
| 643 | assert(err == error.MakeFailed); | 652 | @atomicStore(Step.State, &s.state, .success, .SeqCst); |
| 644 | @atomicStore(Step.State, &s.state, .failure, .SeqCst); | 653 | } else |err| switch (err) { |
| 645 | return; | 654 | error.MakeFailed => { |
| 646 | }; | 655 | @atomicStore(Step.State, &s.state, .failure, .SeqCst); |
| 647 | 656 | return; | |
| 648 | @atomicStore(Step.State, &s.state, .success, .SeqCst); | 657 | }, |
| 658 | error.MakeSkipped => @atomicStore(Step.State, &s.state, .skipped, .SeqCst), | ||
| 659 | } | ||
| 649 | 660 | ||
| 650 | // Successful completion of a step, so we queue up its dependants as well. | 661 | // Successful completion of a step, so we queue up its dependants as well. |
| 651 | for (s.dependants.items) |dep| { | 662 | for (s.dependants.items) |dep| { |
lib/std/Build/CheckFileStep.zig+3-4| ... | @@ -42,15 +42,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -42,15 +42,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 42 | 42 | ||
| 43 | for (self.expected_matches) |expected_match| { | 43 | for (self.expected_matches) |expected_match| { |
| 44 | if (mem.indexOf(u8, contents, expected_match) == null) { | 44 | if (mem.indexOf(u8, contents, expected_match) == null) { |
| 45 | std.debug.print( | 45 | return step.fail( |
| 46 | \\ | 46 | \\ |
| 47 | \\========= Expected to find: =================== | 47 | \\========= expected to find: =================== |
| 48 | \\{s} | 48 | \\{s} |
| 49 | \\========= But file does not contain it: ======= | 49 | \\========= but file does not contain it: ======= |
| 50 | \\{s} | 50 | \\{s} |
| 51 | \\ | 51 | \\ |
| 52 | , .{ expected_match, contents }); | 52 | , .{ expected_match, contents }); |
| 53 | return error.TestFailed; | ||
| 54 | } | 53 | } |
| 55 | } | 54 | } |
| 56 | } | 55 | } |
lib/std/Build/CheckObjectStep.zig+65-73| ... | @@ -133,7 +133,8 @@ const Action = struct { | ... | @@ -133,7 +133,8 @@ const Action = struct { |
| 133 | /// Will return true if the `phrase` is correctly parsed into an RPN program and | 133 | /// Will return true if the `phrase` is correctly parsed into an RPN program and |
| 134 | /// its reduced, computed value compares using `op` with the expected value, either | 134 | /// its reduced, computed value compares using `op` with the expected value, either |
| 135 | /// a literal or another extracted variable. | 135 | /// a literal or another extracted variable. |
| 136 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { | 136 | fn computeCmp(act: Action, step: *Step, global_vars: anytype) !bool { |
| 137 | const gpa = step.owner.allocator; | ||
| 137 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | 138 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); |
| 138 | var values = std.ArrayList(u64).init(gpa); | 139 | var values = std.ArrayList(u64).init(gpa); |
| 139 | 140 | ||
| ... | @@ -150,11 +151,11 @@ const Action = struct { | ... | @@ -150,11 +151,11 @@ const Action = struct { |
| 150 | } else { | 151 | } else { |
| 151 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { | 152 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { |
| 152 | break :blk global_vars.get(next) orelse { | 153 | break :blk global_vars.get(next) orelse { |
| 153 | std.debug.print( | 154 | try step.addError( |
| 154 | \\ | 155 | \\ |
| 155 | \\========= Variable was not extracted: =========== | 156 | \\========= variable was not extracted: =========== |
| 156 | \\{s} | 157 | \\{s} |
| 157 | \\ | 158 | \\================================================= |
| 158 | , .{next}); | 159 | , .{next}); |
| 159 | return error.UnknownVariable; | 160 | return error.UnknownVariable; |
| 160 | }; | 161 | }; |
| ... | @@ -186,11 +187,11 @@ const Action = struct { | ... | @@ -186,11 +187,11 @@ const Action = struct { |
| 186 | 187 | ||
| 187 | const exp_value = switch (act.expected.?.value) { | 188 | const exp_value = switch (act.expected.?.value) { |
| 188 | .variable => |name| global_vars.get(name) orelse { | 189 | .variable => |name| global_vars.get(name) orelse { |
| 189 | std.debug.print( | 190 | try step.addError( |
| 190 | \\ | 191 | \\ |
| 191 | \\========= Variable was not extracted: =========== | 192 | \\========= variable was not extracted: =========== |
| 192 | \\{s} | 193 | \\{s} |
| 193 | \\ | 194 | \\================================================= |
| 194 | , .{name}); | 195 | , .{name}); |
| 195 | return error.UnknownVariable; | 196 | return error.UnknownVariable; |
| 196 | }, | 197 | }, |
| ... | @@ -323,14 +324,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -323,14 +324,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 323 | ); | 324 | ); |
| 324 | 325 | ||
| 325 | const output = switch (self.obj_format) { | 326 | const output = switch (self.obj_format) { |
| 326 | .macho => try MachODumper.parseAndDump(contents, .{ | 327 | .macho => try MachODumper.parseAndDump(step, contents, .{ |
| 327 | .gpa = gpa, | ||
| 328 | .dump_symtab = self.dump_symtab, | 328 | .dump_symtab = self.dump_symtab, |
| 329 | }), | 329 | }), |
| 330 | .elf => @panic("TODO elf parser"), | 330 | .elf => @panic("TODO elf parser"), |
| 331 | .coff => @panic("TODO coff parser"), | 331 | .coff => @panic("TODO coff parser"), |
| 332 | .wasm => try WasmDumper.parseAndDump(contents, .{ | 332 | .wasm => try WasmDumper.parseAndDump(step, contents, .{ |
| 333 | .gpa = gpa, | ||
| 334 | .dump_symtab = self.dump_symtab, | 333 | .dump_symtab = self.dump_symtab, |
| 335 | }), | 334 | }), |
| 336 | else => unreachable, | 335 | else => unreachable, |
| ... | @@ -346,54 +345,50 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -346,54 +345,50 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 346 | while (it.next()) |line| { | 345 | while (it.next()) |line| { |
| 347 | if (try act.match(line, &vars)) break; | 346 | if (try act.match(line, &vars)) break; |
| 348 | } else { | 347 | } else { |
| 349 | std.debug.print( | 348 | return step.fail( |
| 350 | \\ | 349 | \\ |
| 351 | \\========= Expected to find: ========================== | 350 | \\========= expected to find: ========================== |
| 352 | \\{s} | 351 | \\{s} |
| 353 | \\========= But parsed file does not contain it: ======= | 352 | \\========= but parsed file does not contain it: ======= |
| 354 | \\{s} | 353 | \\{s} |
| 355 | \\ | 354 | \\====================================================== |
| 356 | , .{ act.phrase, output }); | 355 | , .{ act.phrase, output }); |
| 357 | return error.TestFailed; | ||
| 358 | } | 356 | } |
| 359 | }, | 357 | }, |
| 360 | .not_present => { | 358 | .not_present => { |
| 361 | while (it.next()) |line| { | 359 | while (it.next()) |line| { |
| 362 | if (try act.match(line, &vars)) { | 360 | if (try act.match(line, &vars)) { |
| 363 | std.debug.print( | 361 | return step.fail( |
| 364 | \\ | 362 | \\ |
| 365 | \\========= Expected not to find: =================== | 363 | \\========= expected not to find: =================== |
| 366 | \\{s} | 364 | \\{s} |
| 367 | \\========= But parsed file does contain it: ======== | 365 | \\========= but parsed file does contain it: ======== |
| 368 | \\{s} | 366 | \\{s} |
| 369 | \\ | 367 | \\=================================================== |
| 370 | , .{ act.phrase, output }); | 368 | , .{ act.phrase, output }); |
| 371 | return error.TestFailed; | ||
| 372 | } | 369 | } |
| 373 | } | 370 | } |
| 374 | }, | 371 | }, |
| 375 | .compute_cmp => { | 372 | .compute_cmp => { |
| 376 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { | 373 | const res = act.computeCmp(step, vars) catch |err| switch (err) { |
| 377 | error.UnknownVariable => { | 374 | error.UnknownVariable => { |
| 378 | std.debug.print( | 375 | return step.fail( |
| 379 | \\========= From parsed file: ===================== | 376 | \\========= from parsed file: ===================== |
| 380 | \\{s} | 377 | \\{s} |
| 381 | \\ | 378 | \\================================================= |
| 382 | , .{output}); | 379 | , .{output}); |
| 383 | return error.TestFailed; | ||
| 384 | }, | 380 | }, |
| 385 | else => |e| return e, | 381 | else => |e| return e, |
| 386 | }; | 382 | }; |
| 387 | if (!res) { | 383 | if (!res) { |
| 388 | std.debug.print( | 384 | return step.fail( |
| 389 | \\ | 385 | \\ |
| 390 | \\========= Comparison failed for action: =========== | 386 | \\========= comparison failed for action: =========== |
| 391 | \\{s} {} | 387 | \\{s} {} |
| 392 | \\========= From parsed file: ======================= | 388 | \\========= from parsed file: ======================= |
| 393 | \\{s} | 389 | \\{s} |
| 394 | \\ | 390 | \\=================================================== |
| 395 | , .{ act.phrase, act.expected.?, output }); | 391 | , .{ act.phrase, act.expected.?, output }); |
| 396 | return error.TestFailed; | ||
| 397 | } | 392 | } |
| 398 | }, | 393 | }, |
| 399 | } | 394 | } |
| ... | @@ -402,7 +397,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -402,7 +397,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 402 | } | 397 | } |
| 403 | 398 | ||
| 404 | const Opts = struct { | 399 | const Opts = struct { |
| 405 | gpa: ?Allocator = null, | ||
| 406 | dump_symtab: bool = false, | 400 | dump_symtab: bool = false, |
| 407 | }; | 401 | }; |
| 408 | 402 | ||
| ... | @@ -410,8 +404,8 @@ const MachODumper = struct { | ... | @@ -410,8 +404,8 @@ const MachODumper = struct { |
| 410 | const LoadCommandIterator = macho.LoadCommandIterator; | 404 | const LoadCommandIterator = macho.LoadCommandIterator; |
| 411 | const symtab_label = "symtab"; | 405 | const symtab_label = "symtab"; |
| 412 | 406 | ||
| 413 | fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { | 407 | fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { |
| 414 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | 408 | const gpa = step.owner.allocator; |
| 415 | var stream = std.io.fixedBufferStream(bytes); | 409 | var stream = std.io.fixedBufferStream(bytes); |
| 416 | const reader = stream.reader(); | 410 | const reader = stream.reader(); |
| 417 | 411 | ||
| ... | @@ -693,8 +687,8 @@ const MachODumper = struct { | ... | @@ -693,8 +687,8 @@ const MachODumper = struct { |
| 693 | const WasmDumper = struct { | 687 | const WasmDumper = struct { |
| 694 | const symtab_label = "symbols"; | 688 | const symtab_label = "symbols"; |
| 695 | 689 | ||
| 696 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | 690 | fn parseAndDump(step: *Step, bytes: []const u8, opts: Opts) ![]const u8 { |
| 697 | const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator | 691 | const gpa = step.owner.allocator; |
| 698 | if (opts.dump_symtab) { | 692 | if (opts.dump_symtab) { |
| 699 | @panic("TODO: Implement symbol table parsing and dumping"); | 693 | @panic("TODO: Implement symbol table parsing and dumping"); |
| 700 | } | 694 | } |
| ... | @@ -715,20 +709,24 @@ const WasmDumper = struct { | ... | @@ -715,20 +709,24 @@ const WasmDumper = struct { |
| 715 | const writer = output.writer(); | 709 | const writer = output.writer(); |
| 716 | 710 | ||
| 717 | while (reader.readByte()) |current_byte| { | 711 | while (reader.readByte()) |current_byte| { |
| 718 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { | 712 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { |
| 719 | std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); | 713 | return step.fail("Found invalid section id '{d}'", .{current_byte}); |
| 720 | return err; | ||
| 721 | }; | 714 | }; |
| 722 | 715 | ||
| 723 | const section_length = try std.leb.readULEB128(u32, reader); | 716 | const section_length = try std.leb.readULEB128(u32, reader); |
| 724 | try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); | 717 | try parseAndDumpSection(step, section, bytes[fbs.pos..][0..section_length], writer); |
| 725 | fbs.pos += section_length; | 718 | fbs.pos += section_length; |
| 726 | } else |_| {} // reached end of stream | 719 | } else |_| {} // reached end of stream |
| 727 | 720 | ||
| 728 | return output.toOwnedSlice(); | 721 | return output.toOwnedSlice(); |
| 729 | } | 722 | } |
| 730 | 723 | ||
| 731 | fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { | 724 | fn parseAndDumpSection( |
| 725 | step: *Step, | ||
| 726 | section: std.wasm.Section, | ||
| 727 | data: []const u8, | ||
| 728 | writer: anytype, | ||
| 729 | ) !void { | ||
| 732 | var fbs = std.io.fixedBufferStream(data); | 730 | var fbs = std.io.fixedBufferStream(data); |
| 733 | const reader = fbs.reader(); | 731 | const reader = fbs.reader(); |
| 734 | 732 | ||
| ... | @@ -751,7 +749,7 @@ const WasmDumper = struct { | ... | @@ -751,7 +749,7 @@ const WasmDumper = struct { |
| 751 | => { | 749 | => { |
| 752 | const entries = try std.leb.readULEB128(u32, reader); | 750 | const entries = try std.leb.readULEB128(u32, reader); |
| 753 | try writer.print("\nentries {d}\n", .{entries}); | 751 | try writer.print("\nentries {d}\n", .{entries}); |
| 754 | try dumpSection(section, data[fbs.pos..], entries, writer); | 752 | try dumpSection(step, section, data[fbs.pos..], entries, writer); |
| 755 | }, | 753 | }, |
| 756 | .custom => { | 754 | .custom => { |
| 757 | const name_length = try std.leb.readULEB128(u32, reader); | 755 | const name_length = try std.leb.readULEB128(u32, reader); |
| ... | @@ -760,7 +758,7 @@ const WasmDumper = struct { | ... | @@ -760,7 +758,7 @@ const WasmDumper = struct { |
| 760 | try writer.print("\nname {s}\n", .{name}); | 758 | try writer.print("\nname {s}\n", .{name}); |
| 761 | 759 | ||
| 762 | if (mem.eql(u8, name, "name")) { | 760 | if (mem.eql(u8, name, "name")) { |
| 763 | try parseDumpNames(reader, writer, data); | 761 | try parseDumpNames(step, reader, writer, data); |
| 764 | } else if (mem.eql(u8, name, "producers")) { | 762 | } else if (mem.eql(u8, name, "producers")) { |
| 765 | try parseDumpProducers(reader, writer, data); | 763 | try parseDumpProducers(reader, writer, data); |
| 766 | } else if (mem.eql(u8, name, "target_features")) { | 764 | } else if (mem.eql(u8, name, "target_features")) { |
| ... | @@ -776,7 +774,7 @@ const WasmDumper = struct { | ... | @@ -776,7 +774,7 @@ const WasmDumper = struct { |
| 776 | } | 774 | } |
| 777 | } | 775 | } |
| 778 | 776 | ||
| 779 | fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | 777 | fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 780 | var fbs = std.io.fixedBufferStream(data); | 778 | var fbs = std.io.fixedBufferStream(data); |
| 781 | const reader = fbs.reader(); | 779 | const reader = fbs.reader(); |
| 782 | 780 | ||
| ... | @@ -786,19 +784,18 @@ const WasmDumper = struct { | ... | @@ -786,19 +784,18 @@ const WasmDumper = struct { |
| 786 | while (i < entries) : (i += 1) { | 784 | while (i < entries) : (i += 1) { |
| 787 | const func_type = try reader.readByte(); | 785 | const func_type = try reader.readByte(); |
| 788 | if (func_type != std.wasm.function_type) { | 786 | if (func_type != std.wasm.function_type) { |
| 789 | std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); | 787 | return step.fail("expected function type, found byte '{d}'", .{func_type}); |
| 790 | return error.UnexpectedByte; | ||
| 791 | } | 788 | } |
| 792 | const params = try std.leb.readULEB128(u32, reader); | 789 | const params = try std.leb.readULEB128(u32, reader); |
| 793 | try writer.print("params {d}\n", .{params}); | 790 | try writer.print("params {d}\n", .{params}); |
| 794 | var index: u32 = 0; | 791 | var index: u32 = 0; |
| 795 | while (index < params) : (index += 1) { | 792 | while (index < params) : (index += 1) { |
| 796 | try parseDumpType(std.wasm.Valtype, reader, writer); | 793 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 797 | } else index = 0; | 794 | } else index = 0; |
| 798 | const returns = try std.leb.readULEB128(u32, reader); | 795 | const returns = try std.leb.readULEB128(u32, reader); |
| 799 | try writer.print("returns {d}\n", .{returns}); | 796 | try writer.print("returns {d}\n", .{returns}); |
| 800 | while (index < returns) : (index += 1) { | 797 | while (index < returns) : (index += 1) { |
| 801 | try parseDumpType(std.wasm.Valtype, reader, writer); | 798 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 802 | } | 799 | } |
| 803 | } | 800 | } |
| 804 | }, | 801 | }, |
| ... | @@ -812,9 +809,8 @@ const WasmDumper = struct { | ... | @@ -812,9 +809,8 @@ const WasmDumper = struct { |
| 812 | const name = data[fbs.pos..][0..name_len]; | 809 | const name = data[fbs.pos..][0..name_len]; |
| 813 | fbs.pos += name_len; | 810 | fbs.pos += name_len; |
| 814 | 811 | ||
| 815 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { | 812 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch { |
| 816 | std.debug.print("Invalid import kind\n", .{}); | 813 | return step.fail("invalid import kind", .{}); |
| 817 | return err; | ||
| 818 | }; | 814 | }; |
| 819 | 815 | ||
| 820 | try writer.print( | 816 | try writer.print( |
| ... | @@ -831,11 +827,11 @@ const WasmDumper = struct { | ... | @@ -831,11 +827,11 @@ const WasmDumper = struct { |
| 831 | try parseDumpLimits(reader, writer); | 827 | try parseDumpLimits(reader, writer); |
| 832 | }, | 828 | }, |
| 833 | .global => { | 829 | .global => { |
| 834 | try parseDumpType(std.wasm.Valtype, reader, writer); | 830 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 835 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); | 831 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); |
| 836 | }, | 832 | }, |
| 837 | .table => { | 833 | .table => { |
| 838 | try parseDumpType(std.wasm.RefType, reader, writer); | 834 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 839 | try parseDumpLimits(reader, writer); | 835 | try parseDumpLimits(reader, writer); |
| 840 | }, | 836 | }, |
| 841 | } | 837 | } |
| ... | @@ -850,7 +846,7 @@ const WasmDumper = struct { | ... | @@ -850,7 +846,7 @@ const WasmDumper = struct { |
| 850 | .table => { | 846 | .table => { |
| 851 | var i: u32 = 0; | 847 | var i: u32 = 0; |
| 852 | while (i < entries) : (i += 1) { | 848 | while (i < entries) : (i += 1) { |
| 853 | try parseDumpType(std.wasm.RefType, reader, writer); | 849 | try parseDumpType(step, std.wasm.RefType, reader, writer); |
| 854 | try parseDumpLimits(reader, writer); | 850 | try parseDumpLimits(reader, writer); |
| 855 | } | 851 | } |
| 856 | }, | 852 | }, |
| ... | @@ -863,9 +859,9 @@ const WasmDumper = struct { | ... | @@ -863,9 +859,9 @@ const WasmDumper = struct { |
| 863 | .global => { | 859 | .global => { |
| 864 | var i: u32 = 0; | 860 | var i: u32 = 0; |
| 865 | while (i < entries) : (i += 1) { | 861 | while (i < entries) : (i += 1) { |
| 866 | try parseDumpType(std.wasm.Valtype, reader, writer); | 862 | try parseDumpType(step, std.wasm.Valtype, reader, writer); |
| 867 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); | 863 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); |
| 868 | try parseDumpInit(reader, writer); | 864 | try parseDumpInit(step, reader, writer); |
| 869 | } | 865 | } |
| 870 | }, | 866 | }, |
| 871 | .@"export" => { | 867 | .@"export" => { |
| ... | @@ -875,9 +871,8 @@ const WasmDumper = struct { | ... | @@ -875,9 +871,8 @@ const WasmDumper = struct { |
| 875 | const name = data[fbs.pos..][0..name_len]; | 871 | const name = data[fbs.pos..][0..name_len]; |
| 876 | fbs.pos += name_len; | 872 | fbs.pos += name_len; |
| 877 | const kind_byte = try std.leb.readULEB128(u8, reader); | 873 | const kind_byte = try std.leb.readULEB128(u8, reader); |
| 878 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { | 874 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch { |
| 879 | std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); | 875 | return step.fail("invalid export kind value '{d}'", .{kind_byte}); |
| 880 | return err; | ||
| 881 | }; | 876 | }; |
| 882 | const index = try std.leb.readULEB128(u32, reader); | 877 | const index = try std.leb.readULEB128(u32, reader); |
| 883 | try writer.print( | 878 | try writer.print( |
| ... | @@ -892,7 +887,7 @@ const WasmDumper = struct { | ... | @@ -892,7 +887,7 @@ const WasmDumper = struct { |
| 892 | var i: u32 = 0; | 887 | var i: u32 = 0; |
| 893 | while (i < entries) : (i += 1) { | 888 | while (i < entries) : (i += 1) { |
| 894 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | 889 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); |
| 895 | try parseDumpInit(reader, writer); | 890 | try parseDumpInit(step, reader, writer); |
| 896 | 891 | ||
| 897 | const function_indexes = try std.leb.readULEB128(u32, reader); | 892 | const function_indexes = try std.leb.readULEB128(u32, reader); |
| 898 | var function_index: u32 = 0; | 893 | var function_index: u32 = 0; |
| ... | @@ -908,7 +903,7 @@ const WasmDumper = struct { | ... | @@ -908,7 +903,7 @@ const WasmDumper = struct { |
| 908 | while (i < entries) : (i += 1) { | 903 | while (i < entries) : (i += 1) { |
| 909 | const index = try std.leb.readULEB128(u32, reader); | 904 | const index = try std.leb.readULEB128(u32, reader); |
| 910 | try writer.print("memory index 0x{x}\n", .{index}); | 905 | try writer.print("memory index 0x{x}\n", .{index}); |
| 911 | try parseDumpInit(reader, writer); | 906 | try parseDumpInit(step, reader, writer); |
| 912 | const size = try std.leb.readULEB128(u32, reader); | 907 | const size = try std.leb.readULEB128(u32, reader); |
| 913 | try writer.print("size {d}\n", .{size}); | 908 | try writer.print("size {d}\n", .{size}); |
| 914 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments | 909 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments |
| ... | @@ -918,11 +913,10 @@ const WasmDumper = struct { | ... | @@ -918,11 +913,10 @@ const WasmDumper = struct { |
| 918 | } | 913 | } |
| 919 | } | 914 | } |
| 920 | 915 | ||
| 921 | fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { | 916 | fn parseDumpType(step: *Step, comptime WasmType: type, reader: anytype, writer: anytype) !void { |
| 922 | const type_byte = try reader.readByte(); | 917 | const type_byte = try reader.readByte(); |
| 923 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { | 918 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch { |
| 924 | std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); | 919 | return step.fail("Invalid wasm type value '{d}'", .{type_byte}); |
| 925 | return err; | ||
| 926 | }; | 920 | }; |
| 927 | try writer.print("type {s}\n", .{@tagName(valtype)}); | 921 | try writer.print("type {s}\n", .{@tagName(valtype)}); |
| 928 | } | 922 | } |
| ... | @@ -937,11 +931,10 @@ const WasmDumper = struct { | ... | @@ -937,11 +931,10 @@ const WasmDumper = struct { |
| 937 | } | 931 | } |
| 938 | } | 932 | } |
| 939 | 933 | ||
| 940 | fn parseDumpInit(reader: anytype, writer: anytype) !void { | 934 | fn parseDumpInit(step: *Step, reader: anytype, writer: anytype) !void { |
| 941 | const byte = try std.leb.readULEB128(u8, reader); | 935 | const byte = try std.leb.readULEB128(u8, reader); |
| 942 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { | 936 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch { |
| 943 | std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); | 937 | return step.fail("invalid wasm opcode '{d}'", .{byte}); |
| 944 | return err; | ||
| 945 | }; | 938 | }; |
| 946 | switch (opcode) { | 939 | switch (opcode) { |
| 947 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), | 940 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), |
| ... | @@ -953,14 +946,13 @@ const WasmDumper = struct { | ... | @@ -953,14 +946,13 @@ const WasmDumper = struct { |
| 953 | } | 946 | } |
| 954 | const end_opcode = try std.leb.readULEB128(u8, reader); | 947 | const end_opcode = try std.leb.readULEB128(u8, reader); |
| 955 | if (end_opcode != std.wasm.opcode(.end)) { | 948 | if (end_opcode != std.wasm.opcode(.end)) { |
| 956 | std.debug.print("expected 'end' opcode in init expression\n", .{}); | 949 | return step.fail("expected 'end' opcode in init expression", .{}); |
| 957 | return error.MissingEndOpcode; | ||
| 958 | } | 950 | } |
| 959 | } | 951 | } |
| 960 | 952 | ||
| 961 | fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { | 953 | fn parseDumpNames(step: *Step, reader: anytype, writer: anytype, data: []const u8) !void { |
| 962 | while (reader.context.pos < data.len) { | 954 | while (reader.context.pos < data.len) { |
| 963 | try parseDumpType(std.wasm.NameSubsection, reader, writer); | 955 | try parseDumpType(step, std.wasm.NameSubsection, reader, writer); |
| 964 | const size = try std.leb.readULEB128(u32, reader); | 956 | const size = try std.leb.readULEB128(u32, reader); |
| 965 | const entries = try std.leb.readULEB128(u32, reader); | 957 | const entries = try std.leb.readULEB128(u32, reader); |
| 966 | try writer.print( | 958 | try writer.print( |
lib/std/Build/CompileStep.zig+1-2| ... | @@ -538,8 +538,7 @@ pub fn run(cs: *CompileStep) *RunStep { | ... | @@ -538,8 +538,7 @@ pub fn run(cs: *CompileStep) *RunStep { |
| 538 | } | 538 | } |
| 539 | 539 | ||
| 540 | pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | 540 | pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { |
| 541 | const b = self.step.owner; | 541 | return CheckObjectStep.create(self.step.owner, self.getOutputSource(), obj_format); |
| 542 | return CheckObjectStep.create(b, self.getOutputSource(), obj_format); | ||
| 543 | } | 542 | } |
| 544 | 543 | ||
| 545 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { | 544 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { |
lib/std/Build/ConfigHeaderStep.zig+13-10| ... | @@ -192,13 +192,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -192,13 +192,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 192 | try output.appendSlice(c_generated_line); | 192 | try output.appendSlice(c_generated_line); |
| 193 | const src_path = file_source.getPath(b); | 193 | const src_path = file_source.getPath(b); |
| 194 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 194 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); |
| 195 | try render_autoconf(contents, &output, self.values, src_path); | 195 | try render_autoconf(step, contents, &output, self.values, src_path); |
| 196 | }, | 196 | }, |
| 197 | .cmake => |file_source| { | 197 | .cmake => |file_source| { |
| 198 | try output.appendSlice(c_generated_line); | 198 | try output.appendSlice(c_generated_line); |
| 199 | const src_path = file_source.getPath(b); | 199 | const src_path = file_source.getPath(b); |
| 200 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | 200 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); |
| 201 | try render_cmake(contents, &output, self.values, src_path); | 201 | try render_cmake(step, contents, &output, self.values, src_path); |
| 202 | }, | 202 | }, |
| 203 | .blank => { | 203 | .blank => { |
| 204 | try output.appendSlice(c_generated_line); | 204 | try output.appendSlice(c_generated_line); |
| ... | @@ -234,8 +234,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -234,8 +234,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 234 | output_dir; | 234 | output_dir; |
| 235 | 235 | ||
| 236 | var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { | 236 | var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { |
| 237 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 237 | return step.fail("unable to make path '{s}': {s}", .{ output_dir, @errorName(err) }); |
| 238 | return err; | ||
| 239 | }; | 238 | }; |
| 240 | defer dir.close(); | 239 | defer dir.close(); |
| 241 | 240 | ||
| ... | @@ -247,6 +246,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -247,6 +246,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 247 | } | 246 | } |
| 248 | 247 | ||
| 249 | fn render_autoconf( | 248 | fn render_autoconf( |
| 249 | step: *Step, | ||
| 250 | contents: []const u8, | 250 | contents: []const u8, |
| 251 | output: *std.ArrayList(u8), | 251 | output: *std.ArrayList(u8), |
| 252 | values: std.StringArrayHashMap(Value), | 252 | values: std.StringArrayHashMap(Value), |
| ... | @@ -273,7 +273,7 @@ fn render_autoconf( | ... | @@ -273,7 +273,7 @@ fn render_autoconf( |
| 273 | } | 273 | } |
| 274 | const name = it.rest(); | 274 | const name = it.rest(); |
| 275 | const kv = values_copy.fetchSwapRemove(name) orelse { | 275 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 276 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 276 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 277 | src_path, line_index + 1, name, | 277 | src_path, line_index + 1, name, |
| 278 | }); | 278 | }); |
| 279 | any_errors = true; | 279 | any_errors = true; |
| ... | @@ -283,15 +283,17 @@ fn render_autoconf( | ... | @@ -283,15 +283,17 @@ fn render_autoconf( |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | for (values_copy.keys()) |name| { | 285 | for (values_copy.keys()) |name| { |
| 286 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 286 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 287 | any_errors = true; | ||
| 287 | } | 288 | } |
| 288 | 289 | ||
| 289 | if (any_errors) { | 290 | if (any_errors) { |
| 290 | return error.HeaderConfigFailed; | 291 | return error.MakeFailed; |
| 291 | } | 292 | } |
| 292 | } | 293 | } |
| 293 | 294 | ||
| 294 | fn render_cmake( | 295 | fn render_cmake( |
| 296 | step: *Step, | ||
| 295 | contents: []const u8, | 297 | contents: []const u8, |
| 296 | output: *std.ArrayList(u8), | 298 | output: *std.ArrayList(u8), |
| 297 | values: std.StringArrayHashMap(Value), | 299 | values: std.StringArrayHashMap(Value), |
| ... | @@ -317,14 +319,14 @@ fn render_cmake( | ... | @@ -317,14 +319,14 @@ fn render_cmake( |
| 317 | continue; | 319 | continue; |
| 318 | } | 320 | } |
| 319 | const name = it.next() orelse { | 321 | const name = it.next() orelse { |
| 320 | std.debug.print("{s}:{d}: error: missing define name\n", .{ | 322 | try step.addError("{s}:{d}: error: missing define name", .{ |
| 321 | src_path, line_index + 1, | 323 | src_path, line_index + 1, |
| 322 | }); | 324 | }); |
| 323 | any_errors = true; | 325 | any_errors = true; |
| 324 | continue; | 326 | continue; |
| 325 | }; | 327 | }; |
| 326 | const kv = values_copy.fetchSwapRemove(name) orelse { | 328 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 327 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 329 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| 328 | src_path, line_index + 1, name, | 330 | src_path, line_index + 1, name, |
| 329 | }); | 331 | }); |
| 330 | any_errors = true; | 332 | any_errors = true; |
| ... | @@ -334,7 +336,8 @@ fn render_cmake( | ... | @@ -334,7 +336,8 @@ fn render_cmake( |
| 334 | } | 336 | } |
| 335 | 337 | ||
| 336 | for (values_copy.keys()) |name| { | 338 | for (values_copy.keys()) |name| { |
| 337 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | 339 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| 340 | any_errors = true; | ||
| 338 | } | 341 | } |
| 339 | 342 | ||
| 340 | if (any_errors) { | 343 | if (any_errors) { |
lib/std/Build/InstallArtifactStep.zig+7-1| ... | @@ -12,6 +12,9 @@ artifact: *CompileStep, | ... | @@ -12,6 +12,9 @@ artifact: *CompileStep, |
| 12 | dest_dir: InstallDir, | 12 | dest_dir: InstallDir, |
| 13 | pdb_dir: ?InstallDir, | 13 | pdb_dir: ?InstallDir, |
| 14 | h_dir: ?InstallDir, | 14 | h_dir: ?InstallDir, |
| 15 | /// If non-null, adds additional path components relative to dest_dir, and | ||
| 16 | /// overrides the basename of the CompileStep. | ||
| 17 | dest_sub_path: ?[]const u8, | ||
| 15 | 18 | ||
| 16 | pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { | 19 | pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { |
| 17 | if (artifact.install_step) |s| return s; | 20 | if (artifact.install_step) |s| return s; |
| ... | @@ -40,6 +43,7 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { | ... | @@ -40,6 +43,7 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { |
| 40 | } | 43 | } |
| 41 | } else null, | 44 | } else null, |
| 42 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, | 45 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, |
| 46 | .dest_sub_path = null, | ||
| 43 | }; | 47 | }; |
| 44 | self.step.dependOn(&artifact.step); | 48 | self.step.dependOn(&artifact.step); |
| 45 | artifact.install_step = self; | 49 | artifact.install_step = self; |
| ... | @@ -71,7 +75,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -71,7 +75,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 71 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); | 75 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); |
| 72 | const dest_builder = self.dest_builder; | 76 | const dest_builder = self.dest_builder; |
| 73 | 77 | ||
| 74 | const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename); | 78 | const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; |
| 79 | const full_dest_path = dest_builder.getInstallPath(self.dest_dir, dest_sub_path); | ||
| 80 | |||
| 75 | try src_builder.updateFile( | 81 | try src_builder.updateFile( |
| 76 | self.artifact.getOutputSource().getPath(src_builder), | 82 | self.artifact.getOutputSource().getPath(src_builder), |
| 77 | full_dest_path, | 83 | full_dest_path, |
lib/std/Build/ObjCopyStep.zig+1-2| ... | @@ -95,8 +95,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -95,8 +95,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 95 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); | 95 | const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); |
| 96 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 96 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 97 | b.cache_root.handle.makePath(cache_path) catch |err| { | 97 | b.cache_root.handle.makePath(cache_path) catch |err| { |
| 98 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 98 | return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) }); |
| 99 | return err; | ||
| 100 | }; | 99 | }; |
| 101 | 100 | ||
| 102 | var argv = std.ArrayList([]const u8).init(b.allocator); | 101 | var argv = std.ArrayList([]const u8).init(b.allocator); |
lib/std/Build/RunStep.zig+267-105| ... | @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; | ... | @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; |
| 10 | const EnvMap = process.EnvMap; | 10 | const EnvMap = process.EnvMap; |
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | const ExecError = std.Build.ExecError; | 12 | const ExecError = std.Build.ExecError; |
| 13 | const assert = std.debug.assert; | ||
| 13 | 14 | ||
| 14 | const RunStep = @This(); | 15 | const RunStep = @This(); |
| 15 | 16 | ||
| ... | @@ -54,6 +55,8 @@ rename_step_with_output_arg: bool = true, | ... | @@ -54,6 +55,8 @@ rename_step_with_output_arg: bool = true, |
| 54 | /// Command-line arguments such as -fqemu and -fwasmtime may affect whether a | 55 | /// Command-line arguments such as -fqemu and -fwasmtime may affect whether a |
| 55 | /// binary is detected as foreign, as well as system configuration such as | 56 | /// binary is detected as foreign, as well as system configuration such as |
| 56 | /// Rosetta (macOS) and binfmt_misc (Linux). | 57 | /// Rosetta (macOS) and binfmt_misc (Linux). |
| 58 | /// If this RunStep is considered to have side-effects, then this flag does | ||
| 59 | /// nothing. | ||
| 57 | skip_foreign_checks: bool = false, | 60 | skip_foreign_checks: bool = false, |
| 58 | 61 | ||
| 59 | /// If stderr or stdout exceeds this amount, the child process is killed and | 62 | /// If stderr or stdout exceeds this amount, the child process is killed and |
| ... | @@ -79,7 +82,7 @@ pub const StdIo = union(enum) { | ... | @@ -79,7 +82,7 @@ pub const StdIo = union(enum) { |
| 79 | /// conditions. | 82 | /// conditions. |
| 80 | /// Note that an explicit check for exit code 0 needs to be added to this | 83 | /// Note that an explicit check for exit code 0 needs to be added to this |
| 81 | /// list if such a check is desireable. | 84 | /// list if such a check is desireable. |
| 82 | check: []const Check, | 85 | check: std.ArrayList(Check), |
| 83 | 86 | ||
| 84 | pub const Check = union(enum) { | 87 | pub const Check = union(enum) { |
| 85 | expect_stderr_exact: []const u8, | 88 | expect_stderr_exact: []const u8, |
| ... | @@ -214,14 +217,20 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 | ... | @@ -214,14 +217,20 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 |
| 214 | env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); | 217 | env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error"); |
| 215 | } | 218 | } |
| 216 | 219 | ||
| 220 | /// Adds a check for exact stderr match. Does not add any other checks. | ||
| 217 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | 221 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { |
| 218 | const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; | 222 | const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) }; |
| 219 | self.addCheck(new_check); | 223 | self.addCheck(new_check); |
| 220 | } | 224 | } |
| 221 | 225 | ||
| 226 | /// Adds a check for exact stdout match as well as a check for exit code 0, if | ||
| 227 | /// there is not already an expected termination check. | ||
| 222 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | 228 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { |
| 223 | const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; | 229 | const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) }; |
| 224 | self.addCheck(new_check); | 230 | self.addCheck(new_check); |
| 231 | if (!self.hasTermCheck()) { | ||
| 232 | self.expectExitCode(0); | ||
| 233 | } | ||
| 225 | } | 234 | } |
| 226 | 235 | ||
| 227 | pub fn expectExitCode(self: *RunStep, code: u8) void { | 236 | pub fn expectExitCode(self: *RunStep, code: u8) void { |
| ... | @@ -229,19 +238,21 @@ pub fn expectExitCode(self: *RunStep, code: u8) void { | ... | @@ -229,19 +238,21 @@ pub fn expectExitCode(self: *RunStep, code: u8) void { |
| 229 | self.addCheck(new_check); | 238 | self.addCheck(new_check); |
| 230 | } | 239 | } |
| 231 | 240 | ||
| 241 | pub fn hasTermCheck(self: RunStep) bool { | ||
| 242 | for (self.stdio.check.items) |check| switch (check) { | ||
| 243 | .expect_term => return true, | ||
| 244 | else => continue, | ||
| 245 | }; | ||
| 246 | return false; | ||
| 247 | } | ||
| 248 | |||
| 232 | pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { | 249 | pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void { |
| 233 | const arena = self.step.owner.allocator; | ||
| 234 | switch (self.stdio) { | 250 | switch (self.stdio) { |
| 235 | .infer_from_args => { | 251 | .infer_from_args => { |
| 236 | const list = arena.create([1]StdIo.Check) catch @panic("OOM"); | 252 | self.stdio = .{ .check = std.ArrayList(StdIo.Check).init(self.step.owner.allocator) }; |
| 237 | list.* = .{new_check}; | 253 | self.stdio.check.append(new_check) catch @panic("OOM"); |
| 238 | self.stdio = .{ .check = list }; | ||
| 239 | }, | ||
| 240 | .check => |checks| { | ||
| 241 | const new_list = arena.alloc(StdIo.Check, checks.len + 1) catch @panic("OOM"); | ||
| 242 | std.mem.copy(StdIo.Check, new_list, checks); | ||
| 243 | new_list[checks.len] = new_check; | ||
| 244 | }, | 254 | }, |
| 255 | .check => |*checks| checks.append(new_check) catch @panic("OOM"), | ||
| 245 | else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), | 256 | else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"), |
| 246 | } | 257 | } |
| 247 | } | 258 | } |
| ... | @@ -298,14 +309,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -298,14 +309,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 298 | _ = prog_node; | 309 | _ = prog_node; |
| 299 | 310 | ||
| 300 | const b = step.owner; | 311 | const b = step.owner; |
| 312 | const arena = b.allocator; | ||
| 301 | const self = @fieldParentPtr(RunStep, "step", step); | 313 | const self = @fieldParentPtr(RunStep, "step", step); |
| 302 | const has_side_effects = self.hasSideEffects(); | 314 | const has_side_effects = self.hasSideEffects(); |
| 303 | 315 | ||
| 304 | var argv_list = ArrayList([]const u8).init(b.allocator); | 316 | var argv_list = ArrayList([]const u8).init(arena); |
| 305 | var output_placeholders = ArrayList(struct { | 317 | var output_placeholders = ArrayList(struct { |
| 306 | index: usize, | 318 | index: usize, |
| 307 | output: Arg.Output, | 319 | output: Arg.Output, |
| 308 | }).init(b.allocator); | 320 | }).init(arena); |
| 309 | 321 | ||
| 310 | var man = b.cache.obtain(); | 322 | var man = b.cache.obtain(); |
| 311 | defer man.deinit(); | 323 | defer man.deinit(); |
| ... | @@ -357,7 +369,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -357,7 +369,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 357 | const digest = man.final(); | 369 | const digest = man.final(); |
| 358 | for (output_placeholders.items) |placeholder| { | 370 | for (output_placeholders.items) |placeholder| { |
| 359 | placeholder.output.generated_file.path = try b.cache_root.join( | 371 | placeholder.output.generated_file.path = try b.cache_root.join( |
| 360 | b.allocator, | 372 | arena, |
| 361 | &.{ "o", &digest, placeholder.output.basename }, | 373 | &.{ "o", &digest, placeholder.output.basename }, |
| 362 | ); | 374 | ); |
| 363 | } | 375 | } |
| ... | @@ -367,30 +379,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -367,30 +379,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 367 | const digest = man.final(); | 379 | const digest = man.final(); |
| 368 | 380 | ||
| 369 | for (output_placeholders.items) |placeholder| { | 381 | for (output_placeholders.items) |placeholder| { |
| 370 | const output_path = try b.cache_root.join( | 382 | const output_components = .{ "o", &digest, placeholder.output.basename }; |
| 371 | b.allocator, | 383 | const output_sub_path = try fs.path.join(arena, &output_components); |
| 372 | &.{ "o", &digest, placeholder.output.basename }, | 384 | const output_sub_dir_path = fs.path.dirname(output_sub_path).?; |
| 373 | ); | 385 | b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { |
| 374 | const output_dir = fs.path.dirname(output_path).?; | 386 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 375 | fs.cwd().makePath(output_dir) catch |err| { | 387 | b.cache_root, output_sub_dir_path, @errorName(err), |
| 376 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | 388 | }); |
| 377 | return err; | ||
| 378 | }; | 389 | }; |
| 379 | 390 | const output_path = try b.cache_root.join(arena, &output_components); | |
| 380 | placeholder.output.generated_file.path = output_path; | 391 | placeholder.output.generated_file.path = output_path; |
| 381 | argv_list.items[placeholder.index] = output_path; | 392 | argv_list.items[placeholder.index] = output_path; |
| 382 | } | 393 | } |
| 383 | } | 394 | } |
| 384 | 395 | ||
| 385 | try runCommand( | 396 | try runCommand(self, argv_list.items, has_side_effects); |
| 386 | step, | ||
| 387 | self.cwd, | ||
| 388 | argv_list.items, | ||
| 389 | self.env_map, | ||
| 390 | self.stdio, | ||
| 391 | has_side_effects, | ||
| 392 | self.max_stdio_size, | ||
| 393 | ); | ||
| 394 | 397 | ||
| 395 | if (!has_side_effects) { | 398 | if (!has_side_effects) { |
| 396 | try man.writeManifest(); | 399 | try man.writeManifest(); |
| ... | @@ -442,92 +445,150 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) | ... | @@ -442,92 +445,150 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) |
| 442 | }; | 445 | }; |
| 443 | } | 446 | } |
| 444 | 447 | ||
| 445 | fn runCommand( | 448 | fn runCommand(self: *RunStep, argv: []const []const u8, has_side_effects: bool) !void { |
| 446 | step: *Step, | 449 | const step = &self.step; |
| 447 | opt_cwd: ?[]const u8, | ||
| 448 | argv: []const []const u8, | ||
| 449 | env_map: ?*EnvMap, | ||
| 450 | stdio: StdIo, | ||
| 451 | has_side_effects: bool, | ||
| 452 | max_stdio_size: usize, | ||
| 453 | ) !void { | ||
| 454 | const b = step.owner; | 450 | const b = step.owner; |
| 455 | const arena = b.allocator; | 451 | const arena = b.allocator; |
| 456 | const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; | ||
| 457 | 452 | ||
| 458 | try step.handleChildProcUnsupported(opt_cwd, argv); | 453 | try step.handleChildProcUnsupported(self.cwd, argv); |
| 459 | try Step.handleVerbose(step.owner, opt_cwd, argv); | 454 | try Step.handleVerbose(step.owner, self.cwd, argv); |
| 460 | |||
| 461 | var child = std.ChildProcess.init(argv, arena); | ||
| 462 | child.cwd = cwd; | ||
| 463 | child.env_map = env_map orelse b.env_map; | ||
| 464 | |||
| 465 | child.stdin_behavior = switch (stdio) { | ||
| 466 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 467 | .inherit => .Inherit, | ||
| 468 | .check => .Close, | ||
| 469 | }; | ||
| 470 | child.stdout_behavior = switch (stdio) { | ||
| 471 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 472 | .inherit => .Inherit, | ||
| 473 | .check => |checks| if (checksContainStdout(checks)) .Pipe else .Ignore, | ||
| 474 | }; | ||
| 475 | child.stderr_behavior = switch (stdio) { | ||
| 476 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, | ||
| 477 | .inherit => .Inherit, | ||
| 478 | .check => .Pipe, | ||
| 479 | }; | ||
| 480 | |||
| 481 | child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{ | ||
| 482 | argv[0], @errorName(err), | ||
| 483 | }); | ||
| 484 | 455 | ||
| 485 | var stdout_bytes: ?[]const u8 = null; | 456 | var stdout_bytes: ?[]const u8 = null; |
| 486 | var stderr_bytes: ?[]const u8 = null; | 457 | var stderr_bytes: ?[]const u8 = null; |
| 487 | 458 | ||
| 488 | if (child.stdout) |stdout| { | 459 | const term = spawnChildAndCollect(self, argv, &stdout_bytes, &stderr_bytes, has_side_effects) catch |err| term: { |
| 489 | if (child.stderr) |stderr| { | 460 | if (err == error.InvalidExe) interpret: { |
| 490 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ | 461 | // TODO: learn the target from the binary directly rather than from |
| 491 | .stdout = stdout, | 462 | // relying on it being a CompileStep. This will make this logic |
| 492 | .stderr = stderr, | 463 | // work even for the edge case that the binary was produced by a |
| 493 | }); | 464 | // third party. |
| 494 | defer poller.deinit(); | 465 | const exe = switch (self.argv.items[0]) { |
| 466 | .artifact => |exe| exe, | ||
| 467 | else => break :interpret, | ||
| 468 | }; | ||
| 469 | if (exe.kind != .exe) break :interpret; | ||
| 470 | |||
| 471 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); | ||
| 472 | defer interp_argv.deinit(); | ||
| 473 | |||
| 474 | const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; | ||
| 475 | switch (b.host.getExternalExecutor(exe.target_info, .{ | ||
| 476 | .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, | ||
| 477 | .link_libc = exe.is_linking_libc, | ||
| 478 | })) { | ||
| 479 | .native, .rosetta => { | ||
| 480 | if (self.stdio == .check and self.skip_foreign_checks) | ||
| 481 | return error.MakeSkipped; | ||
| 482 | |||
| 483 | break :interpret; | ||
| 484 | }, | ||
| 485 | .wine => |bin_name| { | ||
| 486 | if (b.enable_wine) { | ||
| 487 | try interp_argv.append(bin_name); | ||
| 488 | } else { | ||
| 489 | return failForeign(self, "-fwine", argv[0], exe); | ||
| 490 | } | ||
| 491 | }, | ||
| 492 | .qemu => |bin_name| { | ||
| 493 | if (b.enable_qemu) { | ||
| 494 | const glibc_dir_arg = if (need_cross_glibc) | ||
| 495 | b.glibc_runtimes_dir orelse return | ||
| 496 | else | ||
| 497 | null; | ||
| 498 | |||
| 499 | try interp_argv.append(bin_name); | ||
| 500 | |||
| 501 | if (glibc_dir_arg) |dir| { | ||
| 502 | // TODO look into making this a call to `linuxTriple`. This | ||
| 503 | // needs the directory to be called "i686" rather than | ||
| 504 | // "x86" which is why we do it manually here. | ||
| 505 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | ||
| 506 | const cpu_arch = exe.target.getCpuArch(); | ||
| 507 | const os_tag = exe.target.getOsTag(); | ||
| 508 | const abi = exe.target.getAbi(); | ||
| 509 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | ||
| 510 | "i686" | ||
| 511 | else | ||
| 512 | @tagName(cpu_arch); | ||
| 513 | const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ | ||
| 514 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | ||
| 515 | }); | ||
| 516 | |||
| 517 | try interp_argv.append("-L"); | ||
| 518 | try interp_argv.append(full_dir); | ||
| 519 | } | ||
| 520 | } else { | ||
| 521 | return failForeign(self, "-fqemu", argv[0], exe); | ||
| 522 | } | ||
| 523 | }, | ||
| 524 | .darling => |bin_name| { | ||
| 525 | if (b.enable_darling) { | ||
| 526 | try interp_argv.append(bin_name); | ||
| 527 | } else { | ||
| 528 | return failForeign(self, "-fdarling", argv[0], exe); | ||
| 529 | } | ||
| 530 | }, | ||
| 531 | .wasmtime => |bin_name| { | ||
| 532 | if (b.enable_wasmtime) { | ||
| 533 | try interp_argv.append(bin_name); | ||
| 534 | try interp_argv.append("--dir=."); | ||
| 535 | } else { | ||
| 536 | return failForeign(self, "-fwasmtime", argv[0], exe); | ||
| 537 | } | ||
| 538 | }, | ||
| 539 | .bad_dl => |foreign_dl| { | ||
| 540 | if (self.stdio == .check and self.skip_foreign_checks) | ||
| 541 | return error.MakeSkipped; | ||
| 542 | |||
| 543 | const host_dl = b.host.dynamic_linker.get() orelse "(none)"; | ||
| 495 | 544 | ||
| 496 | while (try poller.poll()) { | 545 | return step.fail( |
| 497 | if (poller.fifo(.stdout).count > max_stdio_size) | 546 | \\the host system is unable to execute binaries from the target |
| 498 | return error.StdoutStreamTooLong; | 547 | \\ because the host dynamic linker is '{s}', |
| 499 | if (poller.fifo(.stderr).count > max_stdio_size) | 548 | \\ while the target dynamic linker is '{s}'. |
| 500 | return error.StderrStreamTooLong; | 549 | \\ consider setting the dynamic linker or enabling skip_foreign_checks in the Run step |
| 550 | , .{ host_dl, foreign_dl }); | ||
| 551 | }, | ||
| 552 | .bad_os_or_cpu => { | ||
| 553 | if (self.stdio == .check and self.skip_foreign_checks) | ||
| 554 | return error.MakeSkipped; | ||
| 555 | |||
| 556 | const host_name = try b.host.target.zigTriple(b.allocator); | ||
| 557 | const foreign_name = try exe.target.zigTriple(b.allocator); | ||
| 558 | |||
| 559 | return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ | ||
| 560 | host_name, foreign_name, | ||
| 561 | }); | ||
| 562 | }, | ||
| 501 | } | 563 | } |
| 502 | 564 | ||
| 503 | stdout_bytes = try poller.fifo(.stdout).toOwnedSlice(); | 565 | if (exe.target.isWindows()) { |
| 504 | stderr_bytes = try poller.fifo(.stderr).toOwnedSlice(); | 566 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 505 | } else { | 567 | RunStep.addPathForDynLibsInternal(&self.step, b, exe); |
| 506 | stdout_bytes = try stdout.reader().readAllAlloc(arena, max_stdio_size); | 568 | } |
| 507 | } | ||
| 508 | } else if (child.stderr) |stderr| { | ||
| 509 | stderr_bytes = try stderr.reader().readAllAlloc(arena, max_stdio_size); | ||
| 510 | } | ||
| 511 | 569 | ||
| 512 | if (stderr_bytes) |stderr| if (stderr.len > 0) { | 570 | try interp_argv.append(argv[0]); |
| 513 | const stderr_is_diagnostic = switch (stdio) { | 571 | |
| 514 | .check => |checks| !checksContainStderr(checks), | 572 | try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); |
| 515 | else => true, | 573 | |
| 516 | }; | 574 | assert(stdout_bytes == null); |
| 517 | if (stderr_is_diagnostic) { | 575 | assert(stderr_bytes == null); |
| 518 | try step.result_error_msgs.append(arena, stderr); | 576 | break :term spawnChildAndCollect(self, interp_argv.items, &stdout_bytes, &stderr_bytes, has_side_effects) catch |inner_err| { |
| 577 | return step.fail("unable to spawn {s}: {s}", .{ | ||
| 578 | interp_argv.items[0], @errorName(inner_err), | ||
| 579 | }); | ||
| 580 | }; | ||
| 519 | } | 581 | } |
| 520 | }; | ||
| 521 | 582 | ||
| 522 | const term = child.wait() catch |err| { | 583 | return step.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); |
| 523 | return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) }); | ||
| 524 | }; | 584 | }; |
| 525 | 585 | ||
| 526 | switch (stdio) { | 586 | switch (self.stdio) { |
| 527 | .check => |checks| for (checks) |check| switch (check) { | 587 | .check => |checks| for (checks.items) |check| switch (check) { |
| 528 | .expect_stderr_exact => |expected_bytes| { | 588 | .expect_stderr_exact => |expected_bytes| { |
| 529 | if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { | 589 | if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) { |
| 530 | return step.fail( | 590 | return step.fail( |
| 591 | \\ | ||
| 531 | \\========= expected this stderr: ========= | 592 | \\========= expected this stderr: ========= |
| 532 | \\{s} | 593 | \\{s} |
| 533 | \\========= but found: ==================== | 594 | \\========= but found: ==================== |
| ... | @@ -537,13 +598,14 @@ fn runCommand( | ... | @@ -537,13 +598,14 @@ fn runCommand( |
| 537 | , .{ | 598 | , .{ |
| 538 | expected_bytes, | 599 | expected_bytes, |
| 539 | stderr_bytes.?, | 600 | stderr_bytes.?, |
| 540 | try Step.allocPrintCmd(arena, opt_cwd, argv), | 601 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 541 | }); | 602 | }); |
| 542 | } | 603 | } |
| 543 | }, | 604 | }, |
| 544 | .expect_stderr_match => |match| { | 605 | .expect_stderr_match => |match| { |
| 545 | if (mem.indexOf(u8, stderr_bytes.?, match) == null) { | 606 | if (mem.indexOf(u8, stderr_bytes.?, match) == null) { |
| 546 | return step.fail( | 607 | return step.fail( |
| 608 | \\ | ||
| 547 | \\========= expected to find in stderr: ========= | 609 | \\========= expected to find in stderr: ========= |
| 548 | \\{s} | 610 | \\{s} |
| 549 | \\========= but stderr does not contain it: ===== | 611 | \\========= but stderr does not contain it: ===== |
| ... | @@ -553,13 +615,14 @@ fn runCommand( | ... | @@ -553,13 +615,14 @@ fn runCommand( |
| 553 | , .{ | 615 | , .{ |
| 554 | match, | 616 | match, |
| 555 | stderr_bytes.?, | 617 | stderr_bytes.?, |
| 556 | try Step.allocPrintCmd(arena, opt_cwd, argv), | 618 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 557 | }); | 619 | }); |
| 558 | } | 620 | } |
| 559 | }, | 621 | }, |
| 560 | .expect_stdout_exact => |expected_bytes| { | 622 | .expect_stdout_exact => |expected_bytes| { |
| 561 | if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { | 623 | if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) { |
| 562 | return step.fail( | 624 | return step.fail( |
| 625 | \\ | ||
| 563 | \\========= expected this stdout: ========= | 626 | \\========= expected this stdout: ========= |
| 564 | \\{s} | 627 | \\{s} |
| 565 | \\========= but found: ==================== | 628 | \\========= but found: ==================== |
| ... | @@ -569,13 +632,14 @@ fn runCommand( | ... | @@ -569,13 +632,14 @@ fn runCommand( |
| 569 | , .{ | 632 | , .{ |
| 570 | expected_bytes, | 633 | expected_bytes, |
| 571 | stdout_bytes.?, | 634 | stdout_bytes.?, |
| 572 | try Step.allocPrintCmd(arena, opt_cwd, argv), | 635 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 573 | }); | 636 | }); |
| 574 | } | 637 | } |
| 575 | }, | 638 | }, |
| 576 | .expect_stdout_match => |match| { | 639 | .expect_stdout_match => |match| { |
| 577 | if (mem.indexOf(u8, stdout_bytes.?, match) == null) { | 640 | if (mem.indexOf(u8, stdout_bytes.?, match) == null) { |
| 578 | return step.fail( | 641 | return step.fail( |
| 642 | \\ | ||
| 579 | \\========= expected to find in stdout: ========= | 643 | \\========= expected to find in stdout: ========= |
| 580 | \\{s} | 644 | \\{s} |
| 581 | \\========= but stdout does not contain it: ===== | 645 | \\========= but stdout does not contain it: ===== |
| ... | @@ -585,7 +649,7 @@ fn runCommand( | ... | @@ -585,7 +649,7 @@ fn runCommand( |
| 585 | , .{ | 649 | , .{ |
| 586 | match, | 650 | match, |
| 587 | stdout_bytes.?, | 651 | stdout_bytes.?, |
| 588 | try Step.allocPrintCmd(arena, opt_cwd, argv), | 652 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 589 | }); | 653 | }); |
| 590 | } | 654 | } |
| 591 | }, | 655 | }, |
| ... | @@ -594,17 +658,89 @@ fn runCommand( | ... | @@ -594,17 +658,89 @@ fn runCommand( |
| 594 | return step.fail("the following command {} (expected {}):\n{s}", .{ | 658 | return step.fail("the following command {} (expected {}):\n{s}", .{ |
| 595 | fmtTerm(term), | 659 | fmtTerm(term), |
| 596 | fmtTerm(expected_term), | 660 | fmtTerm(expected_term), |
| 597 | try Step.allocPrintCmd(arena, opt_cwd, argv), | 661 | try Step.allocPrintCmd(arena, self.cwd, argv), |
| 598 | }); | 662 | }); |
| 599 | } | 663 | } |
| 600 | }, | 664 | }, |
| 601 | }, | 665 | }, |
| 602 | else => { | 666 | else => { |
| 603 | try step.handleChildProcessTerm(term, opt_cwd, argv); | 667 | try step.handleChildProcessTerm(term, self.cwd, argv); |
| 604 | }, | 668 | }, |
| 605 | } | 669 | } |
| 606 | } | 670 | } |
| 607 | 671 | ||
| 672 | fn spawnChildAndCollect( | ||
| 673 | self: *RunStep, | ||
| 674 | argv: []const []const u8, | ||
| 675 | stdout_bytes: *?[]const u8, | ||
| 676 | stderr_bytes: *?[]const u8, | ||
| 677 | has_side_effects: bool, | ||
| 678 | ) !std.ChildProcess.Term { | ||
| 679 | const b = self.step.owner; | ||
| 680 | const arena = b.allocator; | ||
| 681 | const cwd = if (self.cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path; | ||
| 682 | |||
| 683 | var child = std.ChildProcess.init(argv, arena); | ||
| 684 | child.cwd = cwd; | ||
| 685 | child.env_map = self.env_map orelse b.env_map; | ||
| 686 | |||
| 687 | child.stdin_behavior = switch (self.stdio) { | ||
| 688 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 689 | .inherit => .Inherit, | ||
| 690 | .check => .Close, | ||
| 691 | }; | ||
| 692 | child.stdout_behavior = switch (self.stdio) { | ||
| 693 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, | ||
| 694 | .inherit => .Inherit, | ||
| 695 | .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, | ||
| 696 | }; | ||
| 697 | child.stderr_behavior = switch (self.stdio) { | ||
| 698 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, | ||
| 699 | .inherit => .Inherit, | ||
| 700 | .check => .Pipe, | ||
| 701 | }; | ||
| 702 | |||
| 703 | child.spawn() catch |err| return self.step.fail("unable to spawn {s}: {s}", .{ | ||
| 704 | argv[0], @errorName(err), | ||
| 705 | }); | ||
| 706 | |||
| 707 | if (child.stdout) |stdout| { | ||
| 708 | if (child.stderr) |stderr| { | ||
| 709 | var poller = std.io.poll(arena, enum { stdout, stderr }, .{ | ||
| 710 | .stdout = stdout, | ||
| 711 | .stderr = stderr, | ||
| 712 | }); | ||
| 713 | defer poller.deinit(); | ||
| 714 | |||
| 715 | while (try poller.poll()) { | ||
| 716 | if (poller.fifo(.stdout).count > self.max_stdio_size) | ||
| 717 | return error.StdoutStreamTooLong; | ||
| 718 | if (poller.fifo(.stderr).count > self.max_stdio_size) | ||
| 719 | return error.StderrStreamTooLong; | ||
| 720 | } | ||
| 721 | |||
| 722 | stdout_bytes.* = try poller.fifo(.stdout).toOwnedSlice(); | ||
| 723 | stderr_bytes.* = try poller.fifo(.stderr).toOwnedSlice(); | ||
| 724 | } else { | ||
| 725 | stdout_bytes.* = try stdout.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 726 | } | ||
| 727 | } else if (child.stderr) |stderr| { | ||
| 728 | stderr_bytes.* = try stderr.reader().readAllAlloc(arena, self.max_stdio_size); | ||
| 729 | } | ||
| 730 | |||
| 731 | if (stderr_bytes.*) |stderr| if (stderr.len > 0) { | ||
| 732 | const stderr_is_diagnostic = switch (self.stdio) { | ||
| 733 | .check => |checks| !checksContainStderr(checks.items), | ||
| 734 | else => true, | ||
| 735 | }; | ||
| 736 | if (stderr_is_diagnostic) { | ||
| 737 | try self.step.result_error_msgs.append(arena, stderr); | ||
| 738 | } | ||
| 739 | }; | ||
| 740 | |||
| 741 | return child.wait(); | ||
| 742 | } | ||
| 743 | |||
| 608 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { | 744 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { |
| 609 | addPathForDynLibsInternal(&self.step, self.step.owner, artifact); | 745 | addPathForDynLibsInternal(&self.step, self.step.owner, artifact); |
| 610 | } | 746 | } |
| ... | @@ -624,3 +760,29 @@ pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *Co | ... | @@ -624,3 +760,29 @@ pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *Co |
| 624 | } | 760 | } |
| 625 | } | 761 | } |
| 626 | } | 762 | } |
| 763 | |||
| 764 | fn failForeign( | ||
| 765 | self: *RunStep, | ||
| 766 | suggested_flag: []const u8, | ||
| 767 | argv0: []const u8, | ||
| 768 | exe: *CompileStep, | ||
| 769 | ) error{ MakeFailed, MakeSkipped, OutOfMemory } { | ||
| 770 | switch (self.stdio) { | ||
| 771 | .check => { | ||
| 772 | if (self.skip_foreign_checks) | ||
| 773 | return error.MakeSkipped; | ||
| 774 | |||
| 775 | const b = self.step.owner; | ||
| 776 | const host_name = try b.host.target.zigTriple(b.allocator); | ||
| 777 | const foreign_name = try exe.target.zigTriple(b.allocator); | ||
| 778 | |||
| 779 | return self.step.fail( | ||
| 780 | \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) | ||
| 781 | \\ consider using {s} or enabling skip_foreign_checks in the Run step | ||
| 782 | , .{ argv0, foreign_name, host_name, suggested_flag }); | ||
| 783 | }, | ||
| 784 | else => { | ||
| 785 | return self.step.fail("unable to spawn foreign binary '{s}'", .{argv0}); | ||
| 786 | }, | ||
| 787 | } | ||
| 788 | } |
lib/std/Build/Step.zig+16-7| ... | @@ -26,6 +26,9 @@ pub const State = enum { | ... | @@ -26,6 +26,9 @@ pub const State = enum { |
| 26 | dependency_failure, | 26 | dependency_failure, |
| 27 | success, | 27 | success, |
| 28 | failure, | 28 | failure, |
| 29 | /// This state indicates that the step did not complete, however, it also did not fail, | ||
| 30 | /// and it is safe to continue executing its dependencies. | ||
| 31 | skipped, | ||
| 29 | }; | 32 | }; |
| 30 | 33 | ||
| 31 | pub const Id = enum { | 34 | pub const Id = enum { |
| ... | @@ -106,13 +109,15 @@ pub fn init(options: Options) Step { | ... | @@ -106,13 +109,15 @@ pub fn init(options: Options) Step { |
| 106 | /// If the Step's `make` function reports `error.MakeFailed`, it indicates they | 109 | /// If the Step's `make` function reports `error.MakeFailed`, it indicates they |
| 107 | /// have already reported the error. Otherwise, we add a simple error report | 110 | /// have already reported the error. Otherwise, we add a simple error report |
| 108 | /// here. | 111 | /// here. |
| 109 | pub fn make(s: *Step, prog_node: *std.Progress.Node) error{MakeFailed}!void { | 112 | pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkipped }!void { |
| 110 | return s.makeFn(s, prog_node) catch |err| { | 113 | return s.makeFn(s, prog_node) catch |err| switch (err) { |
| 111 | if (err != error.MakeFailed) { | 114 | error.MakeFailed => return error.MakeFailed, |
| 115 | error.MakeSkipped => return error.MakeSkipped, | ||
| 116 | else => { | ||
| 112 | const gpa = s.dependencies.allocator; | 117 | const gpa = s.dependencies.allocator; |
| 113 | s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM"); | 118 | s.result_error_msgs.append(gpa, @errorName(err)) catch @panic("OOM"); |
| 114 | } | 119 | return error.MakeFailed; |
| 115 | return error.MakeFailed; | 120 | }, |
| 116 | }; | 121 | }; |
| 117 | } | 122 | } |
| 118 | 123 | ||
| ... | @@ -192,10 +197,14 @@ pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void { | ... | @@ -192,10 +197,14 @@ pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void { |
| 192 | } | 197 | } |
| 193 | 198 | ||
| 194 | pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { | 199 | pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } { |
| 200 | try step.addError(fmt, args); | ||
| 201 | return error.MakeFailed; | ||
| 202 | } | ||
| 203 | |||
| 204 | pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { | ||
| 195 | const arena = step.owner.allocator; | 205 | const arena = step.owner.allocator; |
| 196 | const msg = try std.fmt.allocPrint(arena, fmt, args); | 206 | const msg = try std.fmt.allocPrint(arena, fmt, args); |
| 197 | try step.result_error_msgs.append(arena, msg); | 207 | try step.result_error_msgs.append(arena, msg); |
| 198 | return error.MakeFailed; | ||
| 199 | } | 208 | } |
| 200 | 209 | ||
| 201 | /// Assumes that argv contains `--listen=-` and that the process being spawned | 210 | /// 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 | ... | @@ -398,5 +407,5 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer |
| 398 | const i = man.failed_file_index orelse return err; | 407 | const i = man.failed_file_index orelse return err; |
| 399 | const pp = man.files.items[i].prefixed_path orelse return err; | 408 | const pp = man.files.items[i].prefixed_path orelse return err; |
| 400 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | 409 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 401 | return s.fail("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | 410 | return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); |
| 402 | } | 411 | } |
lib/std/Build/WriteFileStep.zig+86-28| ... | @@ -37,7 +37,7 @@ pub fn init(owner: *std.Build) WriteFileStep { | ... | @@ -37,7 +37,7 @@ pub fn init(owner: *std.Build) WriteFileStep { |
| 37 | return .{ | 37 | return .{ |
| 38 | .step = Step.init(.{ | 38 | .step = Step.init(.{ |
| 39 | .id = .write_file, | 39 | .id = .write_file, |
| 40 | .name = "writefile", | 40 | .name = "WriteFile", |
| 41 | .owner = owner, | 41 | .owner = owner, |
| 42 | .makeFn = make, | 42 | .makeFn = make, |
| 43 | }), | 43 | }), |
| ... | @@ -56,6 +56,8 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { | ... | @@ -56,6 +56,8 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { |
| 56 | .contents = .{ .bytes = b.dupe(bytes) }, | 56 | .contents = .{ .bytes = b.dupe(bytes) }, |
| 57 | }; | 57 | }; |
| 58 | wf.files.append(gpa, file) catch @panic("OOM"); | 58 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 59 | |||
| 60 | wf.maybeUpdateName(); | ||
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | /// Place the file into the generated directory within the local cache, | 63 | /// 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: [ | ... | @@ -75,6 +77,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [ |
| 75 | .contents = .{ .copy = source }, | 77 | .contents = .{ .copy = source }, |
| 76 | }; | 78 | }; |
| 77 | wf.files.append(gpa, file) catch @panic("OOM"); | 79 | wf.files.append(gpa, file) catch @panic("OOM"); |
| 80 | |||
| 81 | wf.maybeUpdateName(); | ||
| 78 | } | 82 | } |
| 79 | 83 | ||
| 80 | /// A path relative to the package root. | 84 | /// A path relative to the package root. |
| ... | @@ -101,6 +105,15 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo | ... | @@ -101,6 +105,15 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo |
| 101 | return null; | 105 | return null; |
| 102 | } | 106 | } |
| 103 | 107 | ||
| 108 | fn maybeUpdateName(wf: *WriteFileStep) void { | ||
| 109 | if (wf.files.items.len == 1) { | ||
| 110 | // First time adding a file; update name. | ||
| 111 | if (std.mem.eql(u8, wf.step.name, "WriteFile")) { | ||
| 112 | wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wf.files.items[0].sub_path}); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 104 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { | 117 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 105 | _ = prog_node; | 118 | _ = prog_node; |
| 106 | const b = step.owner; | 119 | const b = step.owner; |
| ... | @@ -110,14 +123,39 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -110,14 +123,39 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 110 | // WriteFileStep - arguably it should be a different step. But anyway here | 123 | // WriteFileStep - arguably it should be a different step. But anyway here |
| 111 | // it is, it happens unconditionally and does not interact with the other | 124 | // it is, it happens unconditionally and does not interact with the other |
| 112 | // files here. | 125 | // files here. |
| 126 | var any_miss = false; | ||
| 113 | for (wf.output_source_files.items) |output_source_file| { | 127 | for (wf.output_source_files.items) |output_source_file| { |
| 114 | const basename = fs.path.basename(output_source_file.sub_path); | ||
| 115 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { | 128 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { |
| 116 | var dir = try b.build_root.handle.makeOpenPath(dirname, .{}); | 129 | b.build_root.handle.makePath(dirname) catch |err| { |
| 117 | defer dir.close(); | 130 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 118 | try writeFile(wf, dir, output_source_file.contents, basename); | 131 | b.build_root, dirname, @errorName(err), |
| 119 | } else { | 132 | }); |
| 120 | try writeFile(wf, b.build_root.handle, output_source_file.contents, basename); | 133 | }; |
| 134 | } | ||
| 135 | switch (output_source_file.contents) { | ||
| 136 | .bytes => |bytes| { | ||
| 137 | b.build_root.handle.writeFile(output_source_file.sub_path, bytes) catch |err| { | ||
| 138 | return step.fail("unable to write file '{}{s}': {s}", .{ | ||
| 139 | b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 140 | }); | ||
| 141 | }; | ||
| 142 | any_miss = true; | ||
| 143 | }, | ||
| 144 | .copy => |file_source| { | ||
| 145 | const source_path = file_source.getPath(b); | ||
| 146 | const prev_status = fs.Dir.updateFile( | ||
| 147 | fs.cwd(), | ||
| 148 | source_path, | ||
| 149 | b.build_root.handle, | ||
| 150 | output_source_file.sub_path, | ||
| 151 | .{}, | ||
| 152 | ) catch |err| { | ||
| 153 | return step.fail("unable to update file from '{s}' to '{}{s}': {s}", .{ | ||
| 154 | source_path, b.build_root, output_source_file.sub_path, @errorName(err), | ||
| 155 | }); | ||
| 156 | }; | ||
| 157 | any_miss = any_miss or prev_status == .stale; | ||
| 158 | }, | ||
| 121 | } | 159 | } |
| 122 | } | 160 | } |
| 123 | 161 | ||
| ... | @@ -164,19 +202,52 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -164,19 +202,52 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 164 | const cache_path = "o" ++ fs.path.sep_str ++ digest; | 202 | const cache_path = "o" ++ fs.path.sep_str ++ digest; |
| 165 | 203 | ||
| 166 | var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { | 204 | var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { |
| 167 | std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); | 205 | return step.fail("unable to make path '{}{s}': {s}", .{ |
| 168 | return err; | 206 | b.cache_root, cache_path, @errorName(err), |
| 207 | }); | ||
| 169 | }; | 208 | }; |
| 170 | defer cache_dir.close(); | 209 | defer cache_dir.close(); |
| 171 | 210 | ||
| 172 | for (wf.files.items) |file| { | 211 | for (wf.files.items) |file| { |
| 173 | const basename = fs.path.basename(file.sub_path); | ||
| 174 | if (fs.path.dirname(file.sub_path)) |dirname| { | 212 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 175 | var dir = try b.cache_root.handle.makeOpenPath(dirname, .{}); | 213 | cache_dir.makePath(dirname) catch |err| { |
| 176 | defer dir.close(); | 214 | return step.fail("unable to make path '{}{s}{c}{s}': {s}", .{ |
| 177 | try writeFile(wf, dir, file.contents, basename); | 215 | b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), |
| 178 | } else { | 216 | }); |
| 179 | try writeFile(wf, cache_dir, file.contents, basename); | 217 | }; |
| 218 | } | ||
| 219 | switch (file.contents) { | ||
| 220 | .bytes => |bytes| { | ||
| 221 | cache_dir.writeFile(file.sub_path, bytes) catch |err| { | ||
| 222 | return step.fail("unable to write file '{}{s}{c}{s}': {s}", .{ | ||
| 223 | b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), | ||
| 224 | }); | ||
| 225 | }; | ||
| 226 | }, | ||
| 227 | .copy => |file_source| { | ||
| 228 | const source_path = file_source.getPath(b); | ||
| 229 | const prev_status = fs.Dir.updateFile( | ||
| 230 | fs.cwd(), | ||
| 231 | source_path, | ||
| 232 | cache_dir, | ||
| 233 | file.sub_path, | ||
| 234 | .{}, | ||
| 235 | ) catch |err| { | ||
| 236 | return step.fail("unable to update file from '{s}' to '{}{s}{c}{s}': {s}", .{ | ||
| 237 | source_path, | ||
| 238 | b.cache_root, | ||
| 239 | cache_path, | ||
| 240 | fs.path.sep, | ||
| 241 | file.sub_path, | ||
| 242 | @errorName(err), | ||
| 243 | }); | ||
| 244 | }; | ||
| 245 | // At this point we already will mark the step as a cache miss. | ||
| 246 | // But this is kind of a partial cache hit since individual | ||
| 247 | // file copies may be avoided. Oh well, this information is | ||
| 248 | // discarded. | ||
| 249 | _ = prev_status; | ||
| 250 | }, | ||
| 180 | } | 251 | } |
| 181 | 252 | ||
| 182 | file.generated_file.path = try b.cache_root.join( | 253 | file.generated_file.path = try b.cache_root.join( |
| ... | @@ -188,19 +259,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -188,19 +259,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 188 | try man.writeManifest(); | 259 | try man.writeManifest(); |
| 189 | } | 260 | } |
| 190 | 261 | ||
| 191 | fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { | ||
| 192 | const b = wf.step.owner; | ||
| 193 | // TODO after landing concurrency PR, improve error reporting here | ||
| 194 | switch (contents) { | ||
| 195 | .bytes => |bytes| return dir.writeFile(basename, bytes), | ||
| 196 | .copy => |file_source| { | ||
| 197 | const source_path = file_source.getPath(b); | ||
| 198 | const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); | ||
| 199 | _ = prev_status; // TODO logging (affected by open PR regarding concurrency) | ||
| 200 | }, | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | const std = @import("../std.zig"); | 262 | const std = @import("../std.zig"); |
| 205 | const Step = std.Build.Step; | 263 | const Step = std.Build.Step; |
| 206 | const fs = std.fs; | 264 | const fs = std.fs; |
lib/std/child_process.zig-1| ... | @@ -185,7 +185,6 @@ pub const ChildProcess = struct { | ... | @@ -185,7 +185,6 @@ pub const ChildProcess = struct { |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | /// Blocks until child process terminates and then cleans up all resources. | 187 | /// Blocks until child process terminates and then cleans up all resources. |
| 188 | /// TODO: set the pid to undefined in this function. | ||
| 189 | pub fn wait(self: *ChildProcess) !Term { | 188 | pub fn wait(self: *ChildProcess) !Term { |
| 190 | const term = if (builtin.os.tag == .windows) | 189 | const term = if (builtin.os.tag == .windows) |
| 191 | try self.waitWindows() | 190 | try self.waitWindows() |
test/link/macho/bugs/13457/build.zig+3-1| ... | @@ -13,6 +13,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -13,6 +13,8 @@ pub fn build(b: *std.Build) void { |
| 13 | .target = target, | 13 | .target = target, |
| 14 | }); | 14 | }); |
| 15 | 15 | ||
| 16 | const run = exe.runEmulatable(); | 16 | const run = b.addRunArtifact(exe); |
| 17 | run.skip_foreign_checks = true; | ||
| 18 | run.expectStdOutEqual(""); | ||
| 17 | test_step.dependOn(&run.step); | 19 | test_step.dependOn(&run.step); |
| 18 | } | 20 | } |
test/link/macho/empty/build.zig+2-1| ... | @@ -16,7 +16,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -16,7 +16,8 @@ pub fn build(b: *std.Build) void { |
| 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); | 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); |
| 17 | exe.linkLibC(); | 17 | exe.linkLibC(); |
| 18 | 18 | ||
| 19 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 19 | const run_cmd = b.addRunArtifact(exe); |
| 20 | run_cmd.skip_foreign_checks = true; | ||
| 20 | run_cmd.expectStdOutEqual("Hello!\n"); | 21 | run_cmd.expectStdOutEqual("Hello!\n"); |
| 21 | test_step.dependOn(&run_cmd.step); | 22 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 23 | } |
test/link/macho/needed_library/build.zig+1| ... | @@ -36,5 +36,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -36,5 +36,6 @@ pub fn build(b: *std.Build) void { |
| 36 | check.checkNext("name @rpath/liba.dylib"); | 36 | check.checkNext("name @rpath/liba.dylib"); |
| 37 | 37 | ||
| 38 | const run_cmd = check.runAndCompare(); | 38 | const run_cmd = check.runAndCompare(); |
| 39 | run_cmd.expectStdOutEqual(""); | ||
| 39 | test_step.dependOn(&run_cmd.step); | 40 | test_step.dependOn(&run_cmd.step); |
| 40 | } | 41 | } |
test/link/macho/objc/build.zig+3-1| ... | @@ -17,6 +17,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -17,6 +17,8 @@ pub fn build(b: *std.Build) void { |
| 17 | // populate paths to the sysroot here. | 17 | // populate paths to the sysroot here. |
| 18 | exe.linkFramework("Foundation"); | 18 | exe.linkFramework("Foundation"); |
| 19 | 19 | ||
| 20 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | 20 | const run_cmd = b.addRunArtifact(exe); |
| 21 | run_cmd.skip_foreign_checks = true; | ||
| 22 | run_cmd.expectStdOutEqual(""); | ||
| 21 | test_step.dependOn(&run_cmd.step); | 23 | test_step.dependOn(&run_cmd.step); |
| 22 | } | 24 | } |
test/link/macho/search_strategy/build.zig+2-1| ... | @@ -27,7 +27,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -27,7 +27,8 @@ pub fn build(b: *std.Build) void { |
| 27 | const exe = createScenario(b, optimize, target); | 27 | const exe = createScenario(b, optimize, target); |
| 28 | exe.search_strategy = .paths_first; | 28 | exe.search_strategy = .paths_first; |
| 29 | 29 | ||
| 30 | const run = std.Build.EmulatableRunStep.create(b, "run", exe); | 30 | const run = b.addRunArtifact(exe); |
| 31 | run.skip_foreign_checks = true; | ||
| 31 | run.cwd = b.pathFromRoot("."); | 32 | run.cwd = b.pathFromRoot("."); |
| 32 | run.expectStdOutEqual("Hello world"); | 33 | run.expectStdOutEqual("Hello world"); |
| 33 | test_step.dependOn(&run.step); | 34 | test_step.dependOn(&run.step); |
test/link/macho/stack_size/build.zig+1| ... | @@ -21,5 +21,6 @@ pub fn build(b: *std.Build) void { | ... | @@ -21,5 +21,6 @@ pub fn build(b: *std.Build) void { |
| 21 | check_exe.checkNext("stacksize 100000000"); | 21 | check_exe.checkNext("stacksize 100000000"); |
| 22 | 22 | ||
| 23 | const run = check_exe.runAndCompare(); | 23 | const run = check_exe.runAndCompare(); |
| 24 | run.expectStdOutEqual(""); | ||
| 24 | test_step.dependOn(&run.step); | 25 | test_step.dependOn(&run.step); |
| 25 | } | 26 | } |
test/link/macho/uuid/build.zig+16-60| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Builder = std.Build.Builder; | ||
| 3 | const CompileStep = std.Build.CompileStep; | 2 | const CompileStep = std.Build.CompileStep; |
| 4 | const FileSource = std.Build.FileSource; | 3 | const FileSource = std.Build.FileSource; |
| 5 | const Step = std.Build.Step; | 4 | const Step = std.Build.Step; |
| ... | @@ -38,13 +37,15 @@ fn testUuid( | ... | @@ -38,13 +37,15 @@ fn testUuid( |
| 38 | // stay the same across builds. | 37 | // stay the same across builds. |
| 39 | { | 38 | { |
| 40 | const dylib = simpleDylib(b, optimize, target); | 39 | const dylib = simpleDylib(b, optimize, target); |
| 41 | const install_step = installWithRename(dylib, "test1.dylib"); | 40 | const install_step = b.addInstallArtifact(dylib); |
| 41 | install_step.dest_sub_path = "test1.dylib"; | ||
| 42 | install_step.step.dependOn(&dylib.step); | 42 | install_step.step.dependOn(&dylib.step); |
| 43 | } | 43 | } |
| 44 | { | 44 | { |
| 45 | const dylib = simpleDylib(b, optimize, target); | 45 | const dylib = simpleDylib(b, optimize, target); |
| 46 | dylib.strip = true; | 46 | dylib.strip = true; |
| 47 | const install_step = installWithRename(dylib, "test2.dylib"); | 47 | const install_step = b.addInstallArtifact(dylib); |
| 48 | install_step.dest_sub_path = "test2.dylib"; | ||
| 48 | install_step.step.dependOn(&dylib.step); | 49 | install_step.step.dependOn(&dylib.step); |
| 49 | } | 50 | } |
| 50 | 51 | ||
| ... | @@ -68,70 +69,23 @@ fn simpleDylib( | ... | @@ -68,70 +69,23 @@ fn simpleDylib( |
| 68 | return dylib; | 69 | return dylib; |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename { | ||
| 72 | const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name); | ||
| 73 | cs.builder.getInstallStep().dependOn(&step.step); | ||
| 74 | return step; | ||
| 75 | } | ||
| 76 | |||
| 77 | const InstallWithRename = struct { | ||
| 78 | pub const base_id = .custom; | ||
| 79 | |||
| 80 | step: Step, | ||
| 81 | builder: *Builder, | ||
| 82 | source: FileSource, | ||
| 83 | name: []const u8, | ||
| 84 | |||
| 85 | pub fn create( | ||
| 86 | builder: *Builder, | ||
| 87 | source: FileSource, | ||
| 88 | name: []const u8, | ||
| 89 | ) *InstallWithRename { | ||
| 90 | const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); | ||
| 91 | self.* = InstallWithRename{ | ||
| 92 | .builder = builder, | ||
| 93 | .step = Step.init(builder.allocator, .{ | ||
| 94 | .id = .custom, | ||
| 95 | .name = builder.fmt("install and rename: {s} -> {s}", .{ | ||
| 96 | source.getDisplayName(), name, | ||
| 97 | }), | ||
| 98 | .makeFn = make, | ||
| 99 | }), | ||
| 100 | .source = source, | ||
| 101 | .name = builder.dupe(name), | ||
| 102 | }; | ||
| 103 | return self; | ||
| 104 | } | ||
| 105 | |||
| 106 | fn make(step: *Step) anyerror!void { | ||
| 107 | const self = @fieldParentPtr(InstallWithRename, "step", step); | ||
| 108 | const source_path = self.source.getPath(self.builder); | ||
| 109 | const target_path = self.builder.getInstallPath(.lib, self.name); | ||
| 110 | self.builder.updateFile(source_path, target_path) catch |err| { | ||
| 111 | std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path }); | ||
| 112 | return err; | ||
| 113 | }; | ||
| 114 | } | ||
| 115 | }; | ||
| 116 | |||
| 117 | const CompareUuid = struct { | 72 | const CompareUuid = struct { |
| 118 | pub const base_id = .custom; | 73 | pub const base_id = .custom; |
| 119 | 74 | ||
| 120 | step: Step, | 75 | step: Step, |
| 121 | builder: *Builder, | ||
| 122 | lhs: []const u8, | 76 | lhs: []const u8, |
| 123 | rhs: []const u8, | 77 | rhs: []const u8, |
| 124 | 78 | ||
| 125 | pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { | 79 | pub fn create(owner: *std.Build, lhs: []const u8, rhs: []const u8) *CompareUuid { |
| 126 | const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); | 80 | const self = owner.allocator.create(CompareUuid) catch @panic("OOM"); |
| 127 | self.* = CompareUuid{ | 81 | self.* = CompareUuid{ |
| 128 | .builder = builder, | 82 | .step = Step.init(.{ |
| 129 | .step = Step.init(builder.allocator, .{ | 83 | .id = base_id, |
| 130 | .id = .custom, | 84 | .name = owner.fmt("compare uuid: {s} and {s}", .{ |
| 131 | .name = builder.fmt("compare uuid: {s} and {s}", .{ | ||
| 132 | lhs, | 85 | lhs, |
| 133 | rhs, | 86 | rhs, |
| 134 | }), | 87 | }), |
| 88 | .owner = owner, | ||
| 135 | .makeFn = make, | 89 | .makeFn = make, |
| 136 | }), | 90 | }), |
| 137 | .lhs = lhs, | 91 | .lhs = lhs, |
| ... | @@ -140,16 +94,18 @@ const CompareUuid = struct { | ... | @@ -140,16 +94,18 @@ const CompareUuid = struct { |
| 140 | return self; | 94 | return self; |
| 141 | } | 95 | } |
| 142 | 96 | ||
| 143 | fn make(step: *Step) anyerror!void { | 97 | fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void { |
| 98 | _ = prog_node; | ||
| 99 | const b = step.owner; | ||
| 144 | const self = @fieldParentPtr(CompareUuid, "step", step); | 100 | const self = @fieldParentPtr(CompareUuid, "step", step); |
| 145 | const gpa = self.builder.allocator; | 101 | const gpa = b.allocator; |
| 146 | 102 | ||
| 147 | var lhs_uuid: [16]u8 = undefined; | 103 | var lhs_uuid: [16]u8 = undefined; |
| 148 | const lhs_path = self.builder.getInstallPath(.lib, self.lhs); | 104 | const lhs_path = b.getInstallPath(.lib, self.lhs); |
| 149 | try parseUuid(gpa, lhs_path, &lhs_uuid); | 105 | try parseUuid(gpa, lhs_path, &lhs_uuid); |
| 150 | 106 | ||
| 151 | var rhs_uuid: [16]u8 = undefined; | 107 | var rhs_uuid: [16]u8 = undefined; |
| 152 | const rhs_path = self.builder.getInstallPath(.lib, self.rhs); | 108 | const rhs_path = b.getInstallPath(.lib, self.rhs); |
| 153 | try parseUuid(gpa, rhs_path, &rhs_uuid); | 109 | try parseUuid(gpa, rhs_path, &rhs_uuid); |
| 154 | 110 | ||
| 155 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); | 111 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); |
test/link/wasm/extern/build.zig+2-1| ... | @@ -11,7 +11,8 @@ pub fn build(b: *std.Build) void { | ... | @@ -11,7 +11,8 @@ pub fn build(b: *std.Build) void { |
| 11 | exe.use_llvm = false; | 11 | exe.use_llvm = false; |
| 12 | exe.use_lld = false; | 12 | exe.use_lld = false; |
| 13 | 13 | ||
| 14 | const run = exe.runEmulatable(); | 14 | const run = b.addRunArtifact(exe); |
| 15 | run.skip_foreign_checks = true; | ||
| 15 | run.expectStdOutEqual("Result: 30"); | 16 | run.expectStdOutEqual("Result: 30"); |
| 16 | 17 | ||
| 17 | const test_step = b.step("test", "Run linker test"); | 18 | const test_step = b.step("test", "Run linker test"); |