authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-01 22:56:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-15 10:48:13-07:00
log58edefc6d1716c0731ee2fe672ec8d073651aafb
tree9de4d030be9f44d3bc953d114eaef0812bd11cf4
parentd0f675827c28b1d50e8aea6a7d29cb45ad8d4e67

zig build: many enhancements related to parallel building

Rework std.Build.Step to have an `owner: *Build` field. This simplified the implementation of installation steps, as well as provided some much-needed common API for the new parallelized build system. --verbose is now defined very concretely: it prints to stderr just before spawning a child process. Child process execution is updated to conform to the new parallel-friendly make() function semantics. DRY up the failWithCacheError handling code. It now integrates properly with the step graph instead of incorrectly dumping to stderr and calling process exit. In the main CLI, fix `zig fmt` crash when there are no errors and stdin is used. Deleted steps: * EmulatableRunStep - this entire thing can be removed in favor of a flag added to std.Build.RunStep called `skip_foreign_checks`. * LogStep - this doesn't really fit with a multi-threaded build runner and is effectively superseded by the new build summary output. build runner: * add -fsummary and -fno-summary to override the default behavior, which is to print a summary if any of the build steps fail. * print the dep prefix when emitting error messages for steps. std.Build.FmtStep: * This step now supports exclude paths as well as a check flag. * The check flag decides between two modes, modify mode, and check mode. These can be used to update source files in place, or to fail the build, respectively. Zig's own build.zig: * The `test-fmt` step will do all the `zig fmt` checking that we expect to be done. Since the `test` step depends on this one, we can simply remove the explicit call to `zig fmt` in the CI. * The new `fmt` step will actually perform `zig fmt` and update source files in place. std.Build.RunStep: * expose max_stdio_size is a field (previously an unchangeable hard-coded value). * rework the API. Instead of configuring each stream independently, there is a `stdio` field where you can choose between `infer_from_args`, `inherit`, or `check`. These determine whether the RunStep is considered to have side-effects or not. The previous field, `condition` is gone. * when stdio mode is set to `check` there is a slice of any number of checks to make, which include things like exit code, stderr matching, or stdout matching. * remove the ill-defined `print` field. * when adding an output arg, it takes the opportunity to give itself a better name. * The flag `skip_foreign_checks` is added. If this is true, a RunStep which is configured to check the output of the executed binary will not fail the build if the binary cannot be executed due to being for a foreign binary to the host system which is running the build graph. Command-line arguments such as -fqemu and -fwasmtime may affect whether a binary is detected as foreign, as well as system configuration such as Rosetta (macOS) and binfmt_misc (Linux). - This makes EmulatableRunStep no longer needed. * Fix the child process handling to properly integrate with the new bulid API and to avoid deadlocks in stdout/stderr streams by polling if necessary. std.Build.RemoveDirStep now uses the open build_root directory handle instead of an absolute path.

23 files changed, 1113 insertions(+), 1167 deletions(-)

build.zig+18-6
...@@ -61,8 +61,6 @@ pub fn build(b: *std.Build) !void {...@@ -61,8 +61,6 @@ pub fn build(b: *std.Build) !void {
61 test_cases.stack_size = stack_size;61 test_cases.stack_size = stack_size;
62 test_cases.single_threaded = single_threaded;62 test_cases.single_threaded = single_threaded;
6363
64 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
65
66 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;64 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
67 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;65 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
68 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;66 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
...@@ -386,10 +384,24 @@ pub fn build(b: *std.Build) !void {...@@ -386,10 +384,24 @@ pub fn build(b: *std.Build) !void {
386 }384 }
387 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];385 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
388386
389 // run stage1 `zig fmt` on this build.zig file just to make sure it works387 const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" };
390 test_step.dependOn(&fmt_build_zig.step);388 const fmt_exclude_paths = &.{ "test/cases" };
391 const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works");389 const check_fmt = b.addFmt(.{
392 fmt_step.dependOn(&fmt_build_zig.step);390 .paths = fmt_include_paths,
391 .exclude_paths = fmt_exclude_paths,
392 .check = true,
393 });
394 const do_fmt = b.addFmt(.{
395 .paths = fmt_include_paths,
396 .exclude_paths = fmt_exclude_paths,
397 });
398
399 const test_fmt_step = b.step("test-fmt", "Check whether source files have conforming formatting");
400 test_fmt_step.dependOn(&check_fmt.step);
401
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);
404
393405
394 test_step.dependOn(tests.addPkgTests(406 test_step.dependOn(tests.addPkgTests(
395 b,407 b,
lib/build_runner.zig+46-25
...@@ -93,6 +93,7 @@ pub fn main() !void {...@@ -93,6 +93,7 @@ pub fn main() !void {
9393
94 var install_prefix: ?[]const u8 = null;94 var install_prefix: ?[]const u8 = null;
95 var dir_list = std.Build.DirList{};95 var dir_list = std.Build.DirList{};
96 var enable_summary: ?bool = null;
9697
97 const Color = enum { auto, off, on };98 const Color = enum { auto, off, on };
98 var color: Color = .auto;99 var color: Color = .auto;
...@@ -217,6 +218,10 @@ pub fn main() !void {...@@ -217,6 +218,10 @@ pub fn main() !void {
217 builder.enable_darling = true;218 builder.enable_darling = true;
218 } else if (mem.eql(u8, arg, "-fno-darling")) {219 } else if (mem.eql(u8, arg, "-fno-darling")) {
219 builder.enable_darling = false;220 builder.enable_darling = false;
221 } else if (mem.eql(u8, arg, "-fsummary")) {
222 enable_summary = true;
223 } else if (mem.eql(u8, arg, "-fno-summary")) {
224 enable_summary = false;
220 } else if (mem.eql(u8, arg, "-freference-trace")) {225 } else if (mem.eql(u8, arg, "-freference-trace")) {
221 builder.reference_trace = 256;226 builder.reference_trace = 256;
222 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {227 } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
...@@ -252,8 +257,9 @@ pub fn main() !void {...@@ -252,8 +257,9 @@ pub fn main() !void {
252 }257 }
253 }258 }
254259
260 const stderr = std.io.getStdErr();
255 const ttyconf: std.debug.TTY.Config = switch (color) {261 const ttyconf: std.debug.TTY.Config = switch (color) {
256 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),262 .auto => std.debug.detectTTYConfig(stderr),
257 .on => .escape_codes,263 .on => .escape_codes,
258 .off => .no_color,264 .off => .no_color,
259 };265 };
...@@ -279,6 +285,8 @@ pub fn main() !void {...@@ -279,6 +285,8 @@ pub fn main() !void {
279 main_progress_node,285 main_progress_node,
280 thread_pool_options,286 thread_pool_options,
281 ttyconf,287 ttyconf,
288 stderr,
289 enable_summary,
282 ) catch |err| switch (err) {290 ) catch |err| switch (err) {
283 error.UncleanExit => process.exit(1),291 error.UncleanExit => process.exit(1),
284 else => return err,292 else => return err,
...@@ -292,6 +300,8 @@ fn runStepNames(...@@ -292,6 +300,8 @@ fn runStepNames(
292 parent_prog_node: *std.Progress.Node,300 parent_prog_node: *std.Progress.Node,
293 thread_pool_options: std.Thread.Pool.Options,301 thread_pool_options: std.Thread.Pool.Options,
294 ttyconf: std.debug.TTY.Config,302 ttyconf: std.debug.TTY.Config,
303 stderr: std.fs.File,
304 enable_summary: ?bool,
295) !void {305) !void {
296 const gpa = b.allocator;306 const gpa = b.allocator;
297 var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{};307 var step_stack: std.AutoArrayHashMapUnmanaged(*Step, void) = .{};
...@@ -382,28 +392,35 @@ fn runStepNames(...@@ -382,28 +392,35 @@ fn runStepNames(
382392
383 // A proper command line application defaults to silently succeeding.393 // A proper command line application defaults to silently succeeding.
384 // The user may request verbose mode if they have a different preference.394 // The user may request verbose mode if they have a different preference.
385 if (failure_count == 0 and !b.verbose) return cleanExit();395 if (failure_count == 0 and enable_summary != true) return cleanExit();
386396
387 const stderr = std.io.getStdErr();397 if (enable_summary != false) {
388398 const total_count = success_count + failure_count + pending_count;
389 const total_count = success_count + failure_count + pending_count;399 ttyconf.setColor(stderr, .Cyan) catch {};
390 ttyconf.setColor(stderr, .Cyan) catch {};400 stderr.writeAll("Build Summary:") catch {};
391 stderr.writeAll("Build Summary: ") catch {};401 ttyconf.setColor(stderr, .Reset) catch {};
392 ttyconf.setColor(stderr, .Reset) catch {};402 stderr.writer().print(" {d}/{d} steps succeeded; {d} failed", .{
393 stderr.writer().print("{d}/{d} steps succeeded; {d} failed; {d} total compile errors\n", .{403 success_count, total_count, failure_count,
394 success_count, total_count, failure_count, total_compile_errors,404 }) catch {};
395 }) catch {};405
406 if (enable_summary == null) {
407 ttyconf.setColor(stderr, .Dim) catch {};
408 stderr.writeAll(" (disable with -fno-summary)") catch {};
409 ttyconf.setColor(stderr, .Reset) catch {};
410 }
411 stderr.writeAll("\n") catch {};
396412
397 // Print a fancy tree with build results.413 // Print a fancy tree with build results.
398 var print_node: PrintNode = .{ .parent = null };414 var print_node: PrintNode = .{ .parent = null };
399 if (step_names.len == 0) {415 if (step_names.len == 0) {
400 print_node.last = true;416 print_node.last = true;
401 printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {};417 printTreeStep(b, b.default_step, stderr, ttyconf, &print_node, &step_stack) catch {};
402 } else {418 } else {
403 for (step_names, 0..) |step_name, i| {419 for (step_names, 0..) |step_name, i| {
404 const tls = b.top_level_steps.get(step_name).?;420 const tls = b.top_level_steps.get(step_name).?;
405 print_node.last = i + 1 == b.top_level_steps.count();421 print_node.last = i + 1 == b.top_level_steps.count();
406 printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {};422 printTreeStep(b, &tls.step, stderr, ttyconf, &print_node, &step_stack) catch {};
423 }
407 }424 }
408 }425 }
409426
...@@ -453,9 +470,9 @@ fn printTreeStep(...@@ -453,9 +470,9 @@ fn printTreeStep(
453 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),470 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
454) !void {471) !void {
455 const first = step_stack.swapRemove(s);472 const first = step_stack.swapRemove(s);
456 if (!first) try ttyconf.setColor(stderr, .Dim);
457 try printPrefix(parent_node, stderr);473 try printPrefix(parent_node, stderr);
458474
475 if (!first) try ttyconf.setColor(stderr, .Dim);
459 if (parent_node.parent != null) {476 if (parent_node.parent != null) {
460 if (parent_node.last) {477 if (parent_node.last) {
461 try stderr.writeAll("└─ ");478 try stderr.writeAll("└─ ");
...@@ -464,7 +481,7 @@ fn printTreeStep(...@@ -464,7 +481,7 @@ fn printTreeStep(
464 }481 }
465 }482 }
466483
467 // TODO print the dep prefix too?484 // dep_prefix omitted here because it is redundant with the tree.
468 try stderr.writeAll(s.name);485 try stderr.writeAll(s.name);
469486
470 if (first) {487 if (first) {
...@@ -608,8 +625,10 @@ fn workerMakeOneStep(...@@ -608,8 +625,10 @@ fn workerMakeOneStep(
608 const stderr = std.io.getStdErr();625 const stderr = std.io.getStdErr();
609626
610 for (s.result_error_msgs.items) |msg| {627 for (s.result_error_msgs.items) |msg| {
611 // TODO print the dep prefix too628 // Sometimes it feels like you just can't catch a break. Finally,
629 // with Zig, you can.
612 ttyconf.setColor(stderr, .Bold) catch break;630 ttyconf.setColor(stderr, .Bold) catch break;
631 stderr.writeAll(s.owner.dep_prefix) catch break;
613 stderr.writeAll(s.name) catch break;632 stderr.writeAll(s.name) catch break;
614 stderr.writeAll(": ") catch break;633 stderr.writeAll(": ") catch break;
615 ttyconf.setColor(stderr, .Red) catch break;634 ttyconf.setColor(stderr, .Red) catch break;
...@@ -735,6 +754,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi...@@ -735,6 +754,8 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
735 \\Advanced Options:754 \\Advanced Options:
736 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error755 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
737 \\ -fno-reference-trace Disable reference trace756 \\ -fno-reference-trace Disable reference trace
757 \\ -fsummary Print the build summary, even on success
758 \\ -fno-summary Omit the build summary, even on failure
738 \\ --build-file [file] Override path to build.zig759 \\ --build-file [file] Override path to build.zig
739 \\ --cache-dir [path] Override path to local Zig cache directory760 \\ --cache-dir [path] Override path to local Zig cache directory
740 \\ --global-cache-dir [path] Override path to global Zig cache directory761 \\ --global-cache-dir [path] Override path to global Zig cache directory
lib/std/Build.zig+14-212
...@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");...@@ -32,14 +32,12 @@ pub const Step = @import("Build/Step.zig");
32pub const CheckFileStep = @import("Build/CheckFileStep.zig");32pub const CheckFileStep = @import("Build/CheckFileStep.zig");
33pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");33pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
34pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");34pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
35pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
36pub const FmtStep = @import("Build/FmtStep.zig");35pub const FmtStep = @import("Build/FmtStep.zig");
37pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");36pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
38pub const InstallDirStep = @import("Build/InstallDirStep.zig");37pub const InstallDirStep = @import("Build/InstallDirStep.zig");
39pub const InstallFileStep = @import("Build/InstallFileStep.zig");38pub const InstallFileStep = @import("Build/InstallFileStep.zig");
40pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");39pub const ObjCopyStep = @import("Build/ObjCopyStep.zig");
41pub const CompileStep = @import("Build/CompileStep.zig");40pub const CompileStep = @import("Build/CompileStep.zig");
42pub const LogStep = @import("Build/LogStep.zig");
43pub const OptionsStep = @import("Build/OptionsStep.zig");41pub const OptionsStep = @import("Build/OptionsStep.zig");
44pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
45pub const RunStep = @import("Build/RunStep.zig");43pub const RunStep = @import("Build/RunStep.zig");
...@@ -195,7 +193,7 @@ pub fn create(...@@ -195,7 +193,7 @@ pub fn create(
195 env_map.* = try process.getEnvMap(allocator);193 env_map.* = try process.getEnvMap(allocator);
196194
197 const self = try allocator.create(Build);195 const self = try allocator.create(Build);
198 self.* = Build{196 self.* = .{
199 .zig_exe = zig_exe,197 .zig_exe = zig_exe,
200 .build_root = build_root,198 .build_root = build_root,
201 .cache_root = cache_root,199 .cache_root = cache_root,
...@@ -224,16 +222,18 @@ pub fn create(...@@ -224,16 +222,18 @@ pub fn create(
224 .dest_dir = env_map.get("DESTDIR"),222 .dest_dir = env_map.get("DESTDIR"),
225 .installed_files = ArrayList(InstalledFile).init(allocator),223 .installed_files = ArrayList(InstalledFile).init(allocator),
226 .install_tls = .{224 .install_tls = .{
227 .step = Step.init(allocator, .{225 .step = Step.init(.{
228 .id = .top_level,226 .id = .top_level,
229 .name = "install",227 .name = "install",
228 .owner = self,
230 }),229 }),
231 .description = "Copy build artifacts to prefix path",230 .description = "Copy build artifacts to prefix path",
232 },231 },
233 .uninstall_tls = .{232 .uninstall_tls = .{
234 .step = Step.init(allocator, .{233 .step = Step.init(.{
235 .id = .top_level,234 .id = .top_level,
236 .name = "uninstall",235 .name = "uninstall",
236 .owner = self,
237 .makeFn = makeUninstall,237 .makeFn = makeUninstall,
238 }),238 }),
239 .description = "Remove build artifacts from prefix path",239 .description = "Remove build artifacts from prefix path",
...@@ -267,16 +267,18 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc...@@ -267,16 +267,18 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Direc
267 child.* = .{267 child.* = .{
268 .allocator = allocator,268 .allocator = allocator,
269 .install_tls = .{269 .install_tls = .{
270 .step = Step.init(allocator, .{270 .step = Step.init(.{
271 .id = .top_level,271 .id = .top_level,
272 .name = "install",272 .name = "install",
273 .owner = child,
273 }),274 }),
274 .description = "Copy build artifacts to prefix path",275 .description = "Copy build artifacts to prefix path",
275 },276 },
276 .uninstall_tls = .{277 .uninstall_tls = .{
277 .step = Step.init(allocator, .{278 .step = Step.init(.{
278 .id = .top_level,279 .id = .top_level,
279 .name = "uninstall",280 .name = "uninstall",
281 .owner = child,
280 .makeFn = makeUninstall,282 .makeFn = makeUninstall,
281 }),283 }),
282 .description = "Remove build artifacts from prefix path",284 .description = "Remove build artifacts from prefix path",
...@@ -689,21 +691,14 @@ pub fn addWriteFiles(self: *Build) *WriteFileStep {...@@ -689,21 +691,14 @@ pub fn addWriteFiles(self: *Build) *WriteFileStep {
689 return write_file_step;691 return write_file_step;
690}692}
691693
692pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
693 const data = self.fmt(format, args);
694 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
695 log_step.* = LogStep.init(self, data);
696 return log_step;
697}
698
699pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {694pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
700 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");695 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
701 remove_dir_step.* = RemoveDirStep.init(self, dir_path);696 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
702 return remove_dir_step;697 return remove_dir_step;
703}698}
704699
705pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {700pub fn addFmt(b: *Build, options: FmtStep.Options) *FmtStep {
706 return FmtStep.create(self, paths);701 return FmtStep.create(b, options);
707}702}
708703
709pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {704pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
...@@ -870,10 +865,11 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_...@@ -870,10 +865,11 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_
870865
871pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {866pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
872 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");867 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
873 step_info.* = TopLevelStep{868 step_info.* = .{
874 .step = Step.init(self.allocator, .{869 .step = Step.init(.{
875 .id = .top_level,870 .id = .top_level,
876 .name = name,871 .name = name,
872 .owner = self,
877 }),873 }),
878 .description = self.dupe(description),874 .description = self.dupe(description),
879 };875 };
...@@ -1145,10 +1141,6 @@ pub fn validateUserInputDidItFail(self: *Build) bool {...@@ -1145,10 +1141,6 @@ pub fn validateUserInputDidItFail(self: *Build) bool {
1145 return self.invalid_user_input;1141 return self.invalid_user_input;
1146}1142}
11471143
1148pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1149 return self.spawnChildEnvMap(null, self.env_map, argv);
1150}
1151
1152fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {1144fn allocPrintCmd(ally: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
1153 var buf = ArrayList(u8).init(ally);1145 var buf = ArrayList(u8).init(ally);
1154 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});1146 if (opt_cwd) |cwd| try buf.writer().print("cd {s} && ", .{cwd});
...@@ -1163,40 +1155,6 @@ fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {...@@ -1163,40 +1155,6 @@ fn printCmd(ally: Allocator, cwd: ?[]const u8, argv: []const []const u8) void {
1163 std.debug.print("{s}\n", .{text});1155 std.debug.print("{s}\n", .{text});
1164}1156}
11651157
1166pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1167 if (self.verbose) {
1168 printCmd(self.allocator, cwd, argv);
1169 }
1170
1171 if (!process.can_spawn)
1172 return error.ExecNotSupported;
1173
1174 var child = std.ChildProcess.init(argv, self.allocator);
1175 child.cwd = cwd;
1176 child.env_map = env_map;
1177
1178 const term = child.spawnAndWait() catch |err| {
1179 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1180 return err;
1181 };
1182
1183 switch (term) {
1184 .Exited => |code| {
1185 if (code != 0) {
1186 log.err("The following command exited with error code {}:", .{code});
1187 printCmd(self.allocator, cwd, argv);
1188 return error.UncleanExit;
1189 }
1190 },
1191 else => {
1192 log.err("The following command terminated unexpectedly:", .{});
1193 printCmd(self.allocator, cwd, argv);
1194
1195 return error.UncleanExit;
1196 },
1197 }
1198}
1199
1200pub fn installArtifact(self: *Build, artifact: *CompileStep) void {1158pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1201 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);1159 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1202}1160}
...@@ -1403,160 +1361,6 @@ pub fn execAllowFail(...@@ -1403,160 +1361,6 @@ pub fn execAllowFail(
1403 }1361 }
1404}1362}
14051363
1406/// This function is used exclusively for spawning and communicating with the zig compiler.
1407/// TODO: move to build_runner.zig
1408pub fn execFromStep(b: *Build, argv: []const []const u8, s: *Step, prog_node: *std.Progress.Node) ![]const u8 {
1409 assert(argv.len != 0);
1410
1411 if (b.verbose) {
1412 const text = try allocPrintCmd(b.allocator, null, argv);
1413 try s.result_error_msgs.append(b.allocator, text);
1414 }
1415
1416 if (!process.can_spawn) {
1417 try s.result_error_msgs.append(b.allocator, b.fmt("Unable to spawn the following command: cannot spawn child processes\n{s}", .{
1418 try allocPrintCmd(b.allocator, null, argv),
1419 }));
1420 return error.MakeFailed;
1421 }
1422
1423 var child = std.ChildProcess.init(argv, b.allocator);
1424 child.env_map = b.env_map;
1425 child.stdin_behavior = .Pipe;
1426 child.stdout_behavior = .Pipe;
1427 child.stderr_behavior = .Pipe;
1428
1429 try child.spawn();
1430
1431 var poller = std.io.poll(b.allocator, enum { stdout, stderr }, .{
1432 .stdout = child.stdout.?,
1433 .stderr = child.stderr.?,
1434 });
1435 defer poller.deinit();
1436
1437 try sendMessage(child.stdin.?, .update);
1438 try sendMessage(child.stdin.?, .exit);
1439
1440 const Header = std.zig.Server.Message.Header;
1441 var result: ?[]const u8 = null;
1442
1443 var node_name: std.ArrayListUnmanaged(u8) = .{};
1444 defer node_name.deinit(b.allocator);
1445 var sub_prog_node: ?std.Progress.Node = null;
1446 defer if (sub_prog_node) |*n| n.end();
1447
1448 while (try poller.poll()) {
1449 const stdout = poller.fifo(.stdout);
1450 const buf = stdout.readableSlice(0);
1451 assert(stdout.readableLength() == buf.len);
1452 if (buf.len >= @sizeOf(Header)) {
1453 const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
1454 const header_and_msg_len = header.bytes_len + @sizeOf(Header);
1455 if (buf.len >= header_and_msg_len) {
1456 const body = buf[@sizeOf(Header)..][0..header.bytes_len];
1457 switch (header.tag) {
1458 .zig_version => {
1459 if (!mem.eql(u8, builtin.zig_version_string, body)) {
1460 try s.result_error_msgs.append(
1461 b.allocator,
1462 b.fmt("zig version mismatch build runner vs compiler: '{s}' vs '{s}'", .{
1463 builtin.zig_version_string, body,
1464 }),
1465 );
1466 return error.MakeFailed;
1467 }
1468 },
1469 .error_bundle => {
1470 const EbHdr = std.zig.Server.Message.ErrorBundle;
1471 const eb_hdr = @ptrCast(*align(1) const EbHdr, body);
1472 const extra_bytes =
1473 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
1474 const string_bytes =
1475 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
1476 // TODO: use @ptrCast when the compiler supports it
1477 const unaligned_extra = mem.bytesAsSlice(u32, extra_bytes);
1478 const extra_array = try b.allocator.alloc(u32, unaligned_extra.len);
1479 // TODO: use @memcpy when it supports slices
1480 for (extra_array, unaligned_extra) |*dst, src| dst.* = src;
1481 s.result_error_bundle = .{
1482 .string_bytes = try b.allocator.dupe(u8, string_bytes),
1483 .extra = extra_array,
1484 };
1485 },
1486 .progress => {
1487 if (sub_prog_node) |*n| n.end();
1488 node_name.clearRetainingCapacity();
1489 try node_name.appendSlice(b.allocator, body);
1490 sub_prog_node = prog_node.start(node_name.items, 0);
1491 sub_prog_node.?.activate();
1492 },
1493 .emit_bin_path => {
1494 result = try b.allocator.dupe(u8, body);
1495 },
1496 _ => {
1497 // Unrecognized message.
1498 },
1499 }
1500 stdout.discard(header_and_msg_len);
1501 }
1502 }
1503 }
1504
1505 const stderr = poller.fifo(.stderr);
1506 if (stderr.readableLength() > 0) {
1507 try s.result_error_msgs.append(b.allocator, try stderr.toOwnedSlice());
1508 }
1509
1510 // Send EOF to stdin.
1511 child.stdin.?.close();
1512 child.stdin = null;
1513
1514 const term = try child.wait();
1515 switch (term) {
1516 .Exited => |code| {
1517 if (code != 0) {
1518 try s.result_error_msgs.append(b.allocator, b.fmt("the following command exited with error code {d}:\n{s}", .{
1519 code, try allocPrintCmd(b.allocator, null, argv),
1520 }));
1521 return error.MakeFailed;
1522 }
1523 },
1524 .Signal, .Stopped, .Unknown => |code| {
1525 _ = code;
1526 try s.result_error_msgs.append(b.allocator, b.fmt("the following command terminated unexpectedly:\n{s}", .{
1527 try allocPrintCmd(b.allocator, null, argv),
1528 }));
1529 return error.MakeFailed;
1530 },
1531 }
1532
1533 if (s.result_error_bundle.errorMessageCount() > 0) {
1534 try s.result_error_msgs.append(
1535 b.allocator,
1536 b.fmt("the following command failed with {d} compilation errors:\n{s}", .{
1537 s.result_error_bundle.errorMessageCount(),
1538 try allocPrintCmd(b.allocator, null, argv),
1539 }),
1540 );
1541 return error.MakeFailed;
1542 }
1543
1544 return result orelse {
1545 try s.result_error_msgs.append(b.allocator, b.fmt("the following command failed to communicate the compilation result:\n{s}", .{
1546 try allocPrintCmd(b.allocator, null, argv),
1547 }));
1548 return error.MakeFailed;
1549 };
1550}
1551
1552fn sendMessage(file: fs.File, tag: std.zig.Client.Message.Tag) !void {
1553 const header: std.zig.Client.Message.Header = .{
1554 .tag = tag,
1555 .bytes_len = 0,
1556 };
1557 try file.writeAll(std.mem.asBytes(&header));
1558}
1559
1560/// This is a helper function to be called from build.zig scripts, *not* from1364/// This is a helper function to be called from build.zig scripts, *not* from
1561/// inside step make() functions. If any errors occur, it fails the build with1365/// inside step make() functions. If any errors occur, it fails the build with
1562/// a helpful message.1366/// a helpful message.
...@@ -1910,14 +1714,12 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {...@@ -1910,14 +1714,12 @@ pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1910test {1714test {
1911 _ = CheckFileStep;1715 _ = CheckFileStep;
1912 _ = CheckObjectStep;1716 _ = CheckObjectStep;
1913 _ = EmulatableRunStep;
1914 _ = FmtStep;1717 _ = FmtStep;
1915 _ = InstallArtifactStep;1718 _ = InstallArtifactStep;
1916 _ = InstallDirStep;1719 _ = InstallDirStep;
1917 _ = InstallFileStep;1720 _ = InstallFileStep;
1918 _ = ObjCopyStep;1721 _ = ObjCopyStep;
1919 _ = CompileStep;1722 _ = CompileStep;
1920 _ = LogStep;
1921 _ = OptionsStep;1723 _ = OptionsStep;
1922 _ = RemoveDirStep;1724 _ = RemoveDirStep;
1923 _ = RunStep;1725 _ = RunStep;
lib/std/Build/CheckFileStep.zig+9-9
...@@ -8,26 +8,25 @@ const CheckFileStep = @This();...@@ -8,26 +8,25 @@ const CheckFileStep = @This();
8pub const base_id = .check_file;8pub const base_id = .check_file;
99
10step: Step,10step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,11expected_matches: []const []const u8,
13source: std.Build.FileSource,12source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,13max_bytes: usize = 20 * 1024 * 1024,
1514
16pub fn create(15pub fn create(
17 builder: *std.Build,16 owner: *std.Build,
18 source: std.Build.FileSource,17 source: std.Build.FileSource,
19 expected_matches: []const []const u8,18 expected_matches: []const []const u8,
20) *CheckFileStep {19) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");20 const self = owner.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{21 self.* = CheckFileStep{
23 .builder = builder,22 .step = Step.init(.{
24 .step = Step.init(builder.allocator, .{
25 .id = .check_file,23 .id = .check_file,
26 .name = "CheckFile",24 .name = "CheckFile",
25 .owner = owner,
27 .makeFn = make,26 .makeFn = make,
28 }),27 }),
29 .source = source.dupe(builder),28 .source = source.dupe(owner),
30 .expected_matches = builder.dupeStrings(expected_matches),29 .expected_matches = owner.dupeStrings(expected_matches),
31 };30 };
32 self.source.addStepDependencies(&self.step);31 self.source.addStepDependencies(&self.step);
33 return self;32 return self;
...@@ -35,10 +34,11 @@ pub fn create(...@@ -35,10 +34,11 @@ pub fn create(
3534
36fn make(step: *Step, prog_node: *std.Progress.Node) !void {35fn make(step: *Step, prog_node: *std.Progress.Node) !void {
37 _ = prog_node;36 _ = prog_node;
37 const b = step.owner;
38 const self = @fieldParentPtr(CheckFileStep, "step", step);38 const self = @fieldParentPtr(CheckFileStep, "step", step);
3939
40 const src_path = self.source.getPath(self.builder);40 const src_path = self.source.getPath(b);
41 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);41 const contents = try fs.cwd().readFileAlloc(b.allocator, src_path, self.max_bytes);
4242
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) {
lib/std/Build/CheckObjectStep.zig+22-15
...@@ -10,29 +10,31 @@ const CheckObjectStep = @This();...@@ -10,29 +10,31 @@ const CheckObjectStep = @This();
1010
11const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const Step = std.Build.Step;12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
1413
15pub const base_id = .check_object;14pub const base_id = .check_object;
1615
17step: Step,16step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,17source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,18max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),19checks: std.ArrayList(Check),
22dump_symtab: bool = false,20dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,21obj_format: std.Target.ObjectFormat,
2422
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {23pub fn create(
26 const gpa = builder.allocator;24 owner: *std.Build,
25 source: std.Build.FileSource,
26 obj_format: std.Target.ObjectFormat,
27) *CheckObjectStep {
28 const gpa = owner.allocator;
27 const self = gpa.create(CheckObjectStep) catch @panic("OOM");29 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
28 self.* = .{30 self.* = .{
29 .builder = builder,31 .step = Step.init(.{
30 .step = Step.init(gpa, .{
31 .id = .check_file,32 .id = .check_file,
32 .name = "CheckObject",33 .name = "CheckObject",
34 .owner = owner,
33 .makeFn = make,35 .makeFn = make,
34 }),36 }),
35 .source = source.dupe(builder),37 .source = source.dupe(owner),
36 .checks = std.ArrayList(Check).init(gpa),38 .checks = std.ArrayList(Check).init(gpa),
37 .obj_format = obj_format,39 .obj_format = obj_format,
38 };40 };
...@@ -42,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std...@@ -42,14 +44,18 @@ pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std
4244
43/// Runs and (optionally) compares the output of a binary.45/// Runs and (optionally) compares the output of a binary.
44/// Asserts `self` was generated from an executable step.46/// Asserts `self` was generated from an executable step.
45pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {47/// TODO this doesn't actually compare, and there's no apparent reason for it
48/// to depend on the check object step. I don't see why this function should exist,
49/// the caller could just add the run step directly.
50pub fn runAndCompare(self: *CheckObjectStep) *std.Build.RunStep {
46 const dependencies_len = self.step.dependencies.items.len;51 const dependencies_len = self.step.dependencies.items.len;
47 assert(dependencies_len > 0);52 assert(dependencies_len > 0);
48 const exe_step = self.step.dependencies.items[dependencies_len - 1];53 const exe_step = self.step.dependencies.items[dependencies_len - 1];
49 const exe = exe_step.cast(std.Build.CompileStep).?;54 const exe = exe_step.cast(std.Build.CompileStep).?;
50 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);55 const run = self.step.owner.addRunArtifact(exe);
51 emulatable_step.step.dependOn(&self.step);56 run.skip_foreign_checks = true;
52 return emulatable_step;57 run.step.dependOn(&self.step);
58 return run;
53}59}
5460
55/// There two types of actions currently suported:61/// There two types of actions currently suported:
...@@ -253,7 +259,7 @@ const Check = struct {...@@ -253,7 +259,7 @@ const Check = struct {
253259
254/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.260/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
255pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {261pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
256 var new_check = Check.create(self.builder);262 var new_check = Check.create(self.step.owner);
257 new_check.match(phrase);263 new_check.match(phrase);
258 self.checks.append(new_check) catch @panic("OOM");264 self.checks.append(new_check) catch @panic("OOM");
259}265}
...@@ -295,17 +301,18 @@ pub fn checkComputeCompare(...@@ -295,17 +301,18 @@ pub fn checkComputeCompare(
295 program: []const u8,301 program: []const u8,
296 expected: ComputeCompareExpected,302 expected: ComputeCompareExpected,
297) void {303) void {
298 var new_check = Check.create(self.builder);304 var new_check = Check.create(self.step.owner);
299 new_check.computeCmp(program, expected);305 new_check.computeCmp(program, expected);
300 self.checks.append(new_check) catch @panic("OOM");306 self.checks.append(new_check) catch @panic("OOM");
301}307}
302308
303fn make(step: *Step, prog_node: *std.Progress.Node) !void {309fn make(step: *Step, prog_node: *std.Progress.Node) !void {
304 _ = prog_node;310 _ = prog_node;
311 const b = step.owner;
312 const gpa = b.allocator;
305 const self = @fieldParentPtr(CheckObjectStep, "step", step);313 const self = @fieldParentPtr(CheckObjectStep, "step", step);
306314
307 const gpa = self.builder.allocator;315 const src_path = self.source.getPath(b);
308 const src_path = self.source.getPath(self.builder);
309 const contents = try fs.cwd().readFileAllocOptions(316 const contents = try fs.cwd().readFileAllocOptions(
310 gpa,317 gpa,
311 src_path,318 src_path,
lib/std/Build/CompileStep.zig+241-215
...@@ -22,7 +22,6 @@ const InstallDir = std.Build.InstallDir;...@@ -22,7 +22,6 @@ const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;22const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;23const GeneratedFile = std.Build.GeneratedFile;
24const ObjCopyStep = std.Build.ObjCopyStep;24const ObjCopyStep = std.Build.ObjCopyStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;25const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;26const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;27const OptionsStep = std.Build.OptionsStep;
...@@ -32,7 +31,6 @@ const CompileStep = @This();...@@ -32,7 +31,6 @@ const CompileStep = @This();
32pub const base_id: Step.Id = .compile;31pub const base_id: Step.Id = .compile;
3332
34step: Step,33step: Step,
35builder: *std.Build,
36name: []const u8,34name: []const u8,
37target: CrossTarget,35target: CrossTarget,
38target_info: NativeTargetInfo,36target_info: NativeTargetInfo,
...@@ -305,24 +303,23 @@ pub const EmitOption = union(enum) {...@@ -305,24 +303,23 @@ pub const EmitOption = union(enum) {
305 }303 }
306};304};
307305
308pub fn create(builder: *std.Build, options: Options) *CompileStep {306pub fn create(owner: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);307 const name = owner.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;308 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null;
311 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {309 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});310 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313 }311 }
314312
315 const step_name = builder.fmt("compile {s} {s} {s}", .{313 const step_name = owner.fmt("compile {s} {s} {s}", .{
316 name,314 name,
317 @tagName(options.optimize),315 @tagName(options.optimize),
318 options.target.zigTriple(builder.allocator) catch @panic("OOM"),316 options.target.zigTriple(owner.allocator) catch @panic("OOM"),
319 });317 });
320318
321 const self = builder.allocator.create(CompileStep) catch @panic("OOM");319 const self = owner.allocator.create(CompileStep) catch @panic("OOM");
322 self.* = CompileStep{320 self.* = CompileStep{
323 .strip = null,321 .strip = null,
324 .unwind_tables = null,322 .unwind_tables = null,
325 .builder = builder,
326 .verbose_link = false,323 .verbose_link = false,
327 .verbose_cc = false,324 .verbose_cc = false,
328 .optimize = options.optimize,325 .optimize = options.optimize,
...@@ -331,27 +328,28 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -331,27 +328,28 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
331 .kind = options.kind,328 .kind = options.kind,
332 .root_src = root_src,329 .root_src = root_src,
333 .name = name,330 .name = name,
334 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),331 .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator),
335 .step = Step.init(builder.allocator, .{332 .step = Step.init(.{
336 .id = base_id,333 .id = base_id,
337 .name = step_name,334 .name = step_name,
335 .owner = owner,
338 .makeFn = make,336 .makeFn = make,
339 }),337 }),
340 .version = options.version,338 .version = options.version,
341 .out_filename = undefined,339 .out_filename = undefined,
342 .out_h_filename = builder.fmt("{s}.h", .{name}),340 .out_h_filename = owner.fmt("{s}.h", .{name}),
343 .out_lib_filename = undefined,341 .out_lib_filename = undefined,
344 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),342 .out_pdb_filename = owner.fmt("{s}.pdb", .{name}),
345 .major_only_filename = null,343 .major_only_filename = null,
346 .name_only_filename = null,344 .name_only_filename = null,
347 .modules = std.StringArrayHashMap(*Module).init(builder.allocator),345 .modules = std.StringArrayHashMap(*Module).init(owner.allocator),
348 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),346 .include_dirs = ArrayList(IncludeDir).init(owner.allocator),
349 .link_objects = ArrayList(LinkObject).init(builder.allocator),347 .link_objects = ArrayList(LinkObject).init(owner.allocator),
350 .c_macros = ArrayList([]const u8).init(builder.allocator),348 .c_macros = ArrayList([]const u8).init(owner.allocator),
351 .lib_paths = ArrayList([]const u8).init(builder.allocator),349 .lib_paths = ArrayList([]const u8).init(owner.allocator),
352 .rpaths = ArrayList([]const u8).init(builder.allocator),350 .rpaths = ArrayList([]const u8).init(owner.allocator),
353 .framework_dirs = ArrayList([]const u8).init(builder.allocator),351 .framework_dirs = ArrayList([]const u8).init(owner.allocator),
354 .installed_headers = ArrayList(*Step).init(builder.allocator),352 .installed_headers = ArrayList(*Step).init(owner.allocator),
355 .object_src = undefined,353 .object_src = undefined,
356 .c_std = std.Build.CStd.C99,354 .c_std = std.Build.CStd.C99,
357 .zig_lib_dir = null,355 .zig_lib_dir = null,
...@@ -382,9 +380,10 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {...@@ -382,9 +380,10 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep {
382}380}
383381
384fn computeOutFileNames(self: *CompileStep) void {382fn computeOutFileNames(self: *CompileStep) void {
383 const b = self.step.owner;
385 const target = self.target_info.target;384 const target = self.target_info.target;
386385
387 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{386 self.out_filename = std.zig.binNameAlloc(b.allocator, .{
388 .root_name = self.name,387 .root_name = self.name,
389 .target = target,388 .target = target,
390 .output_mode = switch (self.kind) {389 .output_mode = switch (self.kind) {
...@@ -404,30 +403,30 @@ fn computeOutFileNames(self: *CompileStep) void {...@@ -404,30 +403,30 @@ fn computeOutFileNames(self: *CompileStep) void {
404 self.out_lib_filename = self.out_filename;403 self.out_lib_filename = self.out_filename;
405 } else if (self.version) |version| {404 } else if (self.version) |version| {
406 if (target.isDarwin()) {405 if (target.isDarwin()) {
407 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{406 self.major_only_filename = b.fmt("lib{s}.{d}.dylib", .{
408 self.name,407 self.name,
409 version.major,408 version.major,
410 });409 });
411 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});410 self.name_only_filename = b.fmt("lib{s}.dylib", .{self.name});
412 self.out_lib_filename = self.out_filename;411 self.out_lib_filename = self.out_filename;
413 } else if (target.os.tag == .windows) {412 } else if (target.os.tag == .windows) {
414 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});413 self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
415 } else {414 } else {
416 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });415 self.major_only_filename = b.fmt("lib{s}.so.{d}", .{ self.name, version.major });
417 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});416 self.name_only_filename = b.fmt("lib{s}.so", .{self.name});
418 self.out_lib_filename = self.out_filename;417 self.out_lib_filename = self.out_filename;
419 }418 }
420 } else {419 } else {
421 if (target.isDarwin()) {420 if (target.isDarwin()) {
422 self.out_lib_filename = self.out_filename;421 self.out_lib_filename = self.out_filename;
423 } else if (target.os.tag == .windows) {422 } else if (target.os.tag == .windows) {
424 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});423 self.out_lib_filename = b.fmt("{s}.lib", .{self.name});
425 } else {424 } else {
426 self.out_lib_filename = self.out_filename;425 self.out_lib_filename = self.out_filename;
427 }426 }
428 }427 }
429 if (self.output_dir != null) {428 if (self.output_dir != null) {
430 self.output_lib_path_source.path = self.builder.pathJoin(429 self.output_lib_path_source.path = b.pathJoin(
431 &.{ self.output_dir.?, self.out_lib_filename },430 &.{ self.output_dir.?, self.out_lib_filename },
432 );431 );
433 }432 }
...@@ -435,17 +434,20 @@ fn computeOutFileNames(self: *CompileStep) void {...@@ -435,17 +434,20 @@ fn computeOutFileNames(self: *CompileStep) void {
435}434}
436435
437pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {436pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
438 self.output_dir = self.builder.dupePath(dir);437 const b = self.step.owner;
438 self.output_dir = b.dupePath(dir);
439}439}
440440
441pub fn install(self: *CompileStep) void {441pub fn install(self: *CompileStep) void {
442 self.builder.installArtifact(self);442 const b = self.step.owner;
443 b.installArtifact(self);
443}444}
444445
445pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {446pub fn installHeader(cs: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
446 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);447 const b = cs.step.owner;
447 a.builder.getInstallStep().dependOn(&install_file.step);448 const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
448 a.installed_headers.append(&install_file.step) catch @panic("OOM");449 b.getInstallStep().dependOn(&install_file.step);
450 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
449}451}
450452
451pub const InstallConfigHeaderOptions = struct {453pub const InstallConfigHeaderOptions = struct {
...@@ -459,13 +461,14 @@ pub fn installConfigHeader(...@@ -459,13 +461,14 @@ pub fn installConfigHeader(
459 options: InstallConfigHeaderOptions,461 options: InstallConfigHeaderOptions,
460) void {462) void {
461 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;463 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
462 const install_file = cs.builder.addInstallFileWithDir(464 const b = cs.step.owner;
465 const install_file = b.addInstallFileWithDir(
463 .{ .generated = &config_header.output_file },466 .{ .generated = &config_header.output_file },
464 options.install_dir,467 options.install_dir,
465 dest_rel_path,468 dest_rel_path,
466 );469 );
467 install_file.step.dependOn(&config_header.step);470 install_file.step.dependOn(&config_header.step);
468 cs.builder.getInstallStep().dependOn(&install_file.step);471 b.getInstallStep().dependOn(&install_file.step);
469 cs.installed_headers.append(&install_file.step) catch @panic("OOM");472 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
470}473}
471474
...@@ -482,91 +485,84 @@ pub fn installHeadersDirectory(...@@ -482,91 +485,84 @@ pub fn installHeadersDirectory(
482}485}
483486
484pub fn installHeadersDirectoryOptions(487pub fn installHeadersDirectoryOptions(
485 a: *CompileStep,488 cs: *CompileStep,
486 options: std.Build.InstallDirStep.Options,489 options: std.Build.InstallDirStep.Options,
487) void {490) void {
488 const install_dir = a.builder.addInstallDirectory(options);491 const b = cs.step.owner;
489 a.builder.getInstallStep().dependOn(&install_dir.step);492 const install_dir = b.addInstallDirectory(options);
490 a.installed_headers.append(&install_dir.step) catch @panic("OOM");493 b.getInstallStep().dependOn(&install_dir.step);
494 cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
491}495}
492496
493pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {497pub fn installLibraryHeaders(cs: *CompileStep, l: *CompileStep) void {
494 assert(l.kind == .lib);498 assert(l.kind == .lib);
495 const install_step = a.builder.getInstallStep();499 const b = cs.step.owner;
500 const install_step = b.getInstallStep();
496 // Copy each element from installed_headers, modifying the builder501 // Copy each element from installed_headers, modifying the builder
497 // to be the new parent's builder.502 // to be the new parent's builder.
498 for (l.installed_headers.items) |step| {503 for (l.installed_headers.items) |step| {
499 const step_copy = switch (step.id) {504 const step_copy = switch (step.id) {
500 inline .install_file, .install_dir => |id| blk: {505 inline .install_file, .install_dir => |id| blk: {
501 const T = id.Type();506 const T = id.Type();
502 const ptr = a.builder.allocator.create(T) catch @panic("OOM");507 const ptr = b.allocator.create(T) catch @panic("OOM");
503 ptr.* = step.cast(T).?.*;508 ptr.* = step.cast(T).?.*;
504 ptr.override_source_builder = ptr.builder;509 ptr.dest_builder = b;
505 ptr.builder = a.builder;
506 break :blk &ptr.step;510 break :blk &ptr.step;
507 },511 },
508 else => unreachable,512 else => unreachable,
509 };513 };
510 a.installed_headers.append(step_copy) catch @panic("OOM");514 cs.installed_headers.append(step_copy) catch @panic("OOM");
511 install_step.dependOn(step_copy);515 install_step.dependOn(step_copy);
512 }516 }
513 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");517 cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
514}518}
515519
516pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {520pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep {
521 const b = cs.step.owner;
517 var copy = options;522 var copy = options;
518 if (copy.basename == null) {523 if (copy.basename == null) {
519 if (options.format) |f| {524 if (options.format) |f| {
520 copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) });525 copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
521 } else {526 } else {
522 copy.basename = cs.name;527 copy.basename = cs.name;
523 }528 }
524 }529 }
525 return cs.builder.addObjCopy(cs.getOutputSource(), copy);530 return b.addObjCopy(cs.getOutputSource(), copy);
526}531}
527532
528/// Deprecated: use `std.Build.addRunArtifact`533/// Deprecated: use `std.Build.addRunArtifact`
529/// This function will run in the context of the package that created the executable,534/// This function will run in the context of the package that created the executable,
530/// which is undesirable when running an executable provided by a dependency package.535/// which is undesirable when running an executable provided by a dependency package.
531pub fn run(exe: *CompileStep) *RunStep {536pub fn run(cs: *CompileStep) *RunStep {
532 return exe.builder.addRunArtifact(exe);537 return cs.step.owner.addRunArtifact(cs);
533}
534
535/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
536/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
537/// When a binary cannot be ran through emulation or the option is disabled, a warning
538/// will be printed and the binary will *NOT* be ran.
539pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
540 assert(exe.kind == .exe or exe.kind == .test_exe);
541
542 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
543 if (exe.vcpkg_bin_path) |path| {
544 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
545 }
546 return run_step;
547}538}
548539
549pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {540pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
550 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);541 const b = self.step.owner;
542 return CheckObjectStep.create(b, self.getOutputSource(), obj_format);
551}543}
552544
553pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {545pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
554 self.linker_script = source.dupe(self.builder);546 const b = self.step.owner;
547 self.linker_script = source.dupe(b);
555 source.addStepDependencies(&self.step);548 source.addStepDependencies(&self.step);
556}549}
557550
558pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {551pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
559 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");552 const b = self.step.owner;
553 self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM");
560}554}
561555
562pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {556pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
563 self.frameworks.put(self.builder.dupe(framework_name), .{557 const b = self.step.owner;
558 self.frameworks.put(b.dupe(framework_name), .{
564 .needed = true,559 .needed = true,
565 }) catch @panic("OOM");560 }) catch @panic("OOM");
566}561}
567562
568pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {563pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
569 self.frameworks.put(self.builder.dupe(framework_name), .{564 const b = self.step.owner;
565 self.frameworks.put(b.dupe(framework_name), .{
570 .weak = true,566 .weak = true,
571 }) catch @panic("OOM");567 }) catch @panic("OOM");
572}568}
...@@ -619,21 +615,24 @@ pub fn linkLibCpp(self: *CompileStep) void {...@@ -619,21 +615,24 @@ pub fn linkLibCpp(self: *CompileStep) void {
619/// If the value is omitted, it is set to 1.615/// If the value is omitted, it is set to 1.
620/// `name` and `value` need not live longer than the function call.616/// `name` and `value` need not live longer than the function call.
621pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {617pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
622 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);618 const b = self.step.owner;
619 const macro = std.Build.constructCMacro(b.allocator, name, value);
623 self.c_macros.append(macro) catch @panic("OOM");620 self.c_macros.append(macro) catch @panic("OOM");
624}621}
625622
626/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.623/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
627pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {624pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
628 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");625 const b = self.step.owner;
626 self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM");
629}627}
630628
631/// This one has no integration with anything, it just puts -lname on the command line.629/// This one has no integration with anything, it just puts -lname on the command line.
632/// Prefer to use `linkSystemLibrary` instead.630/// Prefer to use `linkSystemLibrary` instead.
633pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {631pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
632 const b = self.step.owner;
634 self.link_objects.append(.{633 self.link_objects.append(.{
635 .system_lib = .{634 .system_lib = .{
636 .name = self.builder.dupe(name),635 .name = b.dupe(name),
637 .needed = false,636 .needed = false,
638 .weak = false,637 .weak = false,
639 .use_pkg_config = .no,638 .use_pkg_config = .no,
...@@ -644,9 +643,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {...@@ -644,9 +643,10 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
644/// This one has no integration with anything, it just puts -needed-lname on the command line.643/// This one has no integration with anything, it just puts -needed-lname on the command line.
645/// Prefer to use `linkSystemLibraryNeeded` instead.644/// Prefer to use `linkSystemLibraryNeeded` instead.
646pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {645pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
646 const b = self.step.owner;
647 self.link_objects.append(.{647 self.link_objects.append(.{
648 .system_lib = .{648 .system_lib = .{
649 .name = self.builder.dupe(name),649 .name = b.dupe(name),
650 .needed = true,650 .needed = true,
651 .weak = false,651 .weak = false,
652 .use_pkg_config = .no,652 .use_pkg_config = .no,
...@@ -657,9 +657,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {...@@ -657,9 +657,10 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
657/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the657/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
658/// command line. Prefer to use `linkSystemLibraryWeak` instead.658/// command line. Prefer to use `linkSystemLibraryWeak` instead.
659pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {659pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
660 const b = self.step.owner;
660 self.link_objects.append(.{661 self.link_objects.append(.{
661 .system_lib = .{662 .system_lib = .{
662 .name = self.builder.dupe(name),663 .name = b.dupe(name),
663 .needed = false,664 .needed = false,
664 .weak = true,665 .weak = true,
665 .use_pkg_config = .no,666 .use_pkg_config = .no,
...@@ -670,9 +671,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {...@@ -670,9 +671,10 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
670/// This links against a system library, exclusively using pkg-config to find the library.671/// This links against a system library, exclusively using pkg-config to find the library.
671/// Prefer to use `linkSystemLibrary` instead.672/// Prefer to use `linkSystemLibrary` instead.
672pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {673pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
674 const b = self.step.owner;
673 self.link_objects.append(.{675 self.link_objects.append(.{
674 .system_lib = .{676 .system_lib = .{
675 .name = self.builder.dupe(lib_name),677 .name = b.dupe(lib_name),
676 .needed = false,678 .needed = false,
677 .weak = false,679 .weak = false,
678 .use_pkg_config = .force,680 .use_pkg_config = .force,
...@@ -683,9 +685,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)...@@ -683,9 +685,10 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8)
683/// This links against a system library, exclusively using pkg-config to find the library.685/// This links against a system library, exclusively using pkg-config to find the library.
684/// Prefer to use `linkSystemLibraryNeeded` instead.686/// Prefer to use `linkSystemLibraryNeeded` instead.
685pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {687pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
688 const b = self.step.owner;
686 self.link_objects.append(.{689 self.link_objects.append(.{
687 .system_lib = .{690 .system_lib = .{
688 .name = self.builder.dupe(lib_name),691 .name = b.dupe(lib_name),
689 .needed = true,692 .needed = true,
690 .weak = false,693 .weak = false,
691 .use_pkg_config = .force,694 .use_pkg_config = .force,
...@@ -696,13 +699,14 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons...@@ -696,13 +699,14 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons
696/// Run pkg-config for the given library name and parse the output, returning the arguments699/// Run pkg-config for the given library name and parse the output, returning the arguments
697/// that should be passed to zig to link the given library.700/// that should be passed to zig to link the given library.
698pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {701pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
702 const b = self.step.owner;
699 const pkg_name = match: {703 const pkg_name = match: {
700 // First we have to map the library name to pkg config name. Unfortunately,704 // First we have to map the library name to pkg config name. Unfortunately,
701 // there are several examples where this is not straightforward:705 // there are several examples where this is not straightforward:
702 // -lSDL2 -> pkg-config sdl2706 // -lSDL2 -> pkg-config sdl2
703 // -lgdk-3 -> pkg-config gdk-3.0707 // -lgdk-3 -> pkg-config gdk-3.0
704 // -latk-1.0 -> pkg-config atk708 // -latk-1.0 -> pkg-config atk
705 const pkgs = try getPkgConfigList(self.builder);709 const pkgs = try getPkgConfigList(b);
706710
707 // Exact match means instant winner.711 // Exact match means instant winner.
708 for (pkgs) |pkg| {712 for (pkgs) |pkg| {
...@@ -742,7 +746,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -742,7 +746,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
742 };746 };
743747
744 var code: u8 = undefined;748 var code: u8 = undefined;
745 const stdout = if (self.builder.execAllowFail(&[_][]const u8{749 const stdout = if (b.execAllowFail(&[_][]const u8{
746 "pkg-config",750 "pkg-config",
747 pkg_name,751 pkg_name,
748 "--cflags",752 "--cflags",
...@@ -755,7 +759,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -755,7 +759,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
755 else => return err,759 else => return err,
756 };760 };
757761
758 var zig_args = ArrayList([]const u8).init(self.builder.allocator);762 var zig_args = ArrayList([]const u8).init(b.allocator);
759 defer zig_args.deinit();763 defer zig_args.deinit();
760764
761 var it = mem.tokenize(u8, stdout, " \r\n\t");765 var it = mem.tokenize(u8, stdout, " \r\n\t");
...@@ -780,7 +784,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u...@@ -780,7 +784,7 @@ pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u
780 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });784 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
781 } else if (mem.startsWith(u8, tok, "-D")) {785 } else if (mem.startsWith(u8, tok, "-D")) {
782 try zig_args.append(tok);786 try zig_args.append(tok);
783 } else if (self.builder.verbose) {787 } else if (b.verbose) {
784 log.warn("Ignoring pkg-config flag '{s}'", .{tok});788 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
785 }789 }
786 }790 }
...@@ -804,6 +808,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -804,6 +808,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
804 needed: bool = false,808 needed: bool = false,
805 weak: bool = false,809 weak: bool = false,
806}) void {810}) void {
811 const b = self.step.owner;
807 if (isLibCLibrary(name)) {812 if (isLibCLibrary(name)) {
808 self.linkLibC();813 self.linkLibC();
809 return;814 return;
...@@ -815,7 +820,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -815,7 +820,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
815820
816 self.link_objects.append(.{821 self.link_objects.append(.{
817 .system_lib = .{822 .system_lib = .{
818 .name = self.builder.dupe(name),823 .name = b.dupe(name),
819 .needed = opts.needed,824 .needed = opts.needed,
820 .weak = opts.weak,825 .weak = opts.weak,
821 .use_pkg_config = .yes,826 .use_pkg_config = .yes,
...@@ -824,26 +829,30 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {...@@ -824,26 +829,30 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
824}829}
825830
826pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {831pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
832 const b = self.step.owner;
827 assert(self.kind == .@"test" or self.kind == .test_exe);833 assert(self.kind == .@"test" or self.kind == .test_exe);
828 self.name_prefix = self.builder.dupe(text);834 self.name_prefix = b.dupe(text);
829}835}
830836
831pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {837pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
838 const b = self.step.owner;
832 assert(self.kind == .@"test" or self.kind == .test_exe);839 assert(self.kind == .@"test" or self.kind == .test_exe);
833 self.filter = if (text) |t| self.builder.dupe(t) else null;840 self.filter = if (text) |t| b.dupe(t) else null;
834}841}
835842
836pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {843pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
844 const b = self.step.owner;
837 assert(self.kind == .@"test" or self.kind == .test_exe);845 assert(self.kind == .@"test" or self.kind == .test_exe);
838 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;846 self.test_runner = if (path) |p| b.dupePath(p) else null;
839}847}
840848
841/// Handy when you have many C/C++ source files and want them all to have the same flags.849/// Handy when you have many C/C++ source files and want them all to have the same flags.
842pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {850pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
843 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");851 const b = self.step.owner;
852 const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM");
844853
845 const files_copy = self.builder.dupeStrings(files);854 const files_copy = b.dupeStrings(files);
846 const flags_copy = self.builder.dupeStrings(flags);855 const flags_copy = b.dupeStrings(flags);
847856
848 c_source_files.* = .{857 c_source_files.* = .{
849 .files = files_copy,858 .files = files_copy,
...@@ -860,8 +869,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con...@@ -860,8 +869,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con
860}869}
861870
862pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {871pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
863 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");872 const b = self.step.owner;
864 c_source_file.* = source.dupe(self.builder);873 const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM");
874 c_source_file.* = source.dupe(b);
865 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");875 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
866 source.source.addStepDependencies(&self.step);876 source.source.addStepDependencies(&self.step);
867}877}
...@@ -875,15 +885,18 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {...@@ -875,15 +885,18 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void {
875}885}
876886
877pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {887pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
878 self.zig_lib_dir = self.builder.dupePath(dir_path);888 const b = self.step.owner;
889 self.zig_lib_dir = b.dupePath(dir_path);
879}890}
880891
881pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {892pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
882 self.main_pkg_path = self.builder.dupePath(dir_path);893 const b = self.step.owner;
894 self.main_pkg_path = b.dupePath(dir_path);
883}895}
884896
885pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {897pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
886 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;898 const b = self.step.owner;
899 self.libc_file = if (libc_file) |f| f.dupe(b) else null;
887}900}
888901
889/// Returns the generated executable, library or object file.902/// Returns the generated executable, library or object file.
...@@ -914,13 +927,15 @@ pub fn getOutputPdbSource(self: *CompileStep) FileSource {...@@ -914,13 +927,15 @@ pub fn getOutputPdbSource(self: *CompileStep) FileSource {
914}927}
915928
916pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {929pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
930 const b = self.step.owner;
917 self.link_objects.append(.{931 self.link_objects.append(.{
918 .assembly_file = .{ .path = self.builder.dupe(path) },932 .assembly_file = .{ .path = b.dupe(path) },
919 }) catch @panic("OOM");933 }) catch @panic("OOM");
920}934}
921935
922pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {936pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
923 const source_duped = source.dupe(self.builder);937 const b = self.step.owner;
938 const source_duped = source.dupe(b);
924 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");939 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
925 source_duped.addStepDependencies(&self.step);940 source_duped.addStepDependencies(&self.step);
926}941}
...@@ -930,7 +945,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {...@@ -930,7 +945,8 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
930}945}
931946
932pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {947pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
933 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");948 const b = self.step.owner;
949 self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM");
934 source.addStepDependencies(&self.step);950 source.addStepDependencies(&self.step);
935}951}
936952
...@@ -945,11 +961,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");...@@ -945,11 +961,13 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath");
945pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");961pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
946962
947pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {963pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
948 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");964 const b = self.step.owner;
965 self.include_dirs.append(IncludeDir{ .raw_path_system = b.dupe(path) }) catch @panic("OOM");
949}966}
950967
951pub fn addIncludePath(self: *CompileStep, path: []const u8) void {968pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
952 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");969 const b = self.step.owner;
970 self.include_dirs.append(IncludeDir{ .raw_path = b.dupe(path) }) catch @panic("OOM");
953}971}
954972
955pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {973pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
...@@ -958,23 +976,27 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi...@@ -958,23 +976,27 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi
958}976}
959977
960pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {978pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
961 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");979 const b = self.step.owner;
980 self.lib_paths.append(b.dupe(path)) catch @panic("OOM");
962}981}
963982
964pub fn addRPath(self: *CompileStep, path: []const u8) void {983pub fn addRPath(self: *CompileStep, path: []const u8) void {
965 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");984 const b = self.step.owner;
985 self.rpaths.append(b.dupe(path)) catch @panic("OOM");
966}986}
967987
968pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {988pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
969 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");989 const b = self.step.owner;
990 self.framework_dirs.append(b.dupe(dir_path)) catch @panic("OOM");
970}991}
971992
972/// Adds a module to be used with `@import` and exposing it in the current993/// Adds a module to be used with `@import` and exposing it in the current
973/// package's module table using `name`.994/// package's module table using `name`.
974pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {995pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
975 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");996 const b = cs.step.owner;
997 cs.modules.put(b.dupe(name), module) catch @panic("OOM");
976998
977 var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator);999 var done = std.AutoHashMap(*Module, void).init(b.allocator);
978 defer done.deinit();1000 defer done.deinit();
979 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");1001 cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM");
980}1002}
...@@ -982,7 +1004,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {...@@ -982,7 +1004,8 @@ pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
982/// Adds a module to be used with `@import` without exposing it in the current1004/// Adds a module to be used with `@import` without exposing it in the current
983/// package's module table.1005/// package's module table.
984pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {1006pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
985 const module = cs.builder.createModule(options);1007 const b = cs.step.owner;
1008 const module = b.createModule(options);
986 return addModule(cs, name, module);1009 return addModule(cs, name, module);
987}1010}
9881011
...@@ -1002,12 +1025,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM...@@ -1002,12 +1025,13 @@ fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashM
1002/// If Vcpkg was found on the system, it will be added to include and lib1025/// If Vcpkg was found on the system, it will be added to include and lib
1003/// paths for the specified target.1026/// paths for the specified target.
1004pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {1027pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
1028 const b = self.step.owner;
1005 // Ideally in the Unattempted case we would call the function recursively1029 // Ideally in the Unattempted case we would call the function recursively
1006 // after findVcpkgRoot and have only one switch statement, but the compiler1030 // after findVcpkgRoot and have only one switch statement, but the compiler
1007 // cannot resolve the error set.1031 // cannot resolve the error set.
1008 switch (self.builder.vcpkg_root) {1032 switch (b.vcpkg_root) {
1009 .unattempted => {1033 .unattempted => {
1010 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|1034 b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root|
1011 VcpkgRoot{ .found = root }1035 VcpkgRoot{ .found = root }
1012 else1036 else
1013 .not_found;1037 .not_found;
...@@ -1016,31 +1040,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {...@@ -1016,31 +1040,32 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
1016 .found => {},1040 .found => {},
1017 }1041 }
10181042
1019 switch (self.builder.vcpkg_root) {1043 switch (b.vcpkg_root) {
1020 .unattempted => unreachable,1044 .unattempted => unreachable,
1021 .not_found => return error.VcpkgNotFound,1045 .not_found => return error.VcpkgNotFound,
1022 .found => |root| {1046 .found => |root| {
1023 const allocator = self.builder.allocator;1047 const allocator = b.allocator;
1024 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);1048 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1025 defer self.builder.allocator.free(triplet);1049 defer b.allocator.free(triplet);
10261050
1027 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });1051 const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" });
1028 errdefer allocator.free(include_path);1052 errdefer allocator.free(include_path);
1029 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });1053 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
10301054
1031 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });1055 const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" });
1032 try self.lib_paths.append(lib_path);1056 try self.lib_paths.append(lib_path);
10331057
1034 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });1058 self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" });
1035 },1059 },
1036 }1060 }
1037}1061}
10381062
1039pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {1063pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1064 const b = self.step.owner;
1040 assert(self.kind == .@"test");1065 assert(self.kind == .@"test");
1041 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");1066 const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1042 for (args, 0..) |arg, i| {1067 for (args, 0..) |arg, i| {
1043 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;1068 duped_args[i] = if (arg) |a| b.dupe(a) else null;
1044 }1069 }
1045 self.exec_cmd_args = duped_args;1070 self.exec_cmd_args = duped_args;
1046}1071}
...@@ -1055,16 +1080,17 @@ fn appendModuleArgs(...@@ -1055,16 +1080,17 @@ fn appendModuleArgs(
1055 cs: *CompileStep,1080 cs: *CompileStep,
1056 zig_args: *ArrayList([]const u8),1081 zig_args: *ArrayList([]const u8),
1057) error{OutOfMemory}!void {1082) error{OutOfMemory}!void {
1083 const b = cs.step.owner;
1058 // First, traverse the whole dependency graph and give every module a unique name, ideally one1084 // First, traverse the whole dependency graph and give every module a unique name, ideally one
1059 // named after what it's called somewhere in the graph. It will help here to have both a mapping1085 // named after what it's called somewhere in the graph. It will help here to have both a mapping
1060 // from module to name and a set of all the currently-used names.1086 // from module to name and a set of all the currently-used names.
1061 var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator);1087 var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator);
1062 var names = std.StringHashMap(void).init(cs.builder.allocator);1088 var names = std.StringHashMap(void).init(b.allocator);
10631089
1064 var to_name = std.ArrayList(struct {1090 var to_name = std.ArrayList(struct {
1065 name: []const u8,1091 name: []const u8,
1066 mod: *Module,1092 mod: *Module,
1067 }).init(cs.builder.allocator);1093 }).init(b.allocator);
1068 {1094 {
1069 var it = cs.modules.iterator();1095 var it = cs.modules.iterator();
1070 while (it.next()) |kv| {1096 while (it.next()) |kv| {
...@@ -1085,7 +1111,7 @@ fn appendModuleArgs(...@@ -1085,7 +1111,7 @@ fn appendModuleArgs(
1085 if (mod_names.contains(dep.mod)) continue;1111 if (mod_names.contains(dep.mod)) continue;
10861112
1087 // We'll use this buffer to store the name we decide on1113 // We'll use this buffer to store the name we decide on
1088 var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32);1114 var buf = try b.allocator.alloc(u8, dep.name.len + 32);
1089 // First, try just the exposed dependency name1115 // First, try just the exposed dependency name
1090 std.mem.copy(u8, buf, dep.name);1116 std.mem.copy(u8, buf, dep.name);
1091 var name = buf[0..dep.name.len];1117 var name = buf[0..dep.name.len];
...@@ -1122,15 +1148,15 @@ fn appendModuleArgs(...@@ -1122,15 +1148,15 @@ fn appendModuleArgs(
1122 const mod = kv.key_ptr.*;1148 const mod = kv.key_ptr.*;
1123 const name = kv.value_ptr.*;1149 const name = kv.value_ptr.*;
11241150
1125 const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies);1151 const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies);
1126 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));1152 const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder));
1127 try zig_args.append("--mod");1153 try zig_args.append("--mod");
1128 try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));1154 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src }));
1129 }1155 }
1130 }1156 }
11311157
1132 // Lastly, output the root dependencies1158 // Lastly, output the root dependencies
1133 const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules);1159 const deps_str = try constructDepString(b.allocator, mod_names, cs.modules);
1134 if (deps_str.len > 0) {1160 if (deps_str.len > 0) {
1135 try zig_args.append("--deps");1161 try zig_args.append("--deps");
1136 try zig_args.append(deps_str);1162 try zig_args.append(deps_str);
...@@ -1161,18 +1187,18 @@ fn constructDepString(...@@ -1161,18 +1187,18 @@ fn constructDepString(
1161}1187}
11621188
1163fn make(step: *Step, prog_node: *std.Progress.Node) !void {1189fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1190 const b = step.owner;
1164 const self = @fieldParentPtr(CompileStep, "step", step);1191 const self = @fieldParentPtr(CompileStep, "step", step);
1165 const builder = self.builder;
11661192
1167 if (self.root_src == null and self.link_objects.items.len == 0) {1193 if (self.root_src == null and self.link_objects.items.len == 0) {
1168 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});1194 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1169 return error.NeedAnObject;1195 return error.NeedAnObject;
1170 }1196 }
11711197
1172 var zig_args = ArrayList([]const u8).init(builder.allocator);1198 var zig_args = ArrayList([]const u8).init(b.allocator);
1173 defer zig_args.deinit();1199 defer zig_args.deinit();
11741200
1175 try zig_args.append(builder.zig_exe);1201 try zig_args.append(b.zig_exe);
11761202
1177 const cmd = switch (self.kind) {1203 const cmd = switch (self.kind) {
1178 .lib => "build-lib",1204 .lib => "build-lib",
...@@ -1183,15 +1209,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1183,15 +1209,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1183 };1209 };
1184 try zig_args.append(cmd);1210 try zig_args.append(cmd);
11851211
1186 if (builder.reference_trace) |some| {1212 if (b.reference_trace) |some| {
1187 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));1213 try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some}));
1188 }1214 }
11891215
1190 try addFlag(&zig_args, "LLVM", self.use_llvm);1216 try addFlag(&zig_args, "LLVM", self.use_llvm);
1191 try addFlag(&zig_args, "LLD", self.use_lld);1217 try addFlag(&zig_args, "LLD", self.use_lld);
11921218
1193 if (self.target.ofmt) |ofmt| {1219 if (self.target.ofmt) |ofmt| {
1194 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));1220 try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1195 }1221 }
11961222
1197 if (self.entry_symbol_name) |entry| {1223 if (self.entry_symbol_name) |entry| {
...@@ -1201,18 +1227,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1201,18 +1227,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
12011227
1202 if (self.stack_size) |stack_size| {1228 if (self.stack_size) |stack_size| {
1203 try zig_args.append("--stack");1229 try zig_args.append("--stack");
1204 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));1230 try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size}));
1205 }1231 }
12061232
1207 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));1233 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b));
12081234
1209 // We will add link objects from transitive dependencies, but we want to keep1235 // We will add link objects from transitive dependencies, but we want to keep
1210 // all link objects in the same order provided.1236 // all link objects in the same order provided.
1211 // This array is used to keep self.link_objects immutable.1237 // This array is used to keep self.link_objects immutable.
1212 var transitive_deps: TransitiveDeps = .{1238 var transitive_deps: TransitiveDeps = .{
1213 .link_objects = ArrayList(LinkObject).init(builder.allocator),1239 .link_objects = ArrayList(LinkObject).init(b.allocator),
1214 .seen_system_libs = StringHashMap(void).init(builder.allocator),1240 .seen_system_libs = StringHashMap(void).init(b.allocator),
1215 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),1241 .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator),
1216 .is_linking_libcpp = self.is_linking_libcpp,1242 .is_linking_libcpp = self.is_linking_libcpp,
1217 .is_linking_libc = self.is_linking_libc,1243 .is_linking_libc = self.is_linking_libc,
1218 .frameworks = &self.frameworks,1244 .frameworks = &self.frameworks,
...@@ -1225,14 +1251,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1225,14 +1251,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
12251251
1226 for (transitive_deps.link_objects.items) |link_object| {1252 for (transitive_deps.link_objects.items) |link_object| {
1227 switch (link_object) {1253 switch (link_object) {
1228 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),1254 .static_path => |static_path| try zig_args.append(static_path.getPath(b)),
12291255
1230 .other_step => |other| switch (other.kind) {1256 .other_step => |other| switch (other.kind) {
1231 .exe => @panic("Cannot link with an executable build artifact"),1257 .exe => @panic("Cannot link with an executable build artifact"),
1232 .test_exe => @panic("Cannot link with an executable build artifact"),1258 .test_exe => @panic("Cannot link with an executable build artifact"),
1233 .@"test" => @panic("Cannot link with a test"),1259 .@"test" => @panic("Cannot link with a test"),
1234 .obj => {1260 .obj => {
1235 try zig_args.append(other.getOutputSource().getPath(builder));1261 try zig_args.append(other.getOutputSource().getPath(b));
1236 },1262 },
1237 .lib => l: {1263 .lib => l: {
1238 if (self.isStaticLibrary() and other.isStaticLibrary()) {1264 if (self.isStaticLibrary() and other.isStaticLibrary()) {
...@@ -1240,7 +1266,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1240,7 +1266,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1240 break :l;1266 break :l;
1241 }1267 }
12421268
1243 const full_path_lib = other.getOutputLibSource().getPath(builder);1269 const full_path_lib = other.getOutputLibSource().getPath(b);
1244 try zig_args.append(full_path_lib);1270 try zig_args.append(full_path_lib);
12451271
1246 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {1272 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
...@@ -1262,7 +1288,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1262,7 +1288,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1262 break :prefix "-l";1288 break :prefix "-l";
1263 };1289 };
1264 switch (system_lib.use_pkg_config) {1290 switch (system_lib.use_pkg_config) {
1265 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),1291 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
1266 .yes, .force => {1292 .yes, .force => {
1267 if (self.runPkgConfig(system_lib.name)) |args| {1293 if (self.runPkgConfig(system_lib.name)) |args| {
1268 try zig_args.appendSlice(args);1294 try zig_args.appendSlice(args);
...@@ -1276,7 +1302,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1276,7 +1302,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1276 .yes => {1302 .yes => {
1277 // pkg-config failed, so fall back to linking the library1303 // pkg-config failed, so fall back to linking the library
1278 // by name directly.1304 // by name directly.
1279 try zig_args.append(builder.fmt("{s}{s}", .{1305 try zig_args.append(b.fmt("{s}{s}", .{
1280 prefix,1306 prefix,
1281 system_lib.name,1307 system_lib.name,
1282 }));1308 }));
...@@ -1299,7 +1325,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1299,7 +1325,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1299 try zig_args.append("--");1325 try zig_args.append("--");
1300 prev_has_extra_flags = false;1326 prev_has_extra_flags = false;
1301 }1327 }
1302 try zig_args.append(asm_file.getPath(builder));1328 try zig_args.append(asm_file.getPath(b));
1303 },1329 },
13041330
1305 .c_source_file => |c_source_file| {1331 .c_source_file => |c_source_file| {
...@@ -1316,7 +1342,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1316,7 +1342,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1316 }1342 }
1317 try zig_args.append("--");1343 try zig_args.append("--");
1318 }1344 }
1319 try zig_args.append(c_source_file.source.getPath(builder));1345 try zig_args.append(c_source_file.source.getPath(b));
1320 },1346 },
13211347
1322 .c_source_files => |c_source_files| {1348 .c_source_files => |c_source_files| {
...@@ -1334,7 +1360,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1334,7 +1360,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1334 try zig_args.append("--");1360 try zig_args.append("--");
1335 }1361 }
1336 for (c_source_files.files) |file| {1362 for (c_source_files.files) |file| {
1337 try zig_args.append(builder.pathFromRoot(file));1363 try zig_args.append(b.pathFromRoot(file));
1338 }1364 }
1339 },1365 },
1340 }1366 }
...@@ -1350,7 +1376,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1350,7 +1376,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
13501376
1351 if (self.image_base) |image_base| {1377 if (self.image_base) |image_base| {
1352 try zig_args.append("--image-base");1378 try zig_args.append("--image-base");
1353 try zig_args.append(builder.fmt("0x{x}", .{image_base}));1379 try zig_args.append(b.fmt("0x{x}", .{image_base}));
1354 }1380 }
13551381
1356 if (self.filter) |filter| {1382 if (self.filter) |filter| {
...@@ -1369,32 +1395,32 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1369,32 +1395,32 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
13691395
1370 if (self.test_runner) |test_runner| {1396 if (self.test_runner) |test_runner| {
1371 try zig_args.append("--test-runner");1397 try zig_args.append("--test-runner");
1372 try zig_args.append(builder.pathFromRoot(test_runner));1398 try zig_args.append(b.pathFromRoot(test_runner));
1373 }1399 }
13741400
1375 for (builder.debug_log_scopes) |log_scope| {1401 for (b.debug_log_scopes) |log_scope| {
1376 try zig_args.append("--debug-log");1402 try zig_args.append("--debug-log");
1377 try zig_args.append(log_scope);1403 try zig_args.append(log_scope);
1378 }1404 }
13791405
1380 if (builder.debug_compile_errors) {1406 if (b.debug_compile_errors) {
1381 try zig_args.append("--debug-compile-errors");1407 try zig_args.append("--debug-compile-errors");
1382 }1408 }
13831409
1384 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");1410 if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
1385 if (builder.verbose_air) try zig_args.append("--verbose-air");1411 if (b.verbose_air) try zig_args.append("--verbose-air");
1386 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");1412 if (b.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1387 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");1413 if (b.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1388 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");1414 if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1389 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");1415 if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
13901416
1391 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);1417 if (self.emit_analysis.getArg(b, "emit-analysis")) |arg| try zig_args.append(arg);
1392 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);1418 if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg);
1393 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);1419 if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg);
1394 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);1420 if (self.emit_docs.getArg(b, "emit-docs")) |arg| try zig_args.append(arg);
1395 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);1421 if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg);
1396 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);1422 if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1397 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);1423 if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg);
13981424
1399 if (self.emit_h) try zig_args.append("-femit-h");1425 if (self.emit_h) try zig_args.append("-femit-h");
14001426
...@@ -1435,31 +1461,31 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1435,31 +1461,31 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1435 }1461 }
1436 if (self.link_z_common_page_size) |size| {1462 if (self.link_z_common_page_size) |size| {
1437 try zig_args.append("-z");1463 try zig_args.append("-z");
1438 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));1464 try zig_args.append(b.fmt("common-page-size={d}", .{size}));
1439 }1465 }
1440 if (self.link_z_max_page_size) |size| {1466 if (self.link_z_max_page_size) |size| {
1441 try zig_args.append("-z");1467 try zig_args.append("-z");
1442 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));1468 try zig_args.append(b.fmt("max-page-size={d}", .{size}));
1443 }1469 }
14441470
1445 if (self.libc_file) |libc_file| {1471 if (self.libc_file) |libc_file| {
1446 try zig_args.append("--libc");1472 try zig_args.append("--libc");
1447 try zig_args.append(libc_file.getPath(builder));1473 try zig_args.append(libc_file.getPath(b));
1448 } else if (builder.libc_file) |libc_file| {1474 } else if (b.libc_file) |libc_file| {
1449 try zig_args.append("--libc");1475 try zig_args.append("--libc");
1450 try zig_args.append(libc_file);1476 try zig_args.append(libc_file);
1451 }1477 }
14521478
1453 switch (self.optimize) {1479 switch (self.optimize) {
1454 .Debug => {}, // Skip since it's the default.1480 .Debug => {}, // Skip since it's the default.
1455 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),1481 else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
1456 }1482 }
14571483
1458 try zig_args.append("--cache-dir");1484 try zig_args.append("--cache-dir");
1459 try zig_args.append(builder.cache_root.path orelse ".");1485 try zig_args.append(b.cache_root.path orelse ".");
14601486
1461 try zig_args.append("--global-cache-dir");1487 try zig_args.append("--global-cache-dir");
1462 try zig_args.append(builder.global_cache_root.path orelse ".");1488 try zig_args.append(b.global_cache_root.path orelse ".");
14631489
1464 try zig_args.append("--name");1490 try zig_args.append("--name");
1465 try zig_args.append(self.name);1491 try zig_args.append(self.name);
...@@ -1471,11 +1497,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1471,11 +1497,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1471 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {1497 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1472 if (self.version) |version| {1498 if (self.version) |version| {
1473 try zig_args.append("--version");1499 try zig_args.append("--version");
1474 try zig_args.append(builder.fmt("{}", .{version}));1500 try zig_args.append(b.fmt("{}", .{version}));
1475 }1501 }
14761502
1477 if (self.target.isDarwin()) {1503 if (self.target.isDarwin()) {
1478 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{1504 const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
1479 self.target.libPrefix(),1505 self.target.libPrefix(),
1480 self.name,1506 self.name,
1481 self.target.dynamicLibSuffix(),1507 self.target.dynamicLibSuffix(),
...@@ -1489,7 +1515,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1489,7 +1515,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1489 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });1515 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1490 }1516 }
1491 if (self.pagezero_size) |pagezero_size| {1517 if (self.pagezero_size) |pagezero_size| {
1492 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});1518 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size});
1493 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });1519 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1494 }1520 }
1495 if (self.search_strategy) |strat| switch (strat) {1521 if (self.search_strategy) |strat| switch (strat) {
...@@ -1497,7 +1523,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1497,7 +1523,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1497 .dylibs_first => try zig_args.append("-search_dylibs_first"),1523 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1498 };1524 };
1499 if (self.headerpad_size) |headerpad_size| {1525 if (self.headerpad_size) |headerpad_size| {
1500 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});1526 const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size});
1501 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });1527 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1502 }1528 }
1503 if (self.headerpad_max_install_names) {1529 if (self.headerpad_max_install_names) {
...@@ -1545,16 +1571,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1545,16 +1571,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1545 try zig_args.append("--export-table");1571 try zig_args.append("--export-table");
1546 }1572 }
1547 if (self.initial_memory) |initial_memory| {1573 if (self.initial_memory) |initial_memory| {
1548 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));1574 try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory}));
1549 }1575 }
1550 if (self.max_memory) |max_memory| {1576 if (self.max_memory) |max_memory| {
1551 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));1577 try zig_args.append(b.fmt("--max-memory={d}", .{max_memory}));
1552 }1578 }
1553 if (self.shared_memory) {1579 if (self.shared_memory) {
1554 try zig_args.append("--shared-memory");1580 try zig_args.append("--shared-memory");
1555 }1581 }
1556 if (self.global_base) |global_base| {1582 if (self.global_base) |global_base| {
1557 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));1583 try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
1558 }1584 }
15591585
1560 if (self.code_model != .default) {1586 if (self.code_model != .default) {
...@@ -1562,16 +1588,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1562,16 +1588,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1562 try zig_args.append(@tagName(self.code_model));1588 try zig_args.append(@tagName(self.code_model));
1563 }1589 }
1564 if (self.wasi_exec_model) |model| {1590 if (self.wasi_exec_model) |model| {
1565 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));1591 try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)}));
1566 }1592 }
1567 for (self.export_symbol_names) |symbol_name| {1593 for (self.export_symbol_names) |symbol_name| {
1568 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));1594 try zig_args.append(b.fmt("--export={s}", .{symbol_name}));
1569 }1595 }
15701596
1571 if (!self.target.isNative()) {1597 if (!self.target.isNative()) {
1572 try zig_args.appendSlice(&.{1598 try zig_args.appendSlice(&.{
1573 "-target", try self.target.zigTriple(builder.allocator),1599 "-target", try self.target.zigTriple(b.allocator),
1574 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),1600 "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()),
1575 });1601 });
15761602
1577 if (self.target.dynamic_linker.get()) |dynamic_linker| {1603 if (self.target.dynamic_linker.get()) |dynamic_linker| {
...@@ -1582,12 +1608,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1582,12 +1608,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
15821608
1583 if (self.linker_script) |linker_script| {1609 if (self.linker_script) |linker_script| {
1584 try zig_args.append("--script");1610 try zig_args.append("--script");
1585 try zig_args.append(linker_script.getPath(builder));1611 try zig_args.append(linker_script.getPath(b));
1586 }1612 }
15871613
1588 if (self.version_script) |version_script| {1614 if (self.version_script) |version_script| {
1589 try zig_args.append("--version-script");1615 try zig_args.append("--version-script");
1590 try zig_args.append(builder.pathFromRoot(version_script));1616 try zig_args.append(b.pathFromRoot(version_script));
1591 }1617 }
15921618
1593 if (self.kind == .@"test") {1619 if (self.kind == .@"test") {
...@@ -1603,23 +1629,23 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1603,23 +1629,23 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1603 } else {1629 } else {
1604 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;1630 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
16051631
1606 switch (builder.host.getExternalExecutor(self.target_info, .{1632 switch (b.host.getExternalExecutor(self.target_info, .{
1607 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,1633 .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
1608 .link_libc = transitive_deps.is_linking_libc,1634 .link_libc = transitive_deps.is_linking_libc,
1609 })) {1635 })) {
1610 .native => {},1636 .native => {},
1611 .bad_dl, .bad_os_or_cpu => {1637 .bad_dl, .bad_os_or_cpu => {
1612 try zig_args.append("--test-no-exec");1638 try zig_args.append("--test-no-exec");
1613 },1639 },
1614 .rosetta => if (builder.enable_rosetta) {1640 .rosetta => if (b.enable_rosetta) {
1615 try zig_args.append("--test-cmd-bin");1641 try zig_args.append("--test-cmd-bin");
1616 } else {1642 } else {
1617 try zig_args.append("--test-no-exec");1643 try zig_args.append("--test-no-exec");
1618 },1644 },
1619 .qemu => |bin_name| ok: {1645 .qemu => |bin_name| ok: {
1620 if (builder.enable_qemu) qemu: {1646 if (b.enable_qemu) qemu: {
1621 const glibc_dir_arg = if (need_cross_glibc)1647 const glibc_dir_arg = if (need_cross_glibc)
1622 builder.glibc_runtimes_dir orelse break :qemu1648 b.glibc_runtimes_dir orelse break :qemu
1623 else1649 else
1624 null;1650 null;
1625 try zig_args.append("--test-cmd");1651 try zig_args.append("--test-cmd");
...@@ -1636,7 +1662,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1636,7 +1662,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1636 "i686"1662 "i686"
1637 else1663 else
1638 @tagName(cpu_arch);1664 @tagName(cpu_arch);
1639 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{1665 const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{
1640 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),1666 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1641 });1667 });
16421668
...@@ -1650,14 +1676,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1650,14 +1676,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1650 }1676 }
1651 try zig_args.append("--test-no-exec");1677 try zig_args.append("--test-no-exec");
1652 },1678 },
1653 .wine => |bin_name| if (builder.enable_wine) {1679 .wine => |bin_name| if (b.enable_wine) {
1654 try zig_args.append("--test-cmd");1680 try zig_args.append("--test-cmd");
1655 try zig_args.append(bin_name);1681 try zig_args.append(bin_name);
1656 try zig_args.append("--test-cmd-bin");1682 try zig_args.append("--test-cmd-bin");
1657 } else {1683 } else {
1658 try zig_args.append("--test-no-exec");1684 try zig_args.append("--test-no-exec");
1659 },1685 },
1660 .wasmtime => |bin_name| if (builder.enable_wasmtime) {1686 .wasmtime => |bin_name| if (b.enable_wasmtime) {
1661 try zig_args.append("--test-cmd");1687 try zig_args.append("--test-cmd");
1662 try zig_args.append(bin_name);1688 try zig_args.append(bin_name);
1663 try zig_args.append("--test-cmd");1689 try zig_args.append("--test-cmd");
...@@ -1666,7 +1692,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1666,7 +1692,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1666 } else {1692 } else {
1667 try zig_args.append("--test-no-exec");1693 try zig_args.append("--test-no-exec");
1668 },1694 },
1669 .darling => |bin_name| if (builder.enable_darling) {1695 .darling => |bin_name| if (b.enable_darling) {
1670 try zig_args.append("--test-cmd");1696 try zig_args.append("--test-cmd");
1671 try zig_args.append(bin_name);1697 try zig_args.append(bin_name);
1672 try zig_args.append("--test-cmd-bin");1698 try zig_args.append("--test-cmd-bin");
...@@ -1685,18 +1711,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1685,18 +1711,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1685 switch (include_dir) {1711 switch (include_dir) {
1686 .raw_path => |include_path| {1712 .raw_path => |include_path| {
1687 try zig_args.append("-I");1713 try zig_args.append("-I");
1688 try zig_args.append(builder.pathFromRoot(include_path));1714 try zig_args.append(b.pathFromRoot(include_path));
1689 },1715 },
1690 .raw_path_system => |include_path| {1716 .raw_path_system => |include_path| {
1691 if (builder.sysroot != null) {1717 if (b.sysroot != null) {
1692 try zig_args.append("-iwithsysroot");1718 try zig_args.append("-iwithsysroot");
1693 } else {1719 } else {
1694 try zig_args.append("-isystem");1720 try zig_args.append("-isystem");
1695 }1721 }
16961722
1697 const resolved_include_path = builder.pathFromRoot(include_path);1723 const resolved_include_path = b.pathFromRoot(include_path);
16981724
1699 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {1725 const common_include_path = if (builtin.os.tag == .windows and b.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1700 // We need to check for disk designator and strip it out from dir path so1726 // We need to check for disk designator and strip it out from dir path so
1701 // that zig/clang can concat resolved_include_path with sysroot.1727 // that zig/clang can concat resolved_include_path with sysroot.
1702 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);1728 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
...@@ -1712,7 +1738,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1712,7 +1738,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1712 },1738 },
1713 .other_step => |other| {1739 .other_step => |other| {
1714 if (other.emit_h) {1740 if (other.emit_h) {
1715 const h_path = other.getOutputHSource().getPath(builder);1741 const h_path = other.getOutputHSource().getPath(b);
1716 try zig_args.append("-isystem");1742 try zig_args.append("-isystem");
1717 try zig_args.append(fs.path.dirname(h_path).?);1743 try zig_args.append(fs.path.dirname(h_path).?);
1718 }1744 }
...@@ -1721,8 +1747,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1721,8 +1747,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1721 try install_step.make(prog_node);1747 try install_step.make(prog_node);
1722 }1748 }
1723 try zig_args.append("-I");1749 try zig_args.append("-I");
1724 try zig_args.append(builder.pathJoin(&.{1750 try zig_args.append(b.pathJoin(&.{
1725 other.builder.install_prefix, "include",1751 other.step.owner.install_prefix, "include",
1726 }));1752 }));
1727 }1753 }
1728 },1754 },
...@@ -1751,7 +1777,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1751,7 +1777,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
17511777
1752 if (self.target.isDarwin()) {1778 if (self.target.isDarwin()) {
1753 for (self.framework_dirs.items) |dir| {1779 for (self.framework_dirs.items) |dir| {
1754 if (builder.sysroot != null) {1780 if (b.sysroot != null) {
1755 try zig_args.append("-iframeworkwithsysroot");1781 try zig_args.append("-iframeworkwithsysroot");
1756 } else {1782 } else {
1757 try zig_args.append("-iframework");1783 try zig_args.append("-iframework");
...@@ -1784,17 +1810,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1784,17 +1810,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1784 }1810 }
1785 }1811 }
17861812
1787 if (builder.sysroot) |sysroot| {1813 if (b.sysroot) |sysroot| {
1788 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });1814 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1789 }1815 }
17901816
1791 for (builder.search_prefixes.items) |search_prefix| {1817 for (b.search_prefixes.items) |search_prefix| {
1792 try zig_args.append("-L");1818 try zig_args.append("-L");
1793 try zig_args.append(builder.pathJoin(&.{1819 try zig_args.append(b.pathJoin(&.{
1794 search_prefix, "lib",1820 search_prefix, "lib",
1795 }));1821 }));
1796 try zig_args.append("-I");1822 try zig_args.append("-I");
1797 try zig_args.append(builder.pathJoin(&.{1823 try zig_args.append(b.pathJoin(&.{
1798 search_prefix, "include",1824 search_prefix, "include",
1799 }));1825 }));
1800 }1826 }
...@@ -1805,15 +1831,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1805,15 +1831,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
18051831
1806 if (self.zig_lib_dir) |dir| {1832 if (self.zig_lib_dir) |dir| {
1807 try zig_args.append("--zig-lib-dir");1833 try zig_args.append("--zig-lib-dir");
1808 try zig_args.append(builder.pathFromRoot(dir));1834 try zig_args.append(b.pathFromRoot(dir));
1809 } else if (builder.zig_lib_dir) |dir| {1835 } else if (b.zig_lib_dir) |dir| {
1810 try zig_args.append("--zig-lib-dir");1836 try zig_args.append("--zig-lib-dir");
1811 try zig_args.append(dir);1837 try zig_args.append(dir);
1812 }1838 }
18131839
1814 if (self.main_pkg_path) |dir| {1840 if (self.main_pkg_path) |dir| {
1815 try zig_args.append("--main-pkg-path");1841 try zig_args.append("--main-pkg-path");
1816 try zig_args.append(builder.pathFromRoot(dir));1842 try zig_args.append(b.pathFromRoot(dir));
1817 }1843 }
18181844
1819 try addFlag(&zig_args, "PIC", self.force_pic);1845 try addFlag(&zig_args, "PIC", self.force_pic);
...@@ -1846,15 +1872,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1846,15 +1872,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1846 args_length += arg.len + 1; // +1 to account for null terminator1872 args_length += arg.len + 1; // +1 to account for null terminator
1847 }1873 }
1848 if (args_length >= 30 * 1024) {1874 if (args_length >= 30 * 1024) {
1849 try builder.cache_root.handle.makePath("args");1875 try b.cache_root.handle.makePath("args");
18501876
1851 const args_to_escape = zig_args.items[2..];1877 const args_to_escape = zig_args.items[2..];
1852 var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len);1878 var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len);
1853 arg_blk: for (args_to_escape) |arg| {1879 arg_blk: for (args_to_escape) |arg| {
1854 for (arg, 0..) |c, arg_idx| {1880 for (arg, 0..) |c, arg_idx| {
1855 if (c == '\\' or c == '"') {1881 if (c == '\\' or c == '"') {
1856 // Slow path for arguments that need to be escaped. We'll need to allocate and copy1882 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1857 var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1);1883 var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1);
1858 const writer = escaped.writer();1884 const writer = escaped.writer();
1859 try writer.writeAll(arg[0..arg_idx]);1885 try writer.writeAll(arg[0..arg_idx]);
1860 for (arg[arg_idx..]) |to_escape| {1886 for (arg[arg_idx..]) |to_escape| {
...@@ -1870,8 +1896,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1870,8 +1896,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
18701896
1871 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with1897 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1872 // other zig build commands running in parallel.1898 // other zig build commands running in parallel.
1873 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);1899 const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items);
1874 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });1900 const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
18751901
1876 var args_hash: [Sha256.digest_length]u8 = undefined;1902 var args_hash: [Sha256.digest_length]u8 = undefined;
1877 Sha256.hash(args, &args_hash, .{});1903 Sha256.hash(args, &args_hash, .{});
...@@ -1883,18 +1909,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1883,18 +1909,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1883 );1909 );
18841910
1885 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;1911 const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1886 try builder.cache_root.handle.writeFile(args_file, args);1912 try b.cache_root.handle.writeFile(args_file, args);
18871913
1888 const resolved_args_file = try mem.concat(builder.allocator, u8, &.{1914 const resolved_args_file = try mem.concat(b.allocator, u8, &.{
1889 "@",1915 "@",
1890 try builder.cache_root.join(builder.allocator, &.{args_file}),1916 try b.cache_root.join(b.allocator, &.{args_file}),
1891 });1917 });
18921918
1893 zig_args.shrinkRetainingCapacity(2);1919 zig_args.shrinkRetainingCapacity(2);
1894 try zig_args.append(resolved_args_file);1920 try zig_args.append(resolved_args_file);
1895 }1921 }
18961922
1897 const output_bin_path = try builder.execFromStep(zig_args.items, &self.step, prog_node);1923 const output_bin_path = try step.evalZigProcess(zig_args.items, prog_node);
1898 const build_output_dir = fs.path.dirname(output_bin_path).?;1924 const build_output_dir = fs.path.dirname(output_bin_path).?;
18991925
1900 if (self.output_dir) |output_dir| {1926 if (self.output_dir) |output_dir| {
...@@ -1928,25 +1954,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1928,25 +1954,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
19281954
1929 // Update generated files1955 // Update generated files
1930 if (self.output_dir != null) {1956 if (self.output_dir != null) {
1931 self.output_path_source.path = builder.pathJoin(1957 self.output_path_source.path = b.pathJoin(
1932 &.{ self.output_dir.?, self.out_filename },1958 &.{ self.output_dir.?, self.out_filename },
1933 );1959 );
19341960
1935 if (self.emit_h) {1961 if (self.emit_h) {
1936 self.output_h_path_source.path = builder.pathJoin(1962 self.output_h_path_source.path = b.pathJoin(
1937 &.{ self.output_dir.?, self.out_h_filename },1963 &.{ self.output_dir.?, self.out_h_filename },
1938 );1964 );
1939 }1965 }
19401966
1941 if (self.target.isWindows() or self.target.isUefi()) {1967 if (self.target.isWindows() or self.target.isUefi()) {
1942 self.output_pdb_path_source.path = builder.pathJoin(1968 self.output_pdb_path_source.path = b.pathJoin(
1943 &.{ self.output_dir.?, self.out_pdb_filename },1969 &.{ self.output_dir.?, self.out_pdb_filename },
1944 );1970 );
1945 }1971 }
1946 }1972 }
19471973
1948 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {1974 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1949 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);1975 try doAtomicSymLinks(b.allocator, self.getOutputSource().getPath(b), self.major_only_filename.?, self.name_only_filename.?);
1950 }1976 }
1951}1977}
19521978
lib/std/Build/ConfigHeaderStep.zig+13-14
...@@ -34,7 +34,6 @@ pub const Value = union(enum) {...@@ -34,7 +34,6 @@ pub const Value = union(enum) {
34};34};
3535
36step: Step,36step: Step,
37builder: *std.Build,
38values: std.StringArrayHashMap(Value),37values: std.StringArrayHashMap(Value),
39output_file: std.Build.GeneratedFile,38output_file: std.Build.GeneratedFile,
4039
...@@ -49,8 +48,8 @@ pub const Options = struct {...@@ -49,8 +48,8 @@ pub const Options = struct {
49 first_ret_addr: ?usize = null,48 first_ret_addr: ?usize = null,
50};49};
5150
52pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {51pub fn create(owner: *std.Build, options: Options) *ConfigHeaderStep {
53 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");52 const self = owner.allocator.create(ConfigHeaderStep) catch @panic("OOM");
5453
55 var include_path: []const u8 = "config.h";54 var include_path: []const u8 = "config.h";
5655
...@@ -69,29 +68,28 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {...@@ -69,29 +68,28 @@ pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
69 }68 }
7069
71 const name = if (options.style.getFileSource()) |s|70 const name = if (options.style.getFileSource()) |s|
72 builder.fmt("configure {s} header {s} to {s}", .{71 owner.fmt("configure {s} header {s} to {s}", .{
73 @tagName(options.style), s.getDisplayName(), include_path,72 @tagName(options.style), s.getDisplayName(), include_path,
74 })73 })
75 else74 else
76 builder.fmt("configure {s} header to {s}", .{@tagName(options.style), include_path});75 owner.fmt("configure {s} header to {s}", .{ @tagName(options.style), include_path });
7776
78 self.* = .{77 self.* = .{
79 .builder = builder,78 .step = Step.init(.{
80 .step = Step.init(builder.allocator, .{
81 .id = base_id,79 .id = base_id,
82 .name = name,80 .name = name,
81 .owner = owner,
83 .makeFn = make,82 .makeFn = make,
84 .first_ret_addr = options.first_ret_addr orelse @returnAddress(),83 .first_ret_addr = options.first_ret_addr orelse @returnAddress(),
85 }),84 }),
86 .style = options.style,85 .style = options.style,
87 .values = std.StringArrayHashMap(Value).init(builder.allocator),86 .values = std.StringArrayHashMap(Value).init(owner.allocator),
8887
89 .max_bytes = options.max_bytes,88 .max_bytes = options.max_bytes,
90 .include_path = include_path,89 .include_path = include_path,
91 .output_file = .{ .step = &self.step },90 .output_file = .{ .step = &self.step },
92 };91 };
9392
94
95 return self;93 return self;
96}94}
9795
...@@ -161,8 +159,9 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v...@@ -161,8 +159,9 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v
161159
162fn make(step: *Step, prog_node: *std.Progress.Node) !void {160fn make(step: *Step, prog_node: *std.Progress.Node) !void {
163 _ = prog_node;161 _ = prog_node;
162 const b = step.owner;
164 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);163 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
165 const gpa = self.builder.allocator;164 const gpa = b.allocator;
166165
167 // The cache is used here not really as a way to speed things up - because writing166 // The cache is used here not really as a way to speed things up - because writing
168 // the data to a file would probably be very fast - but as a way to find a canonical167 // the data to a file would probably be very fast - but as a way to find a canonical
...@@ -191,13 +190,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -191,13 +190,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
191 switch (self.style) {190 switch (self.style) {
192 .autoconf => |file_source| {191 .autoconf => |file_source| {
193 try output.appendSlice(c_generated_line);192 try output.appendSlice(c_generated_line);
194 const src_path = file_source.getPath(self.builder);193 const src_path = file_source.getPath(b);
195 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);
196 try render_autoconf(contents, &output, self.values, src_path);195 try render_autoconf(contents, &output, self.values, src_path);
197 },196 },
198 .cmake => |file_source| {197 .cmake => |file_source| {
199 try output.appendSlice(c_generated_line);198 try output.appendSlice(c_generated_line);
200 const src_path = file_source.getPath(self.builder);199 const src_path = file_source.getPath(b);
201 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);
202 try render_cmake(contents, &output, self.values, src_path);201 try render_cmake(contents, &output, self.values, src_path);
203 },202 },
...@@ -222,7 +221,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -222,7 +221,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
222 .{std.fmt.fmtSliceHexLower(&digest)},221 .{std.fmt.fmtSliceHexLower(&digest)},
223 ) catch unreachable;222 ) catch unreachable;
224223
225 const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename });224 const output_dir = try b.cache_root.join(gpa, &.{ "o", &hash_basename });
226225
227 // If output_path has directory parts, deal with them. Example:226 // If output_path has directory parts, deal with them. Example:
228 // output_dir is zig-cache/o/HASH227 // output_dir is zig-cache/o/HASH
...@@ -242,7 +241,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -242,7 +241,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
242241
243 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);242 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
244243
245 self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{244 self.output_file.path = try std.fs.path.join(b.allocator, &.{
246 output_dir, self.include_path,245 output_dir, self.include_path,
247 });246 });
248}247}
lib/std/Build/EmulatableRunStep.zig deleted-218
...@@ -1,218 +0,0 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_action: RunStep.StdIoAction = .inherit,
39
40/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
41/// or through emulation.
42hide_foreign_binaries_warning: bool,
43
44/// Creates a step that will execute the given artifact. This step will allow running the
45/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
46/// When set to false, and the binary is foreign, running the executable is skipped.
47/// Asserts given artifact is an executable.
48pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
51
52 const option_name = "hide-foreign-warnings";
53 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
54 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
55 } else false;
56
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(builder.allocator, .{
60 .id = .emulatable_run,
61 .name = name,
62 .makeFn = make,
63 }),
64 .exe = artifact,
65 .env_map = null,
66 .cwd = null,
67 .hide_foreign_binaries_warning = hide_warnings,
68 };
69 self.step.dependOn(&artifact.step);
70
71 return self;
72}
73
74fn make(step: *Step, prog_node: *std.Progress.Node) !void {
75 _ = prog_node;
76 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
77 const host_info = self.builder.host;
78
79 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
80 defer argv_list.deinit();
81
82 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
83 switch (host_info.getExternalExecutor(self.exe.target_info, .{
84 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
85 .link_libc = self.exe.is_linking_libc,
86 })) {
87 .native => {},
88 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
89 .wine => |bin_name| if (self.builder.enable_wine) {
90 try argv_list.append(bin_name);
91 } else return,
92 .qemu => |bin_name| if (self.builder.enable_qemu) {
93 const glibc_dir_arg = if (need_cross_glibc)
94 self.builder.glibc_runtimes_dir orelse return
95 else
96 null;
97 try argv_list.append(bin_name);
98 if (glibc_dir_arg) |dir| {
99 // TODO look into making this a call to `linuxTriple`. This
100 // needs the directory to be called "i686" rather than
101 // "x86" which is why we do it manually here.
102 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
103 const cpu_arch = self.exe.target.getCpuArch();
104 const os_tag = self.exe.target.getOsTag();
105 const abi = self.exe.target.getAbi();
106 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
107 "i686"
108 else
109 @tagName(cpu_arch);
110 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
111 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
112 });
113
114 try argv_list.append("-L");
115 try argv_list.append(full_dir);
116 }
117 } else return warnAboutForeignBinaries(self),
118 .darling => |bin_name| if (self.builder.enable_darling) {
119 try argv_list.append(bin_name);
120 } else return warnAboutForeignBinaries(self),
121 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
122 try argv_list.append(bin_name);
123 try argv_list.append("--dir=.");
124 } else return warnAboutForeignBinaries(self),
125 else => return warnAboutForeignBinaries(self),
126 }
127
128 if (self.exe.target.isWindows()) {
129 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
130 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
131 }
132
133 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
134 try argv_list.append(executable_path);
135
136 try RunStep.runCommand(
137 argv_list.items,
138 self.builder,
139 self.expected_term,
140 self.stdout_action,
141 self.stderr_action,
142 .Inherit,
143 self.env_map,
144 self.cwd,
145 false,
146 );
147}
148
149pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
150 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
151}
152
153pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
154 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
155}
156
157fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
158 if (step.hide_foreign_binaries_warning) return;
159 const builder = step.builder;
160 const artifact = step.exe;
161
162 const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
163 const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
164 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
165 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
166 switch (builder.host.getExternalExecutor(target_info, .{
167 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
168 .link_libc = artifact.is_linking_libc,
169 })) {
170 .native => unreachable,
171 .bad_dl => |foreign_dl| {
172 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
173 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
174 host_dl, foreign_dl, host_dl,
175 });
176 },
177 .bad_os_or_cpu => {
178 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
179 host_name, foreign_name,
180 });
181 },
182 .darling => if (!builder.enable_darling) {
183 std.debug.print(
184 "the host system ({s}) does not appear to be capable of executing binaries " ++
185 "from the target ({s}). Consider enabling darling.\n",
186 .{ host_name, foreign_name },
187 );
188 },
189 .rosetta => if (!builder.enable_rosetta) {
190 std.debug.print(
191 "the host system ({s}) does not appear to be capable of executing binaries " ++
192 "from the target ({s}). Consider enabling rosetta.\n",
193 .{ host_name, foreign_name },
194 );
195 },
196 .wine => if (!builder.enable_wine) {
197 std.debug.print(
198 "the host system ({s}) does not appear to be capable of executing binaries " ++
199 "from the target ({s}). Consider enabling wine.\n",
200 .{ host_name, foreign_name },
201 );
202 },
203 .qemu => if (!builder.enable_qemu) {
204 std.debug.print(
205 "the host system ({s}) does not appear to be capable of executing binaries " ++
206 "from the target ({s}). Consider enabling qemu.\n",
207 .{ host_name, foreign_name },
208 );
209 },
210 .wasmtime => {
211 std.debug.print(
212 "the host system ({s}) does not appear to be capable of executing binaries " ++
213 "from the target ({s}). Consider enabling wasmtime.\n",
214 .{ host_name, foreign_name },
215 );
216 },
217 }
218}
lib/std/Build/FmtStep.zig+58-22
...@@ -1,37 +1,73 @@...@@ -1,37 +1,73 @@
1const std = @import("../std.zig");1//! This step has two modes:
2const Step = std.Build.Step;2//! * Modify mode: directly modify source files, formatting them in place.
3const FmtStep = @This();3//! * Check mode: fail the step if a non-conforming file is found.
4
5step: Step,
6paths: []const []const u8,
7exclude_paths: []const []const u8,
8check: bool,
49
5pub const base_id = .fmt;10pub const base_id = .fmt;
611
7step: Step,12pub const Options = struct {
8builder: *std.Build,13 paths: []const []const u8 = &.{},
9argv: [][]const u8,14 exclude_paths: []const []const u8 = &.{},
1015 /// If true, fails the build step when any non-conforming files are encountered.
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {16 check: bool = false,
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");17};
13 const name = "zig fmt";18
14 self.* = FmtStep{19pub fn create(owner: *std.Build, options: Options) *FmtStep {
15 .step = Step.init(builder.allocator, .{20 const self = owner.allocator.create(FmtStep) catch @panic("OOM");
16 .id = .fmt,21 const name = if (options.check) "zig fmt --check" else "zig fmt";
22 self.* = .{
23 .step = Step.init(.{
24 .id = base_id,
17 .name = name,25 .name = name,
26 .owner = owner,
18 .makeFn = make,27 .makeFn = make,
19 }),28 }),
20 .builder = builder,29 .paths = options.paths,
21 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),30 .exclude_paths = options.exclude_paths,
31 .check = options.check,
22 };32 };
23
24 self.argv[0] = builder.zig_exe;
25 self.argv[1] = "fmt";
26 for (paths, 0..) |path, i| {
27 self.argv[2 + i] = builder.pathFromRoot(path);
28 }
29 return self;33 return self;
30}34}
3135
32fn make(step: *Step, prog_node: *std.Progress.Node) !void {36fn make(step: *Step, prog_node: *std.Progress.Node) !void {
37 // zig fmt is fast enough that no progress is needed.
33 _ = prog_node;38 _ = prog_node;
39
40 // TODO: if check=false, this means we are modifying source files in place, which
41 // is an operation that could race against other operations also modifying source files
42 // in place. In this case, this step should obtain a write lock while making those
43 // modifications.
44
45 const b = step.owner;
46 const arena = b.allocator;
34 const self = @fieldParentPtr(FmtStep, "step", step);47 const self = @fieldParentPtr(FmtStep, "step", step);
3548
36 return self.builder.spawnChild(self.argv);49 var argv: std.ArrayListUnmanaged([]const u8) = .{};
50 try argv.ensureUnusedCapacity(arena, 2 + 1 + self.paths.len + 2 * self.exclude_paths.len);
51
52 argv.appendAssumeCapacity(b.zig_exe);
53 argv.appendAssumeCapacity("fmt");
54
55 if (self.check) {
56 argv.appendAssumeCapacity("--check");
57 }
58
59 for (self.paths) |p| {
60 argv.appendAssumeCapacity(b.pathFromRoot(p));
61 }
62
63 for (self.exclude_paths) |p| {
64 argv.appendAssumeCapacity("--exclude");
65 argv.appendAssumeCapacity(b.pathFromRoot(p));
66 }
67
68 return step.evalChildProcess(argv.items);
37}69}
70
71const std = @import("../std.zig");
72const Step = std.Build.Step;
73const FmtStep = @This();
lib/std/Build/InstallArtifactStep.zig+27-22
...@@ -7,23 +7,24 @@ const InstallArtifactStep = @This();...@@ -7,23 +7,24 @@ const InstallArtifactStep = @This();
7pub const base_id = .install_artifact;7pub const base_id = .install_artifact;
88
9step: Step,9step: Step,
10builder: *std.Build,10dest_builder: *std.Build,
11artifact: *CompileStep,11artifact: *CompileStep,
12dest_dir: InstallDir,12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,14h_dir: ?InstallDir,
1515
16pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {16pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;17 if (artifact.install_step) |s| return s;
1818
19 const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");19 const self = owner.allocator.create(InstallArtifactStep) catch @panic("OOM");
20 self.* = InstallArtifactStep{20 self.* = InstallArtifactStep{
21 .builder = builder,21 .step = Step.init(.{
22 .step = Step.init(builder.allocator, .{
23 .id = base_id,22 .id = base_id,
24 .name = builder.fmt("install {s}", .{artifact.name}),23 .name = owner.fmt("install {s}", .{artifact.name}),
24 .owner = owner,
25 .makeFn = make,25 .makeFn = make,
26 }),26 }),
27 .dest_builder = owner,
27 .artifact = artifact,28 .artifact = artifact,
28 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {29 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
29 .obj => @panic("Cannot install a .obj build artifact."),30 .obj => @panic("Cannot install a .obj build artifact."),
...@@ -43,48 +44,52 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep...@@ -43,48 +44,52 @@ pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep
43 self.step.dependOn(&artifact.step);44 self.step.dependOn(&artifact.step);
44 artifact.install_step = self;45 artifact.install_step = self;
4546
46 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);47 owner.pushInstalledFile(self.dest_dir, artifact.out_filename);
47 if (self.artifact.isDynamicLibrary()) {48 if (self.artifact.isDynamicLibrary()) {
48 if (artifact.major_only_filename) |name| {49 if (artifact.major_only_filename) |name| {
49 builder.pushInstalledFile(.lib, name);50 owner.pushInstalledFile(.lib, name);
50 }51 }
51 if (artifact.name_only_filename) |name| {52 if (artifact.name_only_filename) |name| {
52 builder.pushInstalledFile(.lib, name);53 owner.pushInstalledFile(.lib, name);
53 }54 }
54 if (self.artifact.target.isWindows()) {55 if (self.artifact.target.isWindows()) {
55 builder.pushInstalledFile(.lib, artifact.out_lib_filename);56 owner.pushInstalledFile(.lib, artifact.out_lib_filename);
56 }57 }
57 }58 }
58 if (self.pdb_dir) |pdb_dir| {59 if (self.pdb_dir) |pdb_dir| {
59 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);60 owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
60 }61 }
61 if (self.h_dir) |h_dir| {62 if (self.h_dir) |h_dir| {
62 builder.pushInstalledFile(h_dir, artifact.out_h_filename);63 owner.pushInstalledFile(h_dir, artifact.out_h_filename);
63 }64 }
64 return self;65 return self;
65}66}
6667
67fn make(step: *Step, prog_node: *std.Progress.Node) !void {68fn make(step: *Step, prog_node: *std.Progress.Node) !void {
68 _ = prog_node;69 _ = prog_node;
70 const src_builder = step.owner;
69 const self = @fieldParentPtr(InstallArtifactStep, "step", step);71 const self = @fieldParentPtr(InstallArtifactStep, "step", step);
70 const builder = self.builder;72 const dest_builder = self.dest_builder;
7173
72 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);74 const full_dest_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
73 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);75 try src_builder.updateFile(
76 self.artifact.getOutputSource().getPath(src_builder),
77 full_dest_path,
78 );
74 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {79 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
75 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);80 try CompileStep.doAtomicSymLinks(src_builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
76 }81 }
77 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {82 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
78 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);83 const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
79 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);84 try src_builder.updateFile(self.artifact.getOutputLibSource().getPath(src_builder), full_implib_path);
80 }85 }
81 if (self.pdb_dir) |pdb_dir| {86 if (self.pdb_dir) |pdb_dir| {
82 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);87 const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
83 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);88 try src_builder.updateFile(self.artifact.getOutputPdbSource().getPath(src_builder), full_pdb_path);
84 }89 }
85 if (self.h_dir) |h_dir| {90 if (self.h_dir) |h_dir| {
86 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);91 const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename);
87 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);92 try src_builder.updateFile(self.artifact.getOutputHSource().getPath(src_builder), full_h_path);
88 }93 }
89 self.artifact.installed_path = full_dest_path;94 self.artifact.installed_path = full_dest_path;
90}95}
lib/std/Build/InstallDirStep.zig+16-18
...@@ -7,11 +7,10 @@ const InstallDirStep = @This();...@@ -7,11 +7,10 @@ const InstallDirStep = @This();
7const log = std.log;7const log = std.log;
88
9step: Step,9step: Step,
10builder: *std.Build,
11options: Options,10options: Options,
12/// This is used by the build system when a file being installed comes from one11/// This is used by the build system when a file being installed comes from one
13/// package but is being installed by another.12/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,13dest_builder: *std.Build,
1514
16pub const base_id = .install_dir;15pub const base_id = .install_dir;
1716
...@@ -40,27 +39,26 @@ pub const Options = struct {...@@ -40,27 +39,26 @@ pub const Options = struct {
40 }39 }
41};40};
4241
43pub fn init(42pub fn init(owner: *std.Build, options: Options) InstallDirStep {
44 builder: *std.Build,43 owner.pushInstalledFile(options.install_dir, options.install_subdir);
45 options: Options,
46) InstallDirStep {
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);
48 return .{44 return .{
49 .builder = builder,45 .step = Step.init(.{
50 .step = Step.init(builder.allocator, .{
51 .id = .install_dir,46 .id = .install_dir,
52 .name = builder.fmt("install {s}/", .{options.source_dir}),47 .name = owner.fmt("install {s}/", .{options.source_dir}),
48 .owner = owner,
53 .makeFn = make,49 .makeFn = make,
54 }),50 }),
55 .options = options.dupe(builder),51 .options = options.dupe(owner),
52 .dest_builder = owner,
56 };53 };
57}54}
5855
59fn make(step: *Step, prog_node: *std.Progress.Node) !void {56fn make(step: *Step, prog_node: *std.Progress.Node) !void {
60 _ = prog_node;57 _ = prog_node;
61 const self = @fieldParentPtr(InstallDirStep, "step", step);58 const self = @fieldParentPtr(InstallDirStep, "step", step);
62 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);59 const dest_builder = self.dest_builder;
63 const src_builder = self.override_source_builder orelse self.builder;60 const dest_prefix = dest_builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
61 const src_builder = self.step.owner;
64 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);62 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
65 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {63 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
66 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{64 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
...@@ -69,7 +67,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -69,7 +67,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
69 return error.StepFailed;67 return error.StepFailed;
70 };68 };
71 defer src_dir.close();69 defer src_dir.close();
72 var it = try src_dir.walk(self.builder.allocator);70 var it = try src_dir.walk(dest_builder.allocator);
73 next_entry: while (try it.next()) |entry| {71 next_entry: while (try it.next()) |entry| {
74 for (self.options.exclude_extensions) |ext| {72 for (self.options.exclude_extensions) |ext| {
75 if (mem.endsWith(u8, entry.path, ext)) {73 if (mem.endsWith(u8, entry.path, ext)) {
...@@ -77,20 +75,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -77,20 +75,20 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
77 }75 }
78 }76 }
7977
80 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });78 const full_path = dest_builder.pathJoin(&.{ full_src_dir, entry.path });
81 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });79 const dest_path = dest_builder.pathJoin(&.{ dest_prefix, entry.path });
8280
83 switch (entry.kind) {81 switch (entry.kind) {
84 .Directory => try fs.cwd().makePath(dest_path),82 .Directory => try fs.cwd().makePath(dest_path),
85 .File => {83 .File => {
86 for (self.options.blank_extensions) |ext| {84 for (self.options.blank_extensions) |ext| {
87 if (mem.endsWith(u8, entry.path, ext)) {85 if (mem.endsWith(u8, entry.path, ext)) {
88 try self.builder.truncateFile(dest_path);86 try dest_builder.truncateFile(dest_path);
89 continue :next_entry;87 continue :next_entry;
90 }88 }
91 }89 }
9290
93 try self.builder.updateFile(full_path, dest_path);91 try dest_builder.updateFile(full_path, dest_path);
94 },92 },
95 else => continue,93 else => continue,
96 }94 }
lib/std/Build/InstallFileStep.zig+15-14
...@@ -7,39 +7,40 @@ const InstallFileStep = @This();...@@ -7,39 +7,40 @@ const InstallFileStep = @This();
7pub const base_id = .install_file;7pub const base_id = .install_file;
88
9step: Step,9step: Step,
10builder: *std.Build,
11source: FileSource,10source: FileSource,
12dir: InstallDir,11dir: InstallDir,
13dest_rel_path: []const u8,12dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one13/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.14/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,15dest_builder: *std.Build,
1716
18pub fn init(17pub fn init(
19 builder: *std.Build,18 owner: *std.Build,
20 source: FileSource,19 source: FileSource,
21 dir: InstallDir,20 dir: InstallDir,
22 dest_rel_path: []const u8,21 dest_rel_path: []const u8,
23) InstallFileStep {22) InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);23 owner.pushInstalledFile(dir, dest_rel_path);
25 return InstallFileStep{24 return InstallFileStep{
26 .builder = builder,25 .step = Step.init(.{
27 .step = Step.init(builder.allocator, .{26 .id = base_id,
28 .id = .install_file,27 .name = owner.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),
29 .name = builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }),28 .owner = owner,
30 .makeFn = make,29 .makeFn = make,
31 }),30 }),
32 .source = source.dupe(builder),31 .source = source.dupe(owner),
33 .dir = dir.dupe(builder),32 .dir = dir.dupe(owner),
34 .dest_rel_path = builder.dupePath(dest_rel_path),33 .dest_rel_path = owner.dupePath(dest_rel_path),
34 .dest_builder = owner,
35 };35 };
36}36}
3737
38fn make(step: *Step, prog_node: *std.Progress.Node) !void {38fn make(step: *Step, prog_node: *std.Progress.Node) !void {
39 _ = prog_node;39 _ = prog_node;
40 const src_builder = step.owner;
40 const self = @fieldParentPtr(InstallFileStep, "step", step);41 const self = @fieldParentPtr(InstallFileStep, "step", step);
41 const src_builder = self.override_source_builder orelse self.builder;42 const dest_builder = self.dest_builder;
42 const full_src_path = self.source.getPath2(src_builder, step);43 const full_src_path = self.source.getPath2(src_builder, step);
43 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);44 const full_dest_path = dest_builder.getInstallPath(self.dir, self.dest_rel_path);
44 try self.builder.updateFile(full_src_path, full_dest_path);45 try dest_builder.updateFile(full_src_path, full_dest_path);
45}46}
lib/std/Build/LogStep.zig deleted-28
...@@ -1,28 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub fn init(builder: *std.Build, data: []const u8) LogStep {
13 return LogStep{
14 .builder = builder,
15 .step = Step.init(builder.allocator, .{
16 .id = .log,
17 .name = builder.fmt("log {s}", .{data}),
18 .makeFn = make,
19 }),
20 .data = builder.dupe(data),
21 };
22}
23
24fn make(step: *Step, prog_node: *std.Progress.Node) anyerror!void {
25 _ = prog_node;
26 const self = @fieldParentPtr(LogStep, "step", step);
27 log.info("{s}", .{self.data});
28}
lib/std/Build/ObjCopyStep.zig+8-25
...@@ -21,7 +21,6 @@ pub const RawFormat = enum {...@@ -21,7 +21,6 @@ pub const RawFormat = enum {
21};21};
2222
23step: Step,23step: Step,
24builder: *std.Build,
25file_source: std.Build.FileSource,24file_source: std.Build.FileSource,
26basename: []const u8,25basename: []const u8,
27output_file: std.Build.GeneratedFile,26output_file: std.Build.GeneratedFile,
...@@ -38,18 +37,18 @@ pub const Options = struct {...@@ -38,18 +37,18 @@ pub const Options = struct {
38};37};
3938
40pub fn create(39pub fn create(
41 builder: *std.Build,40 owner: *std.Build,
42 file_source: std.Build.FileSource,41 file_source: std.Build.FileSource,
43 options: Options,42 options: Options,
44) *ObjCopyStep {43) *ObjCopyStep {
45 const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM");44 const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM");
46 self.* = ObjCopyStep{45 self.* = ObjCopyStep{
47 .step = Step.init(builder.allocator, .{46 .step = Step.init(.{
48 .id = base_id,47 .id = base_id,
49 .name = builder.fmt("objcopy {s}", .{file_source.getDisplayName()}),48 .name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}),
49 .owner = owner,
50 .makeFn = make,50 .makeFn = make,
51 }),51 }),
52 .builder = builder,
53 .file_source = file_source,52 .file_source = file_source,
54 .basename = options.basename orelse file_source.getDisplayName(),53 .basename = options.basename orelse file_source.getDisplayName(),
55 .output_file = std.Build.GeneratedFile{ .step = &self.step },54 .output_file = std.Build.GeneratedFile{ .step = &self.step },
...@@ -67,9 +66,8 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {...@@ -67,9 +66,8 @@ pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {
67}66}
6867
69fn make(step: *Step, prog_node: *std.Progress.Node) !void {68fn make(step: *Step, prog_node: *std.Progress.Node) !void {
70 _ = prog_node;69 const b = step.owner;
71 const self = @fieldParentPtr(ObjCopyStep, "step", step);70 const self = @fieldParentPtr(ObjCopyStep, "step", step);
72 const b = self.builder;
7371
74 var man = b.cache.obtain();72 var man = b.cache.obtain();
75 defer man.deinit();73 defer man.deinit();
...@@ -84,7 +82,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -84,7 +82,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
84 man.hash.addOptional(self.pad_to);82 man.hash.addOptional(self.pad_to);
85 man.hash.addOptional(self.format);83 man.hash.addOptional(self.format);
8684
87 if (man.hit() catch |err| failWithCacheError(man, err)) {85 if (try step.cacheHit(&man)) {
88 // Cache hit, skip subprocess execution.86 // Cache hit, skip subprocess execution.
89 const digest = man.final();87 const digest = man.final();
90 self.output_file.path = try b.cache_root.join(b.allocator, &.{88 self.output_file.path = try b.cache_root.join(b.allocator, &.{
...@@ -116,23 +114,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -116,23 +114,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
116 };114 };
117115
118 try argv.appendSlice(&.{ full_src_path, full_dest_path });116 try argv.appendSlice(&.{ full_src_path, full_dest_path });
119 _ = try self.builder.execFromStep(argv.items, &self.step);117 _ = try step.spawnZigProcess(argv.items, prog_node);
120118
121 self.output_file.path = full_dest_path;119 self.output_file.path = full_dest_path;
122 try man.writeManifest();120 try man.writeManifest();
123}121}
124
125/// TODO consolidate this with the same function in RunStep?
126/// Also properly deal with concurrency (see open PR)
127fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
128 const i = man.failed_file_index orelse failWithSimpleError(err);
129 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
130 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
131 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
132 std.process.exit(1);
133}
134
135fn failWithSimpleError(err: anyerror) noreturn {
136 std.debug.print("{s}\n", .{@errorName(err)});
137 std.process.exit(1);
138}
lib/std/Build/OptionsStep.zig+17-17
...@@ -12,25 +12,24 @@ pub const base_id = .options;...@@ -12,25 +12,24 @@ pub const base_id = .options;
1212
13step: Step,13step: Step,
14generated_file: GeneratedFile,14generated_file: GeneratedFile,
15builder: *std.Build,
1615
17contents: std.ArrayList(u8),16contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),17artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),18file_source_args: std.ArrayList(OptionFileSourceArg),
2019
21pub fn create(builder: *std.Build) *OptionsStep {20pub fn create(owner: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");21 const self = owner.allocator.create(OptionsStep) catch @panic("OOM");
23 self.* = .{22 self.* = .{
24 .builder = builder,23 .step = Step.init(.{
25 .step = Step.init(builder.allocator, .{
26 .id = base_id,24 .id = base_id,
27 .name = "options",25 .name = "options",
26 .owner = owner,
28 .makeFn = make,27 .makeFn = make,
29 }),28 }),
30 .generated_file = undefined,29 .generated_file = undefined,
31 .contents = std.ArrayList(u8).init(builder.allocator),30 .contents = std.ArrayList(u8).init(owner.allocator),
32 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),31 .artifact_args = std.ArrayList(OptionArtifactArg).init(owner.allocator),
33 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),32 .file_source_args = std.ArrayList(OptionFileSourceArg).init(owner.allocator),
34 };33 };
35 self.generated_file = .{ .step = &self.step };34 self.generated_file = .{ .step = &self.step };
3635
...@@ -196,7 +195,7 @@ pub fn addOptionFileSource(...@@ -196,7 +195,7 @@ pub fn addOptionFileSource(
196) void {195) void {
197 self.file_source_args.append(.{196 self.file_source_args.append(.{
198 .name = name,197 .name = name,
199 .source = source.dupe(self.builder),198 .source = source.dupe(self.step.owner),
200 }) catch @panic("OOM");199 }) catch @panic("OOM");
201 source.addStepDependencies(&self.step);200 source.addStepDependencies(&self.step);
202}201}
...@@ -204,12 +203,12 @@ pub fn addOptionFileSource(...@@ -204,12 +203,12 @@ pub fn addOptionFileSource(
204/// The value is the path in the cache dir.203/// The value is the path in the cache dir.
205/// Adds a dependency automatically.204/// Adds a dependency automatically.
206pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {205pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
207 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");206 self.artifact_args.append(.{ .name = self.step.owner.dupe(name), .artifact = artifact }) catch @panic("OOM");
208 self.step.dependOn(&artifact.step);207 self.step.dependOn(&artifact.step);
209}208}
210209
211pub fn createModule(self: *OptionsStep) *std.Build.Module {210pub fn createModule(self: *OptionsStep) *std.Build.Module {
212 return self.builder.createModule(.{211 return self.step.owner.createModule(.{
213 .source_file = self.getSource(),212 .source_file = self.getSource(),
214 .dependencies = &.{},213 .dependencies = &.{},
215 });214 });
...@@ -220,14 +219,17 @@ pub fn getSource(self: *OptionsStep) FileSource {...@@ -220,14 +219,17 @@ pub fn getSource(self: *OptionsStep) FileSource {
220}219}
221220
222fn make(step: *Step, prog_node: *std.Progress.Node) !void {221fn make(step: *Step, prog_node: *std.Progress.Node) !void {
222 // This step completes so quickly that no progress is necessary.
223 _ = prog_node;223 _ = prog_node;
224
225 const b = step.owner;
224 const self = @fieldParentPtr(OptionsStep, "step", step);226 const self = @fieldParentPtr(OptionsStep, "step", step);
225227
226 for (self.artifact_args.items) |item| {228 for (self.artifact_args.items) |item| {
227 self.addOption(229 self.addOption(
228 []const u8,230 []const u8,
229 item.name,231 item.name,
230 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),232 b.pathFromRoot(item.artifact.getOutputSource().getPath(b)),
231 );233 );
232 }234 }
233235
...@@ -235,20 +237,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -235,20 +237,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
235 self.addOption(237 self.addOption(
236 []const u8,238 []const u8,
237 item.name,239 item.name,
238 item.source.getPath(self.builder),240 item.source.getPath(b),
239 );241 );
240 }242 }
241243
242 var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{});244 var options_dir = try b.cache_root.handle.makeOpenPath("options", .{});
243 defer options_dir.close();245 defer options_dir.close();
244246
245 const basename = self.hashContentsToFileName();247 const basename = self.hashContentsToFileName();
246248
247 try options_dir.writeFile(&basename, self.contents.items);249 try options_dir.writeFile(&basename, self.contents.items);
248250
249 self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{251 self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename });
250 "options", &basename,
251 });
252}252}
253253
254fn hashContentsToFileName(self: *OptionsStep) [64]u8 {254fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
lib/std/Build/RemoveDirStep.zig+19-10
...@@ -7,28 +7,37 @@ const RemoveDirStep = @This();...@@ -7,28 +7,37 @@ const RemoveDirStep = @This();
7pub const base_id = .remove_dir;7pub const base_id = .remove_dir;
88
9step: Step,9step: Step,
10builder: *std.Build,
11dir_path: []const u8,10dir_path: []const u8,
1211
13pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {12pub fn init(owner: *std.Build, dir_path: []const u8) RemoveDirStep {
14 return RemoveDirStep{13 return RemoveDirStep{
15 .builder = builder,14 .step = Step.init(.{
16 .step = Step.init(builder.allocator, .{
17 .id = .remove_dir,15 .id = .remove_dir,
18 .name = builder.fmt("RemoveDir {s}", .{dir_path}),16 .name = owner.fmt("RemoveDir {s}", .{dir_path}),
17 .owner = owner,
19 .makeFn = make,18 .makeFn = make,
20 }),19 }),
21 .dir_path = builder.dupePath(dir_path),20 .dir_path = owner.dupePath(dir_path),
22 };21 };
23}22}
2423
25fn make(step: *Step, prog_node: *std.Progress.Node) !void {24fn make(step: *Step, prog_node: *std.Progress.Node) !void {
25 // TODO update progress node while walking file system.
26 // Should the standard library support this use case??
26 _ = prog_node;27 _ = prog_node;
28
29 const b = step.owner;
27 const self = @fieldParentPtr(RemoveDirStep, "step", step);30 const self = @fieldParentPtr(RemoveDirStep, "step", step);
2831
29 const full_path = self.builder.pathFromRoot(self.dir_path);32 b.build_root.handle.deleteTree(self.dir_path) catch |err| {
30 fs.cwd().deleteTree(full_path) catch |err| {33 if (b.build_root.path) |base| {
31 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });34 return step.fail("unable to recursively delete path '{s}/{s}': {s}", .{
32 return err;35 base, self.dir_path, @errorName(err),
36 });
37 } else {
38 return step.fail("unable to recursively delete path '{s}': {s}", .{
39 self.dir_path, @errorName(err),
40 });
41 }
33 };42 };
34}43}
lib/std/Build/RunStep.zig+295-216
...@@ -11,14 +11,11 @@ const EnvMap = process.EnvMap;...@@ -11,14 +11,11 @@ const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;12const ExecError = std.Build.ExecError;
1313
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
15
16const RunStep = @This();14const RunStep = @This();
1715
18pub const base_id: Step.Id = .run;16pub const base_id: Step.Id = .run;
1917
20step: Step,18step: Step,
21builder: *std.Build,
2219
23/// See also addArg and addArgs to modifying this directly20/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),21argv: ArrayList(Arg),
...@@ -29,35 +26,68 @@ cwd: ?[]const u8,...@@ -29,35 +26,68 @@ cwd: ?[]const u8,
29/// Override this field to modify the environment, or use setEnvironmentVariable26/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,27env_map: ?*EnvMap,
3128
32stdout_action: StdIoAction = .inherit,29/// Configures whether the RunStep is considered to have side-effects, and also
33stderr_action: StdIoAction = .inherit,30/// whether the RunStep will inherit stdio streams, forwarding them to the
3431/// parent process, in which case will require a global lock to prevent other
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,32/// steps from interfering with stdio while the subprocess associated with this
3633/// RunStep is running.
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution34/// If the RunStep is determined to not have side-effects, then execution will
38expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 },35/// be skipped if all output files are up-to-date and input files are
3936/// unchanged.
40/// Print the command before running it37stdio: StdIo = .infer_from_args,
41print: bool,
42/// Controls whether execution is skipped if the output file is up-to-date.
43/// The default is to always run if there is no output file, and to skip
44/// running if all output files are up-to-date.
45condition: enum { output_outdated, always } = .output_outdated,
4638
47/// Additional file paths relative to build.zig that, when modified, indicate39/// Additional file paths relative to build.zig that, when modified, indicate
48/// that the RunStep should be re-executed.40/// that the RunStep should be re-executed.
41/// If the RunStep is determined to have side-effects, this field is ignored
42/// and the RunStep is always executed when it appears in the build graph.
49extra_file_dependencies: []const []const u8 = &.{},43extra_file_dependencies: []const []const u8 = &.{},
5044
51/// After adding an output argument, this step will by default rename itself45/// After adding an output argument, this step will by default rename itself
52/// for a better display name in the build summary.46/// for a better display name in the build summary.
53/// This can be disabled by setting this to false.47/// This can be disabled by setting this to false.
54rename_step_with_output_arg: bool,48rename_step_with_output_arg: bool = true,
5549
56pub const StdIoAction = union(enum) {50/// If this is true, a RunStep which is configured to check the output of the
51/// executed binary will not fail the build if the binary cannot be executed
52/// due to being for a foreign binary to the host system which is running the
53/// build graph.
54/// 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/// Rosetta (macOS) and binfmt_misc (Linux).
57skip_foreign_checks: bool = false,
58
59/// If stderr or stdout exceeds this amount, the child process is killed and
60/// the step fails.
61max_stdio_size: usize = 10 * 1024 * 1024,
62
63pub const StdIo = union(enum) {
64 /// Whether the RunStep has side-effects will be determined by whether or not one
65 /// of the args is an output file (added with `addOutputFileArg`).
66 /// If the RunStep is determined to have side-effects, this is the same as `inherit`.
67 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
68 infer_from_args,
69 /// Causes the RunStep to be considered to have side-effects, and therefore
70 /// always execute when it appears in the build graph.
71 /// It also means that this step will obtain a global lock to prevent other
72 /// steps from running in the meantime.
73 /// The step will fail if the subprocess crashes or returns a non-zero exit code.
57 inherit,74 inherit,
58 ignore,75 /// Causes the RunStep to be considered to *not* have side-effects. The
59 expect_exact: []const u8,76 /// process will be re-executed if any of the input dependencies are
60 expect_matches: []const []const u8,77 /// modified. The exit code and standard I/O streams will be checked for
78 /// certain conditions, and the step will succeed or fail based on these
79 /// conditions.
80 /// Note that an explicit check for exit code 0 needs to be added to this
81 /// list if such a check is desireable.
82 check: []const Check,
83
84 pub const Check = union(enum) {
85 expect_stderr_exact: []const u8,
86 expect_stderr_match: []const u8,
87 expect_stdout_exact: []const u8,
88 expect_stdout_match: []const u8,
89 expect_term: std.ChildProcess.Term,
90 };
61};91};
6292
63pub const Arg = union(enum) {93pub const Arg = union(enum) {
...@@ -72,20 +102,20 @@ pub const Arg = union(enum) {...@@ -72,20 +102,20 @@ pub const Arg = union(enum) {
72 };102 };
73};103};
74104
75pub fn create(builder: *std.Build, name: []const u8) *RunStep {105pub fn create(owner: *std.Build, name: []const u8) *RunStep {
76 const self = builder.allocator.create(RunStep) catch @panic("OOM");106 const self = owner.allocator.create(RunStep) catch @panic("OOM");
77 self.* = .{107 self.* = .{
78 .builder = builder,108 .step = Step.init(.{
79 .step = Step.init(builder.allocator, .{
80 .id = base_id,109 .id = base_id,
81 .name = name,110 .name = name,
111 .owner = owner,
82 .makeFn = make,112 .makeFn = make,
83 }),113 }),
84 .argv = ArrayList(Arg).init(builder.allocator),114 .argv = ArrayList(Arg).init(owner.allocator),
85 .cwd = null,115 .cwd = null,
86 .env_map = null,116 .env_map = null,
87 .print = builder.verbose,
88 .rename_step_with_output_arg = true,117 .rename_step_with_output_arg = true,
118 .max_stdio_size = 10 * 1024 * 1024,
89 };119 };
90 return self;120 return self;
91}121}
...@@ -99,16 +129,17 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {...@@ -99,16 +129,17 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
99/// run, and returns a FileSource which can be used as inputs to other APIs129/// run, and returns a FileSource which can be used as inputs to other APIs
100/// throughout the build system.130/// throughout the build system.
101pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {131pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
102 const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");132 const b = rs.step.owner;
133 const generated_file = b.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
103 generated_file.* = .{ .step = &rs.step };134 generated_file.* = .{ .step = &rs.step };
104 rs.argv.append(.{ .output = .{135 rs.argv.append(.{ .output = .{
105 .generated_file = generated_file,136 .generated_file = generated_file,
106 .basename = rs.builder.dupe(basename),137 .basename = b.dupe(basename),
107 } }) catch @panic("OOM");138 } }) catch @panic("OOM");
108139
109 if (rs.rename_step_with_output_arg) {140 if (rs.rename_step_with_output_arg) {
110 rs.rename_step_with_output_arg = false;141 rs.rename_step_with_output_arg = false;
111 rs.step.name = rs.builder.fmt("{s} ({s})", .{ rs.step.name, basename });142 rs.step.name = b.fmt("{s} ({s})", .{ rs.step.name, basename });
112 }143 }
113144
114 return .{ .generated = generated_file };145 return .{ .generated = generated_file };
...@@ -116,13 +147,13 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource...@@ -116,13 +147,13 @@ pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource
116147
117pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {148pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
118 self.argv.append(Arg{149 self.argv.append(Arg{
119 .file_source = file_source.dupe(self.builder),150 .file_source = file_source.dupe(self.step.owner),
120 }) catch @panic("OOM");151 }) catch @panic("OOM");
121 file_source.addStepDependencies(&self.step);152 file_source.addStepDependencies(&self.step);
122}153}
123154
124pub fn addArg(self: *RunStep, arg: []const u8) void {155pub fn addArg(self: *RunStep, arg: []const u8) void {
125 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");156 self.argv.append(Arg{ .bytes = self.step.owner.dupe(arg) }) catch @panic("OOM");
126}157}
127158
128pub fn addArgs(self: *RunStep, args: []const []const u8) void {159pub fn addArgs(self: *RunStep, args: []const []const u8) void {
...@@ -132,13 +163,14 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {...@@ -132,13 +163,14 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void {
132}163}
133164
134pub fn clearEnvironment(self: *RunStep) void {165pub fn clearEnvironment(self: *RunStep) void {
135 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");166 const b = self.step.owner;
136 new_env_map.* = EnvMap.init(self.builder.allocator);167 const new_env_map = b.allocator.create(EnvMap) catch @panic("OOM");
168 new_env_map.* = EnvMap.init(b.allocator);
137 self.env_map = new_env_map;169 self.env_map = new_env_map;
138}170}
139171
140pub fn addPathDir(self: *RunStep, search_path: []const u8) void {172pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
141 addPathDirInternal(&self.step, self.builder, search_path);173 addPathDirInternal(&self.step, self.step.owner, search_path);
142}174}
143175
144/// For internal use only, users of `RunStep` should use `addPathDir` directly.176/// For internal use only, users of `RunStep` should use `addPathDir` directly.
...@@ -157,13 +189,12 @@ pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const...@@ -157,13 +189,12 @@ pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const
157}189}
158190
159pub fn getEnvMap(self: *RunStep) *EnvMap {191pub fn getEnvMap(self: *RunStep) *EnvMap {
160 return getEnvMapInternal(&self.step, self.builder.allocator);192 return getEnvMapInternal(&self.step, self.step.owner.allocator);
161}193}
162194
163fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {195fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
164 const maybe_env_map = switch (step.id) {196 const maybe_env_map = switch (step.id) {
165 .run => step.cast(RunStep).?.env_map,197 .run => step.cast(RunStep).?.env_map,
166 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
167 else => unreachable,198 else => unreachable,
168 };199 };
169 return maybe_env_map orelse {200 return maybe_env_map orelse {
...@@ -171,7 +202,6 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {...@@ -171,7 +202,6 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
171 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");202 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
172 switch (step.id) {203 switch (step.id) {
173 .run => step.cast(RunStep).?.env_map = env_map,204 .run => step.cast(RunStep).?.env_map = env_map,
174 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
175 else => unreachable,205 else => unreachable,
176 }206 }
177 return env_map;207 return env_map;
...@@ -179,41 +209,85 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {...@@ -179,41 +209,85 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
179}209}
180210
181pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {211pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
212 const b = self.step.owner;
182 const env_map = self.getEnvMap();213 const env_map = self.getEnvMap();
183 env_map.put(214 env_map.put(b.dupe(key), b.dupe(value)) catch @panic("unhandled error");
184 self.builder.dupe(key),
185 self.builder.dupe(value),
186 ) catch @panic("unhandled error");
187}215}
188216
189pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {217pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
190 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };218 const new_check: StdIo.Check = .{ .expect_stderr_exact = self.step.owner.dupe(bytes) };
219 self.addCheck(new_check);
191}220}
192221
193pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {222pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
194 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };223 const new_check: StdIo.Check = .{ .expect_stdout_exact = self.step.owner.dupe(bytes) };
224 self.addCheck(new_check);
195}225}
196226
197fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {227pub fn expectExitCode(self: *RunStep, code: u8) void {
198 return switch (action) {228 const new_check: StdIo.Check = .{ .expect_term = .{ .Exited = code } };
199 .ignore => .Ignore,229 self.addCheck(new_check);
200 .inherit => .Inherit,
201 .expect_exact, .expect_matches => .Pipe,
202 };
203}230}
204231
205fn needOutputCheck(self: RunStep) bool {232pub fn addCheck(self: *RunStep, new_check: StdIo.Check) void {
206 switch (self.condition) {233 const arena = self.step.owner.allocator;
207 .always => return false,234 switch (self.stdio) {
208 .output_outdated => {},235 .infer_from_args => {
236 const list = arena.create([1]StdIo.Check) catch @panic("OOM");
237 list.* = .{new_check};
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 },
245 else => @panic("illegal call to addCheck: conflicting helper method calls. Suggest to directly set stdio field of RunStep instead"),
209 }246 }
210 if (self.extra_file_dependencies.len > 0) return true;247}
248
249/// Returns whether the RunStep has side effects *other than* updating the output arguments.
250fn hasSideEffects(self: RunStep) bool {
251 return switch (self.stdio) {
252 .infer_from_args => !self.hasAnyOutputArgs(),
253 .inherit => true,
254 .check => false,
255 };
256}
211257
258fn hasAnyOutputArgs(self: RunStep) bool {
212 for (self.argv.items) |arg| switch (arg) {259 for (self.argv.items) |arg| switch (arg) {
213 .output => return true,260 .output => return true,
214 else => continue,261 else => continue,
215 };262 };
263 return false;
264}
216265
266fn checksContainStdout(checks: []const StdIo.Check) bool {
267 for (checks) |check| switch (check) {
268 .expect_stderr_exact,
269 .expect_stderr_match,
270 .expect_term,
271 => continue,
272
273 .expect_stdout_exact,
274 .expect_stdout_match,
275 => return true,
276 };
277 return false;
278}
279
280fn checksContainStderr(checks: []const StdIo.Check) bool {
281 for (checks) |check| switch (check) {
282 .expect_stdout_exact,
283 .expect_stdout_match,
284 .expect_term,
285 => continue,
286
287 .expect_stderr_exact,
288 .expect_stderr_match,
289 => return true,
290 };
217 return false;291 return false;
218}292}
219293
...@@ -223,16 +297,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -223,16 +297,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
223 // processes could use to supply progress updates.297 // processes could use to supply progress updates.
224 _ = prog_node;298 _ = prog_node;
225299
300 const b = step.owner;
226 const self = @fieldParentPtr(RunStep, "step", step);301 const self = @fieldParentPtr(RunStep, "step", step);
227 const need_output_check = self.needOutputCheck();302 const has_side_effects = self.hasSideEffects();
228303
229 var argv_list = ArrayList([]const u8).init(self.builder.allocator);304 var argv_list = ArrayList([]const u8).init(b.allocator);
230 var output_placeholders = ArrayList(struct {305 var output_placeholders = ArrayList(struct {
231 index: usize,306 index: usize,
232 output: Arg.Output,307 output: Arg.Output,
233 }).init(self.builder.allocator);308 }).init(b.allocator);
234309
235 var man = self.builder.cache.obtain();310 var man = b.cache.obtain();
236 defer man.deinit();311 defer man.deinit();
237312
238 for (self.argv.items) |arg| {313 for (self.argv.items) |arg| {
...@@ -242,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -242,7 +317,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
242 man.hash.addBytes(bytes);317 man.hash.addBytes(bytes);
243 },318 },
244 .file_source => |file| {319 .file_source => |file| {
245 const file_path = file.getPath(self.builder);320 const file_path = file.getPath(b);
246 try argv_list.append(file_path);321 try argv_list.append(file_path);
247 _ = try man.addFile(file_path, null);322 _ = try man.addFile(file_path, null);
248 },323 },
...@@ -252,7 +327,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -252,7 +327,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
252 self.addPathForDynLibs(artifact);327 self.addPathForDynLibs(artifact);
253 }328 }
254 const file_path = artifact.installed_path orelse329 const file_path = artifact.installed_path orelse
255 artifact.getOutputSource().getPath(self.builder);330 artifact.getOutputSource().getPath(b);
256331
257 try argv_list.append(file_path);332 try argv_list.append(file_path);
258333
...@@ -272,17 +347,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -272,17 +347,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
272 }347 }
273 }348 }
274349
275 if (need_output_check) {350 if (!has_side_effects) {
276 for (self.extra_file_dependencies) |file_path| {351 for (self.extra_file_dependencies) |file_path| {
277 _ = try man.addFile(self.builder.pathFromRoot(file_path), null);352 _ = try man.addFile(b.pathFromRoot(file_path), null);
278 }353 }
279354
280 if (man.hit() catch |err| failWithCacheError(man, err)) {355 if (try step.cacheHit(&man)) {
281 // cache hit, skip running command356 // cache hit, skip running command
282 const digest = man.final();357 const digest = man.final();
283 for (output_placeholders.items) |placeholder| {358 for (output_placeholders.items) |placeholder| {
284 placeholder.output.generated_file.path = try self.builder.cache_root.join(359 placeholder.output.generated_file.path = try b.cache_root.join(
285 self.builder.allocator,360 b.allocator,
286 &.{ "o", &digest, placeholder.output.basename },361 &.{ "o", &digest, placeholder.output.basename },
287 );362 );
288 }363 }
...@@ -292,8 +367,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -292,8 +367,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
292 const digest = man.final();367 const digest = man.final();
293368
294 for (output_placeholders.items) |placeholder| {369 for (output_placeholders.items) |placeholder| {
295 const output_path = try self.builder.cache_root.join(370 const output_path = try b.cache_root.join(
296 self.builder.allocator,371 b.allocator,
297 &.{ "o", &digest, placeholder.output.basename },372 &.{ "o", &digest, placeholder.output.basename },
298 );373 );
299 const output_dir = fs.path.dirname(output_path).?;374 const output_dir = fs.path.dirname(output_path).?;
...@@ -308,18 +383,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -308,18 +383,16 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
308 }383 }
309384
310 try runCommand(385 try runCommand(
386 step,
387 self.cwd,
311 argv_list.items,388 argv_list.items,
312 self.builder,
313 self.expected_term,
314 self.stdout_action,
315 self.stderr_action,
316 self.stdin_behavior,
317 self.env_map,389 self.env_map,
318 self.cwd,390 self.stdio,
319 self.print,391 has_side_effects,
392 self.max_stdio_size,
320 );393 );
321394
322 if (need_output_check) {395 if (!has_side_effects) {
323 try man.writeManifest();396 try man.writeManifest();
324 }397 }
325}398}
...@@ -369,165 +442,171 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)...@@ -369,165 +442,171 @@ fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term)
369 };442 };
370}443}
371444
372pub fn runCommand(445fn runCommand(
446 step: *Step,
447 opt_cwd: ?[]const u8,
373 argv: []const []const u8,448 argv: []const []const u8,
374 builder: *std.Build,
375 expected_term: ?std.ChildProcess.Term,
376 stdout_action: StdIoAction,
377 stderr_action: StdIoAction,
378 stdin_behavior: std.ChildProcess.StdIo,
379 env_map: ?*EnvMap,449 env_map: ?*EnvMap,
380 maybe_cwd: ?[]const u8,450 stdio: StdIo,
381 print: bool,451 has_side_effects: bool,
452 max_stdio_size: usize,
382) !void {453) !void {
383 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path;454 const b = step.owner;
384455 const arena = b.allocator;
385 if (!std.process.can_spawn) {456 const cwd = if (opt_cwd) |cwd| b.pathFromRoot(cwd) else b.build_root.path;
386 const cmd = try std.mem.join(builder.allocator, " ", argv);
387 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
388 @tagName(builtin.os.tag), cmd,
389 });
390 builder.allocator.free(cmd);
391 return ExecError.ExecNotSupported;
392 }
393457
394 var child = std.ChildProcess.init(argv, builder.allocator);458 try step.handleChildProcUnsupported(opt_cwd, argv);
395 child.cwd = cwd;459 try Step.handleVerbose(step.owner, opt_cwd, argv);
396 child.env_map = env_map orelse builder.env_map;
397460
398 child.stdin_behavior = stdin_behavior;461 var child = std.ChildProcess.init(argv, arena);
399 child.stdout_behavior = stdIoActionToBehavior(stdout_action);462 child.cwd = cwd;
400 child.stderr_behavior = stdIoActionToBehavior(stderr_action);463 child.env_map = env_map orelse b.env_map;
401
402 if (print)
403 printCmd(cwd, argv);
404464
405 child.spawn() catch |err| {465 child.stdin_behavior = switch (stdio) {
406 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });466 .infer_from_args => if (has_side_effects) .Inherit else .Ignore,
407 return err;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,
408 };479 };
409480
410 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).481 child.spawn() catch |err| return step.fail("unable to spawn {s}: {s}", .{
411482 argv[0], @errorName(err),
412 var stdout: ?[]const u8 = null;483 });
413 defer if (stdout) |s| builder.allocator.free(s);484
485 var stdout_bytes: ?[]const u8 = null;
486 var stderr_bytes: ?[]const u8 = null;
487
488 if (child.stdout) |stdout| {
489 if (child.stderr) |stderr| {
490 var poller = std.io.poll(arena, enum { stdout, stderr }, .{
491 .stdout = stdout,
492 .stderr = stderr,
493 });
494 defer poller.deinit();
495
496 while (try poller.poll()) {
497 if (poller.fifo(.stdout).count > max_stdio_size)
498 return error.StdoutStreamTooLong;
499 if (poller.fifo(.stderr).count > max_stdio_size)
500 return error.StderrStreamTooLong;
501 }
414502
415 switch (stdout_action) {503 stdout_bytes = try poller.fifo(.stdout).toOwnedSlice();
416 .expect_exact, .expect_matches => {504 stderr_bytes = try poller.fifo(.stderr).toOwnedSlice();
417 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);505 } else {
418 },506 stdout_bytes = try stdout.reader().readAllAlloc(arena, max_stdio_size);
419 .inherit, .ignore => {},507 }
508 } else if (child.stderr) |stderr| {
509 stderr_bytes = try stderr.reader().readAllAlloc(arena, max_stdio_size);
420 }510 }
421511
422 var stderr: ?[]const u8 = null;512 if (stderr_bytes) |stderr| if (stderr.len > 0) {
423 defer if (stderr) |s| builder.allocator.free(s);513 const stderr_is_diagnostic = switch (stdio) {
424514 .check => |checks| !checksContainStderr(checks),
425 switch (stderr_action) {515 else => true,
426 .expect_exact, .expect_matches => {516 };
427 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);517 if (stderr_is_diagnostic) {
428 },518 try step.result_error_msgs.append(arena, stderr);
429 .inherit, .ignore => {},519 }
430 }520 };
431521
432 const term = child.wait() catch |err| {522 const term = child.wait() catch |err| {
433 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });523 return step.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
434 return err;
435 };524 };
436525
437 if (!termMatches(expected_term, term)) {526 switch (stdio) {
438 std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) });527 .check => |checks| for (checks) |check| switch (check) {
439 printCmd(cwd, argv);528 .expect_stderr_exact => |expected_bytes| {
440 return error.UnexpectedExit;529 if (!mem.eql(u8, expected_bytes, stderr_bytes.?)) {
441 }530 return step.fail(
442531 \\========= expected this stderr: =========
443 switch (stderr_action) {532 \\{s}
444 .inherit, .ignore => {},533 \\========= but found: ====================
445 .expect_exact => |expected_bytes| {534 \\{s}
446 if (!mem.eql(u8, expected_bytes, stderr.?)) {535 \\========= from the following command: ===
447 std.debug.print(536 \\{s}
448 \\537 , .{
449 \\========= Expected this stderr: =========538 expected_bytes,
450 \\{s}539 stderr_bytes.?,
451 \\========= But found: ====================540 try Step.allocPrintCmd(arena, opt_cwd, argv),
452 \\{s}541 });
453 \\542 }
454 , .{ expected_bytes, stderr.? });543 },
455 printCmd(cwd, argv);544 .expect_stderr_match => |match| {
456 return error.TestFailed;545 if (mem.indexOf(u8, stderr_bytes.?, match) == null) {
457 }546 return step.fail(
458 },547 \\========= expected to find in stderr: =========
459 .expect_matches => |matches| for (matches) |match| {548 \\{s}
460 if (mem.indexOf(u8, stderr.?, match) == null) {549 \\========= but stderr does not contain it: =====
461 std.debug.print(550 \\{s}
462 \\551 \\========= from the following command: =========
463 \\========= Expected to find in stderr: =========552 \\{s}
464 \\{s}553 , .{
465 \\========= But stderr does not contain it: =====554 match,
466 \\{s}555 stderr_bytes.?,
467 \\556 try Step.allocPrintCmd(arena, opt_cwd, argv),
468 , .{ match, stderr.? });557 });
469 printCmd(cwd, argv);558 }
470 return error.TestFailed;559 },
471 }560 .expect_stdout_exact => |expected_bytes| {
472 },561 if (!mem.eql(u8, expected_bytes, stdout_bytes.?)) {
473 }562 return step.fail(
474563 \\========= expected this stdout: =========
475 switch (stdout_action) {564 \\{s}
476 .inherit, .ignore => {},565 \\========= but found: ====================
477 .expect_exact => |expected_bytes| {566 \\{s}
478 if (!mem.eql(u8, expected_bytes, stdout.?)) {567 \\========= from the following command: ===
479 std.debug.print(568 \\{s}
480 \\569 , .{
481 \\========= Expected this stdout: =========570 expected_bytes,
482 \\{s}571 stdout_bytes.?,
483 \\========= But found: ====================572 try Step.allocPrintCmd(arena, opt_cwd, argv),
484 \\{s}573 });
485 \\574 }
486 , .{ expected_bytes, stdout.? });575 },
487 printCmd(cwd, argv);576 .expect_stdout_match => |match| {
488 return error.TestFailed;577 if (mem.indexOf(u8, stdout_bytes.?, match) == null) {
489 }578 return step.fail(
579 \\========= expected to find in stdout: =========
580 \\{s}
581 \\========= but stdout does not contain it: =====
582 \\{s}
583 \\========= from the following command: =========
584 \\{s}
585 , .{
586 match,
587 stdout_bytes.?,
588 try Step.allocPrintCmd(arena, opt_cwd, argv),
589 });
590 }
591 },
592 .expect_term => |expected_term| {
593 if (!termMatches(expected_term, term)) {
594 return step.fail("the following command {} (expected {}):\n{s}", .{
595 fmtTerm(term),
596 fmtTerm(expected_term),
597 try Step.allocPrintCmd(arena, opt_cwd, argv),
598 });
599 }
600 },
490 },601 },
491 .expect_matches => |matches| for (matches) |match| {602 else => {
492 if (mem.indexOf(u8, stdout.?, match) == null) {603 try step.handleChildProcessTerm(term, opt_cwd, argv);
493 std.debug.print(
494 \\
495 \\========= Expected to find in stdout: =========
496 \\{s}
497 \\========= But stdout does not contain it: =====
498 \\{s}
499 \\
500 , .{ match, stdout.? });
501 printCmd(cwd, argv);
502 return error.TestFailed;
503 }
504 },604 },
505 }605 }
506}606}
507607
508fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
509 const i = man.failed_file_index orelse failWithSimpleError(err);
510 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
511 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
512 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
513 std.process.exit(1);
514}
515
516fn failWithSimpleError(err: anyerror) noreturn {
517 std.debug.print("{s}\n", .{@errorName(err)});
518 std.process.exit(1);
519}
520
521fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
522 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
523 for (argv) |arg| {
524 std.debug.print("{s} ", .{arg});
525 }
526 std.debug.print("\n", .{});
527}
528
529fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {608fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
530 addPathForDynLibsInternal(&self.step, self.builder, artifact);609 addPathForDynLibsInternal(&self.step, self.step.owner, artifact);
531}610}
532611
533/// This should only be used for internal usage, this is called automatically612/// This should only be used for internal usage, this is called automatically
lib/std/Build/Step.zig+236-5
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1id: Id,1id: Id,
2name: []const u8,2name: []const u8,
3owner: *Build,
3makeFn: MakeFn,4makeFn: MakeFn,
4dependencies: std.ArrayList(*Step),5dependencies: std.ArrayList(*Step),
5/// This field is empty during execution of the user's build script, and6/// This field is empty during execution of the user's build script, and
...@@ -39,7 +40,6 @@ pub const Id = enum {...@@ -39,7 +40,6 @@ pub const Id = enum {
39 translate_c,40 translate_c,
40 write_file,41 write_file,
41 run,42 run,
42 emulatable_run,
43 check_file,43 check_file,
44 check_object,44 check_object,
45 config_header,45 config_header,
...@@ -60,7 +60,6 @@ pub const Id = enum {...@@ -60,7 +60,6 @@ pub const Id = enum {
60 .translate_c => Build.TranslateCStep,60 .translate_c => Build.TranslateCStep,
61 .write_file => Build.WriteFileStep,61 .write_file => Build.WriteFileStep,
62 .run => Build.RunStep,62 .run => Build.RunStep,
63 .emulatable_run => Build.EmulatableRunStep,
64 .check_file => Build.CheckFileStep,63 .check_file => Build.CheckFileStep,
65 .check_object => Build.CheckObjectStep,64 .check_object => Build.CheckObjectStep,
66 .config_header => Build.ConfigHeaderStep,65 .config_header => Build.ConfigHeaderStep,
...@@ -74,11 +73,14 @@ pub const Id = enum {...@@ -74,11 +73,14 @@ pub const Id = enum {
74pub const Options = struct {73pub const Options = struct {
75 id: Id,74 id: Id,
76 name: []const u8,75 name: []const u8,
76 owner: *Build,
77 makeFn: MakeFn = makeNoOp,77 makeFn: MakeFn = makeNoOp,
78 first_ret_addr: ?usize = null,78 first_ret_addr: ?usize = null,
79};79};
8080
81pub fn init(allocator: Allocator, options: Options) Step {81pub fn init(options: Options) Step {
82 const arena = options.owner.allocator;
83
82 var addresses = [1]usize{0} ** n_debug_stack_frames;84 var addresses = [1]usize{0} ** n_debug_stack_frames;
83 const first_ret_addr = options.first_ret_addr orelse @returnAddress();85 const first_ret_addr = options.first_ret_addr orelse @returnAddress();
84 var stack_trace = std.builtin.StackTrace{86 var stack_trace = std.builtin.StackTrace{
...@@ -89,9 +91,10 @@ pub fn init(allocator: Allocator, options: Options) Step {...@@ -89,9 +91,10 @@ pub fn init(allocator: Allocator, options: Options) Step {
8991
90 return .{92 return .{
91 .id = options.id,93 .id = options.id,
92 .name = allocator.dupe(u8, options.name) catch @panic("OOM"),94 .name = arena.dupe(u8, options.name) catch @panic("OOM"),
95 .owner = options.owner,
93 .makeFn = options.makeFn,96 .makeFn = options.makeFn,
94 .dependencies = std.ArrayList(*Step).init(allocator),97 .dependencies = std.ArrayList(*Step).init(arena),
95 .dependants = .{},98 .dependants = .{},
96 .state = .precheck_unstarted,99 .state = .precheck_unstarted,
97 .debug_stack_trace = addresses,100 .debug_stack_trace = addresses,
...@@ -168,3 +171,231 @@ const std = @import("../std.zig");...@@ -168,3 +171,231 @@ const std = @import("../std.zig");
168const Build = std.Build;171const Build = std.Build;
169const Allocator = std.mem.Allocator;172const Allocator = std.mem.Allocator;
170const assert = std.debug.assert;173const assert = std.debug.assert;
174const builtin = @import("builtin");
175
176pub fn evalChildProcess(s: *Step, argv: []const []const u8) !void {
177 const arena = s.owner.allocator;
178
179 try handleChildProcUnsupported(s, null, argv);
180 try handleVerbose(s.owner, null, argv);
181
182 const result = std.ChildProcess.exec(.{
183 .allocator = arena,
184 .argv = argv,
185 }) catch |err| return s.fail("unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
186
187 if (result.stderr.len > 0) {
188 try s.result_error_msgs.append(arena, result.stderr);
189 }
190
191 try handleChildProcessTerm(s, result.term, null, argv);
192}
193
194pub fn fail(step: *Step, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, MakeFailed } {
195 const arena = step.owner.allocator;
196 const msg = try std.fmt.allocPrint(arena, fmt, args);
197 try step.result_error_msgs.append(arena, msg);
198 return error.MakeFailed;
199}
200
201/// Assumes that argv contains `--listen=-` and that the process being spawned
202/// is the zig compiler - the same version that compiled the build runner.
203pub fn evalZigProcess(
204 s: *Step,
205 argv: []const []const u8,
206 prog_node: *std.Progress.Node,
207) ![]const u8 {
208 assert(argv.len != 0);
209 const b = s.owner;
210 const arena = b.allocator;
211 const gpa = arena;
212
213 try handleChildProcUnsupported(s, null, argv);
214 try handleVerbose(s.owner, null, argv);
215
216 var child = std.ChildProcess.init(argv, arena);
217 child.env_map = b.env_map;
218 child.stdin_behavior = .Pipe;
219 child.stdout_behavior = .Pipe;
220 child.stderr_behavior = .Pipe;
221
222 child.spawn() catch |err| return s.fail("unable to spawn {s}: {s}", .{
223 argv[0], @errorName(err),
224 });
225
226 var poller = std.io.poll(gpa, enum { stdout, stderr }, .{
227 .stdout = child.stdout.?,
228 .stderr = child.stderr.?,
229 });
230 defer poller.deinit();
231
232 try sendMessage(child.stdin.?, .update);
233 try sendMessage(child.stdin.?, .exit);
234
235 const Header = std.zig.Server.Message.Header;
236 var result: ?[]const u8 = null;
237
238 var node_name: std.ArrayListUnmanaged(u8) = .{};
239 defer node_name.deinit(gpa);
240 var sub_prog_node: ?std.Progress.Node = null;
241 defer if (sub_prog_node) |*n| n.end();
242
243 while (try poller.poll()) {
244 const stdout = poller.fifo(.stdout);
245 const buf = stdout.readableSlice(0);
246 assert(stdout.readableLength() == buf.len);
247 if (buf.len >= @sizeOf(Header)) {
248 const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]);
249 const header_and_msg_len = header.bytes_len + @sizeOf(Header);
250 if (buf.len >= header_and_msg_len) {
251 const body = buf[@sizeOf(Header)..][0..header.bytes_len];
252 switch (header.tag) {
253 .zig_version => {
254 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
255 return s.fail(
256 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
257 .{ builtin.zig_version_string, body },
258 );
259 }
260 },
261 .error_bundle => {
262 const EbHdr = std.zig.Server.Message.ErrorBundle;
263 const eb_hdr = @ptrCast(*align(1) const EbHdr, body);
264 const extra_bytes =
265 body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
266 const string_bytes =
267 body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
268 // TODO: use @ptrCast when the compiler supports it
269 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
270 const extra_array = try arena.alloc(u32, unaligned_extra.len);
271 // TODO: use @memcpy when it supports slices
272 for (extra_array, unaligned_extra) |*dst, src| dst.* = src;
273 s.result_error_bundle = .{
274 .string_bytes = try arena.dupe(u8, string_bytes),
275 .extra = extra_array,
276 };
277 },
278 .progress => {
279 if (sub_prog_node) |*n| n.end();
280 node_name.clearRetainingCapacity();
281 try node_name.appendSlice(gpa, body);
282 sub_prog_node = prog_node.start(node_name.items, 0);
283 sub_prog_node.?.activate();
284 },
285 .emit_bin_path => {
286 result = try arena.dupe(u8, body);
287 },
288 _ => {
289 // Unrecognized message.
290 },
291 }
292 stdout.discard(header_and_msg_len);
293 }
294 }
295 }
296
297 const stderr = poller.fifo(.stderr);
298 if (stderr.readableLength() > 0) {
299 try s.result_error_msgs.append(arena, try stderr.toOwnedSlice());
300 }
301
302 // Send EOF to stdin.
303 child.stdin.?.close();
304 child.stdin = null;
305
306 const term = child.wait() catch |err| {
307 return s.fail("unable to wait for {s}: {s}", .{ argv[0], @errorName(err) });
308 };
309 try handleChildProcessTerm(s, term, null, argv);
310
311 if (s.result_error_bundle.errorMessageCount() > 0) {
312 return s.fail("the following command failed with {d} compilation errors:\n{s}", .{
313 s.result_error_bundle.errorMessageCount(),
314 try allocPrintCmd(arena, null, argv),
315 });
316 }
317
318 return result orelse return s.fail(
319 "the following command failed to communicate the compilation result:\n{s}",
320 .{try allocPrintCmd(arena, null, argv)},
321 );
322}
323
324fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
325 const header: std.zig.Client.Message.Header = .{
326 .tag = tag,
327 .bytes_len = 0,
328 };
329 try file.writeAll(std.mem.asBytes(&header));
330}
331
332pub fn handleVerbose(
333 b: *Build,
334 opt_cwd: ?[]const u8,
335 argv: []const []const u8,
336) error{OutOfMemory}!void {
337 if (b.verbose) {
338 // Intention of verbose is to print all sub-process command lines to
339 // stderr before spawning them.
340 const text = try allocPrintCmd(b.allocator, opt_cwd, argv);
341 std.debug.print("{s}\n", .{text});
342 }
343}
344
345pub inline fn handleChildProcUnsupported(
346 s: *Step,
347 opt_cwd: ?[]const u8,
348 argv: []const []const u8,
349) error{ OutOfMemory, MakeFailed }!void {
350 if (!std.process.can_spawn) {
351 return s.fail(
352 "unable to execute the following command: host cannot spawn child processes\n{s}",
353 .{try allocPrintCmd(s.owner.allocator, opt_cwd, argv)},
354 );
355 }
356}
357
358pub fn handleChildProcessTerm(
359 s: *Step,
360 term: std.ChildProcess.Term,
361 opt_cwd: ?[]const u8,
362 argv: []const []const u8,
363) error{ MakeFailed, OutOfMemory }!void {
364 const arena = s.owner.allocator;
365 switch (term) {
366 .Exited => |code| {
367 if (code != 0) {
368 return s.fail(
369 "the following command exited with error code {d}:\n{s}",
370 .{ code, try allocPrintCmd(arena, opt_cwd, argv) },
371 );
372 }
373 },
374 .Signal, .Stopped, .Unknown => {
375 return s.fail(
376 "the following command terminated unexpectedly:\n{s}",
377 .{try allocPrintCmd(arena, opt_cwd, argv)},
378 );
379 },
380 }
381}
382
383pub fn allocPrintCmd(arena: Allocator, opt_cwd: ?[]const u8, argv: []const []const u8) ![]u8 {
384 var buf: std.ArrayListUnmanaged(u8) = .{};
385 if (opt_cwd) |cwd| try buf.writer(arena).print("cd {s} && ", .{cwd});
386 for (argv) |arg| {
387 try buf.writer(arena).print("{s} ", .{arg});
388 }
389 return buf.toOwnedSlice(arena);
390}
391
392pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool {
393 return man.hit() catch |err| return failWithCacheError(s, man, err);
394}
395
396fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror {
397 const i = man.failed_file_index orelse return err;
398 const pp = man.files.items[i].prefixed_path orelse return err;
399 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
400 return s.fail("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
401}
lib/std/Build/TranslateCStep.zig+20-20
...@@ -11,7 +11,6 @@ const TranslateCStep = @This();...@@ -11,7 +11,6 @@ const TranslateCStep = @This();
11pub const base_id = .translate_c;11pub const base_id = .translate_c;
1212
13step: Step,13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,14source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),15include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),16c_macros: std.ArrayList([]const u8),
...@@ -26,19 +25,19 @@ pub const Options = struct {...@@ -26,19 +25,19 @@ pub const Options = struct {
26 optimize: std.builtin.OptimizeMode,25 optimize: std.builtin.OptimizeMode,
27};26};
2827
29pub fn create(builder: *std.Build, options: Options) *TranslateCStep {28pub fn create(owner: *std.Build, options: Options) *TranslateCStep {
30 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");29 const self = owner.allocator.create(TranslateCStep) catch @panic("OOM");
31 const source = options.source_file.dupe(builder);30 const source = options.source_file.dupe(owner);
32 self.* = TranslateCStep{31 self.* = TranslateCStep{
33 .step = Step.init(builder.allocator, .{32 .step = Step.init(.{
34 .id = .translate_c,33 .id = .translate_c,
35 .name = "translate-c",34 .name = "translate-c",
35 .owner = owner,
36 .makeFn = make,36 .makeFn = make,
37 }),37 }),
38 .builder = builder,
39 .source = source,38 .source = source,
40 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),39 .include_dirs = std.ArrayList([]const u8).init(owner.allocator),
41 .c_macros = std.ArrayList([]const u8).init(builder.allocator),40 .c_macros = std.ArrayList([]const u8).init(owner.allocator),
42 .out_basename = undefined,41 .out_basename = undefined,
43 .target = options.target,42 .target = options.target,
44 .optimize = options.optimize,43 .optimize = options.optimize,
...@@ -58,7 +57,7 @@ pub const AddExecutableOptions = struct {...@@ -58,7 +57,7 @@ pub const AddExecutableOptions = struct {
5857
59/// Creates a step to build an executable from the translated source.58/// Creates a step to build an executable from the translated source.
60pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {59pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
61 return self.builder.addExecutable(.{60 return self.step.owner.addExecutable(.{
62 .root_source_file = .{ .generated = &self.output_file },61 .root_source_file = .{ .generated = &self.output_file },
63 .name = options.name orelse "translated_c",62 .name = options.name orelse "translated_c",
64 .version = options.version,63 .version = options.version,
...@@ -69,30 +68,31 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp...@@ -69,30 +68,31 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp
69}68}
7069
71pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {70pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
72 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");71 self.include_dirs.append(self.step.owner.dupePath(include_dir)) catch @panic("OOM");
73}72}
7473
75pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {74pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
76 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));75 return CheckFileStep.create(self.step.owner, .{ .generated = &self.output_file }, self.step.owner.dupeStrings(expected_matches));
77}76}
7877
79/// If the value is omitted, it is set to 1.78/// If the value is omitted, it is set to 1.
80/// `name` and `value` need not live longer than the function call.79/// `name` and `value` need not live longer than the function call.
81pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {80pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
82 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);81 const macro = std.Build.constructCMacro(self.step.owner.allocator, name, value);
83 self.c_macros.append(macro) catch @panic("OOM");82 self.c_macros.append(macro) catch @panic("OOM");
84}83}
8584
86/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.85/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
87pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {86pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
88 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");87 self.c_macros.append(self.step.owner.dupe(name_and_value)) catch @panic("OOM");
89}88}
9089
91fn make(step: *Step, prog_node: *std.Progress.Node) !void {90fn make(step: *Step, prog_node: *std.Progress.Node) !void {
91 const b = step.owner;
92 const self = @fieldParentPtr(TranslateCStep, "step", step);92 const self = @fieldParentPtr(TranslateCStep, "step", step);
9393
94 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);94 var argv_list = std.ArrayList([]const u8).init(b.allocator);
95 try argv_list.append(self.builder.zig_exe);95 try argv_list.append(b.zig_exe);
96 try argv_list.append("translate-c");96 try argv_list.append("translate-c");
97 try argv_list.append("-lc");97 try argv_list.append("-lc");
9898
...@@ -101,12 +101,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -101,12 +101,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
101101
102 if (!self.target.isNative()) {102 if (!self.target.isNative()) {
103 try argv_list.append("-target");103 try argv_list.append("-target");
104 try argv_list.append(try self.target.zigTriple(self.builder.allocator));104 try argv_list.append(try self.target.zigTriple(b.allocator));
105 }105 }
106106
107 switch (self.optimize) {107 switch (self.optimize) {
108 .Debug => {}, // Skip since it's the default.108 .Debug => {}, // Skip since it's the default.
109 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),109 else => try argv_list.append(b.fmt("-O{s}", .{@tagName(self.optimize)})),
110 }110 }
111111
112 for (self.include_dirs.items) |include_dir| {112 for (self.include_dirs.items) |include_dir| {
...@@ -119,15 +119,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -119,15 +119,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
119 try argv_list.append(c_macro);119 try argv_list.append(c_macro);
120 }120 }
121121
122 try argv_list.append(self.source.getPath(self.builder));122 try argv_list.append(self.source.getPath(b));
123123
124 const output_path = try self.builder.execFromStep(argv_list.items, &self.step, prog_node);124 const output_path = try step.evalZigProcess(argv_list.items, prog_node);
125125
126 self.out_basename = fs.path.basename(output_path);126 self.out_basename = fs.path.basename(output_path);
127 const output_dir = fs.path.dirname(output_path).?;127 const output_dir = fs.path.dirname(output_path).?;
128128
129 self.output_file.path = try fs.path.join(129 self.output_file.path = try fs.path.join(
130 self.builder.allocator,130 b.allocator,
131 &[_][]const u8{ output_dir, self.out_basename },131 &[_][]const u8{ output_dir, self.out_basename },
132 );132 );
133}133}
lib/std/Build/WriteFileStep.zig+26-37
...@@ -10,7 +10,6 @@...@@ -10,7 +10,6 @@
10//! control.10//! control.
1111
12step: Step,12step: Step,
13builder: *std.Build,
14/// The elements here are pointers because we need stable pointers for the13/// The elements here are pointers because we need stable pointers for the
15/// GeneratedFile field.14/// GeneratedFile field.
16files: std.ArrayListUnmanaged(*File),15files: std.ArrayListUnmanaged(*File),
...@@ -34,12 +33,12 @@ pub const Contents = union(enum) {...@@ -34,12 +33,12 @@ pub const Contents = union(enum) {
34 copy: std.Build.FileSource,33 copy: std.Build.FileSource,
35};34};
3635
37pub fn init(builder: *std.Build) WriteFileStep {36pub fn init(owner: *std.Build) WriteFileStep {
38 return .{37 return .{
39 .builder = builder,38 .step = Step.init(.{
40 .step = Step.init(builder.allocator, .{
41 .id = .write_file,39 .id = .write_file,
42 .name = "writefile",40 .name = "writefile",
41 .owner = owner,
43 .makeFn = make,42 .makeFn = make,
44 }),43 }),
45 .files = .{},44 .files = .{},
...@@ -48,12 +47,13 @@ pub fn init(builder: *std.Build) WriteFileStep {...@@ -48,12 +47,13 @@ pub fn init(builder: *std.Build) WriteFileStep {
48}47}
4948
50pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {49pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
51 const gpa = wf.builder.allocator;50 const b = wf.step.owner;
51 const gpa = b.allocator;
52 const file = gpa.create(File) catch @panic("OOM");52 const file = gpa.create(File) catch @panic("OOM");
53 file.* = .{53 file.* = .{
54 .generated_file = .{ .step = &wf.step },54 .generated_file = .{ .step = &wf.step },
55 .sub_path = wf.builder.dupePath(sub_path),55 .sub_path = b.dupePath(sub_path),
56 .contents = .{ .bytes = wf.builder.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}59}
...@@ -66,11 +66,12 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {...@@ -66,11 +66,12 @@ pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void {
66/// required sub-path exists.66/// required sub-path exists.
67/// This is the option expected to be used most commonly with `addCopyFile`.67/// This is the option expected to be used most commonly with `addCopyFile`.
68pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {68pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
69 const gpa = wf.builder.allocator;69 const b = wf.step.owner;
70 const gpa = b.allocator;
70 const file = gpa.create(File) catch @panic("OOM");71 const file = gpa.create(File) catch @panic("OOM");
71 file.* = .{72 file.* = .{
72 .generated_file = .{ .step = &wf.step },73 .generated_file = .{ .step = &wf.step },
73 .sub_path = wf.builder.dupePath(sub_path),74 .sub_path = b.dupePath(sub_path),
74 .contents = .{ .copy = source },75 .contents = .{ .copy = source },
75 };76 };
76 wf.files.append(gpa, file) catch @panic("OOM");77 wf.files.append(gpa, file) catch @panic("OOM");
...@@ -83,7 +84,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [...@@ -83,7 +84,8 @@ pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: [
83/// those changes to version control.84/// those changes to version control.
84/// A file added this way is not available with `getFileSource`.85/// A file added this way is not available with `getFileSource`.
85pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {86pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void {
86 wf.output_source_files.append(wf.builder.allocator, .{87 const b = wf.step.owner;
88 wf.output_source_files.append(b.allocator, .{
87 .contents = .{ .copy = source },89 .contents = .{ .copy = source },
88 .sub_path = sub_path,90 .sub_path = sub_path,
89 }) catch @panic("OOM");91 }) catch @panic("OOM");
...@@ -101,6 +103,7 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo...@@ -101,6 +103,7 @@ pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSo
101103
102fn make(step: *Step, prog_node: *std.Progress.Node) !void {104fn make(step: *Step, prog_node: *std.Progress.Node) !void {
103 _ = prog_node;105 _ = prog_node;
106 const b = step.owner;
104 const wf = @fieldParentPtr(WriteFileStep, "step", step);107 const wf = @fieldParentPtr(WriteFileStep, "step", step);
105108
106 // Writing to source files is kind of an extra capability of this109 // Writing to source files is kind of an extra capability of this
...@@ -110,11 +113,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -110,11 +113,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
110 for (wf.output_source_files.items) |output_source_file| {113 for (wf.output_source_files.items) |output_source_file| {
111 const basename = fs.path.basename(output_source_file.sub_path);114 const basename = fs.path.basename(output_source_file.sub_path);
112 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {115 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
113 var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{});116 var dir = try b.build_root.handle.makeOpenPath(dirname, .{});
114 defer dir.close();117 defer dir.close();
115 try writeFile(wf, dir, output_source_file.contents, basename);118 try writeFile(wf, dir, output_source_file.contents, basename);
116 } else {119 } else {
117 try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename);120 try writeFile(wf, b.build_root.handle, output_source_file.contents, basename);
118 }121 }
119 }122 }
120123
...@@ -125,7 +128,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -125,7 +128,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
125 // If, for example, a hard-coded path was used as the location to put WriteFileStep128 // If, for example, a hard-coded path was used as the location to put WriteFileStep
126 // files, then two WriteFileSteps executing in parallel might clobber each other.129 // files, then two WriteFileSteps executing in parallel might clobber each other.
127130
128 var man = wf.builder.cache.obtain();131 var man = b.cache.obtain();
129 defer man.deinit();132 defer man.deinit();
130133
131 // Random bytes to make WriteFileStep unique. Refresh this with134 // Random bytes to make WriteFileStep unique. Refresh this with
...@@ -140,17 +143,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -140,17 +143,17 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
140 man.hash.addBytes(bytes);143 man.hash.addBytes(bytes);
141 },144 },
142 .copy => |file_source| {145 .copy => |file_source| {
143 _ = try man.addFile(file_source.getPath(wf.builder), null);146 _ = try man.addFile(file_source.getPath(b), null);
144 },147 },
145 }148 }
146 }149 }
147150
148 if (man.hit() catch |err| failWithCacheError(man, err)) {151 if (try step.cacheHit(&man)) {
149 // Cache hit, skip writing file data.152 // Cache hit, skip writing file data.
150 const digest = man.final();153 const digest = man.final();
151 for (wf.files.items) |file| {154 for (wf.files.items) |file| {
152 file.generated_file.path = try wf.builder.cache_root.join(155 file.generated_file.path = try b.cache_root.join(
153 wf.builder.allocator,156 b.allocator,
154 &.{ "o", &digest, file.sub_path },157 &.{ "o", &digest, file.sub_path },
155 );158 );
156 }159 }
...@@ -160,7 +163,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -160,7 +163,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
160 const digest = man.final();163 const digest = man.final();
161 const cache_path = "o" ++ fs.path.sep_str ++ digest;164 const cache_path = "o" ++ fs.path.sep_str ++ digest;
162165
163 var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {166 var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
164 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });167 std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
165 return err;168 return err;
166 };169 };
...@@ -169,15 +172,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -169,15 +172,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
169 for (wf.files.items) |file| {172 for (wf.files.items) |file| {
170 const basename = fs.path.basename(file.sub_path);173 const basename = fs.path.basename(file.sub_path);
171 if (fs.path.dirname(file.sub_path)) |dirname| {174 if (fs.path.dirname(file.sub_path)) |dirname| {
172 var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{});175 var dir = try b.cache_root.handle.makeOpenPath(dirname, .{});
173 defer dir.close();176 defer dir.close();
174 try writeFile(wf, dir, file.contents, basename);177 try writeFile(wf, dir, file.contents, basename);
175 } else {178 } else {
176 try writeFile(wf, cache_dir, file.contents, basename);179 try writeFile(wf, cache_dir, file.contents, basename);
177 }180 }
178181
179 file.generated_file.path = try wf.builder.cache_root.join(182 file.generated_file.path = try b.cache_root.join(
180 wf.builder.allocator,183 b.allocator,
181 &.{ cache_path, file.sub_path },184 &.{ cache_path, file.sub_path },
182 );185 );
183 }186 }
...@@ -186,32 +189,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -186,32 +189,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
186}189}
187190
188fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void {191fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void {
192 const b = wf.step.owner;
189 // TODO after landing concurrency PR, improve error reporting here193 // TODO after landing concurrency PR, improve error reporting here
190 switch (contents) {194 switch (contents) {
191 .bytes => |bytes| return dir.writeFile(basename, bytes),195 .bytes => |bytes| return dir.writeFile(basename, bytes),
192 .copy => |file_source| {196 .copy => |file_source| {
193 const source_path = file_source.getPath(wf.builder);197 const source_path = file_source.getPath(b);
194 const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{});198 const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{});
195 _ = prev_status; // TODO logging (affected by open PR regarding concurrency)199 _ = prev_status; // TODO logging (affected by open PR regarding concurrency)
196 },200 },
197 }201 }
198}202}
199203
200/// TODO consolidate this with the same function in RunStep?
201/// Also properly deal with concurrency (see open PR)
202fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn {
203 const i = man.failed_file_index orelse failWithSimpleError(err);
204 const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err);
205 const prefix = man.cache.prefixes()[pp.prefix].path orelse "";
206 std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path });
207 std.process.exit(1);
208}
209
210fn failWithSimpleError(err: anyerror) noreturn {
211 std.debug.print("{s}\n", .{@errorName(err)});
212 std.process.exit(1);
213}
214
215const std = @import("../std.zig");204const std = @import("../std.zig");
216const Step = std.Build.Step;205const Step = std.Build.Step;
217const fs = std.fs;206const fs = std.fs;
src/main.zig+6-6
...@@ -4419,6 +4419,8 @@ pub const usage_build =...@@ -4419,6 +4419,8 @@ pub const usage_build =
4419 \\Options:4419 \\Options:
4420 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error4420 \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error
4421 \\ -fno-reference-trace Disable reference trace4421 \\ -fno-reference-trace Disable reference trace
4422 \\ -fsummary Print the build summary, even on success
4423 \\ -fno-summary Omit the build summary, even on failure
4422 \\ --build-file [file] Override path to build.zig4424 \\ --build-file [file] Override path to build.zig
4423 \\ --cache-dir [path] Override path to local Zig cache directory4425 \\ --cache-dir [path] Override path to local Zig cache directory
4424 \\ --global-cache-dir [path] Override path to global Zig cache directory4426 \\ --global-cache-dir [path] Override path to global Zig cache directory
...@@ -4920,8 +4922,6 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4920,8 +4922,6 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4920 };4922 };
4921 defer tree.deinit(gpa);4923 defer tree.deinit(gpa);
49224924
4923 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
4924 var has_ast_error = false;
4925 if (check_ast_flag) {4925 if (check_ast_flag) {
4926 var file: Module.File = .{4926 var file: Module.File = .{
4927 .status = .never_loaded,4927 .status = .never_loaded,
...@@ -4957,11 +4957,11 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4957,11 +4957,11 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4957 var error_bundle = try wip_errors.toOwnedBundle();4957 var error_bundle = try wip_errors.toOwnedBundle();
4958 defer error_bundle.deinit(gpa);4958 defer error_bundle.deinit(gpa);
4959 error_bundle.renderToStdErr(ttyconf);4959 error_bundle.renderToStdErr(ttyconf);
4960 has_ast_error = true;4960 process.exit(2);
4961 }4961 }
4962 }4962 } else if (tree.errors.len != 0) {
4963 if (tree.errors.len != 0 or has_ast_error) {4963 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
4964 process.exit(1);4964 process.exit(2);
4965 }4965 }
4966 const formatted = try tree.render(gpa);4966 const formatted = try tree.render(gpa);
4967 defer gpa.free(formatted);4967 defer gpa.free(formatted);
test/src/compare_output.zig+1-3
...@@ -166,9 +166,7 @@ pub const CompareOutputContext = struct {...@@ -166,9 +166,7 @@ pub const CompareOutputContext = struct {
166166
167 const run = exe.run();167 const run = exe.run();
168 run.addArgs(case.cli_args);168 run.addArgs(case.cli_args);
169 run.stderr_action = .ignore;169 run.expectExitCode(126);
170 run.stdout_action = .ignore;
171 run.expected_term = .{ .Exited = 126 };
172170
173 self.step.dependOn(&run.step);171 self.step.dependOn(&run.step);
174 },172 },
test/tests.zig+6-10
...@@ -858,10 +858,11 @@ pub const StackTracesContext = struct {...@@ -858,10 +858,11 @@ pub const StackTracesContext = struct {
858 const allocator = context.b.allocator;858 const allocator = context.b.allocator;
859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
860 ptr.* = RunAndCompareStep{860 ptr.* = RunAndCompareStep{
861 .step = Step.init(allocator, .{861 .step = Step.init(.{
862 .id = .custom,862 .id = .custom,
863 .name = "StackTraceCompareOutputStep",863 .name = "StackTraceCompareOutputStep",
864 .makeFn = make,864 .makeFn = make,
865 .owner = context.b,
865 }),866 }),
866 .context = context,867 .context = context,
867 .exe = exe,868 .exe = exe,
...@@ -1121,10 +1122,7 @@ pub const StandaloneContext = struct {...@@ -1121,10 +1122,7 @@ pub const StandaloneContext = struct {
1121 defer zig_args.resize(zig_args_base_len) catch unreachable;1122 defer zig_args.resize(zig_args_base_len) catch unreachable;
11221123
1123 const run_cmd = b.addSystemCommand(zig_args.items);1124 const run_cmd = b.addSystemCommand(zig_args.items);
1124 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) });1125 self.step.dependOn(&run_cmd.step);
1125 log_step.step.dependOn(&run_cmd.step);
1126
1127 self.step.dependOn(&log_step.step);
1128 }1126 }
1129 }1127 }
11301128
...@@ -1150,10 +1148,7 @@ pub const StandaloneContext = struct {...@@ -1150,10 +1148,7 @@ pub const StandaloneContext = struct {
1150 exe.linkSystemLibrary("c");1148 exe.linkSystemLibrary("c");
1151 }1149 }
11521150
1153 const log_step = b.addLog("PASS {s}", .{annotated_case_name});1151 self.step.dependOn(&exe.step);
1154 log_step.step.dependOn(&exe.step);
1155
1156 self.step.dependOn(&log_step.step);
1157 }1152 }
1158 }1153 }
1159};1154};
...@@ -1203,9 +1198,10 @@ pub const GenHContext = struct {...@@ -1203,9 +1198,10 @@ pub const GenHContext = struct {
1203 const allocator = context.b.allocator;1198 const allocator = context.b.allocator;
1204 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;1199 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
1205 ptr.* = GenHCmpOutputStep{1200 ptr.* = GenHCmpOutputStep{
1206 .step = Step.init(allocator, .{1201 .step = Step.init(.{
1207 .id = .custom,1202 .id = .custom,
1208 .name = "ParseCCmpOutput",1203 .name = "ParseCCmpOutput",
1204 .owner = context.b,
1209 .makeFn = make,1205 .makeFn = make,
1210 }),1206 }),
1211 .context = context,1207 .context = context,