std.Build.RunStep: introduce addOutputFileArg API

This provides file path as a command line argument to the command being
run, and returns a FileSource which can be used as inputs to other APIs
throughout the build system.

Unfortunately, it is implemented by pooping a ton of temporary files
into zig-cache/tmp for the time being. I think one of the very next
improvements to the build system should be moving the compiler's cache
system to the standard library and using it in the build system. I had a
look at the dependencies and it is already pretty untangled.
This commit is contained in:
Andrew Kelley 2023-02-05 16:45:51 -07:00
parent b061cc9e3f
commit 2654d0c668

View File

@ -39,6 +39,10 @@ expected_exit_code: ?u8 = 0,
/// Print the command before running it
print: bool,
/// Controls whether execution is skipped if the output file is up-to-date.
/// The default is to always run if there is no output file, and to skip
/// running if all output files are up-to-date.
condition: enum { output_outdated, always } = .output_outdated,
pub const StdIoAction = union(enum) {
inherit,
@ -51,6 +55,12 @@ pub const Arg = union(enum) {
artifact: *CompileStep,
file_source: std.Build.FileSource,
bytes: []u8,
output: Output,
pub const Output = struct {
generated_file: *std.Build.GeneratedFile,
basename: []const u8,
};
};
pub fn create(builder: *std.Build, name: []const u8) *RunStep {
@ -71,6 +81,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
self.step.dependOn(&artifact.step);
}
/// This provides file path as a command line argument to the command being
/// run, and returns a FileSource which can be used as inputs to other APIs
/// throughout the build system.
pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
generated_file.* = .{ .step = &rs.step };
rs.argv.append(.{ .output = .{
.generated_file = generated_file,
.basename = rs.builder.dupe(basename),
} }) catch @panic("OOM");
return .{ .generated = generated_file };
}
pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
self.argv.append(Arg{
.file_source = file_source.dupe(self.builder),
@ -159,10 +183,24 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
};
}
fn needOutputCheck(self: RunStep) bool {
switch (self.condition) {
.always => return false,
.output_outdated => {
for (self.argv.items) |arg| switch (arg) {
.output => return true,
else => continue,
};
return false;
},
}
}
fn make(step: *Step) !void {
const self = @fieldParentPtr(RunStep, "step", step);
var argv_list = ArrayList([]const u8).init(self.builder.allocator);
for (self.argv.items) |arg| {
switch (arg) {
.bytes => |bytes| try argv_list.append(bytes),
@ -172,9 +210,34 @@ fn make(step: *Step) !void {
// On Windows we don't have rpaths so we have to add .dll search paths to PATH
self.addPathForDynLibs(artifact);
}
const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
const executable_path = artifact.installed_path orelse
artifact.getOutputSource().getPath(self.builder);
try argv_list.append(executable_path);
},
.output => |output| {
// TODO: until the cache system is brought into the build system,
// we use a temporary directory here for each run.
var digest: [16]u8 = undefined;
std.crypto.random.bytes(&digest);
var hash_basename: [digest.len * 2]u8 = undefined;
_ = std.fmt.bufPrint(
&hash_basename,
"{s}",
.{std.fmt.fmtSliceHexLower(&digest)},
) catch unreachable;
const output_path = try fs.path.join(self.builder.allocator, &[_][]const u8{
self.builder.cache_root, "tmp", &hash_basename, output.basename,
});
const output_dir = fs.path.dirname(output_path).?;
fs.cwd().makePath(output_dir) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
return err;
};
output.generated_file.path = output_path;
try argv_list.append(output_path);
},
}
}