authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-05 16:45:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-13 06:42:25-07:00
log2654d0c66860d32714e33404554482cbc0cbabf5
tree2ad10365938888bc04741c925e4a80350e99b85c
parentb061cc9e3ff197d4af1408c8e3a8504dda208e10

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.

1 files changed, 64 insertions(+), 1 deletions(-)

lib/std/Build/RunStep.zig+64-1
...@@ -39,6 +39,10 @@ expected_exit_code: ?u8 = 0,...@@ -39,6 +39,10 @@ expected_exit_code: ?u8 = 0,
3939
40/// Print the command before running it40/// Print the command before running it
41print: bool,41print: bool,
42/// Controls whether execution is skipped if the output file is up-to-date.
43/// The default is to always run if there is no output file, and to skip
44/// running if all output files are up-to-date.
45condition: enum { output_outdated, always } = .output_outdated,
4246
43pub const StdIoAction = union(enum) {47pub const StdIoAction = union(enum) {
44 inherit,48 inherit,
...@@ -51,6 +55,12 @@ pub const Arg = union(enum) {...@@ -51,6 +55,12 @@ pub const Arg = union(enum) {
51 artifact: *CompileStep,55 artifact: *CompileStep,
52 file_source: std.Build.FileSource,56 file_source: std.Build.FileSource,
53 bytes: []u8,57 bytes: []u8,
58 output: Output,
59
60 pub const Output = struct {
61 generated_file: *std.Build.GeneratedFile,
62 basename: []const u8,
63 };
54};64};
5565
56pub fn create(builder: *std.Build, name: []const u8) *RunStep {66pub fn create(builder: *std.Build, name: []const u8) *RunStep {
...@@ -71,6 +81,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {...@@ -71,6 +81,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
71 self.step.dependOn(&artifact.step);81 self.step.dependOn(&artifact.step);
72}82}
7383
84/// This provides file path as a command line argument to the command being
85/// run, and returns a FileSource which can be used as inputs to other APIs
86/// throughout the build system.
87pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource {
88 const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM");
89 generated_file.* = .{ .step = &rs.step };
90 rs.argv.append(.{ .output = .{
91 .generated_file = generated_file,
92 .basename = rs.builder.dupe(basename),
93 } }) catch @panic("OOM");
94
95 return .{ .generated = generated_file };
96}
97
74pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {98pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
75 self.argv.append(Arg{99 self.argv.append(Arg{
76 .file_source = file_source.dupe(self.builder),100 .file_source = file_source.dupe(self.builder),
...@@ -159,10 +183,24 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {...@@ -159,10 +183,24 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
159 };183 };
160}184}
161185
186fn needOutputCheck(self: RunStep) bool {
187 switch (self.condition) {
188 .always => return false,
189 .output_outdated => {
190 for (self.argv.items) |arg| switch (arg) {
191 .output => return true,
192 else => continue,
193 };
194 return false;
195 },
196 }
197}
198
162fn make(step: *Step) !void {199fn make(step: *Step) !void {
163 const self = @fieldParentPtr(RunStep, "step", step);200 const self = @fieldParentPtr(RunStep, "step", step);
164201
165 var argv_list = ArrayList([]const u8).init(self.builder.allocator);202 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
203
166 for (self.argv.items) |arg| {204 for (self.argv.items) |arg| {
167 switch (arg) {205 switch (arg) {
168 .bytes => |bytes| try argv_list.append(bytes),206 .bytes => |bytes| try argv_list.append(bytes),
...@@ -172,9 +210,34 @@ fn make(step: *Step) !void {...@@ -172,9 +210,34 @@ fn make(step: *Step) !void {
172 // On Windows we don't have rpaths so we have to add .dll search paths to PATH210 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
173 self.addPathForDynLibs(artifact);211 self.addPathForDynLibs(artifact);
174 }212 }
175 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);213 const executable_path = artifact.installed_path orelse
214 artifact.getOutputSource().getPath(self.builder);
176 try argv_list.append(executable_path);215 try argv_list.append(executable_path);
177 },216 },
217 .output => |output| {
218 // TODO: until the cache system is brought into the build system,
219 // we use a temporary directory here for each run.
220 var digest: [16]u8 = undefined;
221 std.crypto.random.bytes(&digest);
222 var hash_basename: [digest.len * 2]u8 = undefined;
223 _ = std.fmt.bufPrint(
224 &hash_basename,
225 "{s}",
226 .{std.fmt.fmtSliceHexLower(&digest)},
227 ) catch unreachable;
228
229 const output_path = try fs.path.join(self.builder.allocator, &[_][]const u8{
230 self.builder.cache_root, "tmp", &hash_basename, output.basename,
231 });
232 const output_dir = fs.path.dirname(output_path).?;
233 fs.cwd().makePath(output_dir) catch |err| {
234 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
235 return err;
236 };
237
238 output.generated_file.path = output_path;
239 try argv_list.append(output_path);
240 },
178 }241 }
179 }242 }
180243