commit b40b1178ef29ea9b013f73cc3cd4f9b976d0e120 (tree)
parent c5d6277ace183cf4261c13cc9b44a53ec1622199
Author: Mason Remaley <mason@gamesbymason.com>
Date: Wed, 3 Jun 2026 02:24:08 -0700
Adds support for making run step argument paths absolute
* Adds a new API that takes an options struct instead of having a
function for each combination of options
* Adds support for prefix and suffix to every argument type
* Adds support for making paths absolute when passed as arguments
* Deprecates the old API
* Fixes mismatch between documentation and behavior of
`process.currentPath` on WASI
Diffstat:
7 files changed, 384 insertions(+), 132 deletions(-)
diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig
@@ -87,8 +87,9 @@ pub fn make(
const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
}));
+ man.hash.add(arg.flags.make_absolute);
man.hash.addBytesZ(prefix);
man.hash.addBytesZ(suffix);
_ = try man.addFilePath(file_path, null);
@@ -98,9 +99,10 @@ pub fn make(
const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
const resolved_arg = try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
});
argv_list.appendAssumeCapacity(resolved_arg);
+ man.hash.add(arg.flags.make_absolute);
man.hash.addBytes(resolved_arg);
},
.file_content => {
@@ -142,9 +144,12 @@ pub fn make(
const file_path = producer_make_comp.installed_path orelse maker.generatedPath(producer.generated_bin.value.?).*;
argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
}));
+ man.hash.add(arg.flags.make_absolute);
+ man.hash.addBytesZ(prefix);
+ man.hash.addBytesZ(suffix);
_ = try man.addFilePath(file_path, null);
},
.output_file, .output_directory => {
@@ -152,6 +157,7 @@ pub fn make(
const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
const basename = arg.basename.value.?.slice(conf);
+ man.hash.add(arg.flags.make_absolute);
man.hash.addBytesZ(prefix);
man.hash.addBytesZ(basename);
man.hash.addBytesZ(suffix);
@@ -181,7 +187,7 @@ pub fn make(
man.hash.add(conf_run.flags.test_runner_mode);
if (conf_run.flags.test_runner_mode) {
- const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root });
+ const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
try argv_list.ensureUnusedCapacity(gpa, 3);
argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string}));
@@ -1552,7 +1558,7 @@ pub fn rerunInFuzzMode(
const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
}));
},
.path_directory => {
@@ -1560,7 +1566,7 @@ pub fn rerunInFuzzMode(
const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
const resolved_arg = try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
});
argv_list.appendAssumeCapacity(resolved_arg);
},
@@ -1602,7 +1608,7 @@ pub fn rerunInFuzzMode(
producer_make_comp.installed_path orelse
maker.generatedPath(producer.generated_bin.value.?).*;
argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
- prefix, try convertPathArg(arena, run_index, maker, file_path), suffix,
+ prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
}));
},
.output_file => unreachable,
@@ -1612,7 +1618,7 @@ pub fn rerunInFuzzMode(
}
if (conf_run.flags.test_runner_mode) {
- const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root });
+ const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
try argv_list.ensureUnusedCapacity(gpa, 3);
argv_list.appendAssumeCapacity(try allocPrint(arena, "--cache-dir={s}", .{cache_dir_string}));
@@ -1688,7 +1694,7 @@ fn populateGeneratedPathsCreateDirs(
maker.generatedPath(arg.generated.value.?).* = generated_path;
- const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path);
+ const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute);
argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix });
}
}
@@ -2290,11 +2296,18 @@ fn checksContainStderr(conf_run: *const Configuration.Step.Run) bool {
return conf_run.expect_stderr_exact.value != null or conf_run.expect_stderr_match.slice.len != 0;
}
-/// If `path` is cwd-relative, make it relative to the cwd of the child instead.
+/// If `path` is absolute, return it unchanged. If `make_absolute` is true, make it absolute.
+/// Otherwise, make it relative to the cwd of the child.
///
-/// Whenever a path is included in the argv of a child, it should be put through this function first
-/// to make sure the child doesn't see paths relative to a cwd other than its own.
-fn convertPathArg(arena: Allocator, run_index: Configuration.Step.Index, maker: *Maker, path: Path) ![]const u8 {
+/// Whenever a path is included in the argv of a child, it should be put through this function
+/// first.
+fn convertPathArg(
+ arena: Allocator,
+ run_index: Configuration.Step.Index,
+ maker: *Maker,
+ path: Path,
+ make_absolute: bool,
+) ![]const u8 {
const conf = &maker.scanned_config.configuration;
const conf_step = run_index.ptr(conf);
const conf_run = conf_step.extended.get(conf.extra).run;
@@ -2305,6 +2318,11 @@ fn convertPathArg(arena: Allocator, run_index: Configuration.Step.Index, maker:
// Absolute paths don't need changing.
return path_str;
}
+
+ if (make_absolute) {
+ return Dir.path.join(arena, &.{ graph.cache.cwd, path_str });
+ }
+
const child_cwd_rel: []const u8 = rel: {
const child_lazy_cwd = conf_run.cwd.value orelse break :rel path_str;
const child_cwd = try maker.resolveLazyPathIndexAbs(arena, child_lazy_cwd, run_index);
diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig
@@ -312,15 +312,16 @@ const Serialize = struct {
.flags = .{
.tag = .artifact,
.prefix = a.prefix.len != 0,
- .suffix = false,
+ .suffix = a.suffix.len != 0,
.basename = false,
.path = false,
.producer = true,
.generated = false,
.dep_file = false,
+ .make_absolute = a.make_absolute,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
- .suffix = .{ .value = null },
+ .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
.basename = .{ .value = null },
.path = .{ .value = null },
.producer = .{ .value = stepIndex(s, &a.artifact.step) },
@@ -330,15 +331,16 @@ const Serialize = struct {
.flags = .{
.tag = .path_file,
.prefix = a.prefix.len != 0,
- .suffix = false,
+ .suffix = a.suffix.len != 0,
.basename = false,
.path = true,
.producer = false,
.generated = false,
.dep_file = false,
+ .make_absolute = a.make_absolute,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
- .suffix = .{ .value = null },
+ .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
.basename = .{ .value = null },
.path = .{ .value = try addLazyPath(s, a.lazy_path) },
.producer = .{ .value = null },
@@ -354,6 +356,7 @@ const Serialize = struct {
.producer = false,
.generated = false,
.dep_file = false,
+ .make_absolute = a.make_absolute,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
.suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
@@ -366,15 +369,16 @@ const Serialize = struct {
.flags = .{
.tag = .file_content,
.prefix = a.prefix.len != 0,
- .suffix = false,
+ .suffix = a.suffix.len != 0,
.basename = false,
.path = true,
.producer = false,
.generated = false,
.dep_file = false,
+ .make_absolute = false,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
- .suffix = .{ .value = null },
+ .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
.basename = .{ .value = null },
.path = .{ .value = try addLazyPath(s, a.lazy_path) },
.producer = .{ .value = null },
@@ -390,6 +394,7 @@ const Serialize = struct {
.producer = false,
.generated = false,
.dep_file = false,
+ .make_absolute = false,
},
.prefix = .{ .value = try wc.addString(a) },
.suffix = .{ .value = null },
@@ -402,15 +407,16 @@ const Serialize = struct {
.flags = .{
.tag = .output_file,
.prefix = a.prefix.len != 0,
- .suffix = false,
+ .suffix = a.suffix.len != 0,
.basename = a.basename.len != 0,
.path = false,
.producer = false,
.generated = true,
.dep_file = tag == .output_file_dep,
+ .make_absolute = a.make_absolute,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
- .suffix = .{ .value = null },
+ .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
.basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
.path = .{ .value = null },
.producer = .{ .value = null },
@@ -420,15 +426,16 @@ const Serialize = struct {
.flags = .{
.tag = .output_directory,
.prefix = a.prefix.len != 0,
- .suffix = false,
+ .suffix = a.suffix.len != 0,
.basename = a.basename.len != 0,
.path = false,
.producer = false,
.generated = true,
.dep_file = false,
+ .make_absolute = a.make_absolute,
},
.prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
- .suffix = .{ .value = null },
+ .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
.basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
.path = .{ .value = null },
.producer = .{ .value = null },
@@ -444,6 +451,7 @@ const Serialize = struct {
.producer = false,
.generated = false,
.dep_file = false,
+ .make_absolute = false,
},
.prefix = .{ .value = null },
.suffix = .{ .value = null },
@@ -1066,12 +1074,12 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.args = .{ .slice = try s.initArgsList(run.argv.items) },
.cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
.captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
- .basename = try wc.addString(cs.output.basename),
- .generated_file = cs.output.generated_file,
+ .basename = try wc.addString(cs.basename),
+ .generated_file = cs.generated_file,
} else null },
.captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
- .basename = try wc.addString(cs.output.basename),
- .generated_file = cs.output.generated_file,
+ .basename = try wc.addString(cs.basename),
+ .generated_file = cs.generated_file,
} else null },
.environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
.expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig
@@ -610,7 +610,8 @@ pub const Step = extern struct {
producer: bool,
generated: bool,
dep_file: bool,
- _: u21 = 0,
+ make_absolute: bool,
+ _: u20 = 0,
};
pub const Tag = enum(u4) {
diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig
@@ -133,10 +133,10 @@ pub const StdIo = union(enum) {
};
pub const Arg = union(enum) {
- artifact: PrefixedArtifact,
- lazy_path: PrefixedLazyPath,
+ artifact: DecoratedArtifact,
+ lazy_path: DecoratedLazyPath,
decorated_directory: DecoratedLazyPath,
- file_content: PrefixedLazyPath,
+ file_content: DecoratedFileContent,
bytes: []const u8,
output_file: *Output,
output_file_dep: *Output,
@@ -145,17 +145,21 @@ pub const Arg = union(enum) {
passthru,
};
-pub const PrefixedArtifact = struct {
+pub const DecoratedArtifact = struct {
prefix: []const u8,
+ suffix: []const u8,
artifact: *Step.Compile,
+ make_absolute: bool,
};
-pub const PrefixedLazyPath = struct {
+pub const DecoratedLazyPath = struct {
prefix: []const u8,
lazy_path: std.Build.LazyPath,
+ suffix: []const u8,
+ make_absolute: bool,
};
-pub const DecoratedLazyPath = struct {
+pub const DecoratedFileContent = struct {
prefix: []const u8,
lazy_path: std.Build.LazyPath,
suffix: []const u8,
@@ -165,10 +169,14 @@ pub const Output = struct {
generated_file: Configuration.GeneratedFileIndex,
prefix: []const u8,
basename: []const u8,
+ suffix: []const u8,
+ make_absolute: bool,
};
pub const CapturedStdIo = struct {
- output: Output,
+ generated_file: Configuration.GeneratedFileIndex,
+ prefix: []const u8,
+ basename: []const u8,
trim_whitespace: TrimWhitespace,
pub const Options = struct {
@@ -219,17 +227,38 @@ pub fn enableTestRunnerMode(run: *Run) void {
run.test_runner_mode = true;
}
+pub const ArgOptions = struct {
+ prefix: []const u8 = "",
+ suffix: []const u8 = "",
+};
+
+pub const PathArgOptions = struct {
+ prefix: []const u8 = "",
+ suffix: []const u8 = "",
+ /// Makes the path absolute before passing it to the child process. Not supported by all hosts,
+ /// prefer accepting relative paths when possible.
+ make_absolute: bool = false,
+};
+
+/// Deprecated, use `addArtifactArg2`.
pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
- run.addPrefixedArtifactArg("", artifact);
+ run.addArtifactArg2(artifact, .{});
}
+/// Deprecated, use `addArtifactArg2`.
pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Compile) void {
+ run.addArtifactArg2(artifact, .{ .prefix = prefix });
+}
+
+pub fn addArtifactArg2(run: *Run, artifact: *Step.Compile, options: PathArgOptions) void {
const graph = run.step.owner.graph;
const arena = graph.arena;
- const prefixed_artifact: PrefixedArtifact = .{
- .prefix = graph.dupeString(prefix),
+ const prefixed_artifact: DecoratedArtifact = .{
+ .prefix = graph.dupeString(options.prefix),
.artifact = artifact,
+ .suffix = graph.dupeString(options.suffix),
+ .make_absolute = options.make_absolute,
};
run.argv.append(arena, .{ .artifact = prefixed_artifact }) catch @panic("OOM");
@@ -237,19 +266,18 @@ pub fn addPrefixedArtifactArg(run: *Run, prefix: []const u8, artifact: *Step.Com
bin_file.addStepDependencies(&run.step);
}
-/// Provides a file path as a command line argument to the command being run.
-///
-/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
-/// throughout the build system.
-///
-/// `sub_path` is the name of the generated output file which may have zero or
-/// more path components.
-///
-/// Related:
-/// * `addPrefixedOutputFileArg` - same thing but prepends a string to the argument
-/// * `addFileArg` - for input files given to the child process
+/// Deprecated, use `addOutputFileArg2`.
pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
- return run.addPrefixedOutputFileArg("", sub_path);
+ return run.addOutputFileArg2(sub_path, .{});
+}
+
+/// Deprecated, use `addOutputFileArg2`.
+pub fn addPrefixedOutputFileArg(
+ run: *Run,
+ prefix: []const u8,
+ sub_path: []const u8,
+) std.Build.LazyPath {
+ return run.addOutputFileArg2(sub_path, .{ .prefix = prefix });
}
/// Provides a file path as a command line argument to the command being run.
@@ -264,16 +292,15 @@ pub fn addOutputFileArg(run: *Run, sub_path: []const u8) std.Build.LazyPath {
/// throughout the build system.
///
/// Related:
-/// * `addOutputFileArg` - same thing but without the prefix
/// * `addFileArg` - for input files given to the child process
-pub fn addPrefixedOutputFileArg(
+pub fn addOutputFileArg2(
run: *Run,
- prefix: []const u8,
/// The name of the generated output file which may have zero or more path
/// components.
///
/// Asserted to be non-empty.
sub_path: []const u8,
+ options: PathArgOptions,
) std.Build.LazyPath {
const b = run.step.owner;
const graph = b.graph;
@@ -282,9 +309,11 @@ pub fn addPrefixedOutputFileArg(
const output = graph.create(Output);
output.* = .{
- .prefix = graph.dupeString(prefix),
+ .prefix = graph.dupeString(options.prefix),
.basename = graph.dupeString(sub_path),
+ .suffix = graph.dupeString(options.suffix),
.generated_file = graph.addGeneratedFile(&run.step),
+ .make_absolute = options.make_absolute,
};
run.argv.append(arena, .{ .output_file = output }) catch @panic("OOM");
@@ -295,17 +324,14 @@ pub fn addPrefixedOutputFileArg(
return .{ .generated = .{ .index = output.generated_file } };
}
-/// Appends an input file to the command line arguments.
-///
-/// The child process will see a file path. Modifications to this file will be
-/// detected as a cache miss in subsequent builds, causing the child process to
-/// be re-executed.
-///
-/// Related:
-/// * `addPrefixedFileArg` - same thing but prepends a string to the argument
-/// * `addOutputFileArg` - for files generated by the child process
+/// See `addFileArg2`.
pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
- run.addPrefixedFileArg("", lp);
+ run.addFileArg2(lp, .{});
+}
+
+/// See `addFileArg2`.
+pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
+ run.addFileArg2(lp, .{ .prefix = prefix });
}
/// Appends an input file to the command line arguments prepended with a string.
@@ -318,36 +344,29 @@ pub fn addFileArg(run: *Run, lp: std.Build.LazyPath) void {
/// subsequent builds, causing the child process to be re-executed.
///
/// Related:
-/// * `addFileArg` - same thing but without the prefix
/// * `addOutputFileArg` - for files generated by the child process
-pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
+pub fn addFileArg2(run: *Run, lp: std.Build.LazyPath, options: PathArgOptions) void {
const graph = run.step.owner.graph;
const arena = graph.arena;
- const prefixed_file_source: PrefixedLazyPath = .{
- .prefix = graph.dupeString(prefix),
+ const prefixed_file_source: DecoratedLazyPath = .{
+ .prefix = graph.dupeString(options.prefix),
.lazy_path = lp.dupe(graph),
+ .suffix = graph.dupeString(options.suffix),
+ .make_absolute = options.make_absolute,
};
run.argv.append(arena, .{ .lazy_path = prefixed_file_source }) catch @panic("OOM");
lp.addStepDependencies(&run.step);
}
-/// Appends the content of an input file to the command line arguments.
-///
-/// The child process will see a single argument, even if the file contains whitespace.
-/// This means that the entire file content up to EOF is rendered as one contiguous
-/// string, including escape sequences. Notably, any (trailing) newlines will show up
-/// like this: "hello,\nfile world!\n"
-///
-/// Modifications to the source file will be detected as a cache miss in subsequent
-/// builds, causing the child process to be re-executed.
-///
-/// This function may not be used to supply the first argument of a `Run` step.
-///
-/// Related:
-/// * `addPrefixedFileContentArg` - same thing but prepends a string to the argument
+/// Deprecated, use `addFileContentArg2`.
pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
- run.addPrefixedFileContentArg("", lp);
+ return run.addFileContentArg2(lp, .{});
+}
+
+/// Deprecated, use `addFileContentArg2`.
+pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
+ return run.addFileContentArg2(lp, .{ .prefix = prefix });
}
/// Appends the content of an input file to the command line arguments prepended with a string.
@@ -368,7 +387,7 @@ pub fn addFileContentArg(run: *Run, lp: std.Build.LazyPath) void {
///
/// Related:
/// * `addFileContentArg` - same thing but without the prefix
-pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath) void {
+pub fn addFileContentArg2(run: *Run, lp: std.Build.LazyPath, options: ArgOptions) void {
const graph = run.step.owner.graph;
const arena = graph.arena;
@@ -379,24 +398,27 @@ pub fn addPrefixedFileContentArg(run: *Run, prefix: []const u8, lp: std.Build.La
@panic("'addFileContentArg'/'addPrefixedFileContentArg' cannot be first argument");
}
- const prefixed_file_source: PrefixedLazyPath = .{
- .prefix = graph.dupeString(prefix),
+ const file_content: DecoratedFileContent = .{
+ .prefix = graph.dupeString(options.prefix),
.lazy_path = lp.dupe(graph),
+ .suffix = graph.dupeString(options.suffix),
};
- run.argv.append(arena, .{ .file_content = prefixed_file_source }) catch @panic("OOM");
+ run.argv.append(arena, .{ .file_content = file_content }) catch @panic("OOM");
lp.addStepDependencies(&run.step);
}
-/// Provides a directory path as a command line argument to the command being run.
-///
-/// Returns a `std.Build.LazyPath` which can be used as inputs to other APIs
-/// throughout the build system.
-///
-/// Related:
-/// * `addPrefixedOutputDirectoryArg` - same thing but prepends a string to the argument
-/// * `addDirectoryArg` - for input directories given to the child process
+/// Deprecated, use `addOutputDirectoryArg2`.
pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath {
- return run.addPrefixedOutputDirectoryArg("", basename);
+ return run.addOutputDirectoryArg2(basename, .{});
+}
+
+/// Deprecated, use `addOutputDirectoryArg2`.
+pub fn addPrefixedOutputDirectoryArg(
+ run: *Run,
+ prefix: []const u8,
+ basename: []const u8,
+) std.Build.LazyPath {
+ return run.addOutputDirectoryArg2(basename, .{ .prefix = prefix });
}
/// Provides a directory path as a command line argument to the command being run.
@@ -412,12 +434,11 @@ pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath
/// throughout the build system.
///
/// Related:
-/// * `addOutputDirectoryArg` - same thing but without the prefix
/// * `addDirectoryArg` - for input directories given to the child process
-pub fn addPrefixedOutputDirectoryArg(
+pub fn addOutputDirectoryArg2(
run: *Run,
- prefix: []const u8,
basename: []const u8,
+ options: PathArgOptions,
) std.Build.LazyPath {
if (basename.len == 0) @panic("basename must not be empty");
const graph = run.step.owner.graph;
@@ -425,9 +446,11 @@ pub fn addPrefixedOutputDirectoryArg(
const output = arena.create(Output) catch @panic("OOM");
output.* = .{
- .prefix = graph.dupeString(prefix),
+ .prefix = graph.dupeString(options.prefix),
.basename = graph.dupeString(basename),
+ .suffix = graph.dupeString(options.suffix),
.generated_file = graph.addGeneratedFile(&run.step),
+ .make_absolute = options.make_absolute,
};
run.argv.append(arena, .{ .output_directory = output }) catch @panic("OOM");
@@ -438,56 +461,67 @@ pub fn addPrefixedOutputDirectoryArg(
return .{ .generated = .{ .index = output.generated_file } };
}
+/// Deprecated, use `addDirectoryArg2`.
pub fn addDirectoryArg(run: *Run, lazy_directory: std.Build.LazyPath) void {
- run.addDecoratedDirectoryArg("", lazy_directory, "");
+ run.addDirectoryArg2(lazy_directory, .{});
}
+/// Deprecated, use `addDirectoryArg2`.
pub fn addPrefixedDirectoryArg(run: *Run, prefix: []const u8, lazy_directory: std.Build.LazyPath) void {
- const graph = run.step.owner.graph;
- const arena = graph.arena;
- run.argv.append(arena, .{ .decorated_directory = .{
- .prefix = graph.dupeString(prefix),
- .lazy_path = lazy_directory.dupe(graph),
- .suffix = "",
- } }) catch @panic("OOM");
- lazy_directory.addStepDependencies(&run.step);
+ run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix });
}
+/// Deprecated, use `addDirectoryArg2`.
pub fn addDecoratedDirectoryArg(
run: *Run,
prefix: []const u8,
lazy_directory: std.Build.LazyPath,
suffix: []const u8,
) void {
+ run.addDirectoryArg2(lazy_directory, .{ .prefix = prefix, .suffix = suffix });
+}
+
+pub fn addDirectoryArg2(
+ run: *Run,
+ lazy_directory: std.Build.LazyPath,
+ options: PathArgOptions,
+) void {
const graph = run.step.owner.graph;
const arena = graph.arena;
run.argv.append(arena, .{ .decorated_directory = .{
- .prefix = graph.dupeString(prefix),
+ .prefix = graph.dupeString(options.prefix),
.lazy_path = lazy_directory.dupe(graph),
- .suffix = graph.dupeString(suffix),
+ .suffix = graph.dupeString(options.suffix),
+ .make_absolute = options.make_absolute,
} }) catch @panic("OOM");
lazy_directory.addStepDependencies(&run.step);
}
-/// Add a path argument to a dep file (.d) for the child process to write its
-/// discovered additional dependencies.
-/// Only one dep file argument is allowed by instance.
+/// Deprecated, use `addDepFileOutputArg2`.
pub fn addDepFileOutputArg(run: *Run, basename: []const u8) std.Build.LazyPath {
- return run.addPrefixedDepFileOutputArg("", basename);
+ return run.addDepFileOutputArg2(basename, .{});
}
-/// Add a prefixed path argument to a dep file (.d) for the child process to
-/// write its discovered additional dependencies.
+/// Deprecated, use `addDepFileOutputArg2`.
pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
+ return run.addDepFileOutputArg2(basename, .{ .prefix = prefix });
+}
+
+/// Add a path argument to a dep file (.d) for the child process to write its
+/// discovered additional dependencies.
+/// Only one dep file argument is allowed by instance.
+pub fn addDepFileOutputArg2(run: *Run, basename: []const u8, options: PathArgOptions) std.Build.LazyPath {
const b = run.step.owner;
const graph = b.graph;
const arena = graph.arena;
const dep_file = arena.create(Output) catch @panic("OOM");
dep_file.* = .{
- .prefix = graph.dupeString(prefix),
+ .prefix = graph.dupeString(options.prefix),
.basename = graph.dupeString(basename),
+ .suffix = graph.dupeString(options.suffix),
.generated_file = graph.addGeneratedFile(&run.step),
+ .make_absolute = options.make_absolute,
};
run.argv.append(arena, .{ .output_file_dep = dep_file }) catch @panic("OOM");
@@ -642,19 +676,17 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
const graph = b.graph;
const arena = graph.arena;
- if (run.captured_stderr) |captured| return .{ .generated = .{ .index = captured.output.generated_file } };
+ if (run.captured_stderr) |captured| return .{ .generated = .{ .index = captured.generated_file } };
const captured = arena.create(CapturedStdIo) catch @panic("OOM");
captured.* = .{
- .output = .{
- .prefix = "",
- .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
- .generated_file = graph.addGeneratedFile(&run.step),
- },
+ .prefix = "",
+ .basename = if (options.basename) |basename| graph.dupeString(basename) else "stderr",
+ .generated_file = graph.addGeneratedFile(&run.step),
.trim_whitespace = options.trim_whitespace,
};
run.captured_stderr = captured;
- return .{ .generated = .{ .index = captured.output.generated_file } };
+ return .{ .generated = .{ .index = captured.generated_file } };
}
pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
@@ -665,19 +697,17 @@ pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
const graph = b.graph;
const arena = graph.arena;
- if (run.captured_stdout) |captured| return .{ .generated = .{ .index = captured.output.generated_file } };
+ if (run.captured_stdout) |captured| return .{ .generated = .{ .index = captured.generated_file } };
const captured = arena.create(CapturedStdIo) catch @panic("OOM");
captured.* = .{
- .output = .{
- .prefix = "",
- .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
- .generated_file = graph.addGeneratedFile(&run.step),
- },
+ .prefix = "",
+ .basename = if (options.basename) |basename| graph.dupeString(basename) else "stdout",
+ .generated_file = graph.addGeneratedFile(&run.step),
.trim_whitespace = options.trim_whitespace,
};
run.captured_stdout = captured;
- return .{ .generated = .{ .index = captured.output.generated_file } };
+ return .{ .generated = .{ .index = captured.generated_file } };
}
/// Adds an additional input files that, when modified, indicates that this Run
diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon
@@ -196,6 +196,9 @@
.elf2 = .{
.path = "elf2",
},
+ .run_args = .{
+ .path = "run_args",
+ },
},
.paths = .{
"build.zig",
diff --git a/test/standalone/run_args/build.zig b/test/standalone/run_args/build.zig
@@ -0,0 +1,160 @@
+const std = @import("std");
+
+/// Tests that args are passed to run steps correctly.
+///
+/// Note that when `make_absolute` is true we make sure the resulting path argument is absolute, but
+/// when it is false we allow either absolute or relative paths. This is because the maker receives
+/// absolute paths when build is run from anywhere other than the build root.
+pub fn build(b: *std.Build) !void {
+ const step = b.step("test", "Run artifact args standalone test cases");
+ b.default_step = step;
+
+ const exe = b.addExecutable(.{
+ .name = "exe",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = b.graph.host,
+ }),
+ });
+
+ // Arg
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ run.addArg("arg1");
+ run.expectStdErrEqual("arg1\n");
+ }
+
+ // Args
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ run.addArgs(&.{ "arg1", "arg2" });
+ run.expectStdErrEqual("arg1\narg2\n");
+ }
+
+ // Artifact Args
+ {
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addArtifactArg2(exe, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrMatch("abs exe");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addArtifactArg2(exe, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("exe\n");
+ }
+ }
+
+ // File Args
+ {
+ const write_files = b.addWriteFiles();
+ const file = write_files.add("file", "");
+
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addFileArg2(file, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrEqual("abs file\n");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addFileArg2(file, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("file\n");
+ }
+ }
+
+ // File Content
+ {
+ const write_files = b.addWriteFiles();
+ const file = write_files.add("file", "foo bar baz");
+
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addFileContentArg2(file, .{ .prefix = "content-prefix ", .suffix = " content-suffix" });
+ run.expectStdErrEqual("content-prefix foo bar baz content-suffix\n");
+ }
+
+ // Output File Args
+ {
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addOutputFileArg2("output-file", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrEqual("abs output-file\n");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addOutputFileArg2("output-file", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("output-file\n");
+ }
+ }
+
+ // Output Directory Args
+ {
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addOutputDirectoryArg2("output-dir", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrEqual("abs output-dir\n");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addOutputDirectoryArg2("output-dir", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("output-dir\n");
+ }
+ }
+
+ // Directory Args
+ {
+ const write_files = b.addWriteFiles();
+ const directory = try write_files.getDirectory().join(b.graph.arena, "dir");
+
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addDirectoryArg2(directory, .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrEqual("abs dir\n");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addDirectoryArg2(directory, .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("dir\n");
+ }
+ }
+
+ // Dep File Args
+ {
+ // Absolute
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addDepFileOutputArg2("deps.d", .{ .prefix = "path^", .make_absolute = true, .suffix = "$" });
+ run.expectStdErrEqual("abs deps.d\n");
+ }
+ // Relative
+ {
+ const run = b.addRunArtifact(exe);
+ step.dependOn(&run.step);
+ _ = run.addDepFileOutputArg2("deps.d", .{ .prefix = "path^", .make_absolute = false, .suffix = "$" });
+ run.expectStdErrMatch("deps.d\n");
+ }
+ }
+}
diff --git a/test/standalone/run_args/main.zig b/test/standalone/run_args/main.zig
@@ -0,0 +1,32 @@
+const std = @import("std");
+
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const arena = init.arena.allocator();
+
+ var iter = try init.minimal.args.iterateAllocator(arena);
+ std.debug.assert(iter.skip());
+ while (iter.next()) |arg| {
+ const path_prefix = "path^";
+ const path_suffix = "$";
+ if (std.mem.startsWith(u8, arg, path_prefix) and std.mem.endsWith(u8, arg, path_suffix)) {
+ // If we're a path, log whether we're absolute or relative, and log the basename
+ const path = arg[path_prefix.len..][0 .. arg.len - path_prefix.len - path_suffix.len];
+ if (std.fs.path.isAbsolute(path)) {
+ std.debug.print("abs ", .{});
+ } else {
+ std.debug.print("rel ", .{});
+ }
+ std.debug.print("{s}\n", .{std.fs.path.basename(path)});
+
+ // Create an empty dep file if necessary
+ if (std.mem.endsWith(u8, path, ".d")) {
+ const file = try std.Io.Dir.cwd().createFile(io, path, .{});
+ defer file.close(io);
+ }
+ } else {
+ // If it's not a path, log the arg as is
+ std.debug.print("{s}\n", .{arg});
+ }
+ }
+}