zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mlib/compiler/Maker/Step/Run.zig | 44+++++++++++++++++++++++++++++++-------------
Mlib/compiler/configurer.zig | 36++++++++++++++++++++++--------------
Mlib/std/Build/Configuration.zig | 3++-
Mlib/std/Build/Step/Run.zig | 238++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mtest/standalone/build.zig.zon | 3+++
Atest/standalone/run_args/build.zig | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/standalone/run_args/main.zig | 32++++++++++++++++++++++++++++++++
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}); + } + } +}