Hi there!
I’m new to zig and currently going through this years advent of code in it. So far I like a lot of the ideas I have run into with the language, but it is still a bunch of things I haven’t fully gotten used to so the going is quite slow.
My current setup for the build system supports a zig build [run/test/time] -Dday=X
format, where run runs on the full input read from file, test runs on small sample input defined in test and time uses hyperfine to time the run.
One of the things I tried was to set up the build system so that I could build a different day with a flag to the build tools, and have the resulting binary only contain the code for the selected day.
Currently I think it kind of works, but I have to manually edit the main file for each new day, so not really clean.
So a few questions:
- Can I support picking any day with the
-Dday=X
format, or some other way, without needing the manual stuff in the main file? And it should be selected at compile time which day is run. - Can I set the optimization options based on the command chosen through dependsOn somehow? E.g. if I run the
build time
I would like optimization for fast run, but for test and run I would probably prefer fast build times. - Would there be a nice way to have it run/test/time all of the existing ones in sequence if no input was given for the day?
build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "aoc24",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// load the mecha dependency from build.zig.zon
const mecha_package = b.dependency("mecha", .{
.target = target,
.optimize = optimize,
});
// load the "mecha" module from the package
const mecha_module = mecha_package.module("mecha");
// make mecha usable in main
exe.root_module.addImport("mecha", mecha_module);
// Add a build option for the day
const day = b.option(u8, "day", "Which day to compile for.") orelse 1;
const options = b.addOptions();
options.addOption(u8, "day", day);
exe.root_module.addOptions("config", options);
// Run command
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Profile command
const time_run = b.addSystemCommand(&.{ "hyperfine", "./zig-out/bin/aoc24" });
time_run.step.dependOn(&run_cmd.step);
const time_step = b.step("time", "Run timing check");
time_step.dependOn(&time_run.step);
// Test command
var buffer: [20]u8 = undefined;
const test_file = std.fmt.bufPrint(buffer[0..], "src/day{d:0>2}.zig", .{day}) catch unreachable;
const day_tests = b.addTest(.{
.root_source_file = b.path(test_file),
.target = target,
.optimize = optimize,
});
// make mecha module usable in main tests
day_tests.root_module.addImport("mecha", mecha_module);
const run_tests = b.addRunArtifact(day_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_tests.step);
}
main.zig
const std = @import("std");
const common = @import("common.zig");
const config = @import("config");
const dayModule = switch (config.day) {
1 => @import("day01.zig"),
2 => @import("day02.zig"),
3 => @import("day03.zig"),
4 => @import("day04.zig"),
5 => @import("day05.zig"),
else => {
std.debug.print("Error: invalid day {d}", .{config.day});
unreachable;
},
};
const tenths = config.day / 10;
const ones = config.day % 10;
const dayNbr = [2]u8{ tenths + '0', ones + '0' };
const dayData = "inputs/day" ++ dayNbr ++ ".txt";
pub fn main() !void {
var allocator_type = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = allocator_type.allocator();
const input = try common.readFile(dayData, &allocator);
defer allocator.free(input);
const part1 = try dayModule.part1(input, &allocator);
const part2 = try dayModule.part2(input, &allocator);
std.debug.print("Part 1: {d}\nPart 2: {d}\n", .{ part1, part2 });
}
dayXX.zig
const std = @import("std");
const testing = std.testing;
const Allocator = std.mem.Allocator;
pub fn part1(input: []const u8, allocator: *const Allocator) !i64 {
_ = input;
_ = allocator;
return 0;
}
pub fn part2(input: []const u8, allocator: *const Allocator) !i64 {
_ = input;
_ = allocator;
return 0;
}
test "Tests" {
const sample_input =
\\
;
const allocator = testing.allocator;
try testing.expect(try part1(sample_input, &allocator) == 0);
try testing.expect(try part2(sample_input, &allocator) == 0);
}