master
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const target = b.standardTargetOptions(.{});
8 const optimize = b.standardOptimizeOption(.{});
9
10 const exe = b.addExecutable(.{
11 .name = "create-file",
12 .root_module = b.createModule(.{
13 .root_source_file = b.path("main.zig"),
14 .target = target,
15 .optimize = optimize,
16 }),
17 });
18
19 {
20 const run_random_with_sideeffects_first = b.addRunArtifact(exe);
21 run_random_with_sideeffects_first.setName("run with side-effects (first)");
22 run_random_with_sideeffects_first.has_side_effects = true;
23
24 const run_random_with_sideeffects_second = b.addRunArtifact(exe);
25 run_random_with_sideeffects_second.setName("run with side-effects (second)");
26 run_random_with_sideeffects_second.has_side_effects = true;
27
28 // ensure that "second" runs after "first"
29 run_random_with_sideeffects_second.step.dependOn(&run_random_with_sideeffects_first.step);
30
31 const first_output = run_random_with_sideeffects_first.addOutputFileArg("a.txt");
32 const second_output = run_random_with_sideeffects_second.addOutputFileArg("a.txt");
33
34 const expect_uncached_dependencies = CheckOutputCaching.init(b, false, &.{ first_output, second_output });
35 test_step.dependOn(&expect_uncached_dependencies.step);
36
37 const expect_unequal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
38 test_step.dependOn(&expect_unequal_output.step);
39
40 const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
41 test_step.dependOn(&check_first_output.step);
42 const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
43 test_step.dependOn(&check_second_output.step);
44 }
45
46 {
47 const run_random_without_sideeffects_1 = b.addRunArtifact(exe);
48 run_random_without_sideeffects_1.setName("run without side-effects (A)");
49
50 const run_random_without_sideeffects_2 = b.addRunArtifact(exe);
51 run_random_without_sideeffects_2.setName("run without side-effects (B)");
52
53 run_random_without_sideeffects_2.step.dependOn(&run_random_without_sideeffects_1.step);
54
55 const first_output = run_random_without_sideeffects_1.addOutputFileArg("a.txt");
56 const second_output = run_random_without_sideeffects_2.addOutputFileArg("a.txt");
57
58 const expect_cached_dependencies = CheckOutputCaching.init(b, true, &.{second_output});
59 test_step.dependOn(&expect_cached_dependencies.step);
60
61 const expect_equal_output = CheckPathEquality.init(b, true, &.{ first_output, second_output });
62 test_step.dependOn(&expect_equal_output.step);
63
64 const check_first_output = b.addCheckFile(first_output, .{ .expected_matches = &.{"a.txt"} });
65 test_step.dependOn(&check_first_output.step);
66 const check_second_output = b.addCheckFile(second_output, .{ .expected_matches = &.{"a.txt"} });
67 test_step.dependOn(&check_second_output.step);
68 }
69}
70
71const CheckOutputCaching = struct {
72 step: std.Build.Step,
73 expect_caching: bool,
74
75 pub fn init(owner: *std.Build, expect_caching: bool, output_paths: []const std.Build.LazyPath) *CheckOutputCaching {
76 const check = owner.allocator.create(CheckOutputCaching) catch @panic("OOM");
77 check.* = .{
78 .step = std.Build.Step.init(.{
79 .id = .custom,
80 .name = "check output caching",
81 .owner = owner,
82 .makeFn = make,
83 }),
84 .expect_caching = expect_caching,
85 };
86 for (output_paths) |output_path| {
87 output_path.addStepDependencies(&check.step);
88 }
89 return check;
90 }
91
92 fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
93 const check: *CheckOutputCaching = @fieldParentPtr("step", step);
94
95 for (step.dependencies.items) |dependency| {
96 if (check.expect_caching) {
97 if (dependency.result_cached) continue;
98 return step.fail("expected '{s}' step to be cached, but it was not", .{dependency.name});
99 } else {
100 if (!dependency.result_cached) continue;
101 return step.fail("expected '{s}' step to not be cached, but it was", .{dependency.name});
102 }
103 }
104 }
105};
106
107const CheckPathEquality = struct {
108 step: std.Build.Step,
109 expected_equality: bool,
110 output_paths: []const std.Build.LazyPath,
111
112 pub fn init(owner: *std.Build, expected_equality: bool, output_paths: []const std.Build.LazyPath) *CheckPathEquality {
113 const check = owner.allocator.create(CheckPathEquality) catch @panic("OOM");
114 check.* = .{
115 .step = std.Build.Step.init(.{
116 .id = .custom,
117 .name = "check output path equality",
118 .owner = owner,
119 .makeFn = make,
120 }),
121 .expected_equality = expected_equality,
122 .output_paths = owner.allocator.dupe(std.Build.LazyPath, output_paths) catch @panic("OOM"),
123 };
124 for (output_paths) |output_path| {
125 output_path.addStepDependencies(&check.step);
126 }
127 return check;
128 }
129
130 fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
131 const check: *CheckPathEquality = @fieldParentPtr("step", step);
132 std.debug.assert(check.output_paths.len != 0);
133 for (check.output_paths[0 .. check.output_paths.len - 1], check.output_paths[1..]) |a, b| {
134 try std.testing.expectEqual(check.expected_equality, std.mem.eql(u8, a.getPath(step.owner), b.getPath(step.owner)));
135 }
136 }
137};