138 lines
5.7 KiB
Zig

const std = @import("std");
pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "create-file",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
{
const run_random_with_sideeffects_first = b.addRunArtifact(exe);
run_random_with_sideeffects_first.setName("run with side-effects (first)");
run_random_with_sideeffects_first.has_side_effects = true;
const run_random_with_sideeffects_second = b.addRunArtifact(exe);
run_random_with_sideeffects_second.setName("run with side-effects (second)");
run_random_with_sideeffects_second.has_side_effects = true;
// ensure that "second" runs after "first"
run_random_with_sideeffects_second.step.dependOn(&run_random_with_sideeffects_first.step);
const first_output = run_random_with_sideeffects_first.addOutputFileArg("a.txt");
const second_output = run_random_with_sideeffects_second.addOutputFileArg("a.txt");
const expect_uncached_dependencies = CheckOutputCaching.init(b, false, &.{ first_output, second_output });
test_step.dependOn(&expect_uncached_dependencies.step);
const expect_unequal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
test_step.dependOn(&expect_unequal_output.step);
const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
test_step.dependOn(&check_first_output.step);
const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
test_step.dependOn(&check_second_output.step);
}
{
const run_random_without_sideeffects_1 = b.addRunArtifact(exe);
run_random_without_sideeffects_1.setName("run without side-effects (A)");
const run_random_without_sideeffects_2 = b.addRunArtifact(exe);
run_random_without_sideeffects_2.setName("run without side-effects (B)");
run_random_without_sideeffects_2.step.dependOn(&run_random_without_sideeffects_1.step);
const first_output = run_random_without_sideeffects_1.addOutputFileArg("a.txt");
const second_output = run_random_without_sideeffects_2.addOutputFileArg("a.txt");
const expect_cached_dependencies = CheckOutputCaching.init(b, true, &.{second_output});
test_step.dependOn(&expect_cached_dependencies.step);
const expect_equal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
test_step.dependOn(&expect_equal_output.step);
const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
test_step.dependOn(&check_first_output.step);
const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
test_step.dependOn(&check_second_output.step);
}
}
const CheckOutputCaching = struct {
step: std.Build.Step,
expect_caching: bool,
pub fn init(owner: *std.Build, expect_caching: bool, output_paths: []const std.Build.LazyPath) *CheckOutputCaching {
const check = owner.allocator.create(CheckOutputCaching) catch @panic("OOM");
check.* = .{
.step = std.Build.Step.init(.{
.id = .custom,
.name = "check output caching",
.owner = owner,
.makeFn = make,
}),
.expect_caching = expect_caching,
};
for (output_paths) |output_path| {
output_path.addStepDependencies(&check.step);
}
return check;
}
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const check: *CheckOutputCaching = @fieldParentPtr("step", step);
for (step.dependencies.items) |dependency| {
if (check.expect_caching) {
if (dependency.result_cached) continue;
return step.fail("expected '{s}' step to be cached, but it was not", .{dependency.name});
} else {
if (!dependency.result_cached) continue;
return step.fail("expected '{s}' step to not be cached, but it was", .{dependency.name});
}
}
}
};
const CheckPathEquality = struct {
step: std.Build.Step,
expected_equality: bool,
output_paths: []const std.Build.LazyPath,
pub fn init(owner: *std.Build, expected_equality: bool, output_paths: []const std.Build.LazyPath) *CheckPathEquality {
const check = owner.allocator.create(CheckPathEquality) catch @panic("OOM");
check.* = .{
.step = std.Build.Step.init(.{
.id = .custom,
.name = "check output path equality",
.owner = owner,
.makeFn = make,
}),
.expected_equality = expected_equality,
.output_paths = owner.allocator.dupe(std.Build.LazyPath, output_paths) catch @panic("OOM"),
};
for (output_paths) |output_path| {
output_path.addStepDependencies(&check.step);
}
return check;
}
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const check: *CheckPathEquality = @fieldParentPtr("step", step);
std.debug.assert(check.output_paths.len != 0);
for (check.output_paths[0 .. check.output_paths.len - 1], check.output_paths[1..]) |a, b| {
try std.testing.expectEqual(check.expected_equality, std.mem.eql(u8, a.getPath(step.owner), b.getPath(step.owner)));
}
}
};