authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 17:23:45-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 17:23:45-08:00
logb4dbe483a720f3e06559e659a410152511087395
tree786b85f7c6237896c59dc70bbae95a46387f491b
parente3b7cad81e483dc79716a388676f39c483589642

std.Build: adjust temp files API

Remove the RemoveDir step with no replacement. This step had no valid purpose. Mutating source files? That should be done with UpdateSourceFiles step. Deleting temporary directories? That required creating the tmp directories in the configure phase which is broken. Deleting cached artifacts? That's going to cause problems. Similarly, remove the `Build.makeTempPath` function. This was used to create a temporary path in the configure place which, again, is the wrong place to do it. Instead, the WriteFile step has been updated with more functionality: tmp mode: In this mode, the directory will be placed inside "tmp" rather than "o", and caching will be skipped. During the `make` phase, the step will always do all the file system operations, and on successful build completion, the dir will be deleted along with all other tmp directories. The directory is therefore eligible to be used for mutations by other steps. `Build.addTempFiles` is introduced to initialize a WriteFile step with this mode. mutate mode: The operations will not be performed against a freshly created directory, but instead act against a temporary directory. `Build.addMutateFiles` is introduced to initialize a WriteFile step with this mode. `Build.tmpPath` is introduced, which is a shortcut for `Build.addTempFiles` followed by `WriteFile.getDirectory`. * give Cache a gpa rather than arena because that's what it asks for

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

lib/compiler/build_runner.zig+1-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 },
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 {