aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorChris Boesch <chrboesch@noreply.codeberg.org>2023-04-07 18:24:42 +0200
committerGitHub <noreply@github.com>2023-04-07 18:24:42 +0200
commite242d821baadbb9ab895f8eacac2e8e72589dffa (patch)
treec6f59c28b8abd9b92a2183ac2078d6c83d869904
parent8bf719e5e22dacccf68916455b7634b7b04453a0 (diff)
parentddc835762c1276c781295164a2bb3e42b35670df (diff)
Merge pull request #212 from perillo/improve-build
build: make the logo a build step
-rw-r--r--README.md11
-rw-r--r--build.zig180
-rw-r--r--src/compat.zig65
3 files changed, 174 insertions, 82 deletions
diff --git a/README.md b/README.md
index 600790b..f23f536 100644
--- a/README.md
+++ b/README.md
@@ -50,18 +50,11 @@ $ git clone https://github.com/ratfactor/ziglings
$ cd ziglings
```
-Then run `zig build 1` and follow the instructions to begin!
+Then run `zig build` and follow the instructions to begin!
```bash
-$ zig build 1
+$ zig build
```
-## :warning: Attention
-Due to Zig's new build system, exercises can currently only be run manually with their number!
-
-```bash
-$ zig build xy
-```
-We hope to be able to offer this again soon in the automatic way.
## A Note About Versions
diff --git a/build.zig b/build.zig
index 5ade0c4..d2702d0 100644
--- a/build.zig
+++ b/build.zig
@@ -1,15 +1,13 @@
const std = @import("std");
const builtin = @import("builtin");
-const Builder = std.build.Builder;
-const Step = std.build.Step;
+const compat = @import("src/compat.zig");
+
+const Build = compat.Build;
+const Step = compat.build.Step;
+
const assert = std.debug.assert;
const print = std.debug.print;
-// When changing this version, be sure to also update README.md in two places:
-// 1) Getting Started
-// 2) Version Changes
-const needed_version = std.SemanticVersion.parse("0.11.0-dev.2157") catch unreachable;
-
const Exercise = struct {
/// main_file must have the format key_name.zig.
/// The key will be used as a shorthand to build
@@ -53,6 +51,11 @@ const Exercise = struct {
while (self.main_file[start_index] == '0') start_index += 1;
return self.main_file[start_index..end_index.?];
}
+
+ /// Returns the exercise key as an integer.
+ pub fn number(self: Exercise) usize {
+ return std.fmt.parseInt(usize, self.key(), 10) catch unreachable;
+ }
};
const exercises = [_]Exercise{
@@ -493,44 +496,8 @@ const exercises = [_]Exercise{
},
};
-/// Check the zig version to make sure it can compile the examples properly.
-/// This will compile with Zig 0.6.0 and later.
-fn checkVersion() bool {
- if (!@hasDecl(builtin, "zig_version")) {
- return false;
- }
-
- const version = builtin.zig_version;
- const order = version.order(needed_version);
- return order != .lt;
-}
-
-pub fn build(b: *Builder) !void {
- // Use a comptime branch for the version check.
- // If this fails, code after this block is not compiled.
- // It is parsed though, so versions of zig from before 0.6.0
- // cannot do the version check and will just fail to compile.
- // We could fix this by moving the ziglings code to a separate file,
- // but 0.5.0 was a long time ago, it is unlikely that anyone who
- // attempts these exercises is still using it.
- if (comptime !checkVersion()) {
- // very old versions of Zig used warn instead of print.
- const stderrPrintFn = if (@hasDecl(std.debug, "print")) std.debug.print else std.debug.warn;
- stderrPrintFn(
- \\ERROR: Sorry, it looks like your version of zig is too old. :-(
- \\
- \\Ziglings requires development build
- \\
- \\ {}
- \\
- \\or higher. Please download a development ("master") build from
- \\
- \\ https://ziglang.org/download/
- \\
- \\
- , .{needed_version});
- std.os.exit(0);
- }
+pub fn build(b: *Build) !void {
+ if (!compat.is_compatible) compat.die();
use_color_escapes = false;
if (std.io.getStdErr().supportsAnsiEscapeCodes()) {
@@ -571,51 +538,88 @@ pub fn build(b: *Builder) !void {
\\
\\
;
- const header_step = b.step("info", logo);
- print("{s}\n", .{logo});
- const verify_all = b.step("ziglings", "Check all ziglings");
- verify_all.dependOn(header_step);
- b.default_step = verify_all;
+ const use_healed = b.option(bool, "healed", "Run exercises from patches/healed") orelse false;
+ const exno: ?usize = b.option(usize, "n", "Select exercise");
- var prev_chain_verify = verify_all;
+ const header_step = PrintStep.create(b, logo, std.io.getStdErr());
- const use_healed = b.option(bool, "healed", "Run exercises from patches/healed") orelse false;
+ if (exno) |i| {
+ const ex = blk: {
+ for (exercises) |ex| {
+ if (ex.number() == i) break :blk ex;
+ }
+
+ print("unknown exercise number: {}\n", .{i});
+ std.os.exit(1);
+ };
- for (exercises) |ex| {
const base_name = ex.baseName();
const file_path = std.fs.path.join(b.allocator, &[_][]const u8{
if (use_healed) "patches/healed" else "exercises", ex.main_file,
}) catch unreachable;
- const build_step = b.addExecutable(.{ .name = base_name, .root_source_file = .{ .path = file_path } });
+ const build_step = b.addExecutable(.{ .name = base_name, .root_source_file = .{ .path = file_path } });
build_step.install();
+ const run_step = build_step.run();
+
+ const test_step = b.step("test", b.fmt("Run {s} without checking output", .{ex.main_file}));
+ test_step.dependOn(&run_step.step);
+
+ const install_step = b.step("install", b.fmt("Install {s} to prefix path", .{ex.main_file}));
+ install_step.dependOn(b.getInstallStep());
+
+ const uninstall_step = b.step("uninstall", b.fmt("Uninstall {s} from prefix path", .{ex.main_file}));
+ uninstall_step.dependOn(b.getUninstallStep());
+
const verify_step = ZiglingStep.create(b, ex, use_healed);
- const key = ex.key();
+ const zigling_step = b.step("zigling", b.fmt("Check the solution of {s}", .{ex.main_file}));
+ zigling_step.dependOn(&verify_step.step);
+ b.default_step = zigling_step;
- const named_test = b.step(b.fmt("{s}_test", .{key}), b.fmt("Run {s} without checking output", .{ex.main_file}));
- const run_step = build_step.run();
- named_test.dependOn(&run_step.step);
+ const start_step = b.step("start", b.fmt("Check all solutions starting at {s}", .{ex.main_file}));
- const named_install = b.step(b.fmt("{s}_install", .{key}), b.fmt("Install {s} to zig-cache/bin", .{ex.main_file}));
- named_install.dependOn(&build_step.install_step.?.step);
+ var prev_step = verify_step;
+ for (exercises) |exn| {
+ const n = exn.number();
+ if (n > i) {
+ const verify_stepn = ZiglingStep.create(b, exn, use_healed);
+ verify_stepn.step.dependOn(&prev_step.step);
- const named_verify = b.step(key, b.fmt("Check {s} only", .{ex.main_file}));
- named_verify.dependOn(&verify_step.step);
+ prev_step = verify_stepn;
+ }
+ }
+ start_step.dependOn(&prev_step.step);
- const chain_verify = b.allocator.create(Step) catch unreachable;
- chain_verify.* = Step.init(Step.Options{ .id = .custom, .name = b.fmt("chain {s}", .{key}), .owner = b });
- chain_verify.dependOn(&verify_step.step);
+ return;
+ }
- const named_chain = b.step(b.fmt("{s}_start", .{key}), b.fmt("Check all solutions starting at {s}", .{ex.main_file}));
- named_chain.dependOn(header_step);
- named_chain.dependOn(chain_verify);
+ const ziglings_step = b.step("ziglings", "Check all ziglings");
+ ziglings_step.dependOn(&header_step.step);
+ b.default_step = ziglings_step;
- prev_chain_verify.dependOn(chain_verify);
- prev_chain_verify = chain_verify;
+ var prev_step: *Step = undefined;
+ for (exercises, 0..) |ex, i| {
+ const base_name = ex.baseName();
+ const file_path = std.fs.path.join(b.allocator, &[_][]const u8{
+ if (use_healed) "patches/healed" else "exercises", ex.main_file,
+ }) catch unreachable;
+
+ const build_step = b.addExecutable(.{ .name = base_name, .root_source_file = .{ .path = file_path } });
+ build_step.install();
+
+ const verify_stepn = ZiglingStep.create(b, ex, use_healed);
+ if (i == 0) {
+ prev_step = &verify_stepn.step;
+ } else {
+ verify_stepn.step.dependOn(prev_step);
+
+ prev_step = &verify_stepn.step;
+ }
}
+ ziglings_step.dependOn(prev_step);
}
var use_color_escapes = false;
@@ -627,10 +631,10 @@ var reset_text: []const u8 = "";
const ZiglingStep = struct {
step: Step,
exercise: Exercise,
- builder: *Builder,
+ builder: *Build,
use_healed: bool,
- pub fn create(builder: *Builder, exercise: Exercise, use_healed: bool) *@This() {
+ pub fn create(builder: *Build, exercise: Exercise, use_healed: bool) *@This() {
const self = builder.allocator.create(@This()) catch unreachable;
self.* = .{
.step = Step.init(Step.Options{ .id = .custom, .name = exercise.main_file, .owner = builder, .makeFn = make }),
@@ -650,7 +654,7 @@ const ZiglingStep = struct {
}
print("\n{s}Edit exercises/{s} and run this again.{s}", .{ red_text, self.exercise.main_file, reset_text });
- print("\n{s}To continue from this zigling, use this command:{s}\n {s}zig build {s}{s}\n", .{ red_text, reset_text, bold_text, self.exercise.key(), reset_text });
+ print("\n{s}To continue from this zigling, use this command:{s}\n {s}zig build -Dn={s}{s}\n", .{ red_text, reset_text, bold_text, self.exercise.key(), reset_text });
std.os.exit(1);
};
}
@@ -804,3 +808,33 @@ const ZiglingStep = struct {
});
}
};
+
+// Print a message to a file.
+const PrintStep = struct {
+ step: Step,
+ message: []const u8,
+ file: std.fs.File,
+
+ pub fn create(owner: *Build, message: []const u8, file: std.fs.File) *PrintStep {
+ const self = owner.allocator.create(PrintStep) catch @panic("OOM");
+ self.* = .{
+ .step = Step.init(.{
+ .id = .custom,
+ .name = "Print",
+ .owner = owner,
+ .makeFn = make,
+ }),
+ .message = message,
+ .file = file,
+ };
+
+ return self;
+ }
+
+ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
+ _ = prog_node;
+ const p = @fieldParentPtr(PrintStep, "step", step);
+
+ try p.file.writeAll(p.message);
+ }
+};
diff --git a/src/compat.zig b/src/compat.zig
new file mode 100644
index 0000000..1adf8c0
--- /dev/null
+++ b/src/compat.zig
@@ -0,0 +1,65 @@
+/// Compatibility support for very old versions of Zig and recent versions before
+/// commit efa25e7d5 (Merge pull request #14498 from ziglang/zig-build-api).
+///
+/// Versions of Zig from before 0.6.0 cannot do the version check and will just
+/// fail to compile, but 0.5.0 was a long time ago, it is unlikely that anyone
+/// who attempts these exercises is still using it.
+const std = @import("std");
+const builtin = @import("builtin");
+
+const debug = std.debug;
+
+// Very old versions of Zig used warn instead of print.
+const print = if (@hasDecl(debug, "print")) debug.print else debug.warn;
+
+// When changing this version, be sure to also update README.md in two places:
+// 1) Getting Started
+// 2) Version Changes
+const needed_version_str = "0.11.0-dev.2401";
+
+fn isCompatible() bool {
+ if (!@hasDecl(builtin, "zig_version") or !@hasDecl(std, "SemanticVersion")) {
+ return false;
+ }
+
+ const needed_version = std.SemanticVersion.parse(needed_version_str) catch unreachable;
+ const version = builtin.zig_version;
+ const order = version.order(needed_version);
+
+ return order != .lt;
+}
+
+pub fn die() noreturn {
+ const error_message =
+ \\ERROR: Sorry, it looks like your version of zig is too old. :-(
+ \\
+ \\Ziglings requires development build
+ \\
+ \\ {s}
+ \\
+ \\or higher. Please download a development ("master") build from
+ \\
+ \\ https://ziglang.org/download/
+ \\
+ \\
+ ;
+
+ print(error_message, .{needed_version_str});
+
+ // Use exit code 2, to differentiate from a normal Zig compiler error.
+ std.os.exit(2);
+}
+
+// A separate function is required because very old versions of Zig doesn't
+// support labeled block expressions.
+pub const is_compatible: bool = isCompatible();
+
+/// This is the type to be used only for the build function definition, since
+/// the type must be compatible with the build runner.
+///
+/// Don't use std.Build.Builder, since it is deprecated and may be removed in
+/// future.
+pub const Build = if (is_compatible) std.Build else std.build.Builder;
+
+/// This is the type to be used for accessing the build namespace.
+pub const build = if (is_compatible) std.Build else std.build;