-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build_runner: add min_zig_version build.zig config
build_runner now checks build.zig for min_zig_version configuration, i.e. ```zig //config min_zig_version 0.10.0-dev.2836+2360f8c49 ``` This provides a standard for projects to document their min zig version. In addition, if zig detects it's too old, it will print an error: ``` error: zig is too old, have 0.9.0 but build.zig has min_zig_version 0.10.0 ``` Note that supporting a max_zig_version would be more difficult. Since zig version numbers contain the commit height, we can tell whether Zig is "too old", but we can't tell whether zig it "too new". If commit height A is less than B, then commit A is too old, but if A is greater than B, then A is not necessarily too new because it could just be a different branch with extra commits.
- Loading branch information
1 parent
0b47e69
commit 5780064
Showing
9 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
const std = @import("std"); | ||
const Version = @This(); | ||
|
||
version: std.builtin.Version, | ||
opt_dev: ?Dev, | ||
|
||
pub fn parse(version_str: []const u8) error{InvalidZigVersion}!Version { | ||
const semver = std.SemanticVersion.parse(version_str) catch |err| { | ||
std.log.err("zig version '{s}' is not a valid semantic version: {s}", .{ version_str, @errorName(err) }); | ||
return error.InvalidZigVersion; | ||
}; | ||
var result = Version{ | ||
.version = .{ | ||
.major = @intCast(u32, semver.major), | ||
.minor = @intCast(u32, semver.minor), | ||
.patch = @intCast(u32, semver.patch), | ||
}, | ||
.opt_dev = null, | ||
}; | ||
if (semver.pre) |pre| { | ||
const dev_prefix = "dev."; | ||
if (!std.mem.startsWith(u8, pre, dev_prefix)) { | ||
std.log.err("invalid zig version '{s}', expected '-{s}' after major/minor/patch, but got '-{s}'", .{ version_str, dev_prefix, pre }); | ||
return error.InvalidZigVersion; | ||
} | ||
const commit_height_str = pre[dev_prefix.len..]; | ||
const build = semver.build orelse { | ||
std.log.err("invalid zig version '{s}', has '-dev.COMMIT_HEIGHT' without a '+BUILD_REVISION'", .{version_str}); | ||
return error.InvalidZigVersion; | ||
}; | ||
if (build.len > 40) { | ||
std.log.err("invalid zig version '{s}', build revision is too long", .{version_str}); | ||
return error.InvalidZigVersion; | ||
} | ||
result.opt_dev = .{ | ||
.commit_height = std.fmt.parseInt(u32, commit_height_str, 10) catch |err| { | ||
std.log.err("invalid zig version '{s}', invalid commit height '{s}': {s}", .{ version_str, commit_height_str, @errorName(err) }); | ||
return error.InvalidZigVersion; | ||
}, | ||
.sha_buf = undefined, | ||
.sha_len = @intCast(u8, build.len), | ||
}; | ||
std.mem.copy(u8, result.opt_dev.?.sha_buf[0..build.len], build); | ||
} else if (semver.build) |_| { | ||
std.log.err("invalid zig version '{s}', has '+BUILD_REVISION' without '-dev.COMMIT_HEIGHT'", .{version_str}); | ||
return error.InvalidZigVersion; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
pub const Order = enum { | ||
eq, | ||
lt, | ||
gt, | ||
/// Version and commit height are equal but hashes differ | ||
commit_height_eq, | ||
/// Version is equal but commit height is less than | ||
commit_height_lt, | ||
/// Version is equal but commit height is greater than | ||
commit_height_gt, | ||
|
||
pub fn inverse(self: Order) Order { | ||
return switch (self) { | ||
.eq => .eq, | ||
.lt => .gt, | ||
.gt => .lt, | ||
.commit_height_eq => .commit_height_eq, | ||
.commit_height_lt => .commit_height_gt, | ||
.commit_height_gt => .commit_height_lt, | ||
}; | ||
} | ||
}; | ||
pub fn order(left: Version, right: Version) Order { | ||
const left_dev = left.opt_dev orelse { | ||
if (right.opt_dev) |_| return right.order(left).inverse(); | ||
return switch (left.version.order(right.version)) { | ||
.eq => .eq, .lt => .lt, .gt => .gt | ||
}; | ||
|
||
}; | ||
const right_dev = right.opt_dev orelse return switch(left.version.order(right.version)) { | ||
// NOTE: .eq is supposed to map to .lt, the dev version is .lt if their semvers are equal | ||
.eq => .lt, .lt => .lt, .gt => .gt, | ||
}; | ||
|
||
switch (left.version.order(right.version)) { | ||
.lt => return .lt, | ||
.gt => return .gt, | ||
.eq => { | ||
if (left_dev.commit_height > right_dev.commit_height) | ||
return .commit_height_gt; | ||
if (left_dev.commit_height < right_dev.commit_height) | ||
return .commit_height_lt; | ||
if (!std.mem.eql(u8, left_dev.sha(), right_dev.sha())) | ||
return .commit_height_eq; | ||
return .eq; | ||
}, | ||
} | ||
} | ||
|
||
pub fn format( | ||
self: Version, | ||
comptime fmt: []const u8, | ||
options: std.fmt.FormatOptions, | ||
writer: anytype, | ||
) !void { | ||
_ = fmt; | ||
_ = options; | ||
try writer.print("{}.{}.{}", .{ self.version.major, self.version.minor, self.version.patch }); | ||
if (self.opt_dev) |dev| { | ||
try writer.print("-dev.{d}+{s}", .{ dev.commit_height, dev.sha() }); | ||
} | ||
} | ||
|
||
pub const Dev = struct { | ||
commit_height: u32, | ||
sha_buf: [40]u8, | ||
sha_len: u8, | ||
pub fn sha(self: *const Dev) []const u8 { | ||
return self.sha_buf[0..self.sha_len]; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/equal-version-build.zig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
const std = @import("std"); | ||
|
||
pub fn build(b: *std.build.Builder) void { | ||
const test_step = b.step("test", "Run the test"); | ||
|
||
{ | ||
const run = testBuild(b, b.pathFromRoot("distant-future-dev-build.zig")); | ||
run.expected_exit_code = 1; | ||
run.stderr_action = .{ .expect_matches = &[_][]const u8 { | ||
"error: zig is too old, have ", | ||
" but build.zig has min_zig_version 999.999.999-dev.999999+5735ce39ae5a9fb2bc2ac9f5a722276c291b28bc\n", | ||
}}; | ||
test_step.dependOn(&run.step); | ||
} | ||
{ | ||
const run = testBuild(b, b.pathFromRoot("distant-future-release-build.zig")); | ||
run.expected_exit_code = 1; | ||
run.stderr_action = .{ .expect_matches = &[_][]const u8 { | ||
"error: zig is too old, have ", | ||
" but build.zig has min_zig_version 999.999.999\n", | ||
}}; | ||
test_step.dependOn(&run.step); | ||
} | ||
{ | ||
const build_zig = b.pathFromRoot("equal-version-build.zig"); | ||
const write_file = b.addWriteFile( | ||
build_zig, | ||
b.fmt( | ||
\\//config min_zig_version {} | ||
\\const std = @import("std"); | ||
\\pub fn build(b: *std.build.Builder) void {{ _ = b; }} | ||
\\ | ||
, .{@import("builtin").zig_version}, | ||
), | ||
); | ||
const run = testBuild(b, build_zig); | ||
run.stdout_action = .{ .expect_exact = "" }; | ||
run.stderr_action = .{ .expect_exact = "" }; | ||
run.step.dependOn(&write_file.step); | ||
test_step.dependOn(&run.step); | ||
} | ||
} | ||
|
||
fn testBuild(b: *std.build.Builder, build_file: []const u8) *std.build.RunStep { | ||
const run = b.addSystemCommand(&[_][]const u8 { | ||
b.zig_exe, | ||
"build", | ||
"--build-file", | ||
build_file, | ||
}); | ||
return run; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//config min_zig_version 999.999.999-dev.999999+5735ce39ae5a9fb2bc2ac9f5a722276c291b28bc | ||
const std = @import("std"); | ||
|
||
pub fn build(b: *std.build.Builder) void { | ||
_ = b; | ||
} |
6 changes: 6 additions & 0 deletions
6
test/standalone/min_zig_version/distant-future-release-build.zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//config min_zig_version 999.999.999 | ||
const std = @import("std"); | ||
|
||
pub fn build(b: *std.build.Builder) void { | ||
_ = b; | ||
} |