authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-05 06:02:25+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-05 06:02:25+01:00
log435cd6f129172733fdc4b9bd62abe3c94f91d583
tree8346c38b5f579ec0ddfda202806e5e70b6b497ba
parent8e091047b5a8fd2f8a74d56a7353f16fd66dff25
parentc8ecfad41a322260fae38a4115a94345ed9f94b7

Merge pull request 'std.Build: adjust temp files API' (#30683) from std.Build-temp into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30683

7 files changed, 232 insertions(+), 213 deletions(-)

lib/compiler/build_runner.zig+15-1
......@@ -82,7 +82,7 @@ pub fn main(init: process.Init.Minimal) !void {
8282 .arena = arena,
8383 .cache = .{
8484 .io = io,
85 .gpa = arena,
85 .gpa = gpa,
8686 .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
8787 .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()),
8888 },
......@@ -784,6 +784,9 @@ fn runStepNames(
784784 var pending_count: usize = 0;
785785 var total_compile_errors: usize = 0;
786786
787 var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
788 defer cleanup_task.await(io);
789
787790 for (step_stack.keys()) |s| {
788791 test_pass_count += s.test_results.passCount();
789792 test_skip_count += s.test_results.skip_count;
......@@ -1849,3 +1852,14 @@ fn initStdoutWriter(io: Io) *Writer {
18491852 stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);
18501853 return &stdout_writer_allocation.interface;
18511854}
1855
1856fn cleanTmpFiles(io: Io, steps: []const *Step) void {
1857 for (steps) |step| {
1858 const wf = step.cast(std.Build.Step.WriteFile) orelse continue;
1859 if (wf.mode != .tmp) continue;
1860 const path = wf.generated_directory.path orelse continue;
1861 Io.Dir.cwd().deleteTree(io, path) catch |err| {
1862 std.log.warn("failed to delete {s}: {t}", .{ path, err });
1863 };
1864 }
1865}
lib/std/Build.zig+42-19
......@@ -111,6 +111,7 @@ pub const ReleaseMode = enum {
111111/// Settings that are here rather than in Build are not configurable per-package.
112112pub const Graph = struct {
113113 io: Io,
114 /// Process lifetime.
114115 arena: Allocator,
115116 system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,
116117 system_package_mode: bool = false,
......@@ -1057,6 +1058,38 @@ pub fn addNamedLazyPath(b: *Build, name: []const u8, lp: LazyPath) void {
10571058 b.named_lazy_paths.put(b.dupe(name), lp.dupe(b)) catch @panic("OOM");
10581059}
10591060
1061/// Creates a step for mutating files inside a temporary directory created lazily
1062/// and automatically cleaned up upon successful build.
1063///
1064/// The directory will be placed inside "tmp" rather than "o", and caching will
1065/// be skipped. During the `make` phase, the step will always do all the file
1066/// system operations, and on successful build completion, the dir will be
1067/// deleted along with all other tmp directories. The directory is therefore
1068/// eligible to be used for mutations by other steps.
1069///
1070/// See also:
1071/// * `addWriteFiles`
1072/// * `addMutateFiles`
1073pub fn addTempFiles(b: *Build) *Step.WriteFile {
1074 const wf = addWriteFiles(b);
1075 wf.mode = .tmp;
1076 return wf;
1077}
1078
1079/// Creates a step for mutating temporary directories created with `addTempFiles`.
1080///
1081/// Consider instead `addWriteFiles` which is for creating a cached directory
1082/// of files to operate on.
1083///
1084/// This should only be used with a `tmp_path` obtained via `addTempFiles` or
1085/// `tmpPath`.
1086pub fn addMutateFiles(b: *Build, tmp_path: LazyPath) *Step.WriteFile {
1087 const wf = addWriteFiles(b);
1088 wf.mode = .{ .mutate = tmp_path };
1089 tmp_path.addStepDependencies(&wf.step);
1090 return wf;
1091}
1092
10601093pub fn addWriteFiles(b: *Build) *Step.WriteFile {
10611094 return Step.WriteFile.create(b);
10621095}
......@@ -1065,10 +1098,6 @@ pub fn addUpdateSourceFiles(b: *Build) *Step.UpdateSourceFiles {
10651098 return Step.UpdateSourceFiles.create(b);
10661099}
10671100
1068pub fn addRemoveDirTree(b: *Build, dir_path: LazyPath) *Step.RemoveDir {
1069 return Step.RemoveDir.create(b, dir_path);
1070}
1071
10721101pub fn addFail(b: *Build, error_msg: []const u8) *Step.Fail {
10731102 return Step.Fail.create(b, error_msg);
10741103}
......@@ -2235,9 +2264,8 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
22352264/// A file that is generated by a build step.
22362265/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
22372266pub const GeneratedFile = struct {
2238 /// The step that generates the file
2267 /// The step that generates the file.
22392268 step: *Step,
2240
22412269 /// The path to the generated file. Must be either absolute or relative to the build runner cwd.
22422270 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
22432271 path: ?[]const u8 = null,
......@@ -2321,9 +2349,11 @@ pub const LazyPath = union(enum) {
23212349
23222350 /// An absolute path or a path relative to the current working directory of
23232351 /// the build runner process.
2352 ///
23242353 /// This is uncommon but used for system environment paths such as `--zig-lib-dir` which
23252354 /// ignore the file system path of build.zig and instead are relative to the directory from
23262355 /// which `zig build` was invoked.
2356 ///
23272357 /// Use of this tag indicates a dependency on the host system.
23282358 cwd_relative: []const u8,
23292359
......@@ -2646,19 +2676,12 @@ pub const InstallDir = union(enum) {
26462676 }
26472677};
26482678
2649/// This function is intended to be called in the `configure` phase only.
2650/// It returns an absolute directory path, which is potentially going to be a
2651/// source of API breakage in the future, so keep that in mind when using this
2652/// function.
2653pub fn makeTempPath(b: *Build) []const u8 {
2654 const io = b.graph.io;
2655 const rand_int = std.crypto.random.int(u64);
2656 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
2657 const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
2658 b.cache_root.handle.createDirPath(io, tmp_dir_sub_path) catch |err| {
2659 std.debug.print("unable to make tmp path '{s}': {t}\n", .{ result_path, err });
2660 };
2661 return result_path;
2679/// Creates a path leading to a directory inside "tmp" subdirectory of
2680/// `cache_root` which is created on demand and cleaned up by the build runner
2681/// upon success.
2682pub fn tmpPath(b: *Build) LazyPath {
2683 const wf = b.addTempFiles();
2684 return wf.getDirectory();
26622685}
26632686
26642687/// A pair of target query and fully resolved target.
lib/std/Build/Step.zig-3
......@@ -173,7 +173,6 @@ pub const Id = enum {
173173 .install_artifact => InstallArtifact,
174174 .install_file => InstallFile,
175175 .install_dir => InstallDir,
176 .remove_dir => RemoveDir,
177176 .fail => Fail,
178177 .fmt => Fmt,
179178 .translate_c => TranslateC,
......@@ -201,7 +200,6 @@ pub const InstallFile = @import("Step/InstallFile.zig");
201200pub const ObjCopy = @import("Step/ObjCopy.zig");
202201pub const Compile = @import("Step/Compile.zig");
203202pub const Options = @import("Step/Options.zig");
204pub const RemoveDir = @import("Step/RemoveDir.zig");
205203pub const Run = @import("Step/Run.zig");
206204pub const TranslateC = @import("Step/TranslateC.zig");
207205pub const WriteFile = @import("Step/WriteFile.zig");
......@@ -1008,7 +1006,6 @@ test {
10081006 _ = ObjCopy;
10091007 _ = Compile;
10101008 _ = Options;
1011 _ = RemoveDir;
10121009 _ = Run;
10131010 _ = TranslateC;
10141011 _ = WriteFile;
lib/std/Build/Step/RemoveDir.zig deleted-45
......@@ -1,45 +0,0 @@
1const std = @import("std");
2const fs = std.fs;
3const Step = std.Build.Step;
4const RemoveDir = @This();
5const LazyPath = std.Build.LazyPath;
6
7pub const base_id: Step.Id = .remove_dir;
8
9step: Step,
10doomed_path: LazyPath,
11
12pub fn create(owner: *std.Build, doomed_path: LazyPath) *RemoveDir {
13 const remove_dir = owner.allocator.create(RemoveDir) catch @panic("OOM");
14 remove_dir.* = .{
15 .step = Step.init(.{
16 .id = base_id,
17 .name = owner.fmt("RemoveDir {s}", .{doomed_path.getDisplayName()}),
18 .owner = owner,
19 .makeFn = make,
20 }),
21 .doomed_path = doomed_path.dupe(owner),
22 };
23 return remove_dir;
24}
25
26fn make(step: *Step, options: Step.MakeOptions) !void {
27 _ = options;
28
29 const b = step.owner;
30 const io = b.graph.io;
31 const remove_dir: *RemoveDir = @fieldParentPtr("step", step);
32
33 step.clearWatchInputs();
34 try step.addWatchInput(remove_dir.doomed_path);
35
36 const full_doomed_path = remove_dir.doomed_path.getPath2(b, step);
37
38 b.build_root.handle.deleteTree(io, full_doomed_path) catch |err| {
39 if (b.build_root.path) |base| {
40 return step.fail("unable to recursively delete path '{s}/{s}': {t}", .{ base, full_doomed_path, err });
41 } else {
42 return step.fail("unable to recursively delete path '{s}': {t}", .{ full_doomed_path, err });
43 }
44 };
45}
lib/std/Build/Step/WriteFile.zig+142-84
......@@ -1,22 +1,42 @@
11//! WriteFile is used to create a directory in an appropriate location inside
22//! the local cache which has a set of files that have either been generated
33//! during the build, or are copied from the source package.
4const WriteFile = @This();
5
46const std = @import("std");
57const Io = std.Io;
8const Dir = std.Io.Dir;
69const Step = std.Build.Step;
7const fs = std.fs;
810const ArrayList = std.ArrayList;
9const WriteFile = @This();
11const assert = std.debug.assert;
1012
1113step: Step,
1214
13// The elements here are pointers because we need stable pointers for the GeneratedFile field.
15/// The elements here are pointers because we need stable pointers for the GeneratedFile field.
1416files: std.ArrayList(File),
1517directories: std.ArrayList(Directory),
1618generated_directory: std.Build.GeneratedFile,
19mode: Mode = .whole_cached,
1720
1821pub const base_id: Step.Id = .write_file;
1922
23pub const Mode = union(enum) {
24 /// Default mode. Integrates with the cache system. The directory should be
25 /// read-only during the make phase. Any different inputs result in
26 /// different "o" subdirectory.
27 whole_cached,
28 /// In this mode, the directory will be placed inside "tmp" rather than
29 /// "o", and caching will be skipped. During the `make` phase, the step
30 /// will always do all the file system operations, and on successful build
31 /// completion, the dir will be deleted along with all other tmp
32 /// directories. The directory is therefore eligible to be used for
33 /// mutations by other steps.
34 tmp,
35 /// The operations will not be performed against a freshly created
36 /// directory, but instead act against a temporary directory.
37 mutate: std.Build.LazyPath,
38};
39
2040pub const File = struct {
2141 sub_path: []const u8,
2242 contents: Contents,
......@@ -175,115 +195,155 @@ fn maybeUpdateName(write_file: *WriteFile) void {
175195fn make(step: *Step, options: Step.MakeOptions) !void {
176196 _ = options;
177197 const b = step.owner;
178 const io = b.graph.io;
198 const graph = b.graph;
199 const io = graph.io;
179200 const arena = b.allocator;
180 const gpa = arena;
201 const gpa = graph.cache.gpa;
181202 const write_file: *WriteFile = @fieldParentPtr("step", step);
182 step.clearWatchInputs();
183
184 // The cache is used here not really as a way to speed things up - because writing
185 // the data to a file would probably be very fast - but as a way to find a canonical
186 // location to put build artifacts.
187203
188 // If, for example, a hard-coded path was used as the location to put WriteFile
189 // files, then two WriteFiles executing in parallel might clobber each other.
204 const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len);
205 var open_dirs_count: usize = 0;
206 defer Io.Dir.closeMany(io, open_dir_cache[0..open_dirs_count]);
190207
191 var man = b.graph.cache.obtain();
192 defer man.deinit();
208 switch (write_file.mode) {
209 .whole_cached => {
210 step.clearWatchInputs();
193211
194 for (write_file.files.items) |file| {
195 man.hash.addBytes(file.sub_path);
212 // The cache is used here not really as a way to speed things up - because writing
213 // the data to a file would probably be very fast - but as a way to find a canonical
214 // location to put build artifacts.
196215
197 switch (file.contents) {
198 .bytes => |bytes| {
199 man.hash.addBytes(bytes);
200 },
201 .copy => |lazy_path| {
202 const path = lazy_path.getPath3(b, step);
203 _ = try man.addFilePath(path, null);
204 try step.addWatchInput(lazy_path);
205 },
206 }
207 }
216 // If, for example, a hard-coded path was used as the location to put WriteFile
217 // files, then two WriteFiles executing in parallel might clobber each other.
208218
209 const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len);
210 var open_dirs_count: usize = 0;
211 defer Io.Dir.closeMany(io, open_dir_cache[0..open_dirs_count]);
219 var man = b.graph.cache.obtain();
220 defer man.deinit();
212221
213 for (write_file.directories.items, open_dir_cache) |dir, *open_dir_cache_elem| {
214 man.hash.addBytes(dir.sub_path);
215 for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
216 if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
222 for (write_file.files.items) |file| {
223 man.hash.addBytes(file.sub_path);
217224
218 const need_derived_inputs = try step.addDirectoryWatchInput(dir.source);
219 const src_dir_path = dir.source.getPath3(b, step);
225 switch (file.contents) {
226 .bytes => |bytes| {
227 man.hash.addBytes(bytes);
228 },
229 .copy => |lazy_path| {
230 const path = lazy_path.getPath3(b, step);
231 _ = try man.addFilePath(path, null);
232 try step.addWatchInput(lazy_path);
233 },
234 }
235 }
220236
221 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
222 return step.fail("unable to open source directory '{f}': {s}", .{
223 src_dir_path, @errorName(err),
224 });
225 };
226 open_dir_cache_elem.* = src_dir;
227 open_dirs_count += 1;
237 for (write_file.directories.items, open_dir_cache) |dir, *open_dir_cache_elem| {
238 man.hash.addBytes(dir.sub_path);
239 for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
240 if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
228241
229 var it = try src_dir.walk(gpa);
230 defer it.deinit();
231 while (try it.next(io)) |entry| {
232 if (!dir.options.pathIncluded(entry.path)) continue;
242 const need_derived_inputs = try step.addDirectoryWatchInput(dir.source);
243 const src_dir_path = dir.source.getPath3(b, step);
233244
234 switch (entry.kind) {
235 .directory => {
236 if (need_derived_inputs) {
237 const entry_path = try src_dir_path.join(arena, entry.path);
238 try step.addDirectoryWatchInputFromPath(entry_path);
245 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
246 return step.fail("unable to open source directory '{f}': {s}", .{
247 src_dir_path, @errorName(err),
248 });
249 };
250 open_dir_cache_elem.* = src_dir;
251 open_dirs_count += 1;
252
253 var it = try src_dir.walk(gpa);
254 defer it.deinit();
255 while (try it.next(io)) |entry| {
256 if (!dir.options.pathIncluded(entry.path)) continue;
257
258 switch (entry.kind) {
259 .directory => {
260 if (need_derived_inputs) {
261 const entry_path = try src_dir_path.join(arena, entry.path);
262 try step.addDirectoryWatchInputFromPath(entry_path);
263 }
264 },
265 .file => {
266 const entry_path = try src_dir_path.join(arena, entry.path);
267 _ = try man.addFilePath(entry_path, null);
268 },
269 else => continue,
239270 }
240 },
241 .file => {
242 const entry_path = try src_dir_path.join(arena, entry.path);
243 _ = try man.addFilePath(entry_path, null);
244 },
245 else => continue,
271 }
246272 }
247 }
248 }
249273
250 if (try step.cacheHit(&man)) {
251 const digest = man.final();
252 write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
253 step.result_cached = true;
254 return;
255 }
274 if (try step.cacheHit(&man)) {
275 const digest = man.final();
276 write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
277 assert(step.result_cached);
278 return;
279 }
280
281 const digest = man.final();
282 const cache_path = "o" ++ Dir.path.sep_str ++ digest;
283
284 write_file.generated_directory.path = try b.cache_root.join(arena, &.{cache_path});
285
286 try operate(write_file, open_dir_cache, .{
287 .root_dir = b.cache_root,
288 .sub_path = cache_path,
289 });
290
291 try step.writeManifest(&man);
292 },
293 .tmp => {
294 step.result_cached = false;
295
296 const rand_int = std.crypto.random.int(u64);
297 const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
256298
257 const digest = man.final();
258 const cache_path = "o" ++ fs.path.sep_str ++ digest;
299 write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path});
300
301 try operate(write_file, open_dir_cache, .{
302 .root_dir = b.cache_root,
303 .sub_path = tmp_dir_sub_path,
304 });
305 },
306 .mutate => |lp| {
307 step.result_cached = false;
308 const root_path = try lp.getPath4(b, step);
309 write_file.generated_directory.path = try root_path.toString(arena);
310 try operate(write_file, open_dir_cache, root_path);
311 },
312 }
313}
259314
260 write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
315fn operate(write_file: *WriteFile, open_dir_cache: []const Io.Dir, root_path: std.Build.Cache.Path) !void {
316 const step = &write_file.step;
317 const b = step.owner;
318 const io = b.graph.io;
319 const gpa = b.graph.cache.gpa;
320 const arena = b.allocator;
261321
262 var cache_dir = b.cache_root.handle.createDirPathOpen(io, cache_path, .{}) catch |err|
263 return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, cache_path, err });
322 var cache_dir = root_path.root_dir.handle.createDirPathOpen(io, root_path.sub_path, .{}) catch |err|
323 return step.fail("unable to make path {f}: {t}", .{ root_path, err });
264324 defer cache_dir.close(io);
265325
266326 for (write_file.files.items) |file| {
267 if (fs.path.dirname(file.sub_path)) |dirname| {
327 if (Dir.path.dirname(file.sub_path)) |dirname| {
268328 cache_dir.createDirPath(io, dirname) catch |err| {
269 return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{
270 b.cache_root, cache_path, fs.path.sep, dirname, err,
329 return step.fail("unable to make path '{f}{c}{s}': {t}", .{
330 root_path, Dir.path.sep, dirname, err,
271331 });
272332 };
273333 }
274334 switch (file.contents) {
275335 .bytes => |bytes| {
276336 cache_dir.writeFile(io, .{ .sub_path = file.sub_path, .data = bytes }) catch |err| {
277 return step.fail("unable to write file '{f}{s}{c}{s}': {t}", .{
278 b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
337 return step.fail("unable to write file '{f}{c}{s}': {t}", .{
338 root_path, Dir.path.sep, file.sub_path, err,
279339 });
280340 };
281341 },
282342 .copy => |file_source| {
283343 const source_path = file_source.getPath2(b, step);
284344 const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir, file.sub_path, .{}) catch |err| {
285 return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {t}", .{
286 source_path, b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
345 return step.fail("unable to update file from '{s}' to '{f}{c}{s}': {t}", .{
346 source_path, root_path, Dir.path.sep, file.sub_path, err,
287347 });
288348 };
289349 // At this point we already will mark the step as a cache miss.
......@@ -301,8 +361,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
301361
302362 if (dest_dirname.len != 0) {
303363 cache_dir.createDirPath(io, dest_dirname) catch |err| {
304 return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
305 b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
364 return step.fail("unable to make path '{f}{c}{s}': {t}", .{
365 root_path, Dir.path.sep, dest_dirname, err,
306366 });
307367 };
308368 }
......@@ -325,8 +385,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
325385 dest_path,
326386 .{},
327387 ) catch |err| {
328 return step.fail("unable to update file from '{f}' to '{f}{s}{c}{s}': {s}", .{
329 src_entry_path, b.cache_root, cache_path, fs.path.sep, dest_path, @errorName(err),
388 return step.fail("unable to update file from '{f}' to '{f}{c}{s}': {t}", .{
389 src_entry_path, root_path, Dir.path.sep, dest_path, err,
330390 });
331391 };
332392 _ = prev_status;
......@@ -335,6 +395,4 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
335395 }
336396 }
337397 }
338
339 try step.writeManifest(&man);
340398}
test/standalone/dirname/build.zig+3-13
......@@ -58,19 +58,9 @@ pub fn build(b: *std.Build) void {
5858 );
5959
6060 // Absolute path:
61 const abs_path = setup_abspath: {
62 // TODO this is a bad pattern, don't do this
63 const io = b.graph.io;
64 const temp_dir = b.makeTempPath();
65
66 var dir = std.Io.Dir.cwd().openDir(io, temp_dir, .{}) catch @panic("failed to open temp dir");
67 defer dir.close(io);
68
69 var file = dir.createFile(io, "foo.txt", .{}) catch @panic("failed to create file");
70 file.close(io);
71
72 break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir };
73 };
61 const write_files = b.addWriteFiles();
62 _ = write_files.add("foo.txt", "");
63 const abs_path = write_files.getDirectory();
7464 addTestRun(test_step, exists_in, abs_path, &.{"foo.txt"});
7565}
7666
test/tests.zig+30-48
......@@ -2026,13 +2026,12 @@ pub fn addLinkTests(
20262026pub fn addCliTests(b: *std.Build) *Step {
20272027 const step = b.step("test-cli", "Test the command line interface");
20282028 const s = std.fs.path.sep_str;
2029 const io = b.graph.io;
20302029
20312030 {
20322031 // Test `zig init`.
2033 const tmp_path = b.makeTempPath();
2032 const tmp_path = b.tmpPath();
20342033 const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init" });
2035 init_exe.setCwd(.{ .cwd_relative = tmp_path });
2034 init_exe.setCwd(tmp_path);
20362035 init_exe.setName("zig init");
20372036 init_exe.expectStdOutEqual("");
20382037 init_exe.expectStdErrEqual("info: created build.zig\n" ++
......@@ -2053,31 +2052,28 @@ pub fn addCliTests(b: *std.Build) *Step {
20532052 run_bad.step.dependOn(&init_exe.step);
20542053
20552054 const run_test = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "test" });
2056 run_test.setCwd(.{ .cwd_relative = tmp_path });
2055 run_test.setCwd(tmp_path);
20572056 run_test.setName("zig build test");
20582057 run_test.expectStdOutEqual("");
20592058 run_test.step.dependOn(&init_exe.step);
20602059
20612060 const run_run = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "run" });
2062 run_run.setCwd(.{ .cwd_relative = tmp_path });
2061 run_run.setCwd(tmp_path);
20632062 run_run.setName("zig build run");
20642063 run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n");
20652064 run_run.expectStdErrMatch("All your codebase are belong to us.\n");
20662065 run_run.step.dependOn(&init_exe.step);
20672066
2068 const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
2069 cleanup.step.dependOn(&run_test.step);
2070 cleanup.step.dependOn(&run_run.step);
2071 cleanup.step.dependOn(&run_bad.step);
2072
2073 step.dependOn(&cleanup.step);
2067 step.dependOn(&run_test.step);
2068 step.dependOn(&run_run.step);
2069 step.dependOn(&run_bad.step);
20742070 }
20752071
20762072 {
20772073 // Test `zig init -m`.
2078 const tmp_path = b.makeTempPath();
2074 const tmp_path = b.tmpPath();
20792075 const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init", "-m" });
2080 init_exe.setCwd(.{ .cwd_relative = tmp_path });
2076 init_exe.setCwd(tmp_path);
20812077 init_exe.setName("zig init -m");
20822078 init_exe.expectStdOutEqual("");
20832079 init_exe.expectStdErrEqual("info: successfully populated 'build.zig.zon' and 'build.zig'\n");
......@@ -2085,7 +2081,7 @@ pub fn addCliTests(b: *std.Build) *Step {
20852081
20862082 // Test Godbolt API
20872083 if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
2088 const tmp_path = b.makeTempPath();
2084 const tmp_path = b.tmpPath();
20892085
20902086 const example_zig = b.addWriteFiles().add("example.zig",
20912087 \\// Type your code here, or load an example.
......@@ -2101,13 +2097,9 @@ pub fn addCliTests(b: *std.Build) *Step {
21012097 );
21022098
21032099 // This is intended to be the exact CLI usage used by godbolt.org.
2104 const run = b.addSystemCommand(&.{
2105 b.graph.zig_exe, "build-obj",
2106 "--cache-dir", tmp_path,
2107 "--name", "example",
2108 "-fno-emit-bin", "-fno-emit-h",
2109 "-fstrip", "-OReleaseFast",
2110 });
2100 const run = b.addSystemCommand(&.{ b.graph.zig_exe, "build-obj", "--cache-dir" });
2101 run.addDirectoryArg(tmp_path);
2102 run.addArgs(&.{ "--name", "example", "-fno-emit-bin", "-fno-emit-h", "-fstrip", "-OReleaseFast" });
21112103 run.addFileArg(example_zig);
21122104 const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s");
21132105
......@@ -2120,10 +2112,7 @@ pub fn addCliTests(b: *std.Build) *Step {
21202112 });
21212113 checkfile.setName("check godbolt.org CLI usage generating valid asm");
21222114
2123 const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
2124 cleanup.step.dependOn(&checkfile.step);
2125
2126 step.dependOn(&cleanup.step);
2115 step.dependOn(&checkfile.step);
21272116 }
21282117
21292118 {
......@@ -2132,22 +2121,19 @@ pub fn addCliTests(b: *std.Build) *Step {
21322121 // directory because this test will be mutating the files. The cache
21332122 // system relies on cache directories being mutated only by their
21342123 // owners.
2135 const tmp_path = b.makeTempPath();
2124 const tmp_wf = b.addTempFiles();
21362125 const unformatted_code = " // no reason for indent";
21372126
2138 var dir = std.Io.Dir.cwd().openDir(io, tmp_path, .{}) catch @panic("unhandled");
2139 defer dir.close(io);
2140 dir.writeFile(io, .{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled");
2141 dir.writeFile(io, .{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled");
2142 dir.createDir(io, "subdir", .default_dir) catch @panic("unhandled");
2143 var subdir = dir.openDir(io, "subdir", .{}) catch @panic("unhandled");
2144 defer subdir.close(io);
2145 subdir.writeFile(io, .{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled");
2127 _ = tmp_wf.add("fmt1.zig", unformatted_code);
2128 _ = tmp_wf.add("fmt2.zig", unformatted_code);
2129 _ = tmp_wf.add("subdir/fmt3.zig", unformatted_code);
2130
2131 const tmp_path = tmp_wf.getDirectory();
21462132
21472133 // Test zig fmt affecting only the appropriate files.
21482134 const run1 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "fmt1.zig" });
21492135 run1.setName("run zig fmt one file");
2150 run1.setCwd(.{ .cwd_relative = tmp_path });
2136 run1.setCwd(tmp_path);
21512137 run1.has_side_effects = true;
21522138 // stdout should be file path + \n
21532139 run1.expectStdOutEqual("fmt1.zig\n");
......@@ -2155,7 +2141,7 @@ pub fn addCliTests(b: *std.Build) *Step {
21552141 // Test excluding files and directories from a run
21562142 const run2 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "--exclude", "fmt2.zig", "--exclude", "subdir", "." });
21572143 run2.setName("run zig fmt on directory with exclusions");
2158 run2.setCwd(.{ .cwd_relative = tmp_path });
2144 run2.setCwd(tmp_path);
21592145 run2.has_side_effects = true;
21602146 run2.expectStdOutEqual("");
21612147 run2.step.dependOn(&run1.step);
......@@ -2163,7 +2149,7 @@ pub fn addCliTests(b: *std.Build) *Step {
21632149 // Test excluding non-existent file
21642150 const run3 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "--exclude", "fmt2.zig", "--exclude", "nonexistent.zig", "." });
21652151 run3.setName("run zig fmt on directory with non-existent exclusion");
2166 run3.setCwd(.{ .cwd_relative = tmp_path });
2152 run3.setCwd(tmp_path);
21672153 run3.has_side_effects = true;
21682154 run3.expectStdOutEqual("." ++ s ++ "subdir" ++ s ++ "fmt3.zig\n");
21692155 run3.step.dependOn(&run2.step);
......@@ -2171,7 +2157,7 @@ pub fn addCliTests(b: *std.Build) *Step {
21712157 // running it on the dir, only the new file should be changed
21722158 const run4 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
21732159 run4.setName("run zig fmt the directory");
2174 run4.setCwd(.{ .cwd_relative = tmp_path });
2160 run4.setCwd(tmp_path);
21752161 run4.has_side_effects = true;
21762162 run4.expectStdOutEqual("." ++ s ++ "fmt2.zig\n");
21772163 run4.step.dependOn(&run3.step);
......@@ -2179,37 +2165,33 @@ pub fn addCliTests(b: *std.Build) *Step {
21792165 // both files have been formatted, nothing should change now
21802166 const run5 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
21812167 run5.setName("run zig fmt with nothing to do");
2182 run5.setCwd(.{ .cwd_relative = tmp_path });
2168 run5.setCwd(tmp_path);
21832169 run5.has_side_effects = true;
21842170 run5.expectStdOutEqual("");
21852171 run5.step.dependOn(&run4.step);
21862172
21872173 const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
2188 const fmt6_path = b.pathJoin(&.{ tmp_path, "fmt6.zig" });
2189 const write6 = b.addUpdateSourceFiles();
2190 write6.addBytesToSource(unformatted_code_utf16, fmt6_path);
2174 const write6 = b.addMutateFiles(tmp_path);
2175 const fmt6_path = write6.add("fmt6.zig", unformatted_code_utf16);
21912176 write6.step.dependOn(&run5.step);
21922177
21932178 // Test `zig fmt` handling UTF-16 decoding.
21942179 const run6 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
21952180 run6.setName("run zig fmt convert UTF-16 to UTF-8");
2196 run6.setCwd(.{ .cwd_relative = tmp_path });
2181 run6.setCwd(tmp_path);
21972182 run6.has_side_effects = true;
21982183 run6.expectStdOutEqual("." ++ s ++ "fmt6.zig\n");
21992184 run6.step.dependOn(&write6.step);
22002185
22012186 // TODO change this to an exact match
2202 const check6 = b.addCheckFile(.{ .cwd_relative = fmt6_path }, .{
2187 const check6 = b.addCheckFile(fmt6_path, .{
22032188 .expected_matches = &.{
22042189 "// no reason",
22052190 },
22062191 });
22072192 check6.step.dependOn(&run6.step);
22082193
2209 const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
2210 cleanup.step.dependOn(&check6.step);
2211
2212 step.dependOn(&cleanup.step);
2194 step.dependOn(&check6.step);
22132195 }
22142196
22152197 {