You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
microzig/build.zig

298 lines
9.6 KiB
Zig

//! Some words on the build script here:
//! We cannot use a test runner here as we're building for freestanding.
//! This means we need to use addExecutable() instead of using
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
const mode = b.standardReleaseOptions();
const test_step = b.step("test", "Builds and runs the library test suite");
const BuildConfig = struct { name: []const u8, backing: Backing };
const all_backings = [_]BuildConfig{
BuildConfig{ .name = "boards.arduino_nano", .backing = Backing{ .board = pkgs.boards.arduino_nano } },
BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = pkgs.boards.mbed_lpc1768 } },
BuildConfig{ .name = "chips.atmega328p", .backing = Backing{ .chip = pkgs.chips.atmega328p } },
BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = pkgs.chips.lpc1768 } },
};
const Test = struct { name: []const u8, source: []const u8 };
const all_tests = [_]Test{
Test{ .name = "minimal", .source = "tests/minimal.zig" },
Test{ .name = "blinky", .source = "tests/blinky.zig" },
Test{ .name = "uart-sync", .source = "tests/uart-sync.zig" },
};
const filter = b.option(std.Target.Cpu.Arch, "filter-target", "Filters for a certain cpu target");
inline for (all_backings) |cfg| {
inline for (all_tests) |tst| {
const exe = addEmbeddedExecutable(
b,
"test-" ++ tst.name ++ "-" ++ cfg.name,
tst.source,
cfg.backing,
);
if (filter == null or exe.target.cpu_arch.? == filter.?) {
exe.setBuildMode(mode);
exe.install();
test_step.dependOn(&exe.step);
}
}
}
}
fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: []const u8, backing: Backing) *std.build.LibExeObjStep {
const Pkg = std.build.Pkg;
const microzig_base = Pkg{
.name = "microzig",
.path = "src/core/microzig.zig",
};
const chip = switch (backing) {
.chip => |c| c,
.board => |b| b.chip,
};
const has_board = (backing == .board);
const chip_package = Pkg{
.name = "chip",
.path = chip.path,
.dependencies = &[_]Pkg{
microzig_base,
pkgs.mmio,
Pkg{
.name = "cpu",
.path = chip.cpu.path,
.dependencies = &[_]Pkg{ microzig_base, pkgs.mmio },
},
Pkg{
.name = "microzig-linker",
.path = "src/modules/linker/linker.zig",
},
},
};
const linker_script_name = blk: {
const hash = hash_blk: {
var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq");
hasher.update(chip.name);
hasher.update(chip.path);
hasher.update(chip.cpu.name);
hasher.update(chip.cpu.path);
var mac: [16]u8 = undefined;
hasher.final(&mac);
break :hash_blk mac;
};
const file_prefix = "zig-cache/microzig/";
const file_suffix = ".ld";
var ld_file_name: [file_prefix.len + 2 * hash.len + file_suffix.len]u8 = undefined;
const filename = std.fmt.bufPrint(&ld_file_name, "{s}{}{s}", .{
file_prefix,
std.fmt.fmtSliceHexLower(&hash),
file_suffix,
}) catch unreachable;
break :blk builder.dupe(filename);
};
const config_file_name = blk: {
const hash = hash_blk: {
var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq");
hasher.update(chip.name);
hasher.update(chip.path);
hasher.update(chip.cpu.name);
hasher.update(chip.cpu.path);
if (backing == .board) {
hasher.update(backing.board.name);
hasher.update(backing.board.path);
}
var mac: [16]u8 = undefined;
hasher.final(&mac);
break :hash_blk mac;
};
const file_prefix = "zig-cache/microzig/config-";
const file_suffix = ".zig";
var ld_file_name: [file_prefix.len + 2 * hash.len + file_suffix.len]u8 = undefined;
const filename = std.fmt.bufPrint(&ld_file_name, "{s}{}{s}", .{
file_prefix,
std.fmt.fmtSliceHexLower(&hash),
file_suffix,
}) catch unreachable;
break :blk builder.dupe(filename);
};
{
var config_file = std.fs.cwd().createFile(config_file_name, .{}) catch unreachable;
defer config_file.close();
var writer = config_file.writer();
writer.print("pub const has_board = {};\n", .{has_board}) catch unreachable;
if (has_board) {
writer.print("pub const board_name = \"{}\";\n", .{std.fmt.fmtSliceEscapeUpper(backing.board.name)}) catch unreachable;
}
writer.print("pub const chip_name = \"{}\";\n", .{std.fmt.fmtSliceEscapeUpper(chip.name)}) catch unreachable;
writer.print("pub const cpu_name = \"{}\";\n", .{std.fmt.fmtSliceEscapeUpper(chip.cpu.name)}) catch unreachable;
}
const config_pkg = Pkg{
.name = "microzig-config",
.path = config_file_name,
};
const linkerscript_gen = builder.addExecutable("linkerscript-gen", "src/tools/linkerscript-gen.zig");
linkerscript_gen.addPackage(chip_package);
linkerscript_gen.addPackage(Pkg{
.name = "microzig-linker",
.path = "src/modules/linker/linker.zig",
});
linkerscript_gen.addBuildOption([]const u8, "microzig_chip_name", chip.name);
linkerscript_gen.addBuildOption([]const u8, "microzig_cpu_name", chip.cpu.name);
linkerscript_gen.addBuildOption([]const u8, "microzig_target_triple", chip.cpu.target.zigTriple(builder.allocator) catch unreachable);
const linkerscript_invocation = linkerscript_gen.run();
linkerscript_invocation.addArg(linker_script_name);
const exe = builder.addExecutable(name, source);
// might not be true for all machines (Pi Pico), but
// for the HAL it's true (it doesn't know the concept of threading)
exe.single_threaded = true;
exe.setTarget(chip.cpu.target);
exe.setLinkerScriptPath(linker_script_name);
exe.step.dependOn(&linkerscript_invocation.step);
// TODO:
// - Generate the linker scripts from the "chip" or "board" package instead of using hardcoded ones.
// - This requires building another tool that runs on the host that compiles those files and emits the linker script.
// - src/tools/linkerscript-gen.zig is the source file for this
// exe.bundle_compiler_rt = false;
switch (backing) {
.chip => {
exe.addPackage(Pkg{
.name = microzig_base.name,
.path = microzig_base.path,
.dependencies = &[_]Pkg{ config_pkg, chip_package },
});
},
.board => |board| {
exe.addPackage(Pkg{
.name = microzig_base.name,
.path = microzig_base.path,
.dependencies = &[_]Pkg{
config_pkg,
chip_package,
Pkg{
.name = "board",
.path = board.path,
.dependencies = &[_]Pkg{ microzig_base, chip_package, pkgs.mmio },
},
},
});
},
}
return exe;
}
const Board = struct {
name: []const u8,
path: []const u8,
chip: Chip,
};
const Chip = struct {
name: []const u8,
path: []const u8,
cpu: Cpu,
};
const Cpu = struct {
name: []const u8,
path: []const u8,
target: std.zig.CrossTarget,
linker_script: []const u8,
};
pub const Backing = union(enum) {
board: Board,
chip: Chip,
};
const pkgs = struct {
const mmio = std.build.Pkg{
.name = "microzig-mmio",
.path = "src/core/mmio.zig",
};
const cpus = struct {
const avr5 = Cpu{
.name = "AVR5",
.path = "src/modules/cpus/avr/avr5.zig",
.linker_script = "src/modules/cpus/avr/linker.ld",
.target = std.zig.CrossTarget{
.cpu_arch = .avr,
.cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 },
.os_tag = .freestanding,
.abi = .eabi,
},
};
const cortex_m3 = Cpu{
.name = "ARM Cortex-M3",
.path = "src/modules/cpus/cortex-m3/cortex-m3.zig",
.linker_script = "src/modules/cpus/cortex-m3/linker.ld",
.target = std.zig.CrossTarget{
.cpu_arch = .arm,
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 },
.os_tag = .freestanding,
.abi = .eabi,
},
};
};
const chips = struct {
const atmega328p = Chip{
.name = "ATmega328p",
.path = "src/modules/chips/atmega328p/atmega328p.zig",
.cpu = cpus.avr5,
};
const lpc1768 = Chip{
.name = "NXP LPC1768",
.path = "src/modules/chips/lpc1768/lpc1768.zig",
.cpu = cpus.cortex_m3,
};
};
const boards = struct {
const arduino_nano = Board{
.name = "Arduino Nano",
.path = "src/modules/boards/arduino-nano/arduino-nano.zig",
.chip = chips.atmega328p,
};
const mbed_lpc1768 = Board{
.name = "mbed LPC1768",
.path = "src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig",
.chip = chips.lpc1768,
};
};
};