aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAbhinav Gupta <mail@abhinavg.net>2024-01-04 15:47:28 -0800
committerGitHub <noreply@github.com>2024-01-04 18:47:28 -0500
commitd3a163f86845bcc7d20e5fd54174e480d1a8ac33 (patch)
tree4e4085a2798bffeee7a7ef6167bd9c833c12d06d /test
parent501a2350ab804f3e5d4253826bcdfb7a3f5d92fb (diff)
downloadzig-d3a163f86845bcc7d20e5fd54174e480d1a8ac33.tar.gz
zig-d3a163f86845bcc7d20e5fd54174e480d1a8ac33.zip
build/LazyPath: Add dirname (#18371)
Adds a variant to the LazyPath union representing a parent directory of a generated path. ```zig const LazyPath = union(enum) { generated_dirname: struct { generated: *const GeneratedFile, up: usize, }, // ... } ``` These can be constructed with the new method: ```zig pub fn dirname(self: LazyPath) LazyPath ``` For the cases where the LazyPath is already known (`.path`, `.cwd_relative`, and `dependency`) this is evaluated right away. For dirnames of generated files and their dirnames, this is evaluated at getPath time. dirname calls can be chained, but for safety, they are not allowed to escape outside a root defined for each case: - path: This is relative to the build root, so dirname can't escape outside the build root. - generated: Can't escape the zig-cache. - cwd_relative: This can be a relative or absolute path. If relative, can't escape the current directory, and if absolute, can't go beyond root (/). - dependency: Can't escape the dependency's root directory. Testing: I've included a standalone case for many of the happy cases. I couldn't find an easy way to test the negatives, though, because tests cannot yet expect panics.
Diffstat (limited to 'test')
-rw-r--r--test/standalone.zig4
-rw-r--r--test/standalone/dirname/build.zig84
-rw-r--r--test/standalone/dirname/exists_in.zig46
-rw-r--r--test/standalone/dirname/has_basename.zig50
-rw-r--r--test/standalone/dirname/touch.zig44
5 files changed, 228 insertions, 0 deletions
diff --git a/test/standalone.zig b/test/standalone.zig
index b26e85c159..45ba46c015 100644
--- a/test/standalone.zig
+++ b/test/standalone.zig
@@ -180,6 +180,10 @@ pub const build_cases = [_]BuildCase{
.import = @import("standalone/dep_shared_builtin/build.zig"),
},
.{
+ .build_root = "test/standalone/dirname",
+ .import = @import("standalone/dirname/build.zig"),
+ },
+ .{
.build_root = "test/standalone/empty_env",
.import = @import("standalone/empty_env/build.zig"),
},
diff --git a/test/standalone/dirname/build.zig b/test/standalone/dirname/build.zig
new file mode 100644
index 0000000000..272ed54b38
--- /dev/null
+++ b/test/standalone/dirname/build.zig
@@ -0,0 +1,84 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const target = b.standardTargetOptions(.{});
+
+ const test_step = b.step("test", "Test it");
+ b.default_step = test_step;
+
+ const touch_src = std.Build.LazyPath{
+ .path = "touch.zig",
+ };
+
+ const touch = b.addExecutable(.{
+ .name = "touch",
+ .root_source_file = touch_src,
+ .optimize = .Debug,
+ .target = target,
+ });
+ const generated = b.addRunArtifact(touch).addOutputFileArg("subdir" ++ std.fs.path.sep_str ++ "generated.txt");
+
+ const exists_in = b.addExecutable(.{
+ .name = "exists_in",
+ .root_source_file = .{ .path = "exists_in.zig" },
+ .optimize = .Debug,
+ .target = target,
+ });
+
+ const has_basename = b.addExecutable(.{
+ .name = "has_basename",
+ .root_source_file = .{ .path = "has_basename.zig" },
+ .optimize = .Debug,
+ .target = target,
+ });
+
+ // Known path:
+ addTestRun(test_step, exists_in, touch_src.dirname(), &.{"touch.zig"});
+
+ // Generated file:
+ addTestRun(test_step, exists_in, generated.dirname(), &.{"generated.txt"});
+
+ // Generated file multiple levels:
+ addTestRun(test_step, exists_in, generated.dirname().dirname(), &.{
+ "subdir" ++ std.fs.path.sep_str ++ "generated.txt",
+ });
+
+ // Cache root:
+ const cache_dir = b.cache_root.path orelse
+ (b.cache_root.join(b.allocator, &.{"."}) catch @panic("OOM"));
+ addTestRun(
+ test_step,
+ has_basename,
+ generated.dirname().dirname().dirname().dirname(),
+ &.{std.fs.path.basename(cache_dir)},
+ );
+
+ // Absolute path:
+ const abs_path = setup_abspath: {
+ const temp_dir = b.makeTempPath();
+
+ var dir = std.fs.openDirAbsolute(temp_dir, .{}) catch @panic("failed to open temp dir");
+ defer dir.close();
+
+ var file = dir.createFile("foo.txt", .{}) catch @panic("failed to create file");
+ file.close();
+
+ break :setup_abspath std.Build.LazyPath{ .cwd_relative = temp_dir };
+ };
+ addTestRun(test_step, exists_in, abs_path, &.{"foo.txt"});
+}
+
+// Runs exe with the parameters [dirname, args...].
+// Expects the exit code to be 0.
+fn addTestRun(
+ test_step: *std.Build.Step,
+ exe: *std.Build.Step.Compile,
+ dirname: std.Build.LazyPath,
+ args: []const []const u8,
+) void {
+ const run = test_step.owner.addRunArtifact(exe);
+ run.addDirectoryArg(dirname);
+ run.addArgs(args);
+ run.expectExitCode(0);
+ test_step.dependOn(&run.step);
+}
diff --git a/test/standalone/dirname/exists_in.zig b/test/standalone/dirname/exists_in.zig
new file mode 100644
index 0000000000..6730200b3f
--- /dev/null
+++ b/test/standalone/dirname/exists_in.zig
@@ -0,0 +1,46 @@
+//! Verifies that a file exists in a directory.
+//!
+//! Usage:
+//!
+//! ```
+//! exists_in <dir> <path>
+//! ```
+//!
+//! Where `<dir>/<path>` is the full path to the file.
+//! `<dir>` must be an absolute path.
+
+const std = @import("std");
+
+pub fn main() !void {
+ var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ const arena = arena_state.allocator();
+ defer arena_state.deinit();
+
+ try run(arena);
+}
+
+fn run(allocator: std.mem.Allocator) !void {
+ var args = try std.process.argsWithAllocator(allocator);
+ defer args.deinit();
+ _ = args.next() orelse unreachable; // skip binary name
+
+ const dir_path = args.next() orelse {
+ std.log.err("missing <dir> argument", .{});
+ return error.BadUsage;
+ };
+
+ if (!std.fs.path.isAbsolute(dir_path)) {
+ std.log.err("expected <dir> to be an absolute path", .{});
+ return error.BadUsage;
+ }
+
+ const relpath = args.next() orelse {
+ std.log.err("missing <path> argument", .{});
+ return error.BadUsage;
+ };
+
+ var dir = try std.fs.openDirAbsolute(dir_path, .{});
+ defer dir.close();
+
+ _ = try dir.statFile(relpath);
+}
diff --git a/test/standalone/dirname/has_basename.zig b/test/standalone/dirname/has_basename.zig
new file mode 100644
index 0000000000..84f473a07c
--- /dev/null
+++ b/test/standalone/dirname/has_basename.zig
@@ -0,0 +1,50 @@
+//! Checks that the basename of the given path matches a string.
+//!
+//! Usage:
+//!
+//! ```
+//! has_basename <path> <basename>
+//! ```
+//!
+//! <path> must be absolute.
+//!
+//! Returns a non-zero exit code if basename
+//! does not match the given string.
+
+const std = @import("std");
+
+pub fn main() !void {
+ var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ const arena = arena_state.allocator();
+ defer arena_state.deinit();
+
+ try run(arena);
+}
+
+fn run(allocator: std.mem.Allocator) !void {
+ var args = try std.process.argsWithAllocator(allocator);
+ defer args.deinit();
+ _ = args.next() orelse unreachable; // skip binary name
+
+ const path = args.next() orelse {
+ std.log.err("missing <path> argument", .{});
+ return error.BadUsage;
+ };
+
+ if (!std.fs.path.isAbsolute(path)) {
+ std.log.err("path must be absolute", .{});
+ return error.BadUsage;
+ }
+
+ const basename = args.next() orelse {
+ std.log.err("missing <basename> argument", .{});
+ return error.BadUsage;
+ };
+
+ const actual_basename = std.fs.path.basename(path);
+ if (std.mem.eql(u8, actual_basename, basename)) {
+ return;
+ }
+
+ return error.NotEqual;
+}
diff --git a/test/standalone/dirname/touch.zig b/test/standalone/dirname/touch.zig
new file mode 100644
index 0000000000..3ca714a3af
--- /dev/null
+++ b/test/standalone/dirname/touch.zig
@@ -0,0 +1,44 @@
+//! Creates a file at the given path, if it doesn't already exist.
+//!
+//! ```
+//! touch <path>
+//! ```
+//!
+//! Path must be absolute.
+
+const std = @import("std");
+
+pub fn main() !void {
+ var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ const arena = arena_state.allocator();
+ defer arena_state.deinit();
+
+ try run(arena);
+}
+
+fn run(allocator: std.mem.Allocator) !void {
+ var args = try std.process.argsWithAllocator(allocator);
+ defer args.deinit();
+ _ = args.next() orelse unreachable; // skip binary name
+
+ const path = args.next() orelse {
+ std.log.err("missing <path> argument", .{});
+ return error.BadUsage;
+ };
+
+ if (!std.fs.path.isAbsolute(path)) {
+ std.log.err("path must be absolute: {s}", .{path});
+ return error.BadUsage;
+ }
+
+ const dir_path = std.fs.path.dirname(path) orelse unreachable;
+ const basename = std.fs.path.basename(path);
+
+ var dir = try std.fs.openDirAbsolute(dir_path, .{});
+ defer dir.close();
+
+ _ = dir.statFile(basename) catch {
+ var file = try dir.createFile(basename, .{});
+ file.close();
+ };
+}