Skip to content

Commit de6ee41

Browse files
committed
force buildpkgs.has to be called at comptime
The reason we want to force buildpkgs.has to be comptime is it is meant to be surrounding a block that contains an @import, so if a user accidently forgets to mark it as comptime then their build.zig will be invalid, but this error will only be caught with certain configurations. I've marked @import("buildpkgs").has with callconv(.Inline) which should force it to be evaluated at comptime, but it looks like this behavior is not implemented yet.
1 parent e3081e0 commit de6ee41

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

src/main.zig

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2675,13 +2675,26 @@ fn generateBuildPkgs(arena: *Allocator, local_cache_directory: Compilation.Direc
26752675
try writer.writeAll(
26762676
\\};
26772677
\\
2678-
\\pub fn has(comptime name: []const u8) bool {
2678+
\\// using .Inline to force this to be comptime
2679+
\\pub fn has(comptime name: []const u8) callconv(.Inline) bool {
2680+
\\ if (!isComptime()) {
2681+
\\ @compileError("buildpkgs.has must be called with comptime");
2682+
\\ }
26792683
\\ inline for (names) |has_name| {
26802684
\\ if (@import("std").mem.eql(u8, name, has_name)) return true;
26812685
\\ }
26822686
\\ return false;
26832687
\\}
26842688
\\
2689+
\\// A temporary workaround until https://github.com/ziglang/zig/issues/425
2690+
\\// is implemented and the callconv(.Inline) on the `has` function forces
2691+
\\// it to be comptime
2692+
\\fn isComptime() bool {
2693+
\\ var t: bool = true;
2694+
\\ const x = if (t) @as(u7, 0) else @as(u8, 0);
2695+
\\ return @TypeOf(x) == u7;
2696+
\\}
2697+
\\
26852698
);
26862699
try buildpkgs_dir.rename("buildpkgs.zig.tmp", "buildpkgs.zig");
26872700
}

test/standalone/build_zig_pkgs/build.zig

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,20 @@ pub fn build(b: *Builder) void {
88
} else {
99
const resolveandroid = b.addExecutable("resolveandroid", "resolveandroid.zig");
1010
resolveandroid.setBuildMode(b.standardReleaseOptions());
11-
1211
const run_resolveandroid = resolveandroid.run();
12+
run_resolveandroid.addArg("expect-pass");
1313
run_resolveandroid.addArg(b.zig_exe);
1414

15-
const resolveandroid_step = b.step("test", "Resolve android and recompile build.zig");
16-
resolveandroid_step.dependOn(&run_resolveandroid.step);
15+
const test_build_error = b.addExecutable("test-build-error", "resolveandroid.zig");
16+
test_build_error.setBuildMode(b.standardReleaseOptions());
17+
const run_test_build_error = test_build_error.run();
18+
run_test_build_error.addArg("expect-fail");
19+
run_test_build_error.addArg(b.zig_exe);
20+
run_test_build_error.addArg("--build-file");
21+
run_test_build_error.addArg("invalid-build.zig");
22+
23+
const test_step = b.step("test", "Resolve android and test the zig build files");
24+
test_step.dependOn(&run_resolveandroid.step);
25+
test_step.dependOn(&run_test_build_error.step);
1726
}
1827
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! Currently the user MUST specify comptime when calling buidpkgs.has
2+
//! until https://github.com/ziglang/zig/issues/425 is implemented.
3+
//! This build.zig file tries to call buildpkgs.has without comptime and the
4+
//! test is to make sure it produces a compile error.
5+
//!
6+
//! Note that the reason why "has" must be evaluated at comptime is
7+
//! because it will always surround an @import statement. The problem is
8+
//! that if they forget to add "comptime" to their call, then their build.zig
9+
//! file will "sometimes work" so long as they are building with the necessary
10+
//! packages configured, but then it will fail once the @import is missing
11+
//! which defeats the whole purpose of providing "has" in the first place.
12+
//!
13+
const Builder = @import("std").build.Builder;
14+
const buildpkgs = @import("buildpkgs");
15+
16+
pub fn build(b: *Builder) void {
17+
// This should be a compile error
18+
if (buildpkgs.has("androidbuild")) {
19+
const androidbuild = @import("androidbuild");
20+
androidbuild.makeApk(b);
21+
}
22+
}
Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
const std = @import("std");
22

3-
pub fn main() !u8 {
4-
const args = try std.process.argsAlloc(std.heap.page_allocator);
3+
pub fn main() !void {
4+
const in_args = try std.process.argsAlloc(std.heap.page_allocator);
55

6-
const zig_exe = args[1];
7-
const child = try std.ChildProcess.init(&[_][]const u8 {
8-
zig_exe, "build",
9-
"--pkg-begin", "androidbuild", "androidbuild.zig", "--pkg-end",
10-
}, std.heap.page_allocator);
6+
const expect_pass = blk: {
7+
if (std.mem.eql(u8, in_args[1], "expect-pass")) break :blk true;
8+
std.debug.assert(std.mem.eql(u8, in_args[1], "expect-fail"));
9+
break :blk false;
10+
};
11+
12+
const zig_exe = in_args[2];
13+
var out_args = std.ArrayList([]const u8).init(std.heap.page_allocator);
14+
try out_args.append(zig_exe);
15+
try out_args.append("build");
16+
try out_args.append("--pkg-begin");
17+
try out_args.append("androidbuild");
18+
try out_args.append("androidbuild.zig");
19+
try out_args.append("--pkg-end");
20+
for (in_args[3..]) |cmd_arg| {
21+
try out_args.append(cmd_arg);
22+
}
23+
24+
const child = try std.ChildProcess.init(out_args.items, std.heap.page_allocator);
1125
defer child.deinit();
12-
switch (try child.spawnAndWait()) {
13-
.Exited => |e| return if (e == 0) 0 else 0xff,
14-
else => |e| {
15-
std.debug.print("Error: zig build process failed with {}\n", .{e});
16-
return error.ZigBuildFailed;
17-
},
26+
27+
// redirect stderr so it doesn't show up in the test log
28+
child.stderr_behavior = .Pipe;
29+
errdefer {
30+
if (child.stderr) |stderr| {
31+
const err = stderr.reader().readAllAlloc(std.heap.page_allocator, std.math.maxInt(usize)) catch unreachable;
32+
std.io.getStdErr().writeAll(err) catch unreachable;
33+
}
34+
}
35+
36+
const passed = switch (try child.spawnAndWait()) {
37+
.Exited => |e| e == 0,
38+
else => false,
39+
};
40+
if (passed) {
41+
if (expect_pass) return;
42+
return error.ZigBuildUnexpectedlyPassed;
43+
}
44+
if (expect_pass) {
45+
return error.ZigBuildFailed;
1846
}
1947
}

0 commit comments

Comments
 (0)