diff --git a/README.md b/README.md index db1040d..ac63212 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,4 @@ Consider the version `0.11.0-abcdef-123` means that this MicroZig version has a - validate that the table on https://github.com/ZigEmbeddedGroup is correct (in CI) - make system build again properly - start porting everything to 0.12/unstable - +- Try to get some autodocs to build. diff --git a/board-support/nordic-nrf5x/build.zig b/board-support/nordic-nrf5x/build.zig index 1690e0c..9bc2b78 100644 --- a/board-support/nordic-nrf5x/build.zig +++ b/board-support/nordic-nrf5x/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const MicroZig = @import("microzig-build"); fn path(comptime suffix: []const u8) std.Build.LazyPath { return .{ @@ -7,7 +8,7 @@ fn path(comptime suffix: []const u8) std.Build.LazyPath { } pub const chips = struct { - pub const nrf52840 = .{ + pub const nrf52840 = MicroZig.Target{ .preferred_format = .elf, .chip = .{ .name = "nrf52840", @@ -29,7 +30,7 @@ pub const chips = struct { }, }; - pub const nrf52832 = .{ + pub const nrf52832 = MicroZig.Target{ .preferred_format = .elf, .chip = .{ .name = "nrf52", @@ -48,7 +49,7 @@ pub const chips = struct { pub const boards = struct { pub const nordic = struct { - pub const nRF52840_Dongle = .{ + pub const nRF52840_Dongle = MicroZig.Target{ .preferred_format = .elf, .chip = chips.nrf52840.chip, .board = .{ diff --git a/board-support/nxp-lpc/build.zig b/board-support/nxp-lpc/build.zig index 639a034..5f75d43 100644 --- a/board-support/nxp-lpc/build.zig +++ b/board-support/nxp-lpc/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const MicroZig = @import("microzig-build"); fn path(comptime suffix: []const u8) std.Build.LazyPath { return .{ @@ -11,7 +12,7 @@ const hal = .{ }; pub const chips = struct { - pub const lpc176x5x = .{ + pub const lpc176x5x = MicroZig.Target{ .preferred_format = .elf, .chip = .{ // TODO: Separate over those chips, this is not generic! @@ -33,7 +34,7 @@ pub const chips = struct { pub const boards = struct { pub const mbed = struct { - pub const lpc1768 = .{ + pub const lpc1768 = MicroZig.Target{ .preferred_format = .hex, .chip = chips.lpc176x5x.chip, .hal = hal, diff --git a/board-support/raspberrypi-rp2040/build.zig b/board-support/raspberrypi-rp2040/build.zig index eae5bc9..73453f3 100644 --- a/board-support/raspberrypi-rp2040/build.zig +++ b/board-support/raspberrypi-rp2040/build.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const microzig = @import("microzig"); +const microzig = @import("microzig-build"); fn root() []const u8 { return comptime (std.fs.path.dirname(@src().file) orelse "."); @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) !void { pub const chips = struct { // Note: This chip has no flash support defined and requires additional configuration! - pub const rp2040 = .{ + pub const rp2040 = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, @@ -28,7 +28,7 @@ pub const chips = struct { pub const boards = struct { pub const raspberry_pi = struct { - pub const pico = .{ + pub const pico = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, @@ -43,7 +43,7 @@ pub const boards = struct { }; pub const waveshare = struct { - pub const rp2040_plus_4m = .{ + pub const rp2040_plus_4m = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, @@ -56,7 +56,7 @@ pub const boards = struct { .configure = rp2040_configure(.w25q080), }; - pub const rp2040_plus_16m = .{ + pub const rp2040_plus_16m = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, @@ -69,7 +69,7 @@ pub const boards = struct { .configure = rp2040_configure(.w25q080), }; - pub const rp2040_eth = .{ + pub const rp2040_eth = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, @@ -82,7 +82,7 @@ pub const boards = struct { .configure = rp2040_configure(.w25q080), }; - pub const rp2040_matrix = .{ + pub const rp2040_matrix = microzig.Target{ .preferred_format = .{ .uf2 = .RP2040 }, .chip = chip, .hal = hal, diff --git a/board-support/stmicro-stm32/build.zig b/board-support/stmicro-stm32/build.zig index dcbf169..4e0e0d5 100644 --- a/board-support/stmicro-stm32/build.zig +++ b/board-support/stmicro-stm32/build.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const microzig = @import("microzig"); +const microzig = @import("microzig-build"); fn root() []const u8 { return comptime (std.fs.path.dirname(@src().file) orelse "."); diff --git a/build.zig b/build.zig index d582de9..2f71192 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,10 @@ const std = @import("std"); +// const examples = @import("examples/build.zig"); pub fn build(b: *std.Build) void { buildTools(b); + + // examples.build(b); } fn buildTools(b: *std.Build) void { diff --git a/build.zig.zon b/build.zig.zon index a4e9652..e0c48b3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,5 +6,13 @@ .url = "https://github.com/ziglibs/eggzon/archive/refs/heads/master.tar.gz", .hash = "1220cd5cec7e9d4911074a9b2dec2dabef76e1adf94d041bca068163ce7666c4be47", }, + .uf2 = .{ + .url = "https://github.com/ZigEmbeddedGroup/uf2/archive/8037b439ccbac862471392b25e94a8995d784e2c.tar.gz", + .hash = "1220cc66563fc1ecefca7990968441dc9d4db717884ffa9a2de657f60ed4bb74a70a", + }, + .regz = .{ + .url = "https://github.com/ZigEmbeddedGroup/regz/archive/d66ffd56f51fc46c071412141b5d0c74dc83c310.tar.gz", + .hash = "122002c5f2e31c11373ede6e8a8dd9a61aabd60d38df667ec33b5f994d1f0b503823", + }, }, } diff --git a/build/build.zig b/build/build.zig index 748833f..2c6df50 100644 --- a/build/build.zig +++ b/build/build.zig @@ -5,6 +5,342 @@ const std = @import("std"); const uf2 = @import("uf2"); +//////////////////////////////////////// +// MicroZig Gen 3 Interface // +//////////////////////////////////////// + +pub const EnvironmentInfo = struct { + /// include package names of your board support packages here: + board_support: []const []const u8, + + /// package name of the build package (optional) + self: []const u8 = "microzig", + + /// package name of the core package (optional) + core: []const u8 = "microzig-core", +}; + +pub fn createBuildEnvironment(b: *std.Build, comptime info: EnvironmentInfo) *BuildEnvironment { + const be = b.allocator.create(BuildEnvironment) catch @panic("out of memory"); + be.* = BuildEnvironment{ + .host_build = b, + .self = undefined, + .microzig_core = undefined, + .board_support_packages = b.allocator.alloc(BoardSupportPackage, info.board_support.len) catch @panic("out of memory"), + .targets = .{}, + }; + + be.self = b.dependency(info.self, .{}); + be.microzig_core = b.dependency(info.core, .{}); + + for (be.board_support_packages, info.board_support) |*out, in| { + out.* = BoardSupportPackage{ + .name = in, + .dep = b.dependency(in, .{}), + }; + } + + // Fetch and collect all supported targets: + inline for (info.board_support) |bsp_name| { + // Keep in sync with the logic from Build.zig:dependency + const build_runner = @import("root"); + const deps = build_runner.dependencies; + + const bsp_root = @field(deps.imports, bsp_name); + + if (@hasDecl(bsp_root, "chips")) { + fetch_microzig_targets("chip:", bsp_root.chips, bsp_name, be); + } + + if (@hasDecl(bsp_root, "boards")) { + fetch_microzig_targets("board:", bsp_root.boards, bsp_name, be); + } + } + + return be; +} + +fn fetch_microzig_targets(comptime prefix: []const u8, comptime namespace: type, comptime bsp_name: []const u8, be: *BuildEnvironment) void { + inline for (@typeInfo(namespace).Struct.decls) |decl_info| { + const decl = @field(namespace, decl_info.name); + const T = @TypeOf(decl); + + const name = comptime prefix ++ decl_info.name; // board:vendor/name + const full_name = comptime name ++ "#" ++ bsp_name; // board:vendor/name#bsp-package-name + + if (T == Target) { + const target: Target = decl; + + be.targets.put(be.host_build.allocator, name, target) catch @panic("out of memory"); + be.targets.put(be.host_build.allocator, full_name, target) catch @panic("out of memory"); + } else { + const ok = blk: { + if (comptime T != type) { + // @compileLog(full_name, "check 1:", T, decl); + break :blk false; + } + + const ti = @typeInfo(decl); + if (comptime ti != .Struct) { + // @compileLog(full_name, "check 2:", ti); + break :blk false; + } + + if (comptime ti.Struct.fields.len > 0) { + // @compileLog(full_name, "check 3:", ti.Struct); + // @compileLog(full_name, "check 3:", ti.Struct.fields); + break :blk false; + } + + fetch_microzig_targets( + comptime name ++ "/", + decl, + bsp_name, + be, + ); + + break :blk true; + }; + if (!ok) { + std.debug.print("Bad BSP: {s} is neither namespace nor a microzig.Target\n", .{ prefix, full_name }); + } + } + } +} + +pub const BoardSupportPackage = struct { + dep: *std.Build.Dependency, + name: []const u8, +}; + +pub const BuildEnvironment = struct { + host_build: *std.Build, + self: *std.Build.Dependency, + + microzig_core: *std.Build.Dependency, + + board_support_packages: []BoardSupportPackage, + + targets: std.StringArrayHashMapUnmanaged(Target), + + pub fn findTarget(env: *const BuildEnvironment, name: []const u8) ?*const Target { + return env.targets.getPtr(name); + } + + /// Declares a new MicroZig firmware file. + pub fn addFirmware( + /// The MicroZig instance that should be used to create the firmware. + env: *BuildEnvironment, + /// The instance of the `build.zig` that is calling this function. + host_build: *std.Build, + /// Options that define how the firmware is built. + options: FirmwareOptions, + ) *Firmware { + const micro_build = env.self.builder; + + const chip = &options.target.chip; + const cpu = chip.cpu.getDescriptor(); + const maybe_hal = options.hal orelse options.target.hal; + const maybe_board = options.board orelse options.target.board; + + const linker_script = options.linker_script orelse options.target.linker_script; + + // TODO: let the user override which ram section to use the stack on, + // for now just using the first ram section in the memory region list + const first_ram = blk: { + for (chip.memory_regions) |region| { + if (region.kind == .ram) + break :blk region; + } else @panic("no ram memory region found for setting the end-of-stack address"); + }; + + // On demand, generate chip definitions via regz: + const chip_source = switch (chip.register_definition) { + .json, .atdf, .svd => |file| blk: { + const regz_exe = env.dependency("regz", .{ .optimize = .ReleaseSafe }).artifact("regz"); + + const regz_gen = host_build.addRunArtifact(regz_exe); + + regz_gen.addArg("--schema"); // Explicitly set schema type, one of: svd, atdf, json + regz_gen.addArg(@tagName(chip.register_definition)); + + regz_gen.addArg("--output_path"); // Write to a file + const zig_file = regz_gen.addOutputFileArg("chip.zig"); + + regz_gen.addFileArg(file); + + break :blk zig_file; + }, + + .zig => |src| src, + }; + + const config = host_build.addOptions(); + config.addOption(bool, "has_hal", (maybe_hal != null)); + config.addOption(bool, "has_board", (maybe_board != null)); + + config.addOption(?[]const u8, "board_name", if (maybe_board) |brd| brd.name else null); + + config.addOption([]const u8, "chip_name", chip.name); + config.addOption([]const u8, "cpu_name", chip.name); + config.addOption(usize, "end_of_stack", first_ram.offset + first_ram.length); + + const fw: *Firmware = host_build.allocator.create(Firmware) catch @panic("out of memory"); + fw.* = Firmware{ + .env = env, + .host_build = host_build, + .artifact = host_build.addExecutable(.{ + .name = options.name, + .optimize = options.optimize, + .target = cpu.target, + .linkage = .static, + .root_source_file = .{ .cwd_relative = env.self.builder.pathFromRoot("src/start.zig") }, + }), + .target = options.target, + .output_files = Firmware.OutputFileMap.init(host_build.allocator), + + .config = config, + + .modules = .{ + .microzig = micro_build.createModule(.{ + .source_file = .{ .cwd_relative = micro_build.pathFromRoot("src/microzig.zig") }, + .dependencies = &.{ + .{ + .name = "config", + .module = micro_build.createModule(.{ .source_file = config.getSource() }), + }, + }, + }), + + .cpu = undefined, + .chip = undefined, + + .board = null, + .hal = null, + + .app = undefined, + }, + }; + errdefer fw.output_files.deinit(); + + fw.modules.chip = micro_build.createModule(.{ + .source_file = chip_source, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + fw.modules.microzig.dependencies.put("chip", fw.modules.chip) catch @panic("out of memory"); + + fw.modules.cpu = micro_build.createModule(.{ + .source_file = cpu.source_file, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + fw.modules.microzig.dependencies.put("cpu", fw.modules.cpu) catch @panic("out of memory"); + + if (maybe_hal) |hal| { + fw.modules.hal = micro_build.createModule(.{ + .source_file = hal.source_file, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + fw.modules.microzig.dependencies.put("hal", fw.modules.hal.?) catch @panic("out of memory"); + } + + if (maybe_board) |brd| { + fw.modules.board = micro_build.createModule(.{ + .source_file = brd.source_file, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + fw.modules.microzig.dependencies.put("board", fw.modules.board.?) catch @panic("out of memory"); + } + + fw.modules.app = host_build.createModule(.{ + .source_file = options.source_file, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + + const umm = env.dependency("umm-zig", .{}).module("umm"); + fw.modules.microzig.dependencies.put("umm", umm) catch @panic("out of memory"); + + fw.artifact.addModule("app", fw.modules.app); + fw.artifact.addModule("microzig", fw.modules.microzig); + + fw.artifact.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded + fw.artifact.single_threaded = options.single_threaded orelse fw.target.single_threaded; + fw.artifact.bundle_compiler_rt = options.bundle_compiler_rt orelse fw.target.bundle_compiler_rt; + + switch (linker_script) { + .generated => { + fw.artifact.setLinkerScript( + generateLinkerScript(host_build, chip.*) catch @panic("out of memory"), + ); + }, + + .source_file => |source| { + fw.artifact.setLinkerScriptPath(source); + }, + } + + if (options.target.configure) |configure| { + configure(host_build, fw); + } + + return fw; + } + + /// Adds a new dependency to the `install` step that will install the `firmware` into the folder `$prefix/firmware`. + pub fn installFirmware( + /// The MicroZig instance that was used to create the firmware. + env: *BuildEnvironment, + /// The instance of the `build.zig` that should perform installation. + b: *std.Build, + /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. + firmware: *Firmware, + /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. + options: InstallFirmwareOptions, + ) void { + std.debug.assert(env == firmware.env); + const install_step = addInstallFirmware(env, b, firmware, options); + b.getInstallStep().dependOn(&install_step.step); + } + + /// Creates a new `std.Build.Step.InstallFile` instance that will install the given firmware to `$prefix/firmware`. + /// + /// **NOTE:** This does not actually install the firmware yet. You have to add the returned step as a dependency to another step. + /// If you want to just install the firmware, use `installFirmware` instead! + pub fn addInstallFirmware( + /// The MicroZig instance that was used to create the firmware. + env: *BuildEnvironment, + /// The instance of the `build.zig` that should perform installation. + b: *std.Build, + /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. + firmware: *Firmware, + /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. + options: InstallFirmwareOptions, + ) *std.Build.Step.InstallFile { + _ = env; + const format = firmware.resolveFormat(options.format); + + const basename = b.fmt("{s}{s}", .{ + firmware.artifact.name, + format.getExtension(), + }); + + return b.addInstallFileWithDir(firmware.getEmittedBin(format), .{ .custom = "firmware" }, basename); + } + + fn dependency(env: *BuildEnvironment, name: []const u8, args: anytype) *std.Build.Dependency { + return env.self.builder.dependency(name, args); + } +}; + //////////////////////////////////////// // MicroZig Gen 2 Interface // //////////////////////////////////////// @@ -372,7 +708,7 @@ pub const FirmwareOptions = struct { name: []const u8, /// The MicroZig target that the firmware is built for. Either a board or a chip. - target: Target, + target: *const Target, /// The optimization level that should be used. Usually `ReleaseSmall` or `Debug` is a good choice. /// Also using `std.Build.standardOptimizeOption` is a good idea. @@ -399,222 +735,12 @@ pub const FirmwareOptions = struct { linker_script: ?LinkerScript = null, }; -/// Declares a new MicroZig firmware file. -pub fn addFirmware( - /// The MicroZig instance that should be used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that is calling this function. - host_build: *std.Build, - /// Options that define how the firmware is built. - options: FirmwareOptions, -) *Firmware { - const micro_build = mz.self.builder; - - const chip = &options.target.chip; - const cpu = chip.cpu.getDescriptor(); - const maybe_hal = options.hal orelse options.target.hal; - const maybe_board = options.board orelse options.target.board; - - const linker_script = options.linker_script orelse options.target.linker_script; - - // TODO: let the user override which ram section to use the stack on, - // for now just using the first ram section in the memory region list - const first_ram = blk: { - for (chip.memory_regions) |region| { - if (region.kind == .ram) - break :blk region; - } else @panic("no ram memory region found for setting the end-of-stack address"); - }; - - // On demand, generate chip definitions via regz: - const chip_source = switch (chip.register_definition) { - .json, .atdf, .svd => |file| blk: { - const regz_exe = mz.dependency("regz", .{ .optimize = .ReleaseSafe }).artifact("regz"); - - const regz_gen = host_build.addRunArtifact(regz_exe); - - regz_gen.addArg("--schema"); // Explicitly set schema type, one of: svd, atdf, json - regz_gen.addArg(@tagName(chip.register_definition)); - - regz_gen.addArg("--output_path"); // Write to a file - const zig_file = regz_gen.addOutputFileArg("chip.zig"); - - regz_gen.addFileArg(file); - - break :blk zig_file; - }, - - .zig => |src| src, - }; - - const config = host_build.addOptions(); - config.addOption(bool, "has_hal", (maybe_hal != null)); - config.addOption(bool, "has_board", (maybe_board != null)); - - config.addOption(?[]const u8, "board_name", if (maybe_board) |brd| brd.name else null); - - config.addOption([]const u8, "chip_name", chip.name); - config.addOption([]const u8, "cpu_name", chip.name); - config.addOption(usize, "end_of_stack", first_ram.offset + first_ram.length); - - const fw: *Firmware = host_build.allocator.create(Firmware) catch @panic("out of memory"); - fw.* = Firmware{ - .mz = mz, - .host_build = host_build, - .artifact = host_build.addExecutable(.{ - .name = options.name, - .optimize = options.optimize, - .target = cpu.target, - .linkage = .static, - .root_source_file = .{ .cwd_relative = mz.self.builder.pathFromRoot("src/start.zig") }, - }), - .target = options.target, - .output_files = Firmware.OutputFileMap.init(host_build.allocator), - - .config = config, - - .modules = .{ - .microzig = micro_build.createModule(.{ - .source_file = .{ .cwd_relative = micro_build.pathFromRoot("src/microzig.zig") }, - .dependencies = &.{ - .{ - .name = "config", - .module = micro_build.createModule(.{ .source_file = config.getSource() }), - }, - }, - }), - - .cpu = undefined, - .chip = undefined, - - .board = null, - .hal = null, - - .app = undefined, - }, - }; - errdefer fw.output_files.deinit(); - - fw.modules.chip = micro_build.createModule(.{ - .source_file = chip_source, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("chip", fw.modules.chip) catch @panic("out of memory"); - - fw.modules.cpu = micro_build.createModule(.{ - .source_file = cpu.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("cpu", fw.modules.cpu) catch @panic("out of memory"); - - if (maybe_hal) |hal| { - fw.modules.hal = micro_build.createModule(.{ - .source_file = hal.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("hal", fw.modules.hal.?) catch @panic("out of memory"); - } - - if (maybe_board) |brd| { - fw.modules.board = micro_build.createModule(.{ - .source_file = brd.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("board", fw.modules.board.?) catch @panic("out of memory"); - } - - fw.modules.app = host_build.createModule(.{ - .source_file = options.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - - const umm = mz.dependency("umm-zig", .{}).module("umm"); - fw.modules.microzig.dependencies.put("umm", umm) catch @panic("out of memory"); - - fw.artifact.addModule("app", fw.modules.app); - fw.artifact.addModule("microzig", fw.modules.microzig); - - fw.artifact.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded - fw.artifact.single_threaded = options.single_threaded orelse fw.target.single_threaded; - fw.artifact.bundle_compiler_rt = options.bundle_compiler_rt orelse fw.target.bundle_compiler_rt; - - switch (linker_script) { - .generated => { - fw.artifact.setLinkerScript( - generateLinkerScript(host_build, chip.*) catch @panic("out of memory"), - ); - }, - - .source_file => |source| { - fw.artifact.setLinkerScriptPath(source); - }, - } - - if (options.target.configure) |configure| { - configure(host_build, fw); - } - - return fw; -} - /// Configuration options for firmware installation. pub const InstallFirmwareOptions = struct { /// Overrides the output format for the binary. If not set, the standard preferred file format for the firmware target is used. format: ?BinaryFormat = null, }; -/// Adds a new dependency to the `install` step that will install the `firmware` into the folder `$prefix/firmware`. -pub fn installFirmware( - /// The MicroZig instance that was used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that should perform installation. - b: *std.Build, - /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. - firmware: *Firmware, - /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. - options: InstallFirmwareOptions, -) void { - std.debug.assert(mz == firmware.mz); - const install_step = addInstallFirmware(mz, b, firmware, options); - b.getInstallStep().dependOn(&install_step.step); -} - -/// Creates a new `std.Build.Step.InstallFile` instance that will install the given firmware to `$prefix/firmware`. -/// -/// **NOTE:** This does not actually install the firmware yet. You have to add the returned step as a dependency to another step. -/// If you want to just install the firmware, use `installFirmware` instead! -pub fn addInstallFirmware( - /// The MicroZig instance that was used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that should perform installation. - b: *std.Build, - /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. - firmware: *Firmware, - /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. - options: InstallFirmwareOptions, -) *std.Build.Step.InstallFile { - const format = firmware.resolveFormat(options.format); - - const basename = b.fmt("{s}{s}", .{ - firmware.artifact.name, - format.getExtension(), - }); - - _ = mz; - - return b.addInstallFileWithDir(firmware.getEmittedBin(format), .{ .custom = "firmware" }, basename); -} - /// Declaration of a firmware build. pub const Firmware = struct { const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false); @@ -629,9 +755,9 @@ pub const Firmware = struct { }; // privates: - mz: *MicroZig, + env: *BuildEnvironment, host_build: *std.Build, - target: Target, + target: *const Target, output_files: OutputFileMap, // publics: @@ -702,7 +828,7 @@ pub const Firmware = struct { }, .uf2 => |family_id| blk: { - const uf2_exe = firmware.mz.dependency("uf2", .{ .optimize = .ReleaseSafe }).artifact("elf2uf2"); + const uf2_exe = firmware.env.dependency("uf2", .{ .optimize = .ReleaseSafe }).artifact("elf2uf2"); const convert = firmware.host_build.addRunArtifact(uf2_exe); @@ -847,10 +973,6 @@ fn buildConfigError(b: *std.Build, comptime fmt: []const u8, args: anytype) nore @panic(msg); } -fn dependency(mz: *MicroZig, name: []const u8, args: anytype) *std.Build.Dependency { - return mz.self.builder.dependency(name, args); -} - fn generateLinkerScript(b: *std.Build, chip: Chip) !std.Build.LazyPath { const cpu = chip.cpu.getDescriptor(); diff --git a/core/build.zig b/core/build.zig index 748833f..1768f51 100644 --- a/core/build.zig +++ b/core/build.zig @@ -3,7 +3,8 @@ //! This means we need to use addExecutable() instead of using const std = @import("std"); -const uf2 = @import("uf2"); +const microbuild = @import("microzig-build"); +// const uf2 = @import("uf2"); //////////////////////////////////////// // MicroZig Gen 2 Interface // @@ -14,43 +15,44 @@ fn root() []const u8 { } const build_root = root(); -const MicroZig = @This(); +// const MicroZig = @This(); -b: *std.Build, -self: *std.Build.Dependency, +// b: *std.Build, +// self: *std.Build.Dependency, -/// Creates a new instance of the MicroZig build support. -/// -/// This is necessary as we need to keep track of some internal state to prevent -/// duplicated work per firmware built. -pub fn init(b: *std.Build, dependency_name: []const u8) *MicroZig { - const mz = b.allocator.create(MicroZig) catch @panic("out of memory"); - mz.* = MicroZig{ - .b = b, - .self = b.dependency(dependency_name, .{}), - }; - return mz; -} +// /// Creates a new instance of the MicroZig build support. +// /// +// /// This is necessary as we need to keep track of some internal state to prevent +// /// duplicated work per firmware built. +// pub fn init(b: *std.Build, dependency_name: []const u8) *MicroZig { +// const mz = b.allocator.create(MicroZig) catch @panic("out of memory"); +// mz.* = MicroZig{ +// .b = b, +// .self = b.dependency(dependency_name, .{}), +// }; +// return mz; +// } /// This build script validates usage patterns we expect from MicroZig pub fn build(b: *std.Build) !void { - const uf2_dep = b.dependency("uf2", .{}); + _ = b; + // const uf2_dep = b.dependency("uf2", .{}); - const build_test = b.addTest(.{ - .root_source_file = .{ .path = "build.zig" }, - }); + // const build_test = b.addTest(.{ + // .root_source_file = .{ .path = "build.zig" }, + // }); - build_test.addAnonymousModule("uf2", .{ - .source_file = .{ .cwd_relative = uf2_dep.builder.pathFromRoot("build.zig") }, - }); + // build_test.addAnonymousModule("uf2", .{ + // .source_file = .{ .cwd_relative = uf2_dep.builder.pathFromRoot("build.zig") }, + // }); - const install_docs = b.addInstallDirectory(.{ - .source_dir = build_test.getEmittedDocs(), - .install_dir = .prefix, - .install_subdir = "docs", - }); + // const install_docs = b.addInstallDirectory(.{ + // .source_dir = build_test.getEmittedDocs(), + // .install_dir = .prefix, + // .install_subdir = "docs", + // }); - b.getInstallStep().dependOn(&install_docs.step); + // b.getInstallStep().dependOn(&install_docs.step); // const backings = @import("test/backings.zig"); // const optimize = b.standardOptimizeOption(.{}); @@ -96,686 +98,686 @@ pub fn build(b: *std.Build) !void { // test_step.dependOn(&b.addRunArtifact(core_tests).step); } -/// The resulting binary format for the firmware file. -/// A lot of embedded systems don't use plain ELF files, thus we provide means -/// to convert the resulting ELF into other common formats. -pub const BinaryFormat = union(enum) { - /// [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), the standard output from the compiler. - elf, - - /// A flat binary, contains only the loaded portions of the firmware with an unspecified base offset. - bin, - - /// The [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format, contains - /// an ASCII description of what memory to load where. - hex, - - /// A [Device Firmware Upgrade](https://www.usb.org/sites/default/files/DFU_1.1.pdf) file. - dfu, - - /// The [USB Flashing Format (UF2)](https://github.com/microsoft/uf2) designed by Microsoft. - uf2: uf2.FamilyId, - - /// The [firmware format](https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/firmware-image-format.html) used by the [esptool](https://github.com/espressif/esptool) bootloader. - esp, - - /// Custom option for non-standard formats. - custom: *Custom, - - /// Returns the standard extension for the resulting binary file. - pub fn getExtension(format: BinaryFormat) []const u8 { - return switch (format) { - .elf => ".elf", - .bin => ".bin", - .hex => ".hex", - .dfu => ".dfu", - .uf2 => ".uf2", - .esp => ".bin", - - .custom => |c| c.extension, - }; - } - - pub const Custom = struct { - /// The standard extension of the format. - extension: []const u8, - - /// A function that will convert a given `elf` file into the custom output format. - /// - /// The `*Custom` format is passed so contextual information can be obtained by using - /// `@fieldParentPtr` to provide access to tooling. - convert: *const fn (*Custom, elf: std.Build.LazyPath) std.Build.LazyPath, - }; - - const Enum = std.meta.Tag(BinaryFormat); - - const Context = struct { - pub fn hash(self: @This(), fmt: BinaryFormat) u32 { - _ = self; - - var hasher = std.hash.XxHash32.init(0x1337_42_21); - - hasher.update(@tagName(fmt)); - - switch (fmt) { - .elf, .bin, .hex, .dfu, .esp => |val| { - if (@TypeOf(val) != void) @compileError("Missing update: Context.hash now requires special care!"); - }, - - .uf2 => |family_id| hasher.update(@tagName(family_id)), - .custom => |custom| hasher.update(std.mem.asBytes(custom)), - } - - return hasher.final(); - } - - pub fn eql(self: @This(), fmt_a: BinaryFormat, fmt_b: BinaryFormat, index: usize) bool { - _ = self; - _ = index; - if (@as(BinaryFormat.Enum, fmt_a) != @as(BinaryFormat.Enum, fmt_b)) - return false; - - return switch (fmt_a) { - .elf, .bin, .hex, .dfu, .esp => |val| { - if (@TypeOf(val) != void) @compileError("Missing update: Context.eql now requires special care!"); - return true; - }, - - .uf2 => |a| (a == fmt_b.uf2), - .custom => |a| (a == fmt_b.custom), - }; - } - }; -}; - -/// The CPU model a target uses. -/// -/// The CPUs usually require special care on how to do interrupts, and getting an entry point. -/// -/// MicroZig officially only supports the CPUs listed here, but other CPUs might be provided -/// via the `custom` field. -pub const CpuModel = union(enum) { - avr5, - cortex_m0, - cortex_m0plus, - cortex_m3, - cortex_m4, - riscv32_imac, - - custom: *const Cpu, - - pub fn getDescriptor(model: CpuModel) *const Cpu { - return switch (@as(std.meta.Tag(CpuModel), model)) { - inline else => |tag| &@field(cpus, @tagName(tag)), - .custom => model.custom, - }; - } -}; - -/// A cpu descriptor. -pub const Cpu = struct { - /// Display name of the CPU. - name: []const u8, - - /// Source file providing startup code and memory initialization routines. - source_file: std.build.LazyPath, - - /// The compiler target we use to compile all the code. - target: std.zig.CrossTarget, -}; - -/// A descriptor for memory regions in a microcontroller. -pub const MemoryRegion = struct { - /// The type of the memory region for generating a proper linker script. - kind: Kind, - offset: u64, - length: u64, - - pub const Kind = union(enum) { - /// This is a (normally) immutable memory region where the code is stored. - flash, - - /// This is a mutable memory region for data storage. - ram, - - /// This is a memory region that maps MMIO devices. - io, - - /// This is a memory region that exists, but is reserved and must not be used. - reserved, - - /// This is a memory region used for internal linking tasks required by the board support package. - private: PrivateRegion, - }; - - pub const PrivateRegion = struct { - /// The name of the memory region. Will not have an automatic numeric counter and must be unique. - name: []const u8, - - /// Is the memory region executable? - executable: bool, - - /// Is the memory region readable? - readable: bool, - - /// Is the memory region writable? - writeable: bool, - }; -}; - -/// Defines a custom microcontroller. -pub const Chip = struct { - /// The display name of the controller. - name: []const u8, - - /// (optional) link to the documentation/vendor page of the controller. - url: ?[]const u8 = null, - - /// The cpu model this controller uses. - cpu: CpuModel, - - /// The provider for register definitions. - register_definition: union(enum) { - /// Use `regz` to create a zig file from a JSON schema. - json: std.Build.LazyPath, - - /// Use `regz` to create a json file from a SVD schema. - svd: std.Build.LazyPath, - - /// Use `regz` to create a zig file from an ATDF schema. - atdf: std.Build.LazyPath, - - /// Use the provided file directly as the chip file. - zig: std.Build.LazyPath, - }, - - /// The memory regions that are present in this chip. - memory_regions: []const MemoryRegion, -}; - -/// Defines a hardware abstraction layer. -pub const HardwareAbstractionLayer = struct { - /// Root source file for this HAL. - source_file: std.Build.LazyPath, -}; - -/// Provides a description of a board. -/// -/// Boards provide additional information to a chip and HAL package. -/// For example, they can list attached peripherials, external crystal frequencies, -/// flash sizes, ... -pub const BoardDefinition = struct { - /// Display name of the board - name: []const u8, - - /// (optional) link to the documentation/vendor page of the board. - url: ?[]const u8 = null, - - /// Provides the root file for the board definition. - source_file: std.Build.LazyPath, -}; - -/// The linker script used to link the firmware. -pub const LinkerScript = union(enum) { - /// Auto-generated linker script derived from the memory regions of the chip. - generated, - - /// Externally defined linker script. - source_file: std.build.LazyPath, -}; - -/// A compilation target for MicroZig. Provides information about the chip, -/// hal, board and so on. -/// -/// This is used instead of `std.zig.CrossTarget` to define a MicroZig Firmware. -pub const Target = struct { - /// The preferred binary format of this MicroZig target. If `null`, the user must - /// explicitly give the `.format` field during a call to `getEmittedBin()` or installation steps. - preferred_format: ?BinaryFormat, - - /// The chip this target uses, - chip: Chip, - - /// Usually, embedded projects are single-threaded and single-core applications. Platforms that - /// support multiple CPUs should set this to `false`. - single_threaded: bool = true, - - /// Determines whether the compiler_rt package is bundled with the application or not. - /// This should always be true except for platforms where compiler_rt cannot be built right now. - bundle_compiler_rt: bool = true, - - /// (optional) Provides a default hardware abstraction layer that is used. - /// If `null`, no `microzig.hal` will be available. - hal: ?HardwareAbstractionLayer = null, - - /// (optional) Provides description of external hardware and connected devices - /// like oscillators and such. - /// - /// This structure isn't used by MicroZig itself, but can be utilized from the HAL - /// if present. - board: ?BoardDefinition = null, - - /// (optional) Provide a custom linker script for the hardware or define a custom generation. - linker_script: LinkerScript = .generated, - - /// (optional) Further configures the created firmware depending on the chip and/or board settings. - /// This can be used to set/change additional properties on the created `*Firmware` object. - configure: ?*const fn (host_build: *std.Build, *Firmware) void = null, - - /// (optional) Post processing step that will patch up and modify the elf file if necessary. - binary_post_process: ?*const fn (host_build: *std.Build, std.Build.LazyPath) std.Build.LazyPath = null, -}; - -/// Options to the `addFirmware` function. -pub const FirmwareOptions = struct { - /// The name of the firmware file. - name: []const u8, - - /// The MicroZig target that the firmware is built for. Either a board or a chip. - target: Target, - - /// The optimization level that should be used. Usually `ReleaseSmall` or `Debug` is a good choice. - /// Also using `std.Build.standardOptimizeOption` is a good idea. - optimize: std.builtin.OptimizeMode, - - /// The root source file for the application. This is your `src/main.zig` file. - source_file: std.Build.LazyPath, - - // Overrides: - - /// If set, overrides the `single_threaded` property of the target. - single_threaded: ?bool = null, - - /// If set, overrides the `bundle_compiler_rt` property of the target. - bundle_compiler_rt: ?bool = null, - - /// If set, overrides the `hal` property of the target. - hal: ?HardwareAbstractionLayer = null, - - /// If set, overrides the `board` property of the target. - board: ?BoardDefinition = null, - - /// If set, overrides the `linker_script` property of the target. - linker_script: ?LinkerScript = null, -}; - -/// Declares a new MicroZig firmware file. -pub fn addFirmware( - /// The MicroZig instance that should be used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that is calling this function. - host_build: *std.Build, - /// Options that define how the firmware is built. - options: FirmwareOptions, -) *Firmware { - const micro_build = mz.self.builder; - - const chip = &options.target.chip; - const cpu = chip.cpu.getDescriptor(); - const maybe_hal = options.hal orelse options.target.hal; - const maybe_board = options.board orelse options.target.board; - - const linker_script = options.linker_script orelse options.target.linker_script; - - // TODO: let the user override which ram section to use the stack on, - // for now just using the first ram section in the memory region list - const first_ram = blk: { - for (chip.memory_regions) |region| { - if (region.kind == .ram) - break :blk region; - } else @panic("no ram memory region found for setting the end-of-stack address"); - }; - - // On demand, generate chip definitions via regz: - const chip_source = switch (chip.register_definition) { - .json, .atdf, .svd => |file| blk: { - const regz_exe = mz.dependency("regz", .{ .optimize = .ReleaseSafe }).artifact("regz"); - - const regz_gen = host_build.addRunArtifact(regz_exe); - - regz_gen.addArg("--schema"); // Explicitly set schema type, one of: svd, atdf, json - regz_gen.addArg(@tagName(chip.register_definition)); - - regz_gen.addArg("--output_path"); // Write to a file - const zig_file = regz_gen.addOutputFileArg("chip.zig"); - - regz_gen.addFileArg(file); - - break :blk zig_file; - }, - - .zig => |src| src, - }; - - const config = host_build.addOptions(); - config.addOption(bool, "has_hal", (maybe_hal != null)); - config.addOption(bool, "has_board", (maybe_board != null)); - - config.addOption(?[]const u8, "board_name", if (maybe_board) |brd| brd.name else null); - - config.addOption([]const u8, "chip_name", chip.name); - config.addOption([]const u8, "cpu_name", chip.name); - config.addOption(usize, "end_of_stack", first_ram.offset + first_ram.length); - - const fw: *Firmware = host_build.allocator.create(Firmware) catch @panic("out of memory"); - fw.* = Firmware{ - .mz = mz, - .host_build = host_build, - .artifact = host_build.addExecutable(.{ - .name = options.name, - .optimize = options.optimize, - .target = cpu.target, - .linkage = .static, - .root_source_file = .{ .cwd_relative = mz.self.builder.pathFromRoot("src/start.zig") }, - }), - .target = options.target, - .output_files = Firmware.OutputFileMap.init(host_build.allocator), - - .config = config, - - .modules = .{ - .microzig = micro_build.createModule(.{ - .source_file = .{ .cwd_relative = micro_build.pathFromRoot("src/microzig.zig") }, - .dependencies = &.{ - .{ - .name = "config", - .module = micro_build.createModule(.{ .source_file = config.getSource() }), - }, - }, - }), - - .cpu = undefined, - .chip = undefined, - - .board = null, - .hal = null, - - .app = undefined, - }, - }; - errdefer fw.output_files.deinit(); - - fw.modules.chip = micro_build.createModule(.{ - .source_file = chip_source, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("chip", fw.modules.chip) catch @panic("out of memory"); - - fw.modules.cpu = micro_build.createModule(.{ - .source_file = cpu.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("cpu", fw.modules.cpu) catch @panic("out of memory"); - - if (maybe_hal) |hal| { - fw.modules.hal = micro_build.createModule(.{ - .source_file = hal.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("hal", fw.modules.hal.?) catch @panic("out of memory"); - } - - if (maybe_board) |brd| { - fw.modules.board = micro_build.createModule(.{ - .source_file = brd.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - fw.modules.microzig.dependencies.put("board", fw.modules.board.?) catch @panic("out of memory"); - } - - fw.modules.app = host_build.createModule(.{ - .source_file = options.source_file, - .dependencies = &.{ - .{ .name = "microzig", .module = fw.modules.microzig }, - }, - }); - - const umm = mz.dependency("umm-zig", .{}).module("umm"); - fw.modules.microzig.dependencies.put("umm", umm) catch @panic("out of memory"); - - fw.artifact.addModule("app", fw.modules.app); - fw.artifact.addModule("microzig", fw.modules.microzig); - - fw.artifact.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded - fw.artifact.single_threaded = options.single_threaded orelse fw.target.single_threaded; - fw.artifact.bundle_compiler_rt = options.bundle_compiler_rt orelse fw.target.bundle_compiler_rt; - - switch (linker_script) { - .generated => { - fw.artifact.setLinkerScript( - generateLinkerScript(host_build, chip.*) catch @panic("out of memory"), - ); - }, - - .source_file => |source| { - fw.artifact.setLinkerScriptPath(source); - }, - } - - if (options.target.configure) |configure| { - configure(host_build, fw); - } - - return fw; -} - -/// Configuration options for firmware installation. -pub const InstallFirmwareOptions = struct { - /// Overrides the output format for the binary. If not set, the standard preferred file format for the firmware target is used. - format: ?BinaryFormat = null, -}; - -/// Adds a new dependency to the `install` step that will install the `firmware` into the folder `$prefix/firmware`. -pub fn installFirmware( - /// The MicroZig instance that was used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that should perform installation. - b: *std.Build, - /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. - firmware: *Firmware, - /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. - options: InstallFirmwareOptions, -) void { - std.debug.assert(mz == firmware.mz); - const install_step = addInstallFirmware(mz, b, firmware, options); - b.getInstallStep().dependOn(&install_step.step); -} - -/// Creates a new `std.Build.Step.InstallFile` instance that will install the given firmware to `$prefix/firmware`. -/// -/// **NOTE:** This does not actually install the firmware yet. You have to add the returned step as a dependency to another step. -/// If you want to just install the firmware, use `installFirmware` instead! -pub fn addInstallFirmware( - /// The MicroZig instance that was used to create the firmware. - mz: *MicroZig, - /// The instance of the `build.zig` that should perform installation. - b: *std.Build, - /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. - firmware: *Firmware, - /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. - options: InstallFirmwareOptions, -) *std.Build.Step.InstallFile { - const format = firmware.resolveFormat(options.format); - - const basename = b.fmt("{s}{s}", .{ - firmware.artifact.name, - format.getExtension(), - }); - - _ = mz; - - return b.addInstallFileWithDir(firmware.getEmittedBin(format), .{ .custom = "firmware" }, basename); -} - -/// Declaration of a firmware build. -pub const Firmware = struct { - const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false); - - const Modules = struct { - app: *std.Build.Module, - cpu: *std.Build.Module, - chip: *std.Build.Module, - board: ?*std.Build.Module, - hal: ?*std.Build.Module, - microzig: *std.Build.Module, - }; - - // privates: - mz: *MicroZig, - host_build: *std.Build, - target: Target, - output_files: OutputFileMap, - - // publics: - - /// The artifact that is built by Zig. - artifact: *std.Build.Step.Compile, - - /// The options step that provides `microzig.config`. If you need custom configuration, you can add this here. - config: *std.Build.Step.Options, - - /// Declaration of the MicroZig modules used by this firmware. - modules: Modules, - - /// Path to the emitted elf file, if any. - emitted_elf: ?std.Build.LazyPath = null, - - /// Returns the emitted ELF file for this firmware. This is useful if you need debug information - /// or want to use a debugger like Segger, ST-Link or similar. - /// - /// **NOTE:** This is similar, but not equivalent to `std.Build.Step.Compile.getEmittedBin`. The call on the compile step does - /// not include post processing of the ELF files necessary by certain targets. - pub fn getEmittedElf(firmware: *Firmware) std.Build.LazyPath { - if (firmware.emitted_elf == null) { - const raw_elf = firmware.artifact.getEmittedBin(); - firmware.emitted_elf = if (firmware.target.binary_post_process) |binary_post_process| - binary_post_process(firmware.host_build, raw_elf) - else - raw_elf; - } - return firmware.emitted_elf.?; - } - - /// Returns the emitted binary for this firmware. The file is either in the preferred file format for - /// the target or in `format` if not null. - /// - /// **NOTE:** The file returned here is the same file that will be installed. - pub fn getEmittedBin(firmware: *Firmware, format: ?BinaryFormat) std.Build.LazyPath { - const actual_format = firmware.resolveFormat(format); - - const gop = firmware.output_files.getOrPut(actual_format) catch @panic("out of memory"); - if (!gop.found_existing) { - const elf_file = firmware.getEmittedElf(); - - const basename = firmware.host_build.fmt("{s}{s}", .{ - firmware.artifact.name, - actual_format.getExtension(), - }); - - gop.value_ptr.* = switch (actual_format) { - .elf => elf_file, - - .bin => blk: { - const objcopy = firmware.host_build.addObjCopy(elf_file, .{ - .basename = basename, - .format = .bin, - }); - - break :blk objcopy.getOutput(); - }, - - .hex => blk: { - const objcopy = firmware.host_build.addObjCopy(elf_file, .{ - .basename = basename, - .format = .hex, - }); - - break :blk objcopy.getOutput(); - }, - - .uf2 => |family_id| blk: { - const uf2_exe = firmware.mz.dependency("uf2", .{ .optimize = .ReleaseSafe }).artifact("elf2uf2"); - - const convert = firmware.host_build.addRunArtifact(uf2_exe); - - convert.addArg("--family-id"); - convert.addArg(firmware.host_build.fmt("0x{X:0>4}", .{@intFromEnum(family_id)})); - - convert.addArg("--elf-path"); - convert.addFileArg(elf_file); - - convert.addArg("--output-path"); - break :blk convert.addOutputFileArg(basename); - }, - - .dfu => buildConfigError(firmware.host_build, "DFU is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/145 for more details!", .{}), - .esp => buildConfigError(firmware.host_build, "ESP firmware image is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/146 for more details!", .{}), - - .custom => |generator| generator.convert(generator, elf_file), - }; - } - return gop.value_ptr.*; - } - - pub const AppDependencyOptions = struct { - depend_on_microzig: bool = false, - }; - - /// Adds a regular dependency to your application. - pub fn addAppDependency(fw: *Firmware, name: []const u8, module: *std.Build.Module, options: AppDependencyOptions) void { - if (options.depend_on_microzig) { - module.dependencies.put("microzig", fw.modules.microzig) catch @panic("OOM"); - } - fw.modules.app.dependencies.put(name, module) catch @panic("OOM"); - } - - pub fn addIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { - fw.artifact.addIncludePath(path); - } - - pub fn addSystemIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { - fw.artifact.addSystemIncludePath(path); - } - - pub fn addCSourceFile(fw: *Firmware, source: std.Build.Step.Compile.CSourceFile) void { - fw.artifact.addCSourceFile(source); - } - - pub fn addOptions(fw: *Firmware, module_name: []const u8, options: *std.Build.OptionsStep) void { - fw.artifact.addOptions(module_name, options); - fw.modules.app.dependencies.put( - module_name, - fw.host_build.createModule(.{ - .source_file = options.getOutput(), - }), - ) catch @panic("OOM"); - } - - pub fn addObjectFile(fw: *Firmware, source: std.Build.LazyPath) void { - fw.artifact.addObjectFile(source); - } - - fn resolveFormat(firmware: *Firmware, format: ?BinaryFormat) BinaryFormat { - if (format) |fmt| return fmt; - - if (firmware.target.preferred_format) |fmt| return fmt; - - buildConfigError(firmware.host_build, "{s} has no preferred output format, please provide one in the `format` option.", .{ - firmware.target.chip.name, - }); - } -}; +// /// The resulting binary format for the firmware file. +// /// A lot of embedded systems don't use plain ELF files, thus we provide means +// /// to convert the resulting ELF into other common formats. +// pub const BinaryFormat = union(enum) { +// /// [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), the standard output from the compiler. +// elf, + +// /// A flat binary, contains only the loaded portions of the firmware with an unspecified base offset. +// bin, + +// /// The [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format, contains +// /// an ASCII description of what memory to load where. +// hex, + +// /// A [Device Firmware Upgrade](https://www.usb.org/sites/default/files/DFU_1.1.pdf) file. +// dfu, + +// /// The [USB Flashing Format (UF2)](https://github.com/microsoft/uf2) designed by Microsoft. +// uf2: uf2.FamilyId, + +// /// The [firmware format](https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/firmware-image-format.html) used by the [esptool](https://github.com/espressif/esptool) bootloader. +// esp, + +// /// Custom option for non-standard formats. +// custom: *Custom, + +// /// Returns the standard extension for the resulting binary file. +// pub fn getExtension(format: BinaryFormat) []const u8 { +// return switch (format) { +// .elf => ".elf", +// .bin => ".bin", +// .hex => ".hex", +// .dfu => ".dfu", +// .uf2 => ".uf2", +// .esp => ".bin", + +// .custom => |c| c.extension, +// }; +// } + +// pub const Custom = struct { +// /// The standard extension of the format. +// extension: []const u8, + +// /// A function that will convert a given `elf` file into the custom output format. +// /// +// /// The `*Custom` format is passed so contextual information can be obtained by using +// /// `@fieldParentPtr` to provide access to tooling. +// convert: *const fn (*Custom, elf: std.Build.LazyPath) std.Build.LazyPath, +// }; + +// const Enum = std.meta.Tag(BinaryFormat); + +// const Context = struct { +// pub fn hash(self: @This(), fmt: BinaryFormat) u32 { +// _ = self; + +// var hasher = std.hash.XxHash32.init(0x1337_42_21); + +// hasher.update(@tagName(fmt)); + +// switch (fmt) { +// .elf, .bin, .hex, .dfu, .esp => |val| { +// if (@TypeOf(val) != void) @compileError("Missing update: Context.hash now requires special care!"); +// }, + +// .uf2 => |family_id| hasher.update(@tagName(family_id)), +// .custom => |custom| hasher.update(std.mem.asBytes(custom)), +// } + +// return hasher.final(); +// } + +// pub fn eql(self: @This(), fmt_a: BinaryFormat, fmt_b: BinaryFormat, index: usize) bool { +// _ = self; +// _ = index; +// if (@as(BinaryFormat.Enum, fmt_a) != @as(BinaryFormat.Enum, fmt_b)) +// return false; + +// return switch (fmt_a) { +// .elf, .bin, .hex, .dfu, .esp => |val| { +// if (@TypeOf(val) != void) @compileError("Missing update: Context.eql now requires special care!"); +// return true; +// }, + +// .uf2 => |a| (a == fmt_b.uf2), +// .custom => |a| (a == fmt_b.custom), +// }; +// } +// }; +// }; + +// /// The CPU model a target uses. +// /// +// /// The CPUs usually require special care on how to do interrupts, and getting an entry point. +// /// +// /// MicroZig officially only supports the CPUs listed here, but other CPUs might be provided +// /// via the `custom` field. +// pub const CpuModel = union(enum) { +// avr5, +// cortex_m0, +// cortex_m0plus, +// cortex_m3, +// cortex_m4, +// riscv32_imac, + +// custom: *const Cpu, + +// pub fn getDescriptor(model: CpuModel) *const Cpu { +// return switch (@as(std.meta.Tag(CpuModel), model)) { +// inline else => |tag| &@field(cpus, @tagName(tag)), +// .custom => model.custom, +// }; +// } +// }; + +// /// A cpu descriptor. +// pub const Cpu = struct { +// /// Display name of the CPU. +// name: []const u8, + +// /// Source file providing startup code and memory initialization routines. +// source_file: std.build.LazyPath, + +// /// The compiler target we use to compile all the code. +// target: std.zig.CrossTarget, +// }; + +// /// A descriptor for memory regions in a microcontroller. +// pub const MemoryRegion = struct { +// /// The type of the memory region for generating a proper linker script. +// kind: Kind, +// offset: u64, +// length: u64, + +// pub const Kind = union(enum) { +// /// This is a (normally) immutable memory region where the code is stored. +// flash, + +// /// This is a mutable memory region for data storage. +// ram, + +// /// This is a memory region that maps MMIO devices. +// io, + +// /// This is a memory region that exists, but is reserved and must not be used. +// reserved, + +// /// This is a memory region used for internal linking tasks required by the board support package. +// private: PrivateRegion, +// }; + +// pub const PrivateRegion = struct { +// /// The name of the memory region. Will not have an automatic numeric counter and must be unique. +// name: []const u8, + +// /// Is the memory region executable? +// executable: bool, + +// /// Is the memory region readable? +// readable: bool, + +// /// Is the memory region writable? +// writeable: bool, +// }; +// }; + +// /// Defines a custom microcontroller. +// pub const Chip = struct { +// /// The display name of the controller. +// name: []const u8, + +// /// (optional) link to the documentation/vendor page of the controller. +// url: ?[]const u8 = null, + +// /// The cpu model this controller uses. +// cpu: CpuModel, + +// /// The provider for register definitions. +// register_definition: union(enum) { +// /// Use `regz` to create a zig file from a JSON schema. +// json: std.Build.LazyPath, + +// /// Use `regz` to create a json file from a SVD schema. +// svd: std.Build.LazyPath, + +// /// Use `regz` to create a zig file from an ATDF schema. +// atdf: std.Build.LazyPath, + +// /// Use the provided file directly as the chip file. +// zig: std.Build.LazyPath, +// }, + +// /// The memory regions that are present in this chip. +// memory_regions: []const MemoryRegion, +// }; + +// /// Defines a hardware abstraction layer. +// pub const HardwareAbstractionLayer = struct { +// /// Root source file for this HAL. +// source_file: std.Build.LazyPath, +// }; + +// /// Provides a description of a board. +// /// +// /// Boards provide additional information to a chip and HAL package. +// /// For example, they can list attached peripherials, external crystal frequencies, +// /// flash sizes, ... +// pub const BoardDefinition = struct { +// /// Display name of the board +// name: []const u8, + +// /// (optional) link to the documentation/vendor page of the board. +// url: ?[]const u8 = null, + +// /// Provides the root file for the board definition. +// source_file: std.Build.LazyPath, +// }; + +// /// The linker script used to link the firmware. +// pub const LinkerScript = union(enum) { +// /// Auto-generated linker script derived from the memory regions of the chip. +// generated, + +// /// Externally defined linker script. +// source_file: std.build.LazyPath, +// }; + +// /// A compilation target for MicroZig. Provides information about the chip, +// /// hal, board and so on. +// /// +// /// This is used instead of `std.zig.CrossTarget` to define a MicroZig Firmware. +// pub const Target = struct { +// /// The preferred binary format of this MicroZig target. If `null`, the user must +// /// explicitly give the `.format` field during a call to `getEmittedBin()` or installation steps. +// preferred_format: ?BinaryFormat, + +// /// The chip this target uses, +// chip: Chip, + +// /// Usually, embedded projects are single-threaded and single-core applications. Platforms that +// /// support multiple CPUs should set this to `false`. +// single_threaded: bool = true, + +// /// Determines whether the compiler_rt package is bundled with the application or not. +// /// This should always be true except for platforms where compiler_rt cannot be built right now. +// bundle_compiler_rt: bool = true, + +// /// (optional) Provides a default hardware abstraction layer that is used. +// /// If `null`, no `microzig.hal` will be available. +// hal: ?HardwareAbstractionLayer = null, + +// /// (optional) Provides description of external hardware and connected devices +// /// like oscillators and such. +// /// +// /// This structure isn't used by MicroZig itself, but can be utilized from the HAL +// /// if present. +// board: ?BoardDefinition = null, + +// /// (optional) Provide a custom linker script for the hardware or define a custom generation. +// linker_script: LinkerScript = .generated, + +// /// (optional) Further configures the created firmware depending on the chip and/or board settings. +// /// This can be used to set/change additional properties on the created `*Firmware` object. +// configure: ?*const fn (host_build: *std.Build, *Firmware) void = null, + +// /// (optional) Post processing step that will patch up and modify the elf file if necessary. +// binary_post_process: ?*const fn (host_build: *std.Build, std.Build.LazyPath) std.Build.LazyPath = null, +// }; + +// /// Options to the `addFirmware` function. +// pub const FirmwareOptions = struct { +// /// The name of the firmware file. +// name: []const u8, + +// /// The MicroZig target that the firmware is built for. Either a board or a chip. +// target: Target, + +// /// The optimization level that should be used. Usually `ReleaseSmall` or `Debug` is a good choice. +// /// Also using `std.Build.standardOptimizeOption` is a good idea. +// optimize: std.builtin.OptimizeMode, + +// /// The root source file for the application. This is your `src/main.zig` file. +// source_file: std.Build.LazyPath, + +// // Overrides: + +// /// If set, overrides the `single_threaded` property of the target. +// single_threaded: ?bool = null, + +// /// If set, overrides the `bundle_compiler_rt` property of the target. +// bundle_compiler_rt: ?bool = null, + +// /// If set, overrides the `hal` property of the target. +// hal: ?HardwareAbstractionLayer = null, + +// /// If set, overrides the `board` property of the target. +// board: ?BoardDefinition = null, + +// /// If set, overrides the `linker_script` property of the target. +// linker_script: ?LinkerScript = null, +// }; + +// /// Declares a new MicroZig firmware file. +// pub fn addFirmware( +// /// The MicroZig instance that should be used to create the firmware. +// mz: *MicroZig, +// /// The instance of the `build.zig` that is calling this function. +// host_build: *std.Build, +// /// Options that define how the firmware is built. +// options: FirmwareOptions, +// ) *Firmware { +// const micro_build = mz.self.builder; + +// const chip = &options.target.chip; +// const cpu = chip.cpu.getDescriptor(); +// const maybe_hal = options.hal orelse options.target.hal; +// const maybe_board = options.board orelse options.target.board; + +// const linker_script = options.linker_script orelse options.target.linker_script; + +// // TODO: let the user override which ram section to use the stack on, +// // for now just using the first ram section in the memory region list +// const first_ram = blk: { +// for (chip.memory_regions) |region| { +// if (region.kind == .ram) +// break :blk region; +// } else @panic("no ram memory region found for setting the end-of-stack address"); +// }; + +// // On demand, generate chip definitions via regz: +// const chip_source = switch (chip.register_definition) { +// .json, .atdf, .svd => |file| blk: { +// const regz_exe = mz.dependency("regz", .{ .optimize = .ReleaseSafe }).artifact("regz"); + +// const regz_gen = host_build.addRunArtifact(regz_exe); + +// regz_gen.addArg("--schema"); // Explicitly set schema type, one of: svd, atdf, json +// regz_gen.addArg(@tagName(chip.register_definition)); + +// regz_gen.addArg("--output_path"); // Write to a file +// const zig_file = regz_gen.addOutputFileArg("chip.zig"); + +// regz_gen.addFileArg(file); + +// break :blk zig_file; +// }, + +// .zig => |src| src, +// }; + +// const config = host_build.addOptions(); +// config.addOption(bool, "has_hal", (maybe_hal != null)); +// config.addOption(bool, "has_board", (maybe_board != null)); + +// config.addOption(?[]const u8, "board_name", if (maybe_board) |brd| brd.name else null); + +// config.addOption([]const u8, "chip_name", chip.name); +// config.addOption([]const u8, "cpu_name", chip.name); +// config.addOption(usize, "end_of_stack", first_ram.offset + first_ram.length); + +// const fw: *Firmware = host_build.allocator.create(Firmware) catch @panic("out of memory"); +// fw.* = Firmware{ +// .mz = mz, +// .host_build = host_build, +// .artifact = host_build.addExecutable(.{ +// .name = options.name, +// .optimize = options.optimize, +// .target = cpu.target, +// .linkage = .static, +// .root_source_file = .{ .cwd_relative = mz.self.builder.pathFromRoot("src/start.zig") }, +// }), +// .target = options.target, +// .output_files = Firmware.OutputFileMap.init(host_build.allocator), + +// .config = config, + +// .modules = .{ +// .microzig = micro_build.createModule(.{ +// .source_file = .{ .cwd_relative = micro_build.pathFromRoot("src/microzig.zig") }, +// .dependencies = &.{ +// .{ +// .name = "config", +// .module = micro_build.createModule(.{ .source_file = config.getSource() }), +// }, +// }, +// }), + +// .cpu = undefined, +// .chip = undefined, + +// .board = null, +// .hal = null, + +// .app = undefined, +// }, +// }; +// errdefer fw.output_files.deinit(); + +// fw.modules.chip = micro_build.createModule(.{ +// .source_file = chip_source, +// .dependencies = &.{ +// .{ .name = "microzig", .module = fw.modules.microzig }, +// }, +// }); +// fw.modules.microzig.dependencies.put("chip", fw.modules.chip) catch @panic("out of memory"); + +// fw.modules.cpu = micro_build.createModule(.{ +// .source_file = cpu.source_file, +// .dependencies = &.{ +// .{ .name = "microzig", .module = fw.modules.microzig }, +// }, +// }); +// fw.modules.microzig.dependencies.put("cpu", fw.modules.cpu) catch @panic("out of memory"); + +// if (maybe_hal) |hal| { +// fw.modules.hal = micro_build.createModule(.{ +// .source_file = hal.source_file, +// .dependencies = &.{ +// .{ .name = "microzig", .module = fw.modules.microzig }, +// }, +// }); +// fw.modules.microzig.dependencies.put("hal", fw.modules.hal.?) catch @panic("out of memory"); +// } + +// if (maybe_board) |brd| { +// fw.modules.board = micro_build.createModule(.{ +// .source_file = brd.source_file, +// .dependencies = &.{ +// .{ .name = "microzig", .module = fw.modules.microzig }, +// }, +// }); +// fw.modules.microzig.dependencies.put("board", fw.modules.board.?) catch @panic("out of memory"); +// } + +// fw.modules.app = host_build.createModule(.{ +// .source_file = options.source_file, +// .dependencies = &.{ +// .{ .name = "microzig", .module = fw.modules.microzig }, +// }, +// }); + +// const umm = mz.dependency("umm-zig", .{}).module("umm"); +// fw.modules.microzig.dependencies.put("umm", umm) catch @panic("out of memory"); + +// fw.artifact.addModule("app", fw.modules.app); +// fw.artifact.addModule("microzig", fw.modules.microzig); + +// fw.artifact.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded +// fw.artifact.single_threaded = options.single_threaded orelse fw.target.single_threaded; +// fw.artifact.bundle_compiler_rt = options.bundle_compiler_rt orelse fw.target.bundle_compiler_rt; + +// switch (linker_script) { +// .generated => { +// fw.artifact.setLinkerScript( +// generateLinkerScript(host_build, chip.*) catch @panic("out of memory"), +// ); +// }, + +// .source_file => |source| { +// fw.artifact.setLinkerScriptPath(source); +// }, +// } + +// if (options.target.configure) |configure| { +// configure(host_build, fw); +// } + +// return fw; +// } + +// /// Configuration options for firmware installation. +// pub const InstallFirmwareOptions = struct { +// /// Overrides the output format for the binary. If not set, the standard preferred file format for the firmware target is used. +// format: ?BinaryFormat = null, +// }; + +// /// Adds a new dependency to the `install` step that will install the `firmware` into the folder `$prefix/firmware`. +// pub fn installFirmware( +// /// The MicroZig instance that was used to create the firmware. +// mz: *MicroZig, +// /// The instance of the `build.zig` that should perform installation. +// b: *std.Build, +// /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. +// firmware: *Firmware, +// /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. +// options: InstallFirmwareOptions, +// ) void { +// std.debug.assert(mz == firmware.mz); +// const install_step = addInstallFirmware(mz, b, firmware, options); +// b.getInstallStep().dependOn(&install_step.step); +// } + +// /// Creates a new `std.Build.Step.InstallFile` instance that will install the given firmware to `$prefix/firmware`. +// /// +// /// **NOTE:** This does not actually install the firmware yet. You have to add the returned step as a dependency to another step. +// /// If you want to just install the firmware, use `installFirmware` instead! +// pub fn addInstallFirmware( +// /// The MicroZig instance that was used to create the firmware. +// mz: *MicroZig, +// /// The instance of the `build.zig` that should perform installation. +// b: *std.Build, +// /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. +// firmware: *Firmware, +// /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. +// options: InstallFirmwareOptions, +// ) *std.Build.Step.InstallFile { +// const format = firmware.resolveFormat(options.format); + +// const basename = b.fmt("{s}{s}", .{ +// firmware.artifact.name, +// format.getExtension(), +// }); + +// _ = mz; + +// return b.addInstallFileWithDir(firmware.getEmittedBin(format), .{ .custom = "firmware" }, basename); +// } + +// /// Declaration of a firmware build. +// pub const Firmware = struct { +// const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false); + +// const Modules = struct { +// app: *std.Build.Module, +// cpu: *std.Build.Module, +// chip: *std.Build.Module, +// board: ?*std.Build.Module, +// hal: ?*std.Build.Module, +// microzig: *std.Build.Module, +// }; + +// // privates: +// mz: *MicroZig, +// host_build: *std.Build, +// target: Target, +// output_files: OutputFileMap, + +// // publics: + +// /// The artifact that is built by Zig. +// artifact: *std.Build.Step.Compile, + +// /// The options step that provides `microzig.config`. If you need custom configuration, you can add this here. +// config: *std.Build.Step.Options, + +// /// Declaration of the MicroZig modules used by this firmware. +// modules: Modules, + +// /// Path to the emitted elf file, if any. +// emitted_elf: ?std.Build.LazyPath = null, + +// /// Returns the emitted ELF file for this firmware. This is useful if you need debug information +// /// or want to use a debugger like Segger, ST-Link or similar. +// /// +// /// **NOTE:** This is similar, but not equivalent to `std.Build.Step.Compile.getEmittedBin`. The call on the compile step does +// /// not include post processing of the ELF files necessary by certain targets. +// pub fn getEmittedElf(firmware: *Firmware) std.Build.LazyPath { +// if (firmware.emitted_elf == null) { +// const raw_elf = firmware.artifact.getEmittedBin(); +// firmware.emitted_elf = if (firmware.target.binary_post_process) |binary_post_process| +// binary_post_process(firmware.host_build, raw_elf) +// else +// raw_elf; +// } +// return firmware.emitted_elf.?; +// } + +// /// Returns the emitted binary for this firmware. The file is either in the preferred file format for +// /// the target or in `format` if not null. +// /// +// /// **NOTE:** The file returned here is the same file that will be installed. +// pub fn getEmittedBin(firmware: *Firmware, format: ?BinaryFormat) std.Build.LazyPath { +// const actual_format = firmware.resolveFormat(format); + +// const gop = firmware.output_files.getOrPut(actual_format) catch @panic("out of memory"); +// if (!gop.found_existing) { +// const elf_file = firmware.getEmittedElf(); + +// const basename = firmware.host_build.fmt("{s}{s}", .{ +// firmware.artifact.name, +// actual_format.getExtension(), +// }); + +// gop.value_ptr.* = switch (actual_format) { +// .elf => elf_file, + +// .bin => blk: { +// const objcopy = firmware.host_build.addObjCopy(elf_file, .{ +// .basename = basename, +// .format = .bin, +// }); + +// break :blk objcopy.getOutput(); +// }, + +// .hex => blk: { +// const objcopy = firmware.host_build.addObjCopy(elf_file, .{ +// .basename = basename, +// .format = .hex, +// }); + +// break :blk objcopy.getOutput(); +// }, + +// .uf2 => |family_id| blk: { +// const uf2_exe = firmware.mz.dependency("uf2", .{ .optimize = .ReleaseSafe }).artifact("elf2uf2"); + +// const convert = firmware.host_build.addRunArtifact(uf2_exe); + +// convert.addArg("--family-id"); +// convert.addArg(firmware.host_build.fmt("0x{X:0>4}", .{@intFromEnum(family_id)})); + +// convert.addArg("--elf-path"); +// convert.addFileArg(elf_file); + +// convert.addArg("--output-path"); +// break :blk convert.addOutputFileArg(basename); +// }, + +// .dfu => buildConfigError(firmware.host_build, "DFU is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/145 for more details!", .{}), +// .esp => buildConfigError(firmware.host_build, "ESP firmware image is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/146 for more details!", .{}), + +// .custom => |generator| generator.convert(generator, elf_file), +// }; +// } +// return gop.value_ptr.*; +// } + +// pub const AppDependencyOptions = struct { +// depend_on_microzig: bool = false, +// }; + +// /// Adds a regular dependency to your application. +// pub fn addAppDependency(fw: *Firmware, name: []const u8, module: *std.Build.Module, options: AppDependencyOptions) void { +// if (options.depend_on_microzig) { +// module.dependencies.put("microzig", fw.modules.microzig) catch @panic("OOM"); +// } +// fw.modules.app.dependencies.put(name, module) catch @panic("OOM"); +// } + +// pub fn addIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { +// fw.artifact.addIncludePath(path); +// } + +// pub fn addSystemIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { +// fw.artifact.addSystemIncludePath(path); +// } + +// pub fn addCSourceFile(fw: *Firmware, source: std.Build.Step.Compile.CSourceFile) void { +// fw.artifact.addCSourceFile(source); +// } + +// pub fn addOptions(fw: *Firmware, module_name: []const u8, options: *std.Build.OptionsStep) void { +// fw.artifact.addOptions(module_name, options); +// fw.modules.app.dependencies.put( +// module_name, +// fw.host_build.createModule(.{ +// .source_file = options.getOutput(), +// }), +// ) catch @panic("OOM"); +// } + +// pub fn addObjectFile(fw: *Firmware, source: std.Build.LazyPath) void { +// fw.artifact.addObjectFile(source); +// } + +// fn resolveFormat(firmware: *Firmware, format: ?BinaryFormat) BinaryFormat { +// if (format) |fmt| return fmt; + +// if (firmware.target.preferred_format) |fmt| return fmt; + +// buildConfigError(firmware.host_build, "{s} has no preferred output format, please provide one in the `format` option.", .{ +// firmware.target.chip.name, +// }); +// } +// }; pub const cpus = struct { - pub const avr5 = Cpu{ + pub const avr5 = microbuild.Cpu{ .name = "AVR5", .source_file = .{ .path = build_root ++ "/src/cpus/avr5.zig" }, .target = std.zig.CrossTarget{ @@ -786,7 +788,7 @@ pub const cpus = struct { }, }; - pub const cortex_m0 = Cpu{ + pub const cortex_m0 = microbuild.Cpu{ .name = "ARM Cortex-M0", .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, .target = std.zig.CrossTarget{ @@ -797,7 +799,7 @@ pub const cpus = struct { }, }; - pub const cortex_m0plus = Cpu{ + pub const cortex_m0plus = microbuild.Cpu{ .name = "ARM Cortex-M0+", .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, .target = std.zig.CrossTarget{ @@ -808,7 +810,7 @@ pub const cpus = struct { }, }; - pub const cortex_m3 = Cpu{ + pub const cortex_m3 = microbuild.Cpu{ .name = "ARM Cortex-M3", .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, .target = std.zig.CrossTarget{ @@ -819,7 +821,7 @@ pub const cpus = struct { }, }; - pub const cortex_m4 = Cpu{ + pub const cortex_m4 = microbuild.Cpu{ .name = "ARM Cortex-M4", .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, .target = std.zig.CrossTarget{ @@ -830,7 +832,7 @@ pub const cpus = struct { }, }; - pub const riscv32_imac = Cpu{ + pub const riscv32_imac = microbuild.Cpu{ .name = "RISC-V 32-bit", .source_file = .{ .path = build_root ++ "/src/cpus/riscv32.zig" }, .target = std.zig.CrossTarget{ @@ -842,138 +844,138 @@ pub const cpus = struct { }; }; -fn buildConfigError(b: *std.Build, comptime fmt: []const u8, args: anytype) noreturn { - const msg = b.fmt(fmt, args); - @panic(msg); -} - -fn dependency(mz: *MicroZig, name: []const u8, args: anytype) *std.Build.Dependency { - return mz.self.builder.dependency(name, args); -} - -fn generateLinkerScript(b: *std.Build, chip: Chip) !std.Build.LazyPath { - const cpu = chip.cpu.getDescriptor(); - - var contents = std.ArrayList(u8).init(b.allocator); - const writer = contents.writer(); - try writer.print( - \\/* - \\ * This file was auto-generated by microzig - \\ * - \\ * Target CPU: {[cpu]s} - \\ * Target Chip: {[chip]s} - \\ */ - \\ - // This is not the "true" entry point, but there's no such thing on embedded platforms - // anyways. This is the logical entrypoint that should be invoked when - // stack, .data and .bss are set up and the CPU is ready to be used. - \\ENTRY(microzig_main); - \\ - \\ - , .{ - .cpu = cpu.name, - .chip = chip.name, - }); - - try writer.writeAll("MEMORY\n{\n"); - { - var counters = [4]usize{ 0, 0, 0, 0 }; - for (chip.memory_regions) |region| { - // flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k - - switch (region.kind) { - .flash => { - try writer.print(" flash{d} (rx!w)", .{counters[0]}); - counters[0] += 1; - }, - - .ram => { - try writer.print(" ram{d} (rw!x)", .{counters[1]}); - counters[1] += 1; - }, - - .io => { - try writer.print(" io{d} (rw!x)", .{counters[2]}); - counters[2] += 1; - }, - - .reserved => { - try writer.print(" reserved{d} (rw!x)", .{counters[3]}); - counters[3] += 1; - }, - - .private => |custom| { - try writer.print(" {s} (", .{custom.name}); - if (custom.readable) try writer.writeAll("r"); - if (custom.writeable) try writer.writeAll("w"); - if (custom.executable) try writer.writeAll("x"); - - if (!custom.readable or !custom.writeable or !custom.executable) { - try writer.writeAll("!"); - if (!custom.readable) try writer.writeAll("r"); - if (!custom.writeable) try writer.writeAll("w"); - if (!custom.executable) try writer.writeAll("x"); - } - try writer.writeAll(")"); - }, - } - try writer.print(" : ORIGIN = 0x{X:0>8}, LENGTH = 0x{X:0>8}\n", .{ region.offset, region.length }); - } - } - - try writer.writeAll("}\n\nSECTIONS\n{\n"); - { - try writer.writeAll( - \\ .text : - \\ { - \\ KEEP(*(microzig_flash_start)) - \\ *(.text*) - \\ } > flash0 - \\ - \\ - ); - - switch (cpu.target.getCpuArch()) { - .arm, .thumb => try writer.writeAll( - \\ .ARM.exidx : { - \\ *(.ARM.exidx* .gnu.linkonce.armexidx.*) - \\ } >flash0 - \\ - \\ - ), - else => {}, - } - - try writer.writeAll( - \\ .data : - \\ { - \\ microzig_data_start = .; - \\ *(.rodata*) - \\ *(.data*) - \\ microzig_data_end = .; - \\ } > ram0 AT> flash0 - \\ - \\ .bss (NOLOAD) : - \\ { - \\ microzig_bss_start = .; - \\ *(.bss*) - \\ microzig_bss_end = .; - \\ } > ram0 - \\ - \\ microzig_data_load_start = LOADADDR(.data); - \\ - ); - } - try writer.writeAll("}\n"); - - // TODO: Assert that the flash can actually hold all data! - // try writer.writeAll( - // \\ - // \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" ); - // \\ - // ); - - const write = b.addWriteFiles(); - - return write.add("linker.ld", contents.items); -} +// fn buildConfigError(b: *std.Build, comptime fmt: []const u8, args: anytype) noreturn { +// const msg = b.fmt(fmt, args); +// @panic(msg); +// } + +// fn dependency(mz: *MicroZig, name: []const u8, args: anytype) *std.Build.Dependency { +// return mz.self.builder.dependency(name, args); +// } + +// fn generateLinkerScript(b: *std.Build, chip: Chip) !std.Build.LazyPath { +// const cpu = chip.cpu.getDescriptor(); + +// var contents = std.ArrayList(u8).init(b.allocator); +// const writer = contents.writer(); +// try writer.print( +// \\/* +// \\ * This file was auto-generated by microzig +// \\ * +// \\ * Target CPU: {[cpu]s} +// \\ * Target Chip: {[chip]s} +// \\ */ +// \\ +// // This is not the "true" entry point, but there's no such thing on embedded platforms +// // anyways. This is the logical entrypoint that should be invoked when +// // stack, .data and .bss are set up and the CPU is ready to be used. +// \\ENTRY(microzig_main); +// \\ +// \\ +// , .{ +// .cpu = cpu.name, +// .chip = chip.name, +// }); + +// try writer.writeAll("MEMORY\n{\n"); +// { +// var counters = [4]usize{ 0, 0, 0, 0 }; +// for (chip.memory_regions) |region| { +// // flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k + +// switch (region.kind) { +// .flash => { +// try writer.print(" flash{d} (rx!w)", .{counters[0]}); +// counters[0] += 1; +// }, + +// .ram => { +// try writer.print(" ram{d} (rw!x)", .{counters[1]}); +// counters[1] += 1; +// }, + +// .io => { +// try writer.print(" io{d} (rw!x)", .{counters[2]}); +// counters[2] += 1; +// }, + +// .reserved => { +// try writer.print(" reserved{d} (rw!x)", .{counters[3]}); +// counters[3] += 1; +// }, + +// .private => |custom| { +// try writer.print(" {s} (", .{custom.name}); +// if (custom.readable) try writer.writeAll("r"); +// if (custom.writeable) try writer.writeAll("w"); +// if (custom.executable) try writer.writeAll("x"); + +// if (!custom.readable or !custom.writeable or !custom.executable) { +// try writer.writeAll("!"); +// if (!custom.readable) try writer.writeAll("r"); +// if (!custom.writeable) try writer.writeAll("w"); +// if (!custom.executable) try writer.writeAll("x"); +// } +// try writer.writeAll(")"); +// }, +// } +// try writer.print(" : ORIGIN = 0x{X:0>8}, LENGTH = 0x{X:0>8}\n", .{ region.offset, region.length }); +// } +// } + +// try writer.writeAll("}\n\nSECTIONS\n{\n"); +// { +// try writer.writeAll( +// \\ .text : +// \\ { +// \\ KEEP(*(microzig_flash_start)) +// \\ *(.text*) +// \\ } > flash0 +// \\ +// \\ +// ); + +// switch (cpu.target.getCpuArch()) { +// .arm, .thumb => try writer.writeAll( +// \\ .ARM.exidx : { +// \\ *(.ARM.exidx* .gnu.linkonce.armexidx.*) +// \\ } >flash0 +// \\ +// \\ +// ), +// else => {}, +// } + +// try writer.writeAll( +// \\ .data : +// \\ { +// \\ microzig_data_start = .; +// \\ *(.rodata*) +// \\ *(.data*) +// \\ microzig_data_end = .; +// \\ } > ram0 AT> flash0 +// \\ +// \\ .bss (NOLOAD) : +// \\ { +// \\ microzig_bss_start = .; +// \\ *(.bss*) +// \\ microzig_bss_end = .; +// \\ } > ram0 +// \\ +// \\ microzig_data_load_start = LOADADDR(.data); +// \\ +// ); +// } +// try writer.writeAll("}\n"); + +// // TODO: Assert that the flash can actually hold all data! +// // try writer.writeAll( +// // \\ +// // \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" ); +// // \\ +// // ); + +// const write = b.addWriteFiles(); + +// return write.add("linker.ld", contents.items); +// } diff --git a/examples/build.zig b/examples/build.zig new file mode 100644 index 0000000..bd3f69f --- /dev/null +++ b/examples/build.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const MicroZig = @import("../build/build.zig"); // "microzig-build" + +pub fn build(b: *std.Build) void { + const microzig = MicroZig.createBuildEnvironment(b, .{ + .self = "microzig", // package name of the build package (optional) + .core = "microzig-core", // package name of the core package (optional) + .board_support = &.{ + // package names for BSP packages: + "microzig-bsp-nxp", + "microzig-bsp-rp2040", + }, + }); + + const optimize = b.standardOptimizeOption(.{}); + const target_name = b.option([]const u8, "target", "Select the target to build for.") orelse "board:mbed/lpc1768"; + + const target = microzig.findTarget(target_name).?; + + const firmware = microzig.addFirmware(b, .{ + .name = "blinky", + .target = target, + .optimize = optimize, + .source_file = .{ .path = "src/empty.zig" }, + }); + + microzig.installFirmware(b, firmware, .{}); +} diff --git a/examples/modular/README.md b/examples/modular/README.md deleted file mode 100644 index 7909f7c..0000000 --- a/examples/modular/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Examples for the BSP `nxp-lpc` - -- [Blinky](src/blinky.zig) on [nRF52840 Dongle](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dongle) - TODO: Implement this! - diff --git a/examples/modular/build.zig b/examples/modular/build.zig deleted file mode 100644 index f58e4a8..0000000 --- a/examples/modular/build.zig +++ /dev/null @@ -1,33 +0,0 @@ -const std = @import("std"); -const microzig_build = @import("microzig-build"); -const lpc = @import("lpc"); - -pub fn build(b: *std.Build) void { - const microbuild = microzig_build.init( - b, - b.dependency("microzig", .{}), - ); - - const optimize = b.standardOptimizeOption(.{}); - - // `addFirmware` basically works like addExecutable, but takes a - // `microzig.Target` for target instead of a `std.zig.CrossTarget`. - // - // The target will convey all necessary information on the chip, - // cpu and potentially the board as well. - const firmware = microbuild.addFirmware(b, .{ - .name = "blinky", - .target = lpc.boards.mbed.lpc1768, - .optimize = optimize, - .source_file = .{ .path = "src/blinky.zig" }, - }); - - // `installFirmware()` is the MicroZig pendant to `Build.installArtifact()` - // and allows installing the firmware as a typical firmware file. - // - // This will also install into `$prefix/firmware` instead of `$prefix/bin`. - microbuild.installFirmware(b, firmware, .{}); - - // For debugging, we also always install the firmware as an ELF file - microbuild.installFirmware(b, firmware, .{ .format = .elf }); -} diff --git a/examples/modular/build.zig.zon b/examples/modular/build.zig.zon deleted file mode 100644 index aa5a11a..0000000 --- a/examples/modular/build.zig.zon +++ /dev/null @@ -1,14 +0,0 @@ -.{ - .name = "microzig-nxp-lpc-examples", - .version = "0.1.0", - .dependencies = .{ - .microzig = .{ - .url = "https://github.com/ZigEmbeddedGroup/microzig/archive/c6c9ec4516f57638e751141085c9d76120990312.tar.gz", - .hash = "1220af58bdaa721b8189f3a7adfda660517dd354463463388e96d69fe4ceccf80b92", - }, - .lpc = .{ - .url = "https://github.com/ZigEmbeddedGroup/nxp-lpc/archive/130a1316c0892415e7da958a5e9548ed87bba54d.tar.gz", - .hash = "1220165879f85a1d51656d35b3963a95f3585dc665fc7414f76aa6aad4e6635536cf", - }, - }, -} diff --git a/examples/modular/src/blinky.zig b/examples/modular/src/blinky.zig deleted file mode 100644 index 328d637..0000000 --- a/examples/modular/src/blinky.zig +++ /dev/null @@ -1,56 +0,0 @@ -const std = @import("std"); -const microzig = @import("microzig"); - -const chip = microzig.chip; - -// LED-1: P1.18 -// LED-2: P1.20 -// LED-3: P1.21 -// LED-4: P1.23 - -const conn = chip.peripherals.PINCONNECT; -const gpio: *volatile [5]PatchedGpio = @ptrCast(@alignCast(chip.peripherals.GPIO)); - -const led_mask = [4]u32{ - (1 << 18), - (1 << 20), - (1 << 21), - (1 << 23), -}; -const all_mask = led_mask[0] | led_mask[1] | led_mask[2] | led_mask[3]; - -pub fn main() !void { - conn.PINSEL3.modify(.{ - .P1_18 = .{ .value = .GPIO_P1 }, - .P1_20 = .{ .value = .GPIO_P1 }, - .P1_21 = .{ .value = .GPIO_P1 }, - .P1_23 = .{ .value = .GPIO_P1 }, - }); - - const p1 = &gpio[1]; - - p1.dir = all_mask; - - while (true) { - for (led_mask) |mask| { - p1.pin_clr = (all_mask & ~mask); - p1.pin_set = mask; - microzig.core.experimental.debug.busy_sleep(100_000); - } - } -} - -const PatchedGpio = extern struct { - dir: u32, // 0x2009 C000 - __padding0: u32, // 0x2009 C004 - __padding1: u32, // 0x2009 C008 - __padding2: u32, // 0x2009 C00C - mask: u32, // 0x2009 C010 - pin: u32, // 0x2009 C014 - pin_set: u32, // 0x2009 C018 - pin_clr: u32, // 0x2009 C01C - - comptime { - std.debug.assert(@sizeOf(PatchedGpio) == 0x20); - } -}; diff --git a/examples/next-gen/build.zig b/examples/next-gen/build.zig new file mode 100644 index 0000000..37628be --- /dev/null +++ b/examples/next-gen/build.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const MicroZig = @import("microzig"); + +pub fn build(b: *std.Build) void { + const microzig = MicroZig.createBuildEnvironment(b, .{ + .self = "microzig", // package name of the build package (optional) + .core = "microzig-core", // package name of the core package (optional) + .board_support = &.{ + // package names for BSP packages: + "microzig-bsp-nxp", + "microzig-bsp-rp2040", + }, + }); + + const optimize = b.standardOptimizeOption(.{}); + const target_name = b.option([]const u8, "target", "Select the target to build for.") orelse "board:mbed/lpc1768"; + + for (microzig.targets.keys()) |listed_target_name| { + std.debug.print("- '{s}'\n", .{listed_target_name}); + } + + const target = microzig.findTarget(target_name).?; + + const firmware = microzig.addFirmware(b, .{ + .name = "blinky", + .target = target, + .optimize = optimize, + .source_file = .{ .path = "src/empty.zig" }, + }); + + microzig.installFirmware(b, firmware, .{}); +} diff --git a/examples/next-gen/build.zig.zon b/examples/next-gen/build.zig.zon new file mode 100644 index 0000000..f9d6f77 --- /dev/null +++ b/examples/next-gen/build.zig.zon @@ -0,0 +1,22 @@ +.{ + .name = "microzig-nxp-lpc-examples", + .version = "0.1.0", + .dependencies = .{ + .microzig = .{ + .url = "https://public.devspace.random-projects.net/microzig-build.tar.gz", + .hash = "122068db50e2a071cac3fc5b42d5cd2213ccb563986af60b941cb7af29b83df65b02", + }, + .@"microzig-core" = .{ + .url = "https://public.devspace.random-projects.net/microzig-core.tar.gz", + .hash = "12202df033d2b967108b25b8e66d8c8abcf690348a24baa35474a28086170ff31e54", + }, + .@"microzig-bsp-nxp" = .{ + .url = "https://public.devspace.random-projects.net/board-support/nxp/lpc.tar.gz", + .hash = "12201efd8d8b992caef770c87d78e6bd00bd73051bd1559bc342703f2e83650ef2f9", + }, + .@"microzig-bsp-rp2040" = .{ + .url = "https://public.devspace.random-projects.net/board-support/raspberrypi/rp2040.tar.gz", + .hash = "1220154cd3634d0f3286cd2fb727cce9af3f3ab166772065e3efe4f9577128f4c559", + }, + }, +} \ No newline at end of file diff --git a/examples/next-gen/src/empty.zig b/examples/next-gen/src/empty.zig new file mode 100644 index 0000000..b292538 --- /dev/null +++ b/examples/next-gen/src/empty.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const microzig = @import("microzig"); + +pub fn main() !void { + while (true) { + asm volatile ("" ::: "memory"); + } +} diff --git a/tools/archive-info.zig b/tools/archive-info.zig index 33f8496..034be0d 100644 --- a/tools/archive-info.zig +++ b/tools/archive-info.zig @@ -46,7 +46,7 @@ pub fn main() !void { // defer decompress.deinit(); var arc = try Archive.read_from_tar(arena, buffered.reader(), .{ - .strip_components = 0, + .strip_components = 1, }); defer arc.deinit(arena); @@ -57,7 +57,23 @@ pub fn main() !void { try paths.appendSlice(arc.files.keys()); std.mem.sort([]const u8, paths.items, {}, Archive.path_less_than); - const calculated_hash = try arc.hash(allocator, .ignore_executable_bit); + const calculated_hash = blk: { + var arc_hasher = Hash.init(.{}); + + for (paths.items) |path| { + const archived_file = arc.files.getPtr(path).?; + + var file_hasher = Hash.init(.{}); + file_hasher.update(path); + file_hasher.update(&.{ 0, 0 }); // second part is "executable bit" + file_hasher.update(archived_file.text); + + arc_hasher.update(&file_hasher.finalResult()); + } + + break :blk arc_hasher.finalResult(); + }; + var hash_buf: [4 + 2 * calculated_hash.len]u8 = undefined; const hash_str = try std.fmt.bufPrint(&hash_buf, "1220{}", .{std.fmt.fmtSliceHexLower(&calculated_hash)}); @@ -92,13 +108,13 @@ const Archive = struct { return if (mod > 0) 512 - mod else 0; } - pub fn entry_should_be_skipped(path: []const u8) !bool { - var it = try std.fs.path.componentIterator(path); - const first = it.next().?; - return std.mem.eql(u8, first.name, ".git") or - std.mem.eql(u8, first.name, "zig-out") or - std.mem.eql(u8, first.name, "zig-cache"); - } + // pub fn entry_should_be_skipped(path: []const u8) !bool { + // var it = try std.fs.path.componentIterator(path); + // const first = it.next().?; + // return std.mem.eql(u8, first.name, ".git") or + // std.mem.eql(u8, first.name, "zig-out") or + // std.mem.eql(u8, first.name, "zig-cache"); + // } fn stripComponents(path: []const u8, count: u32) ![]const u8 { var i: usize = 0; @@ -240,35 +256,4 @@ const Archive = struct { return (mode & std.os.S.IXUSR) != 0; } } - - pub fn hash( - archive: Archive, - allocator: Allocator, - executable_bit: WhatToDoWithExecutableBit, - ) ![Hash.digest_length]u8 { - var paths = std.ArrayList([]const u8).init(allocator); - defer paths.deinit(); - - var hashes = std.ArrayList([Hash.digest_length]u8).init(allocator); - defer hashes.deinit(); - - try paths.appendSlice(archive.files.keys()); - try hashes.appendNTimes(undefined, paths.items.len); - std.mem.sort([]const u8, paths.items, {}, path_less_than); - - for (paths.items, hashes.items) |path, *result| { - const file = archive.files.get(path).?; - var hasher = Hash.init(.{}); - hasher.update(path); - hasher.update(&.{ 0, @intFromBool(is_executable(file.mode, executable_bit)) }); - hasher.update(file.text); - hasher.final(result); - } - - var hasher = Hash.init(.{}); - for (hashes.items) |file_hash| - hasher.update(&file_hash); - - return hasher.finalResult(); - } }; diff --git a/tools/bundle.py b/tools/bundle.py index 47761c1..e00cda0 100755 --- a/tools/bundle.py +++ b/tools/bundle.py @@ -16,6 +16,8 @@ from marshmallow import fields from enum import Enum as StrEnum import pathspec import stat +import tarfile + from marshmallow import fields as mm_fields from typing import Optional, Any @@ -26,7 +28,8 @@ REQUIRED_TOOLS = [ "zig", "git", ] -DEPLOYMENT_BASE="https://download.microzig.tech/packages" +# DEPLOYMENT_BASE="https://download.microzig.tech/packages" +DEPLOYMENT_BASE="https://public.devspace.random-projects.net" REPO_ROOT = Path(__file__).parent.parent assert REPO_ROOT.is_dir() @@ -300,13 +303,17 @@ def main(): pkg.package_dir = pkg_dir - if pkg.package_type == PackageType.core: + + if pkg.package_type == PackageType.build: pkg.out_rel_dir = PurePosixPath(".") pkg.out_basename = pkg.package_name - - elif pkg.package_type == PackageType.build: + + elif pkg.package_type == PackageType.core: pkg.out_rel_dir = PurePosixPath(".") pkg.out_basename = pkg.package_name + + # Implicit dependencies: + pkg.inner_dependencies.add("microzig-build") # core requires the build types elif pkg.package_type == PackageType.board_support: parsed_pkg_name = PurePosixPath(pkg.package_name) @@ -314,7 +321,9 @@ def main(): pkg.out_rel_dir = "board-support" / parsed_pkg_name.parent pkg.out_basename = parsed_pkg_name.name - pkg.inner_dependencies.add("microzig-core") # BSPs implicitly depend on the core "microzig" package + # Implicit dependencies: + pkg.inner_dependencies.add("microzig-build") # BSPs also require build types + pkg.inner_dependencies.add("microzig-core") # but also the core types (?) else: assert False @@ -373,7 +382,7 @@ def main(): pkg_cache_dir.mkdir(exist_ok=True) meta_path = pkg_dir / "microzig-package.json" - pkg_zon_file = pkg_cache_dir / "build.zig.zon" + pkg_zon_file = pkg_cache_dir / pkg_dir.name / "build.zig.zon" out_rel_dir: PurePosixPath = pkg.out_rel_dir out_basename: str = pkg.out_basename @@ -383,9 +392,9 @@ def main(): "zig", "build-exe", f"{REPO_ROOT}/tools/extract-bsp-info.zig" , "--cache-dir", f"{REPO_ROOT}/zig-cache", - "--deps", "bsp,microzig", - "--mod", f"bsp:microzig:{pkg_dir}/build.zig", - "--mod", f"microzig:uf2:{REPO_ROOT}/core/build.zig", + "--deps", "bsp,microzig-build", + "--mod", f"bsp:microzig-build:{pkg_dir}/build.zig", + "--mod", f"microzig-build:uf2:{REPO_ROOT}/build/build.zig", "--mod", f"uf2::{REPO_ROOT}/tools/lib/dummy_uf2.zig", "--name", "extract-bsp-info", cwd=pkg_cache_dir, @@ -440,7 +449,8 @@ def main(): print() # tar -cf "${out_tar}" $(git ls-files -- . ':!:microzig-package.json') - execute("tar", "-cf", out_file_tar, "--hard-dereference", *package_files, cwd=pkg_dir) + + execute("tar", "-cf", out_file_tar, "--hard-dereference", *( f"{pkg_dir.name}/{file}" for file in package_files), cwd=pkg_dir.parent) zon_data = slurp( tools["create_pkg_descriptor"], @@ -448,12 +458,14 @@ def main(): input=PackageConfigurationSchema.dumps(evaluation_ordered_packages, many=True ).encode(), ) + pkg_zon_file.parent.mkdir(exist_ok=True) + with pkg_zon_file.open("wb") as f: f.write(zon_data) slurp("zig", "fmt", pkg_zon_file) # slurp the message away - execute("tar", "-rf", out_file_tar, "--hard-dereference", pkg_zon_file.name, cwd=pkg_zon_file.parent) + execute("tar", "-rf", out_file_tar, "--hard-dereference", f"{pkg_zon_file.parent.name}/{pkg_zon_file.name}", cwd=pkg_zon_file.parent.parent) # tar --list --file "${out_tar}" > "${pkg_cache_dir}/contents.list" diff --git a/tools/create-pkg-descriptor.zig b/tools/create-pkg-descriptor.zig index d41abeb..70985d8 100644 --- a/tools/create-pkg-descriptor.zig +++ b/tools/create-pkg-descriptor.zig @@ -127,7 +127,7 @@ pub fn main() !void { try renderDep( stdout, - "microzig", + dep_name, dep.download_url.?, dep.package.?.hash, ); diff --git a/tools/extract-bsp-info.zig b/tools/extract-bsp-info.zig index 9d83f7d..b0f5627 100644 --- a/tools/extract-bsp-info.zig +++ b/tools/extract-bsp-info.zig @@ -5,7 +5,7 @@ const std = @import("std"); const bsp = @import("bsp"); -const microzig = @import("microzig"); +const microzig = @import("microzig-build"); const JsonTarget = struct { id: []const u8, diff --git a/tools/patch-build-zon.py b/tools/patch-build-zon.py new file mode 100755 index 0000000..85cc962 --- /dev/null +++ b/tools/patch-build-zon.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# +# Hacky auto-patcher to update dependencies in examples. +# +# Receives a **formatted(!)** build.zig.zon that requires ".url" and ".hash" to be written in that order on different lines. +# + +import sys +import json +from urllib.parse import urlparse, urlunparse +from urllib.request import urlopen +from pathlib import Path, PurePosixPath +import re + + +def main(): + + build_zig_zon = Path(sys.argv[1]) + assert build_zig_zon.is_file() + + input_lines = build_zig_zon.read_text().splitlines() + + output_lines = [] + + last_pkg_url: urllib.parse.ParseResult = None + for line in input_lines: + + stripped = line.strip() + if stripped.startswith(".url = \""): + + match = re.match('\s*\.url\s*=\s*"([^"]+)",', line) + + urlstr = match.group(1) + + last_pkg_url = urlparse(urlstr) + + output_lines.append(line) + + elif stripped.startswith(".hash = \""): + try: + pkg_path = PurePosixPath(last_pkg_url.path) + assert pkg_path.suffixes == ['.tar', '.gz'] + pkg_json_url = urlunparse( + # scheme, netloc, url, params, query, fragment + ( + last_pkg_url.scheme, # scheme + last_pkg_url.netloc, # netloc + pkg_path.with_suffix("").with_suffix(".json").as_posix(), # url + last_pkg_url.params, # params + last_pkg_url.query, # query + last_pkg_url.fragment, # fragment + ) + ) + + metadata = json.loads(urlopen(pkg_json_url).read()) + + pkg_hash = metadata["package"]["hash"] + + line_prefix = re.match("^(\s*)", line).group(1) + + output_lines.append(f'{line_prefix}.hash = "{pkg_hash}",') + last_pkg_url = None + + + except BaseException as ex: + print(ex) + output_lines.append(line) + else: + output_lines.append(line) + + build_zig_zon.write_text("\n".join(output_lines)) + +if __name__ == "__main__": + main() \ No newline at end of file