Hey all,
I ran into enough friction and headaches determining how precisely to map flags like -mcpu, -mfpu, -mfloat-abi
using gcc-arm-none-eabi
to Zig’s build system that I made a whole utility to take care of it for you:
The github link will have the full info but the highlights are:
- Utility to easily translate
gcc-arm-none-eabi
into Zig build targets - Works for all ARM Cortex-M based devices
- A
gatz translate
command for decodinggcc-arm-none-eabi
args into args forzig build
:
gatz translate --mcpu=cortex-m7 --mfloat-abi=hard --mfpu=fpv5-sp-d16
Translated `zig build` options: -Dtarget=thumb-freestanding-eabihf -Dcpu=cortex_m7+fp_armv8d16sp
- A
gatz info
command for seeing all available options for a target:
gatz info --mcpu=cortex-m7
CPU: cortex-m7 | Float ABIS: soft,softfp,hard | Fpu Types: vfpv4,vfpv4-d16,fpv4-sp-d16,fpv5-d16,fpv5-sp-d16
- Build helpers that let you declare a target using GCC familiar flags, and translate it into a Zig target:
// Note that the fields in "gatz.Target" match up with the arguments
// to their corresponding flags (-mcpu, -mthumb/-marm, -mfloat-abi, -mfpu)
const gatz_target = gatz.Target{
.cpu = gatz.cpu.@"cortex-m7",
.instruction_set = .thumb,
.endianness = .little,
.float_abi = .hard,
.fpu = gatz.fpu.@"fpv5-sp-d16",
};
// This converts our gatz "target" into an actual Zig target query
const target_query = gatz_target.toTargetQuery() catch |err| {
std.log.err("Something is misconfigured, see: {any}\n", .{err});
unreachable;
};
// Using the normal Zig API to resolve a target query
const target = b.resolveTargetQuery(target_query);
- Makes including the pre-compiled Newlib libc from
gcc-arm-none-eabi
a one liner:
// Linking in the arm-none-eabi-gcc supplied newlib is now a single function call!
// Automatically grabs the correct pre-built libraries based on target. Will also
// check to make sure a compatible target is being used
gatz.linkNewlib(b, target, blinky_exe) catch |err| switch (err) {
gatz.NewlibError.CompilerNotFound => {
std.log.err("Couldn't find arm-none-eabi-gcc compiler!\n", .{});
unreachable;
},
gatz.NewlibError.IncompatibleCpu => {
std.log.err("Cpu: {s} isn't compatible with gatz!\n", .{target.result.cpu.model.name});
unreachable;
},
};
And more!
Any and all feedback is welcome, and hopefully people get some use out of this!
PS:
Was particularly delighted with this piece of comptime magic:
Given a struct that is essentially just a namespace, for example:
const namespace = struct {
pub const a: u8 = 1;
pub const b: u8 = 2;
};
This function returns a variable based on a string matching the name of the variable declaration. In my mind a poor man’s way of simulating Rust’s enums that can be ANY type, rather than just integer types.