mirror of
https://codeberg.org/andyscott/ziglings.git
synced 2024-12-22 22:13:11 -05:00
Merge pull request #212 from perillo/improve-build
build: make the logo a build step
This commit is contained in:
commit
e242d821ba
3 changed files with 177 additions and 85 deletions
11
README.md
11
README.md
|
@ -50,18 +50,11 @@ $ git clone https://github.com/ratfactor/ziglings
|
||||||
$ cd 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
|
```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
|
## A Note About Versions
|
||||||
|
|
||||||
|
|
186
build.zig
186
build.zig
|
@ -1,15 +1,13 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const Builder = std.build.Builder;
|
const compat = @import("src/compat.zig");
|
||||||
const Step = std.build.Step;
|
|
||||||
|
const Build = compat.Build;
|
||||||
|
const Step = compat.build.Step;
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const print = std.debug.print;
|
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 {
|
const Exercise = struct {
|
||||||
/// main_file must have the format key_name.zig.
|
/// main_file must have the format key_name.zig.
|
||||||
/// The key will be used as a shorthand to build
|
/// 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;
|
while (self.main_file[start_index] == '0') start_index += 1;
|
||||||
return self.main_file[start_index..end_index.?];
|
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{
|
const exercises = [_]Exercise{
|
||||||
|
@ -493,44 +496,8 @@ const exercises = [_]Exercise{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Check the zig version to make sure it can compile the examples properly.
|
pub fn build(b: *Build) !void {
|
||||||
/// This will compile with Zig 0.6.0 and later.
|
if (!compat.is_compatible) compat.die();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
use_color_escapes = false;
|
use_color_escapes = false;
|
||||||
if (std.io.getStdErr().supportsAnsiEscapeCodes()) {
|
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;
|
|
||||||
|
|
||||||
var prev_chain_verify = verify_all;
|
|
||||||
|
|
||||||
const use_healed = b.option(bool, "healed", "Run exercises from patches/healed") orelse false;
|
const use_healed = b.option(bool, "healed", "Run exercises from patches/healed") orelse false;
|
||||||
|
const exno: ?usize = b.option(usize, "n", "Select exercise");
|
||||||
|
|
||||||
|
const header_step = PrintStep.create(b, logo, std.io.getStdErr());
|
||||||
|
|
||||||
|
if (exno) |i| {
|
||||||
|
const ex = blk: {
|
||||||
for (exercises) |ex| {
|
for (exercises) |ex| {
|
||||||
|
if (ex.number() == i) break :blk ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("unknown exercise number: {}\n", .{i});
|
||||||
|
std.os.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
const base_name = ex.baseName();
|
const base_name = ex.baseName();
|
||||||
const file_path = std.fs.path.join(b.allocator, &[_][]const u8{
|
const file_path = std.fs.path.join(b.allocator, &[_][]const u8{
|
||||||
if (use_healed) "patches/healed" else "exercises", ex.main_file,
|
if (use_healed) "patches/healed" else "exercises", ex.main_file,
|
||||||
}) catch unreachable;
|
}) 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();
|
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 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 start_step = b.step("start", b.fmt("Check all solutions starting at {s}", .{ex.main_file}));
|
||||||
const run_step = build_step.run();
|
|
||||||
named_test.dependOn(&run_step.step);
|
|
||||||
|
|
||||||
const named_install = b.step(b.fmt("{s}_install", .{key}), b.fmt("Install {s} to zig-cache/bin", .{ex.main_file}));
|
var prev_step = verify_step;
|
||||||
named_install.dependOn(&build_step.install_step.?.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}));
|
prev_step = verify_stepn;
|
||||||
named_verify.dependOn(&verify_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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
prev_chain_verify.dependOn(chain_verify);
|
|
||||||
prev_chain_verify = chain_verify;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
start_step.dependOn(&prev_step.step);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ziglings_step = b.step("ziglings", "Check all ziglings");
|
||||||
|
ziglings_step.dependOn(&header_step.step);
|
||||||
|
b.default_step = ziglings_step;
|
||||||
|
|
||||||
|
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;
|
var use_color_escapes = false;
|
||||||
|
@ -627,10 +631,10 @@ var reset_text: []const u8 = "";
|
||||||
const ZiglingStep = struct {
|
const ZiglingStep = struct {
|
||||||
step: Step,
|
step: Step,
|
||||||
exercise: Exercise,
|
exercise: Exercise,
|
||||||
builder: *Builder,
|
builder: *Build,
|
||||||
use_healed: bool,
|
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;
|
const self = builder.allocator.create(@This()) catch unreachable;
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.step = Step.init(Step.Options{ .id = .custom, .name = exercise.main_file, .owner = builder, .makeFn = make }),
|
.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}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);
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
65
src/compat.zig
Normal file
65
src/compat.zig
Normal file
|
@ -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;
|
Loading…
Reference in a new issue