From cd54dc0589e0363b1dabcc7c7478527bdb2bf2f3 Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Thu, 22 Feb 2024 17:33:42 +1100 Subject: [PATCH] Restructuring the Zig build system This gives libvmm a top-level build.zig which means that projects that depend on libvmm can simply add libvmm as a dependency in their build.zig.zon file and do not have to figure out how to build libvmm.a. This same approach is used by some of the examples. --- build.zig | 92 +++++++++++++++++++++++++++++++++++ build.zig.zon | 13 +++++ examples/simple/build.zig | 92 ++++++++++------------------------- examples/simple/build.zig.zon | 14 ++++++ examples/zig/build.zig | 69 ++++++-------------------- 5 files changed, 158 insertions(+), 122 deletions(-) create mode 100644 build.zig create mode 100644 build.zig.zon create mode 100644 examples/simple/build.zig.zon diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..9ccd745ad --- /dev/null +++ b/build.zig @@ -0,0 +1,92 @@ +const std = @import("std"); + +const src = [_][]const u8{ + "src/guest.c", + "src/util/util.c", + "src/util/printf.c", +}; + +const src_aarch64 = [_][]const u8{ + "src/arch/aarch64/vgic/vgic.c", + "src/arch/aarch64/vgic/vgic_v2.c", + "src/arch/aarch64/fault.c", + "src/arch/aarch64/psci.c", + "src/arch/aarch64/smc.c", + "src/arch/aarch64/virq.c", + "src/arch/aarch64/linux.c", + "src/arch/aarch64/tcb.c", + "src/arch/aarch64/vcpu.c", +}; + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + const target = b.standardTargetOptions(.{}); + + // Getting the path to the Microkit SDK before doing anything else + const microkit_sdk_arg = b.option([]const u8, "sdk", "Path to Microkit SDK"); + if (microkit_sdk_arg == null) { + std.log.err("Missing -Dsdk=/path/to/sdk argument being passed\n", .{}); + std.os.exit(1); + } + const microkit_sdk = microkit_sdk_arg.?; + std.fs.cwd().access(microkit_sdk, .{}) catch |err| { + switch (err) { + error.FileNotFound => std.log.err("Path to SDK '{s}' does not exist\n", .{ microkit_sdk }), + else => std.log.err("Could not acccess SDK path '{s}', error is {any}\n", .{ microkit_sdk, err }) + } + std.os.exit(1); + }; + + const microkit_config = b.option([]const u8, "config", "Microkit config to build for") orelse "debug"; + const microkit_board_arg = b.option([]const u8, "board", "Microkit board to target"); + + if (microkit_board_arg == null) { + std.log.err("Missing -Dboard= argument being passed\n", .{}); + std.os.exit(1); + } + const microkit_board = microkit_board_arg.?; + + const microkit_board_dir = b.fmt("{s}/board/{s}/{s}", .{ microkit_sdk, microkit_board, microkit_config }); + std.fs.cwd().access(microkit_board_dir, .{}) catch |err| { + switch (err) { + error.FileNotFound => std.log.err("Path to '{s}' does not exist\n", .{ microkit_board_dir }), + else => std.log.err("Could not acccess path '{s}', error is {any}\n", .{ microkit_board_dir, err }) + } + std.os.exit(1); + }; + const libmicrokit_include = b.fmt("{s}/include", .{ microkit_board_dir }); + + const libvmm = b.addStaticLibrary(.{ + .name = "vmm", + .target = target, + .optimize = optimize, + }); + + const src_arch = switch (target.result.cpu.arch) { + .aarch64 => src_aarch64, + else => { + std.log.err("Unsupported libvmm architecture given '{s}'", .{ @tagName(target.result.cpu.arch) }); + std.os.exit(1); + } + }; + libvmm.addCSourceFiles(.{ + .files = &(src ++ src_arch), + .flags = &.{ + "-Wall", + "-Werror", + "-Wno-unused-function", + "-mstrict-align", + "-fno-sanitize=undefined", // @ivanv: ideally we wouldn't have to turn off UBSAN + b.fmt("-DBOARD_{s}", .{ microkit_board }) // @ivanv: shouldn't be needed as the library should not depend on the board + } + }); + + // @ivanv: fix all of our libvmm includes! This is a mess! + libvmm.addIncludePath(.{ .path = "src" }); + libvmm.addIncludePath(.{ .path = "src/util/" }); + libvmm.addIncludePath(.{ .path = "src/arch/aarch64" }); + libvmm.addIncludePath(.{ .path = "src/arch/aarch64/vgic/" }); + libvmm.addIncludePath(.{ .path = libmicrokit_include }); + + b.installArtifact(libvmm); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 000000000..e82c2fef0 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,13 @@ +.{ + .name = "libvmm", + .version = "0.1.0", + + .paths = .{ + "LICENSE", + "README.md", + "build.zig", + "build.zig.zon", + "src", + "tools", + } +} diff --git a/examples/simple/build.zig b/examples/simple/build.zig index 85dfa8803..912a29f6f 100644 --- a/examples/simple/build.zig +++ b/examples/simple/build.zig @@ -1,12 +1,5 @@ const std = @import("std"); -var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; -const gpa = general_purpose_allocator.allocator(); - -fn fmtPrint(comptime fmt: []const u8, args: anytype) []const u8 { - return std.fmt.allocPrint(gpa, fmt, args) catch "Could not format print!"; -} - const MicrokitBoard = enum { qemu_arm_virt, odroidc4 @@ -56,7 +49,6 @@ const ConfigOptions = enum { }; pub fn build(b: *std.Build) void { - // @ivanv: need to somehow relate Microkit config with this optimisation level? const optimize = b.standardOptimizeOption(.{}); // Getting the path to the Microkit SDK before doing anything else @@ -82,55 +74,20 @@ pub fn build(b: *std.Build) void { // Since we are relying on Zig to produce the final ELF, it needs to do the // linking step as well. - const microkit_board_dir = fmtPrint("{s}/board/{s}/{s}", .{ microkit_sdk, microkit_board, microkit_config }); - const microkit_tool = fmtPrint("{s}/bin/microkit", .{ microkit_sdk }); - const libmicrokit = fmtPrint("{s}/lib/libmicrokit.a", .{ microkit_board_dir }); - const libmicrokit_linker_script = fmtPrint("{s}/lib/microkit.ld", .{ microkit_board_dir }); - const sdk_board_include_dir = fmtPrint("{s}/include", .{ microkit_board_dir }); - - const libvmm = b.addStaticLibrary(.{ - .name = "vmm", + const microkit_board_dir = b.fmt("{s}/board/{s}/{s}", .{ microkit_sdk, microkit_board, microkit_config }); + const microkit_tool = b.fmt("{s}/bin/microkit", .{ microkit_sdk }); + const libmicrokit = b.fmt("{s}/lib/libmicrokit.a", .{ microkit_board_dir }); + const libmicrokit_linker_script = b.fmt("{s}/lib/microkit.ld", .{ microkit_board_dir }); + const sdk_board_include_dir = b.fmt("{s}/include", .{ microkit_board_dir }); + + const libvmm_dep = b.dependency("libvmm", .{ .target = target, .optimize = optimize, + .sdk = microkit_sdk, + .config = @as([]const u8, microkit_config), + .board = @as([]const u8, microkit_board), }); - - const libvmm_path = "../.."; - const libvmm_tools = libvmm_path ++ "/tools/"; - const libvmm_src = libvmm_path ++ "/src/"; - // Right now we only support AArch64 so this is a safe assumption. - const libvmm_src_arch = libvmm_src ++ "arch/aarch64/"; - libvmm.addCSourceFiles(.{ - .files = &.{ - libvmm_src ++ "guest.c", - libvmm_src ++ "util/util.c", - libvmm_src ++ "util/printf.c", - libvmm_src_arch ++ "vgic/vgic.c", - libvmm_src_arch ++ "vgic/vgic_v2.c", - libvmm_src_arch ++ "fault.c", - libvmm_src_arch ++ "psci.c", - libvmm_src_arch ++ "smc.c", - libvmm_src_arch ++ "virq.c", - libvmm_src_arch ++ "linux.c", - libvmm_src_arch ++ "tcb.c", - libvmm_src_arch ++ "vcpu.c", - }, - .flags = &.{ - "-Wall", - "-Werror", - "-Wno-unused-function", - "-mstrict-align", - "-fno-sanitize=undefined", // @ivanv: ideally we wouldn't have to turn off UBSAN - fmtPrint("-DBOARD_{s}", .{ microkit_board }) // @ivanv: shouldn't be needed as the library should not depend on the board - } - }); - - libvmm.addIncludePath(.{ .path = libvmm_src }); - libvmm.addIncludePath(.{ .path = libvmm_src ++ "util/" }); - libvmm.addIncludePath(.{ .path = libvmm_src_arch }); - libvmm.addIncludePath(.{ .path = libvmm_src_arch ++ "vgic/" }); - libvmm.addIncludePath(.{ .path = sdk_board_include_dir }); - - b.installArtifact(libvmm); + const libvmm = libvmm_dep.artifact("vmm"); const exe = b.addExecutable(.{ .name = "vmm.elf", @@ -143,7 +100,7 @@ pub fn build(b: *std.Build) void { }); // For actually compiling the DTS into a DTB - const dts_path = fmtPrint("board/{s}/linux.dts", .{ microkit_board }); + const dts_path = b.fmt("board/{s}/linux.dts", .{ microkit_board }); const dtc_cmd = b.addSystemCommand(&[_][]const u8{ "dtc", "-q", "-I", "dts", "-O", "dtb" }); @@ -152,8 +109,9 @@ pub fn build(b: *std.Build) void { // Add microkit.h to be used by the API wrapper. exe.addIncludePath(.{ .path = sdk_board_include_dir }); - exe.addIncludePath(.{ .path = libvmm_src }); - exe.addIncludePath(.{ .path = libvmm_src_arch }); + exe.addIncludePath(libvmm_dep.path("src")); + // @ivanv: shouldn't need to do this! fix our includes + exe.addIncludePath(libvmm_dep.path("src/arch/aarch64")); // Add the static library that provides each protection domain's entry // point (`main()`), which runs the main handler loop. exe.addObjectFile(.{ .path = libmicrokit }); @@ -168,7 +126,7 @@ pub fn build(b: *std.Build) void { "-Werror", "-Wno-unused-function", "-mstrict-align", - fmtPrint("-DBOARD_{s}", .{ microkit_board }) + b.fmt("-DBOARD_{s}", .{ microkit_board }) } }); @@ -180,14 +138,14 @@ pub fn build(b: *std.Build) void { // We need to produce the DTB from the DTS before doing anything to produce guest_images guest_images.step.dependOn(&b.addInstallFileWithDir(dtb, .prefix, "linux.dtb").step); - const linux_image_path = fmtPrint("board/{s}/linux", .{ microkit_board }); - const kernel_image_arg = fmtPrint("-DGUEST_KERNEL_IMAGE_PATH=\"{s}\"", .{ linux_image_path }); + const linux_image_path = b.fmt("board/{s}/linux", .{ microkit_board }); + const kernel_image_arg = b.fmt("-DGUEST_KERNEL_IMAGE_PATH=\"{s}\"", .{ linux_image_path }); - const initrd_image_path = fmtPrint("board/{s}/rootfs.cpio.gz", .{ microkit_board }); - const initrd_image_arg = fmtPrint("-DGUEST_INITRD_IMAGE_PATH=\"{s}\"", .{ initrd_image_path }); - const dtb_image_arg = fmtPrint("-DGUEST_DTB_IMAGE_PATH=\"{s}\"", .{ b.getInstallPath(.prefix, "linux.dtb") }); - guest_images.addCSourceFiles(.{ - .files = &.{ libvmm_tools ++ "package_guest_images.S" }, + const initrd_image_path = b.fmt("board/{s}/rootfs.cpio.gz", .{ microkit_board }); + const initrd_image_arg = b.fmt("-DGUEST_INITRD_IMAGE_PATH=\"{s}\"", .{ initrd_image_path }); + const dtb_image_arg = b.fmt("-DGUEST_DTB_IMAGE_PATH=\"{s}\"", .{ b.getInstallPath(.prefix, "linux.dtb") }); + guest_images.addCSourceFile(.{ + .file = libvmm_dep.path("tools/package_guest_images.S"), .flags = &.{ kernel_image_arg, dtb_image_arg, @@ -200,7 +158,7 @@ pub fn build(b: *std.Build) void { exe.addObject(guest_images); b.installArtifact(exe); - const system_description_path = fmtPrint("board/{s}/simple.system", .{ microkit_board }); + const system_description_path = b.fmt("board/{s}/simple.system", .{ microkit_board }); const final_image_dest = b.getInstallPath(.bin, "./loader.img"); const microkit_tool_cmd = b.addSystemCommand(&[_][]const u8{ microkit_tool, @@ -224,7 +182,7 @@ pub fn build(b: *std.Build) void { // This is setting up a `qemu` command for running the system using QEMU, // which we only want to do when we have a board that we can actually simulate. - const loader_arg = fmtPrint("loader,file={s},addr=0x70000000,cpu-num=0", .{ final_image_dest }); + const loader_arg = b.fmt("loader,file={s},addr=0x70000000,cpu-num=0", .{ final_image_dest }); if (std.mem.eql(u8, microkit_board, "qemu_arm_virt")) { const qemu_cmd = b.addSystemCommand(&[_][]const u8{ "qemu-system-aarch64", diff --git a/examples/simple/build.zig.zon b/examples/simple/build.zig.zon new file mode 100644 index 000000000..41f53c65c --- /dev/null +++ b/examples/simple/build.zig.zon @@ -0,0 +1,14 @@ +.{ + .name = "simple", + .version = "1.0.0", + + .dependencies = .{ + .libvmm = .{ + .path = "../../" + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + } +} diff --git a/examples/zig/build.zig b/examples/zig/build.zig index 46faf8942..87eba16ae 100644 --- a/examples/zig/build.zig +++ b/examples/zig/build.zig @@ -1,12 +1,5 @@ const std = @import("std"); -var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; -const gpa = general_purpose_allocator.allocator(); - -fn fmtPrint(comptime fmt: []const u8, args: anytype) []const u8 { - return std.fmt.allocPrint(gpa, fmt, args) catch "Could not format print!"; -} - const ConfigOptions = enum { debug, release, @@ -38,11 +31,11 @@ pub fn build(b: *std.Build) void { const microkit_config = @tagName(microkit_config_option); // Since we are relying on Zig to produce the final ELF, it needs to do the // linking step as well. - const microkit_board_dir = fmtPrint("{s}/board/{s}/{s}", .{ microkit_sdk, microkit_board, microkit_config }); - const microkit_tool = fmtPrint("{s}/bin/microkit", .{ microkit_sdk }); - const libmicrokit = fmtPrint("{s}/lib/libmicrokit.a", .{ microkit_board_dir }); - const libmicrokit_linker_script = fmtPrint("{s}/lib/microkit.ld", .{ microkit_board_dir }); - const sdk_board_include_dir = fmtPrint("{s}/include", .{ microkit_board_dir }); + const microkit_board_dir = b.fmt("{s}/board/{s}/{s}", .{ microkit_sdk, microkit_board, microkit_config }); + const microkit_tool = b.fmt("{s}/bin/microkit", .{ microkit_sdk }); + const libmicrokit = b.fmt("{s}/lib/libmicrokit.a", .{ microkit_board_dir }); + const libmicrokit_linker_script = b.fmt("{s}/lib/microkit.ld", .{ microkit_board_dir }); + const sdk_board_include_dir = b.fmt("{s}/include", .{ microkit_board_dir }); const zig_libmicrokit = b.addObject(.{ .name = "zig_libmicrokit", @@ -53,48 +46,14 @@ pub fn build(b: *std.Build) void { zig_libmicrokit.addIncludePath(.{ .path = "src/" }); zig_libmicrokit.addIncludePath(.{ .path = sdk_board_include_dir }); - const libvmm = b.addStaticLibrary(.{ - .name = "vmm", + const libvmm_dep = b.dependency("libvmm", .{ .target = target, .optimize = optimize, + .sdk = microkit_sdk, + .config = @as([]const u8, microkit_config), + .board = @as([]const u8, microkit_board), }); - - const libvmm_path = "../.."; - const libvmm_src = libvmm_path ++ "/src/"; - // Right now we only support AArch64 so this is a safe assumption. - const libvmm_src_arch = libvmm_src ++ "arch/aarch64/"; - libvmm.addCSourceFiles(.{ - .files = &.{ - libvmm_src ++ "guest.c", - libvmm_src ++ "util/util.c", - libvmm_src ++ "util/printf.c", - libvmm_src_arch ++ "vgic/vgic.c", - libvmm_src_arch ++ "vgic/vgic_v2.c", - libvmm_src_arch ++ "fault.c", - libvmm_src_arch ++ "psci.c", - libvmm_src_arch ++ "smc.c", - libvmm_src_arch ++ "virq.c", - libvmm_src_arch ++ "linux.c", - libvmm_src_arch ++ "tcb.c", - libvmm_src_arch ++ "vcpu.c", - }, - .flags = &.{ - "-Wall", - "-Werror", - "-Wno-unused-function", - "-mstrict-align", - "-DBOARD_qemu_arm_virt", // @ivanv: should not be necessary - "-fno-sanitize=undefined", // @ivanv: ideally we wouldn't have to turn off UBSAN - } - }); - - libvmm.addIncludePath(.{ .path = libvmm_src }); - libvmm.addIncludePath(.{ .path = libvmm_src ++ "util/" }); - libvmm.addIncludePath(.{ .path = libvmm_src_arch }); - libvmm.addIncludePath(.{ .path = libvmm_src_arch ++ "vgic/" }); - libvmm.addIncludePath(.{ .path = sdk_board_include_dir }); - - b.installArtifact(libvmm); + const libvmm = libvmm_dep.artifact("vmm"); const exe = b.addExecutable(.{ .name = "vmm.elf", @@ -122,9 +81,9 @@ pub fn build(b: *std.Build) void { // Add microkit.h to be used by the API wrapper. exe.addIncludePath(.{ .path = sdk_board_include_dir }); - exe.addIncludePath(.{ .path = "../../src/" }); - exe.addIncludePath(.{ .path = "../../src/util/" }); - exe.addIncludePath(.{ .path = "../../src/arch/aarch64/" }); + exe.addIncludePath(libvmm_dep.path("src")); + // @ivanv: shouldn't need to do this! fix our includes + exe.addIncludePath(libvmm_dep.path("src/arch/aarch64")); // Add the static library that provides each protection domain's entry // point (`main()`), which runs the main handler loop. exe.addObjectFile(.{ .path = libmicrokit }); @@ -160,7 +119,7 @@ pub fn build(b: *std.Build) void { b.default_step = microkit_step; // This is setting up a `qemu` command for running the system using QEMU. - const loader_arg = fmtPrint("loader,file={s},addr=0x70000000,cpu-num=0", .{ final_image_dest }); + const loader_arg = b.fmt("loader,file={s},addr=0x70000000,cpu-num=0", .{ final_image_dest }); const qemu_cmd = b.addSystemCommand(&[_][]const u8{ "qemu-system-aarch64", "-machine",