Run: add output directory arguments

This allows running commands that take an output directory argument. The
main thing that was needed for this feature was generated file subpaths,
to allow access to the files in a generated directory. Additionally, a
minor change was required to so that the correct directory is created
for output directory args.
This commit is contained in:
Jacob Young 2024-05-04 15:12:24 -04:00
parent e3424332d3
commit dee9f82f69
12 changed files with 272 additions and 119 deletions

View File

@ -2131,28 +2131,23 @@ test dirnameAllowEmpty {
/// A reference to an existing or future path. /// A reference to an existing or future path.
pub const LazyPath = union(enum) { pub const LazyPath = union(enum) {
/// Deprecated; use the `path` function instead.
path: []const u8,
/// A source file path relative to build root. /// A source file path relative to build root.
src_path: struct { src_path: struct {
owner: *std.Build, owner: *std.Build,
sub_path: []const u8, sub_path: []const u8,
}, },
/// A file that is generated by an interface. Those files usually are generated: struct {
/// not available until built by a build step. file: *const GeneratedFile,
generated: *const GeneratedFile,
/// One of the parent directories of a file generated by an interface.
/// The path is not available until built by a build step.
generated_dirname: struct {
generated: *const GeneratedFile,
/// The number of parent directories to go up. /// The number of parent directories to go up.
/// 0 means the directory of the generated file, /// 0 means the generated file itself.
/// 1 means the parent of that directory, and so on. /// 1 means the directory of the generated file.
up: usize, /// 2 means the parent of that directory, and so on.
up: usize = 0,
/// Applied after `up`.
sub_path: []const u8 = "",
}, },
/// An absolute path or a path relative to the current working directory of /// An absolute path or a path relative to the current working directory of
@ -2168,12 +2163,6 @@ pub const LazyPath = union(enum) {
sub_path: []const u8, sub_path: []const u8,
}, },
/// Deprecated. Call `path` instead.
pub fn relative(sub_path: []const u8) LazyPath {
std.log.warn("deprecated. call std.Build.path instead", .{});
return .{ .path = sub_path };
}
/// Returns a lazy path referring to the directory containing this path. /// Returns a lazy path referring to the directory containing this path.
/// ///
/// The dirname is not allowed to escape the logical root for underlying path. /// The dirname is not allowed to escape the logical root for underlying path.
@ -2183,8 +2172,6 @@ pub const LazyPath = union(enum) {
/// the dirname is not allowed to traverse outside of zig-cache. /// the dirname is not allowed to traverse outside of zig-cache.
pub fn dirname(lazy_path: LazyPath) LazyPath { pub fn dirname(lazy_path: LazyPath) LazyPath {
return switch (lazy_path) { return switch (lazy_path) {
.generated => |gen| .{ .generated_dirname = .{ .generated = gen, .up = 0 } },
.generated_dirname => |gen| .{ .generated_dirname = .{ .generated = gen.generated, .up = gen.up + 1 } },
.src_path => |sp| .{ .src_path = .{ .src_path => |sp| .{ .src_path = .{
.owner = sp.owner, .owner = sp.owner,
.sub_path = dirnameAllowEmpty(sp.sub_path) orelse { .sub_path = dirnameAllowEmpty(sp.sub_path) orelse {
@ -2192,12 +2179,15 @@ pub const LazyPath = union(enum) {
@panic("misconfigured build script"); @panic("misconfigured build script");
}, },
} }, } },
.path => |sub_path| .{ .generated => |generated| .{ .generated = if (dirnameAllowEmpty(generated.sub_path)) |sub_dirname| .{
.path = dirnameAllowEmpty(sub_path) orelse { .file = generated.file,
dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; .up = generated.up,
@panic("misconfigured build script"); .sub_path = sub_dirname,
}, } else .{
}, .file = generated.file,
.up = generated.up + 1,
.sub_path = "",
} },
.cwd_relative => |rel_path| .{ .cwd_relative => |rel_path| .{
.cwd_relative = dirnameAllowEmpty(rel_path) orelse { .cwd_relative = dirnameAllowEmpty(rel_path) orelse {
// If we get null, it means one of two things: // If we get null, it means one of two things:
@ -2234,14 +2224,34 @@ pub const LazyPath = union(enum) {
}; };
} }
pub fn path(lazy_path: LazyPath, b: *Build, sub_path: []const u8) LazyPath {
return switch (lazy_path) {
.src_path => |src| .{ .src_path = .{
.owner = src.owner,
.sub_path = b.pathResolve(&.{ src.sub_path, sub_path }),
} },
.generated => |gen| .{ .generated = .{
.file = gen.file,
.up = gen.up,
.sub_path = b.pathResolve(&.{ gen.sub_path, sub_path }),
} },
.cwd_relative => |cwd_relative| .{
.cwd_relative = b.pathResolve(&.{ cwd_relative, sub_path }),
},
.dependency => |dep| .{ .dependency = .{
.dependency = dep.dependency,
.sub_path = b.pathResolve(&.{ dep.sub_path, sub_path }),
} },
};
}
/// Returns a string that can be shown to represent the file source. /// Returns a string that can be shown to represent the file source.
/// Either returns the path or `"generated"`. /// Either returns the path, `"generated"`, or `"dependency"`.
pub fn getDisplayName(lazy_path: LazyPath) []const u8 { pub fn getDisplayName(lazy_path: LazyPath) []const u8 {
return switch (lazy_path) { return switch (lazy_path) {
.src_path => |src_path| src_path.sub_path, .src_path => |sp| sp.sub_path,
.path, .cwd_relative => |sub_path| sub_path, .cwd_relative => |p| p,
.generated => "generated", .generated => "generated",
.generated_dirname => "generated",
.dependency => "dependency", .dependency => "dependency",
}; };
} }
@ -2249,9 +2259,8 @@ pub const LazyPath = union(enum) {
/// Adds dependencies this file source implies to the given step. /// Adds dependencies this file source implies to the given step.
pub fn addStepDependencies(lazy_path: LazyPath, other_step: *Step) void { pub fn addStepDependencies(lazy_path: LazyPath, other_step: *Step) void {
switch (lazy_path) { switch (lazy_path) {
.src_path, .path, .cwd_relative, .dependency => {}, .src_path, .cwd_relative, .dependency => {},
.generated => |gen| other_step.dependOn(gen.step), .generated => |gen| other_step.dependOn(gen.file.step),
.generated_dirname => |gen| other_step.dependOn(gen.generated.step),
} }
} }
@ -2268,47 +2277,48 @@ pub const LazyPath = union(enum) {
/// run that is asking for the path. /// run that is asking for the path.
pub fn getPath2(lazy_path: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 { pub fn getPath2(lazy_path: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 {
switch (lazy_path) { switch (lazy_path) {
.path => |p| return src_builder.pathFromRoot(p),
.src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path), .src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path),
.cwd_relative => |p| return src_builder.pathFromCwd(p), .cwd_relative => |p| return src_builder.pathFromCwd(p),
.generated => |gen| return gen.step.owner.pathFromRoot(gen.path orelse { .generated => |gen| {
std.debug.getStderrMutex().lock(); var file_path: []const u8 = gen.file.step.owner.pathFromRoot(gen.file.path orelse {
const stderr = std.io.getStdErr(); std.debug.getStderrMutex().lock();
dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; const stderr = std.io.getStdErr();
@panic("misconfigured build script"); dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
}), std.debug.getStderrMutex().unlock();
.generated_dirname => |gen| { @panic("misconfigured build script");
const cache_root_path = src_builder.cache_root.path orelse });
(src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM"));
const gen_step = gen.generated.step; if (gen.up > 0) {
var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); const cache_root_path = src_builder.cache_root.path orelse
var i: usize = 0; (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM"));
while (i <= gen.up) : (i += 1) {
// path is absolute.
// dirname will return null only if we're at root.
// Typically, we'll stop well before that at the cache root.
p = fs.path.dirname(p) orelse {
dumpBadDirnameHelp(gen_step, asking_step,
\\dirname() reached root.
\\No more directories left to go up.
\\
, .{}) catch {};
@panic("misconfigured build script");
};
if (mem.eql(u8, p, cache_root_path) and i < gen.up) { for (0..gen.up) |_| {
// If we hit the cache root and there's still more to go, if (mem.eql(u8, file_path, cache_root_path)) {
// the script attempted to go too far. // If we hit the cache root and there's still more to go,
dumpBadDirnameHelp(gen_step, asking_step, // the script attempted to go too far.
\\dirname() attempted to traverse outside the cache root. dumpBadDirnameHelp(gen.file.step, asking_step,
\\This is not allowed. \\dirname() attempted to traverse outside the cache root.
\\ \\This is not allowed.
, .{}) catch {}; \\
@panic("misconfigured build script"); , .{}) catch {};
@panic("misconfigured build script");
}
// path is absolute.
// dirname will return null only if we're at root.
// Typically, we'll stop well before that at the cache root.
file_path = fs.path.dirname(file_path) orelse {
dumpBadDirnameHelp(gen.file.step, asking_step,
\\dirname() reached root.
\\No more directories left to go up.
\\
, .{}) catch {};
@panic("misconfigured build script");
};
} }
} }
return p;
return src_builder.pathResolve(&.{ file_path, gen.sub_path });
}, },
.dependency => |dep| return dep.dependency.builder.pathFromRoot(dep.sub_path), .dependency => |dep| return dep.dependency.builder.pathFromRoot(dep.sub_path),
} }
@ -2324,15 +2334,12 @@ pub const LazyPath = union(enum) {
.owner = sp.owner, .owner = sp.owner,
.sub_path = sp.owner.dupePath(sp.sub_path), .sub_path = sp.owner.dupePath(sp.sub_path),
} }, } },
.path => |p| .{ .path = b.dupePath(p) },
.cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) }, .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) },
.generated => |gen| .{ .generated = gen }, .generated => |gen| .{ .generated = .{
.generated_dirname => |gen| .{ .file = gen.file,
.generated_dirname = .{ .up = gen.up,
.generated = gen.generated, .sub_path = b.dupePath(gen.sub_path),
.up = gen.up, } },
},
},
.dependency => |dep| .{ .dependency = dep }, .dependency => |dep| .{ .dependency = dep },
}; };
} }

View File

@ -806,14 +806,12 @@ pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void {
} }
fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath { fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath {
if (output_file.*) |g| { if (output_file.*) |file| return .{ .generated = .{ .file = file } };
return .{ .generated = g };
}
const arena = compile.step.owner.allocator; const arena = compile.step.owner.allocator;
const generated_file = arena.create(GeneratedFile) catch @panic("OOM"); const generated_file = arena.create(GeneratedFile) catch @panic("OOM");
generated_file.* = .{ .step = &compile.step }; generated_file.* = .{ .step = &compile.step };
output_file.* = generated_file; output_file.* = generated_file;
return .{ .generated = generated_file }; return .{ .generated = .{ .file = generated_file } };
} }
/// Returns the path to the directory that contains the emitted binary file. /// Returns the path to the directory that contains the emitted binary file.

View File

@ -59,8 +59,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
if (options.style.getPath()) |s| default_include_path: { if (options.style.getPath()) |s| default_include_path: {
const sub_path = switch (s) { const sub_path = switch (s) {
.src_path => |sp| sp.sub_path, .src_path => |sp| sp.sub_path,
.path => |path| path, .generated => break :default_include_path,
.generated, .generated_dirname => break :default_include_path,
.cwd_relative => |sub_path| sub_path, .cwd_relative => |sub_path| sub_path,
.dependency => |dependency| dependency.sub_path, .dependency => |dependency| dependency.sub_path,
}; };
@ -106,7 +105,7 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void {
} }
pub fn getOutput(config_header: *ConfigHeader) std.Build.LazyPath { pub fn getOutput(config_header: *ConfigHeader) std.Build.LazyPath {
return .{ .generated = &config_header.output_file }; return .{ .generated = .{ .file = &config_header.output_file } };
} }
fn addValuesInner(config_header: *ConfigHeader, values: anytype) !void { fn addValuesInner(config_header: *ConfigHeader, values: anytype) !void {

View File

@ -84,10 +84,10 @@ pub fn create(
pub const getOutputSource = getOutput; pub const getOutputSource = getOutput;
pub fn getOutput(objcopy: *const ObjCopy) std.Build.LazyPath { pub fn getOutput(objcopy: *const ObjCopy) std.Build.LazyPath {
return .{ .generated = &objcopy.output_file }; return .{ .generated = .{ .file = &objcopy.output_file } };
} }
pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath { pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath {
return if (objcopy.output_file_debug) |*file| .{ .generated = file } else null; return if (objcopy.output_file_debug) |*file| .{ .generated = .{ .file = file } } else null;
} }
fn make(step: *Step, prog_node: *std.Progress.Node) !void { fn make(step: *Step, prog_node: *std.Progress.Node) !void {

View File

@ -407,7 +407,7 @@ pub const getSource = getOutput;
/// Returns the main artifact of this Build Step which is a Zig source file /// Returns the main artifact of this Build Step which is a Zig source file
/// generated from the key-value pairs of the Options. /// generated from the key-value pairs of the Options.
pub fn getOutput(options: *Options) LazyPath { pub fn getOutput(options: *Options) LazyPath {
return .{ .generated = &options.generated_file }; return .{ .generated = .{ .file = &options.generated_file } };
} }
fn make(step: *Step, prog_node: *std.Progress.Node) !void { fn make(step: *Step, prog_node: *std.Progress.Node) !void {

View File

@ -125,7 +125,8 @@ pub const Arg = union(enum) {
lazy_path: PrefixedLazyPath, lazy_path: PrefixedLazyPath,
directory_source: PrefixedLazyPath, directory_source: PrefixedLazyPath,
bytes: []u8, bytes: []u8,
output: *Output, output_file: *Output,
output_directory: *Output,
}; };
pub const PrefixedLazyPath = struct { pub const PrefixedLazyPath = struct {
@ -225,13 +226,13 @@ pub fn addPrefixedOutputFileArg(
.basename = b.dupe(basename), .basename = b.dupe(basename),
.generated_file = .{ .step = &run.step }, .generated_file = .{ .step = &run.step },
}; };
run.argv.append(b.allocator, .{ .output = output }) catch @panic("OOM"); run.argv.append(b.allocator, .{ .output_file = output }) catch @panic("OOM");
if (run.rename_step_with_output_arg) { if (run.rename_step_with_output_arg) {
run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename })); run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename }));
} }
return .{ .generated = &output.generated_file }; return .{ .generated = .{ .file = &output.generated_file } };
} }
/// Appends an input file to the command line arguments. /// Appends an input file to the command line arguments.
@ -270,6 +271,56 @@ pub fn addPrefixedFileArg(run: *Run, prefix: []const u8, lp: std.Build.LazyPath)
lp.addStepDependencies(&run.step); 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
pub fn addOutputDirectoryArg(run: *Run, basename: []const u8) std.Build.LazyPath {
return run.addPrefixedOutputDirectoryArg("", basename);
}
/// Provides a directory path as a command line argument to the command being run.
/// Asserts `basename` is not empty.
///
/// For example, a prefix of "-o" and basename of "output_dir" will result in
/// the child process seeing something like this: "-ozig-cache/.../output_dir"
///
/// The child process will see a single argument, regardless of whether the
/// prefix or basename have spaces.
///
/// The returned `std.Build.LazyPath` can be used as inputs to other APIs
/// throughout the build system.
///
/// Related:
/// * `addOutputDirectoryArg` - same thing but without the prefix
/// * `addDirectoryArg` - for input directories given to the child process
pub fn addPrefixedOutputDirectoryArg(
run: *Run,
prefix: []const u8,
basename: []const u8,
) std.Build.LazyPath {
if (basename.len == 0) @panic("basename must not be empty");
const b = run.step.owner;
const output = b.allocator.create(Output) catch @panic("OOM");
output.* = .{
.prefix = b.dupe(prefix),
.basename = b.dupe(basename),
.generated_file = .{ .step = &run.step },
};
run.argv.append(b.allocator, .{ .output_directory = output }) catch @panic("OOM");
if (run.rename_step_with_output_arg) {
run.setName(b.fmt("{s} ({s})", .{ run.step.name, basename }));
}
return .{ .generated = .{ .file = &output.generated_file } };
}
/// deprecated: use `addDirectoryArg` /// deprecated: use `addDirectoryArg`
pub const addDirectorySourceArg = addDirectoryArg; pub const addDirectorySourceArg = addDirectoryArg;
@ -314,9 +365,9 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co
run.dep_output_file = dep_file; run.dep_output_file = dep_file;
run.argv.append(b.allocator, .{ .output = dep_file }) catch @panic("OOM"); run.argv.append(b.allocator, .{ .output_file = dep_file }) catch @panic("OOM");
return .{ .generated = &dep_file.generated_file }; return .{ .generated = .{ .file = &dep_file.generated_file } };
} }
pub fn addArg(run: *Run, arg: []const u8) void { pub fn addArg(run: *Run, arg: []const u8) void {
@ -432,7 +483,7 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void {
pub fn captureStdErr(run: *Run) std.Build.LazyPath { pub fn captureStdErr(run: *Run) std.Build.LazyPath {
assert(run.stdio != .inherit); assert(run.stdio != .inherit);
if (run.captured_stderr) |output| return .{ .generated = &output.generated_file }; if (run.captured_stderr) |output| return .{ .generated = .{ .file = &output.generated_file } };
const output = run.step.owner.allocator.create(Output) catch @panic("OOM"); const output = run.step.owner.allocator.create(Output) catch @panic("OOM");
output.* = .{ output.* = .{
@ -441,13 +492,13 @@ pub fn captureStdErr(run: *Run) std.Build.LazyPath {
.generated_file = .{ .step = &run.step }, .generated_file = .{ .step = &run.step },
}; };
run.captured_stderr = output; run.captured_stderr = output;
return .{ .generated = &output.generated_file }; return .{ .generated = .{ .file = &output.generated_file } };
} }
pub fn captureStdOut(run: *Run) std.Build.LazyPath { pub fn captureStdOut(run: *Run) std.Build.LazyPath {
assert(run.stdio != .inherit); assert(run.stdio != .inherit);
if (run.captured_stdout) |output| return .{ .generated = &output.generated_file }; if (run.captured_stdout) |output| return .{ .generated = .{ .file = &output.generated_file } };
const output = run.step.owner.allocator.create(Output) catch @panic("OOM"); const output = run.step.owner.allocator.create(Output) catch @panic("OOM");
output.* = .{ output.* = .{
@ -456,7 +507,7 @@ pub fn captureStdOut(run: *Run) std.Build.LazyPath {
.generated_file = .{ .step = &run.step }, .generated_file = .{ .step = &run.step },
}; };
run.captured_stdout = output; run.captured_stdout = output;
return .{ .generated = &output.generated_file }; return .{ .generated = .{ .file = &output.generated_file } };
} }
/// Adds an additional input files that, when modified, indicates that this Run /// Adds an additional input files that, when modified, indicates that this Run
@ -484,7 +535,7 @@ fn hasAnyOutputArgs(run: Run) bool {
if (run.captured_stdout != null) return true; if (run.captured_stdout != null) return true;
if (run.captured_stderr != null) return true; if (run.captured_stderr != null) return true;
for (run.argv.items) |arg| switch (arg) { for (run.argv.items) |arg| switch (arg) {
.output => return true, .output_file, .output_directory => return true,
else => continue, else => continue,
}; };
return false; return false;
@ -520,6 +571,7 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
const IndexedOutput = struct { const IndexedOutput = struct {
index: usize, index: usize,
tag: @typeInfo(Arg).Union.tag_type.?,
output: *Output, output: *Output,
}; };
fn make(step: *Step, prog_node: *std.Progress.Node) !void { fn make(step: *Step, prog_node: *std.Progress.Node) !void {
@ -563,17 +615,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = try man.addFile(file_path, null); _ = try man.addFile(file_path, null);
}, },
.output => |output| { .output_file, .output_directory => |output| {
man.hash.addBytes(output.prefix); man.hash.addBytes(output.prefix);
man.hash.addBytes(output.basename); man.hash.addBytes(output.basename);
// Add a placeholder into the argument list because we need the // Add a placeholder into the argument list because we need the
// manifest hash to be updated with all arguments before the // manifest hash to be updated with all arguments before the
// object directory is computed. // object directory is computed.
try argv_list.append("");
try output_placeholders.append(.{ try output_placeholders.append(.{
.index = argv_list.items.len - 1, .index = argv_list.items.len,
.tag = arg,
.output = output, .output = output,
}); });
_ = try argv_list.addOne();
}, },
} }
} }
@ -599,11 +652,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
hashStdIo(&man.hash, run.stdio); hashStdIo(&man.hash, run.stdio);
if (has_side_effects) {
try runCommand(run, argv_list.items, has_side_effects, null, prog_node);
return;
}
for (run.extra_file_dependencies) |file_path| { for (run.extra_file_dependencies) |file_path| {
_ = try man.addFile(b.pathFromRoot(file_path), null); _ = try man.addFile(b.pathFromRoot(file_path), null);
} }
@ -611,7 +659,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
_ = try man.addFile(lazy_path.getPath2(b, step), null); _ = try man.addFile(lazy_path.getPath2(b, step), null);
} }
if (try step.cacheHit(&man)) { if (try step.cacheHit(&man) and !has_side_effects) {
// cache hit, skip running command // cache hit, skip running command
const digest = man.final(); const digest = man.final();
@ -628,13 +676,54 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
return; return;
} }
const dep_output_file = run.dep_output_file orelse {
// We already know the final output paths, use them directly.
const digest = man.final();
try populateGeneratedPaths(
arena,
output_placeholders.items,
run.captured_stdout,
run.captured_stderr,
b.cache_root,
&digest,
);
const output_dir_path = "o" ++ fs.path.sep_str ++ &digest;
for (output_placeholders.items) |placeholder| {
const output_sub_path = b.pathJoin(&.{ output_dir_path, placeholder.output.basename });
const output_sub_dir_path = switch (placeholder.tag) {
.output_file => fs.path.dirname(output_sub_path).?,
.output_directory => output_sub_path,
else => unreachable,
};
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err),
});
};
const output_path = placeholder.output.generated_file.path.?;
argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
output_path
else
b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
}
return runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node);
};
// We do not know the final output paths yet, use temp paths to run the command.
const rand_int = std.crypto.random.int(u64); const rand_int = std.crypto.random.int(u64);
const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.Build.hex64(rand_int); const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.Build.hex64(rand_int);
for (output_placeholders.items) |placeholder| { for (output_placeholders.items) |placeholder| {
const output_components = .{ tmp_dir_path, placeholder.output.basename }; const output_components = .{ tmp_dir_path, placeholder.output.basename };
const output_sub_path = b.pathJoin(&output_components); const output_sub_path = b.pathJoin(&output_components);
const output_sub_dir_path = fs.path.dirname(output_sub_path).?; const output_sub_dir_path = switch (placeholder.tag) {
.output_file => fs.path.dirname(output_sub_path).?,
.output_directory => output_sub_path,
else => unreachable,
};
b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
return step.fail("unable to make path '{}{s}': {s}", .{ return step.fail("unable to make path '{}{s}': {s}", .{
b.cache_root, output_sub_dir_path, @errorName(err), b.cache_root, output_sub_dir_path, @errorName(err),
@ -642,17 +731,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
}; };
const output_path = try b.cache_root.join(arena, &output_components); const output_path = try b.cache_root.join(arena, &output_components);
placeholder.output.generated_file.path = output_path; placeholder.output.generated_file.path = output_path;
const cli_arg = if (placeholder.output.prefix.len == 0) argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
output_path output_path
else else
b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
argv_list.items[placeholder.index] = cli_arg;
} }
try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node); try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node);
if (run.dep_output_file) |dep_output_file| try man.addDepFilePost(std.fs.cwd(), dep_output_file.generated_file.getPath());
try man.addDepFilePost(std.fs.cwd(), dep_output_file.generated_file.getPath());
const digest = man.final(); const digest = man.final();
@ -777,7 +864,7 @@ fn runCommand(
run: *Run, run: *Run,
argv: []const []const u8, argv: []const []const u8,
has_side_effects: bool, has_side_effects: bool,
tmp_dir_path: ?[]const u8, output_dir_path: []const u8,
prog_node: *std.Progress.Node, prog_node: *std.Progress.Node,
) !void { ) !void {
const step = &run.step; const step = &run.step;
@ -950,7 +1037,7 @@ fn runCommand(
}, },
}) |stream| { }) |stream| {
if (stream.captured) |output| { if (stream.captured) |output| {
const output_components = .{ tmp_dir_path.?, output.basename }; const output_components = .{ output_dir_path, output.basename };
const output_path = try b.cache_root.join(arena, &output_components); const output_path = try b.cache_root.join(arena, &output_components);
output.generated_file.path = output_path; output.generated_file.path = output_path;

View File

@ -59,7 +59,7 @@ pub const AddExecutableOptions = struct {
}; };
pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath { pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath {
return .{ .generated = &translate_c.output_file }; return .{ .generated = .{ .file = &translate_c.output_file } };
} }
/// Creates a step to build an executable from the translated source. /// Creates a step to build an executable from the translated source.

View File

@ -31,7 +31,7 @@ pub const File = struct {
contents: Contents, contents: Contents,
pub fn getPath(file: *File) std.Build.LazyPath { pub fn getPath(file: *File) std.Build.LazyPath {
return .{ .generated = &file.generated_file }; return .{ .generated = .{ .file = &file.generated_file } };
} }
}; };
@ -58,7 +58,7 @@ pub const Directory = struct {
}; };
pub fn getPath(dir: *Directory) std.Build.LazyPath { pub fn getPath(dir: *Directory) std.Build.LazyPath {
return .{ .generated = &dir.generated_dir }; return .{ .generated = .{ .file = &dir.generated_dir } };
} }
}; };
@ -181,7 +181,7 @@ pub fn addBytesToSource(write_file: *WriteFile, bytes: []const u8, sub_path: []c
/// Returns a `LazyPath` representing the base directory that contains all the /// Returns a `LazyPath` representing the base directory that contains all the
/// files from this `WriteFile`. /// files from this `WriteFile`.
pub fn getDirectory(write_file: *WriteFile) std.Build.LazyPath { pub fn getDirectory(write_file: *WriteFile) std.Build.LazyPath {
return .{ .generated = &write_file.generated_directory }; return .{ .generated = .{ .file = &write_file.generated_directory } };
} }
fn maybeUpdateName(write_file: *WriteFile) void { fn maybeUpdateName(write_file: *WriteFile) void {

View File

@ -164,6 +164,9 @@
.dependencyFromBuildZig = .{ .dependencyFromBuildZig = .{
.path = "dependencyFromBuildZig", .path = "dependencyFromBuildZig",
}, },
.run_output_paths = .{
.path = "run_output_paths",
},
}, },
.paths = .{ .paths = .{
"build.zig", "build.zig",

View File

@ -0,0 +1,40 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const create_file_exe = b.addExecutable(.{
.name = "create_file",
.root_source_file = b.path("create_file.zig"),
.target = target,
.optimize = optimize,
});
const create_first = b.addRunArtifact(create_file_exe);
const first_dir = create_first.addOutputDirectoryArg("first");
create_first.addArg("hello1.txt");
test_step.dependOn(&b.addCheckFile(first_dir.path(b, "hello1.txt"), .{ .expected_matches = &.{
std.fs.path.sep_str ++
\\first
\\hello1.txt
\\Hello, world!
\\
,
} }).step);
const create_second = b.addRunArtifact(create_file_exe);
const second_dir = create_second.addPrefixedOutputDirectoryArg("--dir=", "second");
create_second.addArg("hello2.txt");
test_step.dependOn(&b.addCheckFile(second_dir.path(b, "hello2.txt"), .{ .expected_matches = &.{
std.fs.path.sep_str ++
\\second
\\hello2.txt
\\Hello, world!
\\
,
} }).step);
}

View File

@ -0,0 +1,19 @@
const std = @import("std");
pub fn main() !void {
var args = try std.process.argsWithAllocator(std.heap.page_allocator);
_ = args.skip();
const dir_name = args.next().?;
const dir = try std.fs.cwd().openDir(if (std.mem.startsWith(u8, dir_name, "--dir="))
dir_name["--dir=".len..]
else
dir_name, .{});
const file_name = args.next().?;
const file = try dir.createFile(file_name, .{});
try file.writer().print(
\\{s}
\\{s}
\\Hello, world!
\\
, .{ dir_name, file_name });
}

View File

@ -36,7 +36,7 @@ fn add(
.file = b.path("res/zig.rc"), .file = b.path("res/zig.rc"),
.flags = &.{"/c65001"}, // UTF-8 code page .flags = &.{"/c65001"}, // UTF-8 code page
.include_paths = &.{ .include_paths = &.{
.{ .generated = &generated_h_step.generated_directory }, .{ .generated = .{ .file = &generated_h_step.generated_directory } },
}, },
}); });
exe.rc_includes = switch (rc_includes) { exe.rc_includes = switch (rc_includes) {