commit 435cd6f129172733fdc4b9bd62abe3c94f91d583 (tree)
parent 8e091047b5a8fd2f8a74d56a7353f16fd66dff25
Author: Andrew Kelley <andrewrk@noreply.codeberg.org>
Date: Mon, 5 Jan 2026 06:02:25 +0100
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
Diffstat:
7 files changed, 232 insertions(+), 213 deletions(-)
diff --git a/lib/compiler/build_runner.zig b/lib/compiler/build_runner.zig
@@ -82,7 +82,7 @@ pub fn main(init: process.Init.Minimal) !void {
.arena = arena,
.cache = .{
.io = io,
- .gpa = arena,
+ .gpa = gpa,
.manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
.cwd = try process.getCwdAlloc(single_threaded_arena.allocator()),
},
@@ -784,6 +784,9 @@ fn runStepNames(
var pending_count: usize = 0;
var total_compile_errors: usize = 0;
+ var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
+ defer cleanup_task.await(io);
+
for (step_stack.keys()) |s| {
test_pass_count += s.test_results.passCount();
test_skip_count += s.test_results.skip_count;
@@ -1849,3 +1852,14 @@ fn initStdoutWriter(io: Io) *Writer {
stdout_writer_allocation = Io.File.stdout().writerStreaming(io, &stdio_buffer_allocation);
return &stdout_writer_allocation.interface;
}
+
+fn cleanTmpFiles(io: Io, steps: []const *Step) void {
+ for (steps) |step| {
+ const wf = step.cast(std.Build.Step.WriteFile) orelse continue;
+ if (wf.mode != .tmp) continue;
+ const path = wf.generated_directory.path orelse continue;
+ Io.Dir.cwd().deleteTree(io, path) catch |err| {
+ std.log.warn("failed to delete {s}: {t}", .{ path, err });
+ };
+ }
+}
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
@@ -111,6 +111,7 @@ pub const ReleaseMode = enum {
/// Settings that are here rather than in Build are not configurable per-package.
pub const Graph = struct {
io: Io,
+ /// Process lifetime.
arena: Allocator,
system_library_options: std.StringArrayHashMapUnmanaged(SystemLibraryMode) = .empty,
system_package_mode: bool = false,
@@ -1057,6 +1058,38 @@ pub fn addNamedLazyPath(b: *Build, name: []const u8, lp: LazyPath) void {
b.named_lazy_paths.put(b.dupe(name), lp.dupe(b)) catch @panic("OOM");
}
+/// Creates a step for mutating files inside a temporary directory created lazily
+/// and automatically cleaned up upon successful build.
+///
+/// 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.
+///
+/// See also:
+/// * `addWriteFiles`
+/// * `addMutateFiles`
+pub fn addTempFiles(b: *Build) *Step.WriteFile {
+ const wf = addWriteFiles(b);
+ wf.mode = .tmp;
+ return wf;
+}
+
+/// Creates a step for mutating temporary directories created with `addTempFiles`.
+///
+/// Consider instead `addWriteFiles` which is for creating a cached directory
+/// of files to operate on.
+///
+/// This should only be used with a `tmp_path` obtained via `addTempFiles` or
+/// `tmpPath`.
+pub fn addMutateFiles(b: *Build, tmp_path: LazyPath) *Step.WriteFile {
+ const wf = addWriteFiles(b);
+ wf.mode = .{ .mutate = tmp_path };
+ tmp_path.addStepDependencies(&wf.step);
+ return wf;
+}
+
pub fn addWriteFiles(b: *Build) *Step.WriteFile {
return Step.WriteFile.create(b);
}
@@ -1065,10 +1098,6 @@ pub fn addUpdateSourceFiles(b: *Build) *Step.UpdateSourceFiles {
return Step.UpdateSourceFiles.create(b);
}
-pub fn addRemoveDirTree(b: *Build, dir_path: LazyPath) *Step.RemoveDir {
- return Step.RemoveDir.create(b, dir_path);
-}
-
pub fn addFail(b: *Build, error_msg: []const u8) *Step.Fail {
return Step.Fail.create(b, error_msg);
}
@@ -2235,9 +2264,8 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
/// A file that is generated by a build step.
/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
pub const GeneratedFile = struct {
- /// The step that generates the file
+ /// The step that generates the file.
step: *Step,
-
/// The path to the generated file. Must be either absolute or relative to the build runner cwd.
/// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
path: ?[]const u8 = null,
@@ -2321,9 +2349,11 @@ pub const LazyPath = union(enum) {
/// An absolute path or a path relative to the current working directory of
/// the build runner process.
+ ///
/// This is uncommon but used for system environment paths such as `--zig-lib-dir` which
/// ignore the file system path of build.zig and instead are relative to the directory from
/// which `zig build` was invoked.
+ ///
/// Use of this tag indicates a dependency on the host system.
cwd_relative: []const u8,
@@ -2646,19 +2676,12 @@ pub const InstallDir = union(enum) {
}
};
-/// This function is intended to be called in the `configure` phase only.
-/// It returns an absolute directory path, which is potentially going to be a
-/// source of API breakage in the future, so keep that in mind when using this
-/// function.
-pub fn makeTempPath(b: *Build) []const u8 {
- const io = b.graph.io;
- const rand_int = std.crypto.random.int(u64);
- const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
- const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
- b.cache_root.handle.createDirPath(io, tmp_dir_sub_path) catch |err| {
- std.debug.print("unable to make tmp path '{s}': {t}\n", .{ result_path, err });
- };
- return result_path;
+/// Creates a path leading to a directory inside "tmp" subdirectory of
+/// `cache_root` which is created on demand and cleaned up by the build runner
+/// upon success.
+pub fn tmpPath(b: *Build) LazyPath {
+ const wf = b.addTempFiles();
+ return wf.getDirectory();
}
/// A pair of target query and fully resolved target.
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
@@ -173,7 +173,6 @@ pub const Id = enum {
.install_artifact => InstallArtifact,
.install_file => InstallFile,
.install_dir => InstallDir,
- .remove_dir => RemoveDir,
.fail => Fail,
.fmt => Fmt,
.translate_c => TranslateC,
@@ -201,7 +200,6 @@ pub const InstallFile = @import("Step/InstallFile.zig");
pub const ObjCopy = @import("Step/ObjCopy.zig");
pub const Compile = @import("Step/Compile.zig");
pub const Options = @import("Step/Options.zig");
-pub const RemoveDir = @import("Step/RemoveDir.zig");
pub const Run = @import("Step/Run.zig");
pub const TranslateC = @import("Step/TranslateC.zig");
pub const WriteFile = @import("Step/WriteFile.zig");
@@ -1008,7 +1006,6 @@ test {
_ = ObjCopy;
_ = Compile;
_ = Options;
- _ = RemoveDir;
_ = Run;
_ = TranslateC;
_ = WriteFile;
diff --git a/lib/std/Build/Step/RemoveDir.zig b/lib/std/Build/Step/RemoveDir.zig
@@ -1,45 +0,0 @@
-const std = @import("std");
-const fs = std.fs;
-const Step = std.Build.Step;
-const RemoveDir = @This();
-const LazyPath = std.Build.LazyPath;
-
-pub const base_id: Step.Id = .remove_dir;
-
-step: Step,
-doomed_path: LazyPath,
-
-pub fn create(owner: *std.Build, doomed_path: LazyPath) *RemoveDir {
- const remove_dir = owner.allocator.create(RemoveDir) catch @panic("OOM");
- remove_dir.* = .{
- .step = Step.init(.{
- .id = base_id,
- .name = owner.fmt("RemoveDir {s}", .{doomed_path.getDisplayName()}),
- .owner = owner,
- .makeFn = make,
- }),
- .doomed_path = doomed_path.dupe(owner),
- };
- return remove_dir;
-}
-
-fn make(step: *Step, options: Step.MakeOptions) !void {
- _ = options;
-
- const b = step.owner;
- const io = b.graph.io;
- const remove_dir: *RemoveDir = @fieldParentPtr("step", step);
-
- step.clearWatchInputs();
- try step.addWatchInput(remove_dir.doomed_path);
-
- const full_doomed_path = remove_dir.doomed_path.getPath2(b, step);
-
- b.build_root.handle.deleteTree(io, full_doomed_path) catch |err| {
- if (b.build_root.path) |base| {
- return step.fail("unable to recursively delete path '{s}/{s}': {t}", .{ base, full_doomed_path, err });
- } else {
- return step.fail("unable to recursively delete path '{s}': {t}", .{ full_doomed_path, err });
- }
- };
-}
diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig
@@ -1,22 +1,42 @@
//! WriteFile is used to create a directory in an appropriate location inside
//! the local cache which has a set of files that have either been generated
//! during the build, or are copied from the source package.
+const WriteFile = @This();
+
const std = @import("std");
const Io = std.Io;
+const Dir = std.Io.Dir;
const Step = std.Build.Step;
-const fs = std.fs;
const ArrayList = std.ArrayList;
-const WriteFile = @This();
+const assert = std.debug.assert;
step: Step,
-// The elements here are pointers because we need stable pointers for the GeneratedFile field.
+/// The elements here are pointers because we need stable pointers for the GeneratedFile field.
files: std.ArrayList(File),
directories: std.ArrayList(Directory),
generated_directory: std.Build.GeneratedFile,
+mode: Mode = .whole_cached,
pub const base_id: Step.Id = .write_file;
+pub const Mode = union(enum) {
+ /// Default mode. Integrates with the cache system. The directory should be
+ /// read-only during the make phase. Any different inputs result in
+ /// different "o" subdirectory.
+ whole_cached,
+ /// 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.
+ tmp,
+ /// The operations will not be performed against a freshly created
+ /// directory, but instead act against a temporary directory.
+ mutate: std.Build.LazyPath,
+};
+
pub const File = struct {
sub_path: []const u8,
contents: Contents,
@@ -175,115 +195,155 @@ fn maybeUpdateName(write_file: *WriteFile) void {
fn make(step: *Step, options: Step.MakeOptions) !void {
_ = options;
const b = step.owner;
- const io = b.graph.io;
+ const graph = b.graph;
+ const io = graph.io;
const arena = b.allocator;
- const gpa = arena;
+ const gpa = graph.cache.gpa;
const write_file: *WriteFile = @fieldParentPtr("step", step);
- step.clearWatchInputs();
-
- // The cache is used here not really as a way to speed things up - because writing
- // the data to a file would probably be very fast - but as a way to find a canonical
- // location to put build artifacts.
- // If, for example, a hard-coded path was used as the location to put WriteFile
- // files, then two WriteFiles executing in parallel might clobber each other.
+ const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len);
+ var open_dirs_count: usize = 0;
+ defer Io.Dir.closeMany(io, open_dir_cache[0..open_dirs_count]);
- var man = b.graph.cache.obtain();
- defer man.deinit();
+ switch (write_file.mode) {
+ .whole_cached => {
+ step.clearWatchInputs();
- for (write_file.files.items) |file| {
- man.hash.addBytes(file.sub_path);
+ // The cache is used here not really as a way to speed things up - because writing
+ // the data to a file would probably be very fast - but as a way to find a canonical
+ // location to put build artifacts.
- switch (file.contents) {
- .bytes => |bytes| {
- man.hash.addBytes(bytes);
- },
- .copy => |lazy_path| {
- const path = lazy_path.getPath3(b, step);
- _ = try man.addFilePath(path, null);
- try step.addWatchInput(lazy_path);
- },
- }
- }
+ // If, for example, a hard-coded path was used as the location to put WriteFile
+ // files, then two WriteFiles executing in parallel might clobber each other.
- const open_dir_cache = try arena.alloc(Io.Dir, write_file.directories.items.len);
- var open_dirs_count: usize = 0;
- defer Io.Dir.closeMany(io, open_dir_cache[0..open_dirs_count]);
+ var man = b.graph.cache.obtain();
+ defer man.deinit();
- for (write_file.directories.items, open_dir_cache) |dir, *open_dir_cache_elem| {
- man.hash.addBytes(dir.sub_path);
- for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
- if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
+ for (write_file.files.items) |file| {
+ man.hash.addBytes(file.sub_path);
- const need_derived_inputs = try step.addDirectoryWatchInput(dir.source);
- const src_dir_path = dir.source.getPath3(b, step);
+ switch (file.contents) {
+ .bytes => |bytes| {
+ man.hash.addBytes(bytes);
+ },
+ .copy => |lazy_path| {
+ const path = lazy_path.getPath3(b, step);
+ _ = try man.addFilePath(path, null);
+ try step.addWatchInput(lazy_path);
+ },
+ }
+ }
- var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
- return step.fail("unable to open source directory '{f}': {s}", .{
- src_dir_path, @errorName(err),
- });
- };
- open_dir_cache_elem.* = src_dir;
- open_dirs_count += 1;
+ for (write_file.directories.items, open_dir_cache) |dir, *open_dir_cache_elem| {
+ man.hash.addBytes(dir.sub_path);
+ for (dir.options.exclude_extensions) |ext| man.hash.addBytes(ext);
+ if (dir.options.include_extensions) |incs| for (incs) |inc| man.hash.addBytes(inc);
- var it = try src_dir.walk(gpa);
- defer it.deinit();
- while (try it.next(io)) |entry| {
- if (!dir.options.pathIncluded(entry.path)) continue;
+ const need_derived_inputs = try step.addDirectoryWatchInput(dir.source);
+ const src_dir_path = dir.source.getPath3(b, step);
- switch (entry.kind) {
- .directory => {
- if (need_derived_inputs) {
- const entry_path = try src_dir_path.join(arena, entry.path);
- try step.addDirectoryWatchInputFromPath(entry_path);
+ var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
+ return step.fail("unable to open source directory '{f}': {s}", .{
+ src_dir_path, @errorName(err),
+ });
+ };
+ open_dir_cache_elem.* = src_dir;
+ open_dirs_count += 1;
+
+ var it = try src_dir.walk(gpa);
+ defer it.deinit();
+ while (try it.next(io)) |entry| {
+ if (!dir.options.pathIncluded(entry.path)) continue;
+
+ switch (entry.kind) {
+ .directory => {
+ if (need_derived_inputs) {
+ const entry_path = try src_dir_path.join(arena, entry.path);
+ try step.addDirectoryWatchInputFromPath(entry_path);
+ }
+ },
+ .file => {
+ const entry_path = try src_dir_path.join(arena, entry.path);
+ _ = try man.addFilePath(entry_path, null);
+ },
+ else => continue,
}
- },
- .file => {
- const entry_path = try src_dir_path.join(arena, entry.path);
- _ = try man.addFilePath(entry_path, null);
- },
- else => continue,
+ }
}
- }
- }
- if (try step.cacheHit(&man)) {
- const digest = man.final();
- write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
- step.result_cached = true;
- return;
- }
+ if (try step.cacheHit(&man)) {
+ const digest = man.final();
+ write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
+ assert(step.result_cached);
+ return;
+ }
+
+ const digest = man.final();
+ const cache_path = "o" ++ Dir.path.sep_str ++ digest;
+
+ write_file.generated_directory.path = try b.cache_root.join(arena, &.{cache_path});
+
+ try operate(write_file, open_dir_cache, .{
+ .root_dir = b.cache_root,
+ .sub_path = cache_path,
+ });
+
+ try step.writeManifest(&man);
+ },
+ .tmp => {
+ step.result_cached = false;
+
+ const rand_int = std.crypto.random.int(u64);
+ const tmp_dir_sub_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
- const digest = man.final();
- const cache_path = "o" ++ fs.path.sep_str ++ digest;
+ write_file.generated_directory.path = try b.cache_root.join(arena, &.{tmp_dir_sub_path});
+
+ try operate(write_file, open_dir_cache, .{
+ .root_dir = b.cache_root,
+ .sub_path = tmp_dir_sub_path,
+ });
+ },
+ .mutate => |lp| {
+ step.result_cached = false;
+ const root_path = try lp.getPath4(b, step);
+ write_file.generated_directory.path = try root_path.toString(arena);
+ try operate(write_file, open_dir_cache, root_path);
+ },
+ }
+}
- write_file.generated_directory.path = try b.cache_root.join(arena, &.{ "o", &digest });
+fn operate(write_file: *WriteFile, open_dir_cache: []const Io.Dir, root_path: std.Build.Cache.Path) !void {
+ const step = &write_file.step;
+ const b = step.owner;
+ const io = b.graph.io;
+ const gpa = b.graph.cache.gpa;
+ const arena = b.allocator;
- var cache_dir = b.cache_root.handle.createDirPathOpen(io, cache_path, .{}) catch |err|
- return step.fail("unable to make path '{f}{s}': {t}", .{ b.cache_root, cache_path, err });
+ var cache_dir = root_path.root_dir.handle.createDirPathOpen(io, root_path.sub_path, .{}) catch |err|
+ return step.fail("unable to make path {f}: {t}", .{ root_path, err });
defer cache_dir.close(io);
for (write_file.files.items) |file| {
- if (fs.path.dirname(file.sub_path)) |dirname| {
+ if (Dir.path.dirname(file.sub_path)) |dirname| {
cache_dir.createDirPath(io, dirname) catch |err| {
- return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{
- b.cache_root, cache_path, fs.path.sep, dirname, err,
+ return step.fail("unable to make path '{f}{c}{s}': {t}", .{
+ root_path, Dir.path.sep, dirname, err,
});
};
}
switch (file.contents) {
.bytes => |bytes| {
cache_dir.writeFile(io, .{ .sub_path = file.sub_path, .data = bytes }) catch |err| {
- return step.fail("unable to write file '{f}{s}{c}{s}': {t}", .{
- b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
+ return step.fail("unable to write file '{f}{c}{s}': {t}", .{
+ root_path, Dir.path.sep, file.sub_path, err,
});
};
},
.copy => |file_source| {
const source_path = file_source.getPath2(b, step);
const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir, file.sub_path, .{}) catch |err| {
- return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {t}", .{
- source_path, b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
+ return step.fail("unable to update file from '{s}' to '{f}{c}{s}': {t}", .{
+ source_path, root_path, Dir.path.sep, file.sub_path, err,
});
};
// 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 {
if (dest_dirname.len != 0) {
cache_dir.createDirPath(io, dest_dirname) catch |err| {
- return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
- b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
+ return step.fail("unable to make path '{f}{c}{s}': {t}", .{
+ root_path, Dir.path.sep, dest_dirname, err,
});
};
}
@@ -325,8 +385,8 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
dest_path,
.{},
) catch |err| {
- return step.fail("unable to update file from '{f}' to '{f}{s}{c}{s}': {s}", .{
- src_entry_path, b.cache_root, cache_path, fs.path.sep, dest_path, @errorName(err),
+ return step.fail("unable to update file from '{f}' to '{f}{c}{s}': {t}", .{
+ src_entry_path, root_path, Dir.path.sep, dest_path, err,
});
};
_ = prev_status;
@@ -335,6 +395,4 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
}
}
}
-
- try step.writeManifest(&man);
}
diff --git a/test/standalone/dirname/build.zig b/test/standalone/dirname/build.zig
@@ -58,19 +58,9 @@ pub fn build(b: *std.Build) void {
);
// Absolute path:
- const abs_path = setup_abspath: {
- // TODO this is a bad pattern, don't do this
- const io = b.graph.io;
- const temp_dir = b.makeTempPath();
-
- var dir = std.Io.Dir.cwd().openDir(io, temp_dir, .{}) catch @panic("failed to open temp dir");
- defer dir.close(io);
-
- var file = dir.createFile(io, "foo.txt", .{}) catch @panic("failed to create file");
- file.close(io);
-
- break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir };
- };
+ const write_files = b.addWriteFiles();
+ _ = write_files.add("foo.txt", "");
+ const abs_path = write_files.getDirectory();
addTestRun(test_step, exists_in, abs_path, &.{"foo.txt"});
}
diff --git a/test/tests.zig b/test/tests.zig
@@ -2026,13 +2026,12 @@ pub fn addLinkTests(
pub fn addCliTests(b: *std.Build) *Step {
const step = b.step("test-cli", "Test the command line interface");
const s = std.fs.path.sep_str;
- const io = b.graph.io;
{
// Test `zig init`.
- const tmp_path = b.makeTempPath();
+ const tmp_path = b.tmpPath();
const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init" });
- init_exe.setCwd(.{ .cwd_relative = tmp_path });
+ init_exe.setCwd(tmp_path);
init_exe.setName("zig init");
init_exe.expectStdOutEqual("");
init_exe.expectStdErrEqual("info: created build.zig\n" ++
@@ -2053,31 +2052,28 @@ pub fn addCliTests(b: *std.Build) *Step {
run_bad.step.dependOn(&init_exe.step);
const run_test = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "test" });
- run_test.setCwd(.{ .cwd_relative = tmp_path });
+ run_test.setCwd(tmp_path);
run_test.setName("zig build test");
run_test.expectStdOutEqual("");
run_test.step.dependOn(&init_exe.step);
const run_run = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "run" });
- run_run.setCwd(.{ .cwd_relative = tmp_path });
+ run_run.setCwd(tmp_path);
run_run.setName("zig build run");
run_run.expectStdOutEqual("Run `zig build test` to run the tests.\n");
run_run.expectStdErrMatch("All your codebase are belong to us.\n");
run_run.step.dependOn(&init_exe.step);
- const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
- cleanup.step.dependOn(&run_test.step);
- cleanup.step.dependOn(&run_run.step);
- cleanup.step.dependOn(&run_bad.step);
-
- step.dependOn(&cleanup.step);
+ step.dependOn(&run_test.step);
+ step.dependOn(&run_run.step);
+ step.dependOn(&run_bad.step);
}
{
// Test `zig init -m`.
- const tmp_path = b.makeTempPath();
+ const tmp_path = b.tmpPath();
const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init", "-m" });
- init_exe.setCwd(.{ .cwd_relative = tmp_path });
+ init_exe.setCwd(tmp_path);
init_exe.setName("zig init -m");
init_exe.expectStdOutEqual("");
init_exe.expectStdErrEqual("info: successfully populated 'build.zig.zon' and 'build.zig'\n");
@@ -2085,7 +2081,7 @@ pub fn addCliTests(b: *std.Build) *Step {
// Test Godbolt API
if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
- const tmp_path = b.makeTempPath();
+ const tmp_path = b.tmpPath();
const example_zig = b.addWriteFiles().add("example.zig",
\\// Type your code here, or load an example.
@@ -2101,13 +2097,9 @@ pub fn addCliTests(b: *std.Build) *Step {
);
// This is intended to be the exact CLI usage used by godbolt.org.
- const run = b.addSystemCommand(&.{
- b.graph.zig_exe, "build-obj",
- "--cache-dir", tmp_path,
- "--name", "example",
- "-fno-emit-bin", "-fno-emit-h",
- "-fstrip", "-OReleaseFast",
- });
+ const run = b.addSystemCommand(&.{ b.graph.zig_exe, "build-obj", "--cache-dir" });
+ run.addDirectoryArg(tmp_path);
+ run.addArgs(&.{ "--name", "example", "-fno-emit-bin", "-fno-emit-h", "-fstrip", "-OReleaseFast" });
run.addFileArg(example_zig);
const example_s = run.addPrefixedOutputFileArg("-femit-asm=", "example.s");
@@ -2120,10 +2112,7 @@ pub fn addCliTests(b: *std.Build) *Step {
});
checkfile.setName("check godbolt.org CLI usage generating valid asm");
- const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
- cleanup.step.dependOn(&checkfile.step);
-
- step.dependOn(&cleanup.step);
+ step.dependOn(&checkfile.step);
}
{
@@ -2132,22 +2121,19 @@ pub fn addCliTests(b: *std.Build) *Step {
// directory because this test will be mutating the files. The cache
// system relies on cache directories being mutated only by their
// owners.
- const tmp_path = b.makeTempPath();
+ const tmp_wf = b.addTempFiles();
const unformatted_code = " // no reason for indent";
- var dir = std.Io.Dir.cwd().openDir(io, tmp_path, .{}) catch @panic("unhandled");
- defer dir.close(io);
- dir.writeFile(io, .{ .sub_path = "fmt1.zig", .data = unformatted_code }) catch @panic("unhandled");
- dir.writeFile(io, .{ .sub_path = "fmt2.zig", .data = unformatted_code }) catch @panic("unhandled");
- dir.createDir(io, "subdir", .default_dir) catch @panic("unhandled");
- var subdir = dir.openDir(io, "subdir", .{}) catch @panic("unhandled");
- defer subdir.close(io);
- subdir.writeFile(io, .{ .sub_path = "fmt3.zig", .data = unformatted_code }) catch @panic("unhandled");
+ _ = tmp_wf.add("fmt1.zig", unformatted_code);
+ _ = tmp_wf.add("fmt2.zig", unformatted_code);
+ _ = tmp_wf.add("subdir/fmt3.zig", unformatted_code);
+
+ const tmp_path = tmp_wf.getDirectory();
// Test zig fmt affecting only the appropriate files.
const run1 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "fmt1.zig" });
run1.setName("run zig fmt one file");
- run1.setCwd(.{ .cwd_relative = tmp_path });
+ run1.setCwd(tmp_path);
run1.has_side_effects = true;
// stdout should be file path + \n
run1.expectStdOutEqual("fmt1.zig\n");
@@ -2155,7 +2141,7 @@ pub fn addCliTests(b: *std.Build) *Step {
// Test excluding files and directories from a run
const run2 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "--exclude", "fmt2.zig", "--exclude", "subdir", "." });
run2.setName("run zig fmt on directory with exclusions");
- run2.setCwd(.{ .cwd_relative = tmp_path });
+ run2.setCwd(tmp_path);
run2.has_side_effects = true;
run2.expectStdOutEqual("");
run2.step.dependOn(&run1.step);
@@ -2163,7 +2149,7 @@ pub fn addCliTests(b: *std.Build) *Step {
// Test excluding non-existent file
const run3 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "--exclude", "fmt2.zig", "--exclude", "nonexistent.zig", "." });
run3.setName("run zig fmt on directory with non-existent exclusion");
- run3.setCwd(.{ .cwd_relative = tmp_path });
+ run3.setCwd(tmp_path);
run3.has_side_effects = true;
run3.expectStdOutEqual("." ++ s ++ "subdir" ++ s ++ "fmt3.zig\n");
run3.step.dependOn(&run2.step);
@@ -2171,7 +2157,7 @@ pub fn addCliTests(b: *std.Build) *Step {
// running it on the dir, only the new file should be changed
const run4 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
run4.setName("run zig fmt the directory");
- run4.setCwd(.{ .cwd_relative = tmp_path });
+ run4.setCwd(tmp_path);
run4.has_side_effects = true;
run4.expectStdOutEqual("." ++ s ++ "fmt2.zig\n");
run4.step.dependOn(&run3.step);
@@ -2179,37 +2165,33 @@ pub fn addCliTests(b: *std.Build) *Step {
// both files have been formatted, nothing should change now
const run5 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
run5.setName("run zig fmt with nothing to do");
- run5.setCwd(.{ .cwd_relative = tmp_path });
+ run5.setCwd(tmp_path);
run5.has_side_effects = true;
run5.expectStdOutEqual("");
run5.step.dependOn(&run4.step);
const unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
- const fmt6_path = b.pathJoin(&.{ tmp_path, "fmt6.zig" });
- const write6 = b.addUpdateSourceFiles();
- write6.addBytesToSource(unformatted_code_utf16, fmt6_path);
+ const write6 = b.addMutateFiles(tmp_path);
+ const fmt6_path = write6.add("fmt6.zig", unformatted_code_utf16);
write6.step.dependOn(&run5.step);
// Test `zig fmt` handling UTF-16 decoding.
const run6 = b.addSystemCommand(&.{ b.graph.zig_exe, "fmt", "." });
run6.setName("run zig fmt convert UTF-16 to UTF-8");
- run6.setCwd(.{ .cwd_relative = tmp_path });
+ run6.setCwd(tmp_path);
run6.has_side_effects = true;
run6.expectStdOutEqual("." ++ s ++ "fmt6.zig\n");
run6.step.dependOn(&write6.step);
// TODO change this to an exact match
- const check6 = b.addCheckFile(.{ .cwd_relative = fmt6_path }, .{
+ const check6 = b.addCheckFile(fmt6_path, .{
.expected_matches = &.{
"// no reason",
},
});
check6.step.dependOn(&run6.step);
- const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
- cleanup.step.dependOn(&check6.step);
-
- step.dependOn(&cleanup.step);
+ step.dependOn(&check6.step);
}
{