Skip to content

Commit

Permalink
Run: add output directory arguments
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jacobly0 committed May 5, 2024
1 parent e342433 commit dee9f82
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 121 deletions.
167 changes: 87 additions & 80 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2131,28 +2131,23 @@ test dirnameAllowEmpty {

/// A reference to an existing or future path.
pub const LazyPath = union(enum) {
/// Deprecated; use the `path` function instead.
path: []const u8,

/// A source file path relative to build root.
src_path: struct {
owner: *std.Build,
sub_path: []const u8,
},

/// A file that is generated by an interface. Those files usually are
/// not available until built by a build step.
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,
generated: struct {
file: *const GeneratedFile,

/// The number of parent directories to go up.
/// 0 means the directory of the generated file,
/// 1 means the parent of that directory, and so on.
up: usize,
/// 0 means the generated file itself.
/// 1 means the directory of the generated file.
/// 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
Expand All @@ -2168,12 +2163,6 @@ pub const LazyPath = union(enum) {
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.
///
/// The dirname is not allowed to escape the logical root for underlying path.
Expand All @@ -2183,21 +2172,22 @@ pub const LazyPath = union(enum) {
/// the dirname is not allowed to traverse outside of zig-cache.
pub fn dirname(lazy_path: LazyPath) LazyPath {
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 = .{
.owner = sp.owner,
.sub_path = dirnameAllowEmpty(sp.sub_path) orelse {
dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {};
@panic("misconfigured build script");
},
} },
.path => |sub_path| .{
.path = dirnameAllowEmpty(sub_path) orelse {
dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {};
@panic("misconfigured build script");
},
},
.generated => |generated| .{ .generated = if (dirnameAllowEmpty(generated.sub_path)) |sub_dirname| .{
.file = generated.file,
.up = generated.up,
.sub_path = sub_dirname,
} else .{
.file = generated.file,
.up = generated.up + 1,
.sub_path = "",
} },
.cwd_relative => |rel_path| .{
.cwd_relative = dirnameAllowEmpty(rel_path) orelse {
// If we get null, it means one of two things:
Expand Down Expand Up @@ -2234,24 +2224,43 @@ 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.
/// Either returns the path or `"generated"`.
/// Either returns the path, `"generated"`, or `"dependency"`.
pub fn getDisplayName(lazy_path: LazyPath) []const u8 {
return switch (lazy_path) {
.src_path => |src_path| src_path.sub_path,
.path, .cwd_relative => |sub_path| sub_path,
.src_path => |sp| sp.sub_path,
.cwd_relative => |p| p,
.generated => "generated",
.generated_dirname => "generated",
.dependency => "dependency",
};
}

/// Adds dependencies this file source implies to the given step.
pub fn addStepDependencies(lazy_path: LazyPath, other_step: *Step) void {
switch (lazy_path) {
.src_path, .path, .cwd_relative, .dependency => {},
.generated => |gen| other_step.dependOn(gen.step),
.generated_dirname => |gen| other_step.dependOn(gen.generated.step),
.src_path, .cwd_relative, .dependency => {},
.generated => |gen| other_step.dependOn(gen.file.step),
}
}

Expand All @@ -2268,47 +2277,48 @@ pub const LazyPath = union(enum) {
/// run that is asking for the path.
pub fn getPath2(lazy_path: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 {
switch (lazy_path) {
.path => |p| return src_builder.pathFromRoot(p),
.src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path),
.cwd_relative => |p| return src_builder.pathFromCwd(p),
.generated => |gen| return gen.step.owner.pathFromRoot(gen.path orelse {
std.debug.getStderrMutex().lock();
const stderr = std.io.getStdErr();
dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
@panic("misconfigured build script");
}),
.generated_dirname => |gen| {
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;
var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step);
var i: usize = 0;
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) {
// If we hit the cache root and there's still more to go,
// the script attempted to go too far.
dumpBadDirnameHelp(gen_step, asking_step,
\\dirname() attempted to traverse outside the cache root.
\\This is not allowed.
\\
, .{}) catch {};
@panic("misconfigured build script");
.generated => |gen| {
var file_path: []const u8 = gen.file.step.owner.pathFromRoot(gen.file.path orelse {
std.debug.getStderrMutex().lock();
const stderr = std.io.getStdErr();
dumpBadGetPathHelp(gen.file.step, stderr, src_builder, asking_step) catch {};
std.debug.getStderrMutex().unlock();
@panic("misconfigured build script");
});

if (gen.up > 0) {
const cache_root_path = src_builder.cache_root.path orelse
(src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM"));

for (0..gen.up) |_| {
if (mem.eql(u8, file_path, cache_root_path)) {
// If we hit the cache root and there's still more to go,
// the script attempted to go too far.
dumpBadDirnameHelp(gen.file.step, asking_step,
\\dirname() attempted to traverse outside the cache root.
\\This is not allowed.
\\
, .{}) 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),
}
Expand All @@ -2324,15 +2334,12 @@ pub const LazyPath = union(enum) {
.owner = sp.owner,
.sub_path = sp.owner.dupePath(sp.sub_path),
} },
.path => |p| .{ .path = b.dupePath(p) },
.cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) },
.generated => |gen| .{ .generated = gen },
.generated_dirname => |gen| .{
.generated_dirname = .{
.generated = gen.generated,
.up = gen.up,
},
},
.generated => |gen| .{ .generated = .{
.file = gen.file,
.up = gen.up,
.sub_path = b.dupePath(gen.sub_path),
} },
.dependency => |dep| .{ .dependency = dep },
};
}
Expand Down
6 changes: 2 additions & 4 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -806,14 +806,12 @@ pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void {
}

fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath {
if (output_file.*) |g| {
return .{ .generated = g };
}
if (output_file.*) |file| return .{ .generated = .{ .file = file } };
const arena = compile.step.owner.allocator;
const generated_file = arena.create(GeneratedFile) catch @panic("OOM");
generated_file.* = .{ .step = &compile.step };
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.
Expand Down
5 changes: 2 additions & 3 deletions lib/std/Build/Step/ConfigHeader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader {
if (options.style.getPath()) |s| default_include_path: {
const sub_path = switch (s) {
.src_path => |sp| sp.sub_path,
.path => |path| path,
.generated, .generated_dirname => break :default_include_path,
.generated => break :default_include_path,
.cwd_relative => |sub_path| sub_path,
.dependency => |dependency| dependency.sub_path,
};
Expand Down Expand Up @@ -106,7 +105,7 @@ pub fn addValues(config_header: *ConfigHeader, values: anytype) void {
}

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 {
Expand Down
4 changes: 2 additions & 2 deletions lib/std/Build/Step/ObjCopy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ pub fn create(
pub const getOutputSource = getOutput;

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 {
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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/std/Build/Step/Options.zig
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ pub const getSource = getOutput;
/// Returns the main artifact of this Build Step which is a Zig source file
/// generated from the key-value pairs of the Options.
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 {
Expand Down

0 comments on commit dee9f82

Please sign in to comment.