Makes examples/next-gen kinda build with experimental setup. Packages are now correct and microzig-build can determine all available targets and BSPs.

wch-ch32v003
Felix "xq" Queißner 8 months ago
parent 91fcc6b470
commit cadd5d1b0f

@ -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) - validate that the table on https://github.com/ZigEmbeddedGroup is correct (in CI)
- make system build again properly - make system build again properly
- start porting everything to 0.12/unstable - start porting everything to 0.12/unstable
- Try to get some autodocs to build.

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const MicroZig = @import("microzig-build");
fn path(comptime suffix: []const u8) std.Build.LazyPath { fn path(comptime suffix: []const u8) std.Build.LazyPath {
return .{ return .{
@ -7,7 +8,7 @@ fn path(comptime suffix: []const u8) std.Build.LazyPath {
} }
pub const chips = struct { pub const chips = struct {
pub const nrf52840 = .{ pub const nrf52840 = MicroZig.Target{
.preferred_format = .elf, .preferred_format = .elf,
.chip = .{ .chip = .{
.name = "nrf52840", .name = "nrf52840",
@ -29,7 +30,7 @@ pub const chips = struct {
}, },
}; };
pub const nrf52832 = .{ pub const nrf52832 = MicroZig.Target{
.preferred_format = .elf, .preferred_format = .elf,
.chip = .{ .chip = .{
.name = "nrf52", .name = "nrf52",
@ -48,7 +49,7 @@ pub const chips = struct {
pub const boards = struct { pub const boards = struct {
pub const nordic = struct { pub const nordic = struct {
pub const nRF52840_Dongle = .{ pub const nRF52840_Dongle = MicroZig.Target{
.preferred_format = .elf, .preferred_format = .elf,
.chip = chips.nrf52840.chip, .chip = chips.nrf52840.chip,
.board = .{ .board = .{

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const MicroZig = @import("microzig-build");
fn path(comptime suffix: []const u8) std.Build.LazyPath { fn path(comptime suffix: []const u8) std.Build.LazyPath {
return .{ return .{
@ -11,7 +12,7 @@ const hal = .{
}; };
pub const chips = struct { pub const chips = struct {
pub const lpc176x5x = .{ pub const lpc176x5x = MicroZig.Target{
.preferred_format = .elf, .preferred_format = .elf,
.chip = .{ .chip = .{
// TODO: Separate over those chips, this is not generic! // TODO: Separate over those chips, this is not generic!
@ -33,7 +34,7 @@ pub const chips = struct {
pub const boards = struct { pub const boards = struct {
pub const mbed = struct { pub const mbed = struct {
pub const lpc1768 = .{ pub const lpc1768 = MicroZig.Target{
.preferred_format = .hex, .preferred_format = .hex,
.chip = chips.lpc176x5x.chip, .chip = chips.lpc176x5x.chip,
.hal = hal, .hal = hal,

@ -1,5 +1,5 @@
const std = @import("std"); const std = @import("std");
const microzig = @import("microzig"); const microzig = @import("microzig-build");
fn root() []const u8 { fn root() []const u8 {
return comptime (std.fs.path.dirname(@src().file) orelse "."); return comptime (std.fs.path.dirname(@src().file) orelse ".");
@ -17,7 +17,7 @@ pub fn build(b: *std.Build) !void {
pub const chips = struct { pub const chips = struct {
// Note: This chip has no flash support defined and requires additional configuration! // Note: This chip has no flash support defined and requires additional configuration!
pub const rp2040 = .{ pub const rp2040 = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,
@ -28,7 +28,7 @@ pub const chips = struct {
pub const boards = struct { pub const boards = struct {
pub const raspberry_pi = struct { pub const raspberry_pi = struct {
pub const pico = .{ pub const pico = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,
@ -43,7 +43,7 @@ pub const boards = struct {
}; };
pub const waveshare = struct { pub const waveshare = struct {
pub const rp2040_plus_4m = .{ pub const rp2040_plus_4m = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,
@ -56,7 +56,7 @@ pub const boards = struct {
.configure = rp2040_configure(.w25q080), .configure = rp2040_configure(.w25q080),
}; };
pub const rp2040_plus_16m = .{ pub const rp2040_plus_16m = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,
@ -69,7 +69,7 @@ pub const boards = struct {
.configure = rp2040_configure(.w25q080), .configure = rp2040_configure(.w25q080),
}; };
pub const rp2040_eth = .{ pub const rp2040_eth = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,
@ -82,7 +82,7 @@ pub const boards = struct {
.configure = rp2040_configure(.w25q080), .configure = rp2040_configure(.w25q080),
}; };
pub const rp2040_matrix = .{ pub const rp2040_matrix = microzig.Target{
.preferred_format = .{ .uf2 = .RP2040 }, .preferred_format = .{ .uf2 = .RP2040 },
.chip = chip, .chip = chip,
.hal = hal, .hal = hal,

@ -1,5 +1,5 @@
const std = @import("std"); const std = @import("std");
const microzig = @import("microzig"); const microzig = @import("microzig-build");
fn root() []const u8 { fn root() []const u8 {
return comptime (std.fs.path.dirname(@src().file) orelse "."); return comptime (std.fs.path.dirname(@src().file) orelse ".");

@ -1,7 +1,10 @@
const std = @import("std"); const std = @import("std");
// const examples = @import("examples/build.zig");
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
buildTools(b); buildTools(b);
// examples.build(b);
} }
fn buildTools(b: *std.Build) void { fn buildTools(b: *std.Build) void {

@ -6,5 +6,13 @@
.url = "https://github.com/ziglibs/eggzon/archive/refs/heads/master.tar.gz", .url = "https://github.com/ziglibs/eggzon/archive/refs/heads/master.tar.gz",
.hash = "1220cd5cec7e9d4911074a9b2dec2dabef76e1adf94d041bca068163ce7666c4be47", .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",
},
}, },
} }

@ -5,6 +5,342 @@
const std = @import("std"); const std = @import("std");
const uf2 = @import("uf2"); 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 // // MicroZig Gen 2 Interface //
//////////////////////////////////////// ////////////////////////////////////////
@ -372,7 +708,7 @@ pub const FirmwareOptions = struct {
name: []const u8, name: []const u8,
/// The MicroZig target that the firmware is built for. Either a board or a chip. /// 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. /// The optimization level that should be used. Usually `ReleaseSmall` or `Debug` is a good choice.
/// Also using `std.Build.standardOptimizeOption` is a good idea. /// Also using `std.Build.standardOptimizeOption` is a good idea.
@ -399,222 +735,12 @@ pub const FirmwareOptions = struct {
linker_script: ?LinkerScript = null, 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. /// Configuration options for firmware installation.
pub const InstallFirmwareOptions = struct { 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. /// Overrides the output format for the binary. If not set, the standard preferred file format for the firmware target is used.
format: ?BinaryFormat = null, 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. /// Declaration of a firmware build.
pub const Firmware = struct { pub const Firmware = struct {
const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false); const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false);
@ -629,9 +755,9 @@ pub const Firmware = struct {
}; };
// privates: // privates:
mz: *MicroZig, env: *BuildEnvironment,
host_build: *std.Build, host_build: *std.Build,
target: Target, target: *const Target,
output_files: OutputFileMap, output_files: OutputFileMap,
// publics: // publics:
@ -702,7 +828,7 @@ pub const Firmware = struct {
}, },
.uf2 => |family_id| blk: { .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); 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); @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 { fn generateLinkerScript(b: *std.Build, chip: Chip) !std.Build.LazyPath {
const cpu = chip.cpu.getDescriptor(); const cpu = chip.cpu.getDescriptor();

File diff suppressed because it is too large Load Diff

@ -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, .{});
}

@ -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!

@ -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 });
}

@ -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",
},
},
}

@ -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);
}
};

@ -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, .{});
}

@ -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",
},
},
}

@ -0,0 +1,8 @@
const std = @import("std");
const microzig = @import("microzig");
pub fn main() !void {
while (true) {
asm volatile ("" ::: "memory");
}
}

@ -46,7 +46,7 @@ pub fn main() !void {
// defer decompress.deinit(); // defer decompress.deinit();
var arc = try Archive.read_from_tar(arena, buffered.reader(), .{ var arc = try Archive.read_from_tar(arena, buffered.reader(), .{
.strip_components = 0, .strip_components = 1,
}); });
defer arc.deinit(arena); defer arc.deinit(arena);
@ -57,7 +57,23 @@ pub fn main() !void {
try paths.appendSlice(arc.files.keys()); try paths.appendSlice(arc.files.keys());
std.mem.sort([]const u8, paths.items, {}, Archive.path_less_than); 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; 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)}); 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; return if (mod > 0) 512 - mod else 0;
} }
pub fn entry_should_be_skipped(path: []const u8) !bool { // pub fn entry_should_be_skipped(path: []const u8) !bool {
var it = try std.fs.path.componentIterator(path); // var it = try std.fs.path.componentIterator(path);
const first = it.next().?; // const first = it.next().?;
return std.mem.eql(u8, first.name, ".git") or // 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-out") or
std.mem.eql(u8, first.name, "zig-cache"); // std.mem.eql(u8, first.name, "zig-cache");
} // }
fn stripComponents(path: []const u8, count: u32) ![]const u8 { fn stripComponents(path: []const u8, count: u32) ![]const u8 {
var i: usize = 0; var i: usize = 0;
@ -240,35 +256,4 @@ const Archive = struct {
return (mode & std.os.S.IXUSR) != 0; 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();
}
}; };

@ -16,6 +16,8 @@ from marshmallow import fields
from enum import Enum as StrEnum from enum import Enum as StrEnum
import pathspec import pathspec
import stat import stat
import tarfile
from marshmallow import fields as mm_fields from marshmallow import fields as mm_fields
from typing import Optional, Any from typing import Optional, Any
@ -26,7 +28,8 @@ REQUIRED_TOOLS = [
"zig", "zig",
"git", "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 REPO_ROOT = Path(__file__).parent.parent
assert REPO_ROOT.is_dir() assert REPO_ROOT.is_dir()
@ -300,21 +303,27 @@ def main():
pkg.package_dir = pkg_dir pkg.package_dir = pkg_dir
if pkg.package_type == PackageType.core:
if pkg.package_type == PackageType.build:
pkg.out_rel_dir = PurePosixPath(".") pkg.out_rel_dir = PurePosixPath(".")
pkg.out_basename = pkg.package_name 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_rel_dir = PurePosixPath(".")
pkg.out_basename = pkg.package_name 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: elif pkg.package_type == PackageType.board_support:
parsed_pkg_name = PurePosixPath(pkg.package_name) parsed_pkg_name = PurePosixPath(pkg.package_name)
pkg.out_rel_dir = "board-support" / parsed_pkg_name.parent pkg.out_rel_dir = "board-support" / parsed_pkg_name.parent
pkg.out_basename = parsed_pkg_name.name 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: else:
assert False assert False
@ -373,7 +382,7 @@ def main():
pkg_cache_dir.mkdir(exist_ok=True) pkg_cache_dir.mkdir(exist_ok=True)
meta_path = pkg_dir / "microzig-package.json" 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_rel_dir: PurePosixPath = pkg.out_rel_dir
out_basename: str = pkg.out_basename out_basename: str = pkg.out_basename
@ -383,9 +392,9 @@ def main():
"zig", "build-exe", "zig", "build-exe",
f"{REPO_ROOT}/tools/extract-bsp-info.zig" , f"{REPO_ROOT}/tools/extract-bsp-info.zig" ,
"--cache-dir", f"{REPO_ROOT}/zig-cache", "--cache-dir", f"{REPO_ROOT}/zig-cache",
"--deps", "bsp,microzig", "--deps", "bsp,microzig-build",
"--mod", f"bsp:microzig:{pkg_dir}/build.zig", "--mod", f"bsp:microzig-build:{pkg_dir}/build.zig",
"--mod", f"microzig:uf2:{REPO_ROOT}/core/build.zig", "--mod", f"microzig-build:uf2:{REPO_ROOT}/build/build.zig",
"--mod", f"uf2::{REPO_ROOT}/tools/lib/dummy_uf2.zig", "--mod", f"uf2::{REPO_ROOT}/tools/lib/dummy_uf2.zig",
"--name", "extract-bsp-info", "--name", "extract-bsp-info",
cwd=pkg_cache_dir, cwd=pkg_cache_dir,
@ -440,7 +449,8 @@ def main():
print() print()
# tar -cf "${out_tar}" $(git ls-files -- . ':!:microzig-package.json') # 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( zon_data = slurp(
tools["create_pkg_descriptor"], tools["create_pkg_descriptor"],
@ -448,12 +458,14 @@ def main():
input=PackageConfigurationSchema.dumps(evaluation_ordered_packages, many=True ).encode(), 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: with pkg_zon_file.open("wb") as f:
f.write(zon_data) f.write(zon_data)
slurp("zig", "fmt", pkg_zon_file) # slurp the message away 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" # tar --list --file "${out_tar}" > "${pkg_cache_dir}/contents.list"

@ -127,7 +127,7 @@ pub fn main() !void {
try renderDep( try renderDep(
stdout, stdout,
"microzig", dep_name,
dep.download_url.?, dep.download_url.?,
dep.package.?.hash, dep.package.?.hash,
); );

@ -5,7 +5,7 @@
const std = @import("std"); const std = @import("std");
const bsp = @import("bsp"); const bsp = @import("bsp");
const microzig = @import("microzig"); const microzig = @import("microzig-build");
const JsonTarget = struct { const JsonTarget = struct {
id: []const u8, id: []const u8,

@ -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()
Loading…
Cancel
Save