From 65cfa18d63c2fed868a80d608c61173699db713d Mon Sep 17 00:00:00 2001 From: "Nurul Huda (Apon)" Date: Thu, 16 Jul 2026 04:33:16 +0600 Subject: [PATCH] refactor: use isolated package per targets --- .github/workflows/cd.yml | 2 +- README.md | 22 +++---- lib/build.zig | 133 ++++++++++++++++++++++++--------------- lib/build.zig.zon | 112 +++++++++++++++++++++++++++++++-- lib/targets.zig | 57 +++++++++++++++++ lib/targets.zon | 26 ++++++++ 6 files changed, 283 insertions(+), 69 deletions(-) create mode 100644 lib/targets.zig create mode 100644 lib/targets.zon diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index a709fc4..30f277c 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -184,7 +184,7 @@ jobs: run: | cd lib mkdir -p dist - tar -czf "dist/${{ matrix.target }}.tar.gz" -C zig-out/lib . + tar -czf "dist/${{ matrix.target }}.tar.gz" -C "zig-out/lib/${{ matrix.target }}" . - name: Upload Artifact uses: actions/upload-artifact@v4 diff --git a/README.md b/README.md index 2f9b82e..0eb5b46 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,14 @@ exe.root_module.addImport("temporalz", temporalz.module("temporalz")); Prebuilt libraries are included for the following platforms: -- `x86_64-macos` -- `aarch64-macos` -- `x86_64-linux-gnu` -- `aarch64-linux-gnu` -- `x86_64-windows-gnu` -- `aarch64-windows-gnu` -- `wasm32-freestanding` -- `wasm32-wasi` -- `x86_64-linux-musl` -- `aarch64-linux-musl` -- `aarch64-linux-android` -- `aarch64-ios` +- `x86_64-macos`, `aarch64-macos` +- `x86_64-linux-gnu`, `aarch64-linux-gnu`, `x86-linux-gnu`, `arm-linux-gnueabihf` +- `loongarch64-linux-gnu`, `powerpc64le-linux-gnu`, `riscv64-linux-gnu`, `s390x-linux-gnu` +- `x86_64-windows-gnu`, `aarch64-windows-gnu`, `x86-windows-gnu` +- `x86_64-freebsd`, `x86_64-netbsd` +- `wasm32-freestanding`, `wasm32-wasi` +- `x86_64-linux-musl`, `aarch64-linux-musl` +- `aarch64-linux-android`, `aarch64-ios` For other platforms, the library will build from source; you need the Rust toolchain installed. @@ -91,4 +87,4 @@ zig build run -Dtarget=wasm32-wasi ```bash zig build test -``` \ No newline at end of file +``` diff --git a/lib/build.zig b/lib/build.zig index 8458fe7..2191147 100644 --- a/lib/build.zig +++ b/lib/build.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const targets = @import("targets.zig"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -12,38 +13,65 @@ pub fn build(b: *std.Build) !void { b.fmt("{s}-{s}", .{ arch_str, os_str }) else b.fmt("{s}-{s}-{s}", .{ arch_str, os_str, @tagName(abi) }); + const prebuilt_name = targets.prebuiltName(b, target); const lib_name = if (target.result.os.tag == .windows and abi == .msvc) "temporal_capi.lib" else "libtemporal_capi.a"; - const prebuilt_lib_path = b.fmt("{s}/{s}", .{ target_triple, lib_name }); + // --- Module --- // + const mod = b.addModule("libtemporal", .{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); - // --- Pre-built resolution --- // - var prebuilt_lib_file: ?std.Build.LazyPath = null; - var selected_lib_file: std.Build.LazyPath = undefined; + const temporal_rs = b.dependency("temporal_rs", .{ + .target = target, + .optimize = optimize, + }); - if (!force_build_rust) { - const libtemporal_dep = b.lazyDependency("prebuilt_libtemporal", .{}); - if (libtemporal_dep) |dep| { - const lib_file_candidate = dep.path(prebuilt_lib_path); - if (dep.builder.root.access(b.graph.io, prebuilt_lib_path, .{})) { - prebuilt_lib_file = lib_file_candidate; - } else |_| {} - } + const translated = b.addTranslateC(.{ + .root_source_file = b.path("src/lib.h"), + .target = target, + .optimize = optimize, + .link_libc = false, + }); + translated.addIncludePath(temporal_rs.path("temporal_capi/bindings/c")); + translated.addIncludePath(b.path("src/stubs/c_headers")); + mod.addImport("lib", translated.createModule()); + + // --- Rust Misc Deps --- // + if (target.result.os.tag == .windows) mod.linkSystemLibrary("userenv", .{}); + const unwind_stubs = b.addLibrary(.{ .linkage = .static, .name = "unwind_stubs", .root_module = b.createModule(.{ + .root_source_file = b.path("src/stubs/unwind.zig"), + .target = target, + .optimize = optimize, + }) }); + mod.linkLibrary(unwind_stubs); + + // --- Steps: update prebuilt dependency hashes --- // + { + const update_step = b.step("update", "Fetch prebuilt packages and update build.zig.zon"); + addPrebuiltDependencyFetches(b, update_step); } - if (prebuilt_lib_file) |plf| { - // std.debug.print("Using pre-built temporal_capi library from downloaded artifact at: {s}\n", .{prebuilt_lib_path}); - selected_lib_file = plf; + // --- Static lib resolution (prebuilt or source) --- // + var selected_lib_file: std.Build.LazyPath = undefined; + + if (!force_build_rust and targets.isDeclared(prebuilt_name)) { + const prebuilt_dep = b.lazyDependency(prebuilt_name, .{}) orelse { + return; + }; + std.log.info("using prebuilt libtemporal for {s}", .{prebuilt_name}); + selected_lib_file = prebuilt_dep.path(lib_name); } else { - // std.debug.print("building from source: {s}\n searched for prebuild at: {s}\n", .{ prebuilt_lib_path, prebuilt_lib_path }); + std.log.info("building libtemporal from source for {s}, requires Rust toolchain", .{target_triple}); const build_crab = @import("build_crab"); var zig_target = target.result; if (zig_target.os.tag == .windows) zig_target.abi = .gnu; - // Zig's baseline `.arm` maps to Rust `arm-*`, but rustup ships `armv7-*`. const rust_target_str: []const u8 = if (zig_target.cpu.arch == .arm and zig_target.os.tag == .linux) switch (zig_target.abi) { .gnueabihf, .eabihf => "armv7-unknown-linux-gnueabihf", @@ -71,39 +99,44 @@ pub fn build(b: *std.Build) !void { selected_lib_file = build_dir.path(b, lib_name); } - // --- Install Step (for publishing) --- // - const install_lib = b.addInstallFile(selected_lib_file, b.fmt("lib/{s}/{s}", .{ target_triple, lib_name })); - b.getInstallStep().dependOn(&install_lib.step); - - // --- Zig Module --- // - const mod = b.addModule("libtemporal", .{ - .root_source_file = b.path("src/root.zig"), - .target = target, - .optimize = optimize, - }); - - const temporal_rs = b.dependency("temporal_rs", .{ - .target = target, - .optimize = optimize, - }); - - const translated = b.addTranslateC(.{ - .root_source_file = b.path("src/lib.h"), - .target = target, - .optimize = optimize, - .link_libc = false, - }); - translated.addIncludePath(temporal_rs.path("temporal_capi/bindings/c")); - translated.addIncludePath(b.path("src/stubs/c_headers")); - mod.addImport("lib", translated.createModule()); mod.addObjectFile(selected_lib_file); - // --- Rust Misc Deps --- // - if (target.result.os.tag == .windows) mod.linkSystemLibrary("userenv", .{}); - const unwind_stubs = b.addLibrary(.{ .linkage = .static, .name = "unwind_stubs", .root_module = b.createModule(.{ - .root_source_file = b.path("src/stubs/unwind.zig"), - .target = target, - .optimize = optimize, - }) }); - mod.linkLibrary(unwind_stubs); + // --- Install Step (for publishing) --- // + const install_lib = b.addInstallFile(selected_lib_file, b.fmt("lib/{s}/{s}", .{ prebuilt_name, lib_name })); + b.getInstallStep().dependOn(&install_lib.step); +} + +fn addPrebuiltDependencyFetches(b: *std.Build, parent: *std.Build.Step) void { + var prev_save: ?*std.Build.Step = null; + + inline for (targets.config.targets) |target_triple| { + const url = targets.releaseUrl(b, target_triple); + + const fetch = b.addSystemCommand(&.{ b.graph.zig_exe, "fetch", url }); + fetch.setName(b.fmt("fetch {s}", .{target_triple})); + fetch.setCwd(b.path(".")); + fetch.has_side_effects = true; + fetch.expectExitCode(0); + _ = fetch.captureStdErr(.{}); + + const save = b.addSystemCommand(&.{ + b.graph.zig_exe, + "fetch", + b.fmt("--save={s}", .{target_triple}), + url, + }); + save.setName(b.fmt("save {s}", .{target_triple})); + save.setCwd(b.path(".")); + save.has_side_effects = true; + save.expectExitCode(0); + _ = save.captureStdErr(.{}); + + save.step.dependOn(&fetch.step); + + if (prev_save) |prev| { + save.step.dependOn(prev); + } + parent.dependOn(&save.step); + prev_save = &save.step; + } } diff --git a/lib/build.zig.zon b/lib/build.zig.zon index c41d13a..894789d 100644 --- a/lib/build.zig.zon +++ b/lib/build.zig.zon @@ -7,19 +7,121 @@ .url = "git+https://github.com/boa-dev/temporal?ref=v0.2.4#a5cebbcce231552bf6ed985d7ea13b0c7b195f02", .hash = "N-V-__8AALjnOQBiCk4NffYgEewZ5v9qlLlDyXO22FIyzNdf", }, - .prebuilt_libtemporal = .{ - .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.3/libtemporal.tar.gz", - .hash = "N-V-__8AADYZ_AXnqpmkTa49ZQ6lEQlDXWRLthyxEbgse3Hi", - .lazy = true, - }, .build_crab = .{ .url = "git+https://github.com/nurulhudaapon/build.crab?ref=zig-dev#69a630f8fa9ccca37a8ba2e898e7934e3b5b8fe2", .hash = "build_crab-0.2.1-U0id_zfKAAAvOuam-kPYKMuEQpHuEBjrP96uQCVj94yr", }, + .@"aarch64-ios" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-ios.tar.gz", + .hash = "N-V-__8AAJAyaADZvWvrmaGR5reVCH4y8JZ3-Gfxu_9-pcHB", + .lazy = true, + }, + .@"aarch64-linux-android" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-linux-android.tar.gz", + .hash = "N-V-__8AAKYEkwCQonhHp5MAYaRE8IFcWN493Y2a5nqWJmF7", + .lazy = true, + }, + .@"aarch64-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-linux-gnu.tar.gz", + .hash = "N-V-__8AAGSQjQDYfN0voDb4c1sCM374u570U8hcEJYoAoTc", + .lazy = true, + }, + .@"aarch64-linux-musl" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-linux-musl.tar.gz", + .hash = "N-V-__8AAE7bjwA2arkXXgbgaMe8oHdmJEez8y2XFeCUvSnn", + .lazy = true, + }, + .@"aarch64-macos" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-macos.tar.gz", + .hash = "N-V-__8AAFBxZwC9MEuiOD8EihBoSl_00IDUJnpQsjmnpkmK", + .lazy = true, + }, + .@"aarch64-windows-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/aarch64-windows-gnu.tar.gz", + .hash = "N-V-__8AADKDagD1bmYDywq4IZPBo2la8m1xzSObPCsxyfmW", + .lazy = true, + }, + .@"arm-linux-gnueabihf" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/arm-linux-gnueabihf.tar.gz", + .hash = "N-V-__8AAIAAfgDYWro4rWudgosfdPK1tzxLe77uoRBtL4rl", + .lazy = true, + }, + .@"loongarch64-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/loongarch64-linux-gnu.tar.gz", + .hash = "N-V-__8AAEKptADVBZFKw7ZKvLurwzp_WA-FD-OwZFgEMf4q", + .lazy = true, + }, + .@"powerpc64le-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/powerpc64le-linux-gnu.tar.gz", + .hash = "N-V-__8AAKwdfQDZIHVuhOtUOs9tKx-RJCM_1HnX-FwGA0l2", + .lazy = true, + }, + .@"riscv64-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/riscv64-linux-gnu.tar.gz", + .hash = "N-V-__8AAPBiogBQe_5VZhzDgpZrfwuzfj_54IyUFK2igsQo", + .lazy = true, + }, + .@"s390x-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/s390x-linux-gnu.tar.gz", + .hash = "N-V-__8AAEiKkwC4qkNJoOuzgxHuS_q46SIbqITuODu9Hz3Q", + .lazy = true, + }, + .@"wasm32-freestanding" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/wasm32-freestanding.tar.gz", + .hash = "N-V-__8AACjsTwB3jX1mva0svtTn39kCz5aabikQbczLJlp9", + .lazy = true, + }, + .@"wasm32-wasi" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/wasm32-wasi.tar.gz", + .hash = "N-V-__8AAPbTQADoTDaLXmk3KlebN5pZWX-eXhD6BLPJuxpt", + .lazy = true, + }, + .@"x86-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86-linux-gnu.tar.gz", + .hash = "N-V-__8AALzUbwBdOV8TmU7vPbofrSgkCz70GG0SwvHZGWEZ", + .lazy = true, + }, + .@"x86-windows-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86-windows-gnu.tar.gz", + .hash = "N-V-__8AAAgSYQAjy_QNLkxQurUPnbT8s4fNjunaYgPQjiwB", + .lazy = true, + }, + .@"x86_64-freebsd" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-freebsd.tar.gz", + .hash = "N-V-__8AAJAFkQCAz3khARx91vZb6HMPqV3oBdmJM8kaxd9Y", + .lazy = true, + }, + .@"x86_64-linux-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-linux-gnu.tar.gz", + .hash = "N-V-__8AACAdiQB3WUysJXS42gsJZdpzna_04l6omEQGzJZa", + .lazy = true, + }, + .@"x86_64-linux-musl" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-linux-musl.tar.gz", + .hash = "N-V-__8AAGhKiwDlW7Ecy5CJf7-PEGSdDgr4VnCNLkl9cery", + .lazy = true, + }, + .@"x86_64-macos" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-macos.tar.gz", + .hash = "N-V-__8AAHDyZwCPo6HVOYeFWas2bcTqi67vJuxoL2huROlC", + .lazy = true, + }, + .@"x86_64-netbsd" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-netbsd.tar.gz", + .hash = "N-V-__8AAL7_lgBmD0lfXqzAxRdjDiPLi6bgLvnVVbQszw3p", + .lazy = true, + }, + .@"x86_64-windows-gnu" = .{ + .url = "https://github.com/nurulhudaapon/temporalz/releases/download/v0.2.4/x86_64-windows-gnu.tar.gz", + .hash = "N-V-__8AAD5eTQDfQ9ddNZh3g8e6SaA9CnVSQRMozhHs_jWt", + .lazy = true, + }, }, .paths = .{ "build.zig", "build.zig.zon", + "targets.zon", + "targets.zig", "src", }, } diff --git a/lib/targets.zig b/lib/targets.zig new file mode 100644 index 0000000..8fbc49c --- /dev/null +++ b/lib/targets.zig @@ -0,0 +1,57 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +pub const config = @import("targets.zon"); +const manifest = @import("build.zig.zon"); + +/// Zig `-Dtarget` triple for the host platform's prebuilt dependency name. +pub fn depName() []const u8 { + const triple = targetTripleFromBuiltin(); + if (!isDeclared(triple)) { + @compileError(std.fmt.comptimePrint( + "no prebuilt libtemporal for host {s}", + .{triple}, + )); + } + return triple; +} + +pub fn prebuiltName(b: *std.Build, target: std.Build.ResolvedTarget) []const u8 { + const arch = @tagName(target.result.cpu.arch); + const os = target.result.os.tag; + const abi = target.result.abi; + + if (os == .wasi and (abi == .musl or abi == .none)) + return b.fmt("{s}-wasi", .{arch}); + + if (abi == .none) + return b.fmt("{s}-{s}", .{ arch, @tagName(os) }); + return b.fmt("{s}-{s}-{s}", .{ arch, @tagName(os), @tagName(abi) }); +} + +pub fn isDeclared(target_triple: []const u8) bool { + inline for (config.targets) |supported| { + if (std.mem.eql(u8, supported, target_triple)) { + return comptime @hasField(@TypeOf(manifest.dependencies), supported); + } + } + return false; +} + +pub fn releaseUrl(b: *std.Build, target_triple: []const u8) []const u8 { + return b.fmt( + "https://github.com/nurulhudaapon/temporalz/releases/download/v{s}/{s}.tar.gz", + .{ config.version, target_triple }, + ); +} + +fn targetTripleFromBuiltin() []const u8 { + if (builtin.os.tag == .wasi and (builtin.abi == .musl or builtin.abi == .none)) + return std.fmt.comptimePrint("{s}-wasi", .{@tagName(builtin.cpu.arch)}); + + const abi = builtin.abi; + return if (abi == .none) + std.fmt.comptimePrint("{s}-{s}", .{ @tagName(builtin.cpu.arch), @tagName(builtin.os.tag) }) + else + std.fmt.comptimePrint("{s}-{s}-{s}", .{ @tagName(builtin.cpu.arch), @tagName(builtin.os.tag), @tagName(abi) }); +} diff --git a/lib/targets.zon b/lib/targets.zon new file mode 100644 index 0000000..1afdc5e --- /dev/null +++ b/lib/targets.zon @@ -0,0 +1,26 @@ +.{ + .version = "0.2.4", + .targets = .{ + "x86_64-macos", + "aarch64-macos", + "x86_64-linux-gnu", + "aarch64-linux-gnu", + "x86-linux-gnu", + "arm-linux-gnueabihf", + "loongarch64-linux-gnu", + "powerpc64le-linux-gnu", + "riscv64-linux-gnu", + "s390x-linux-gnu", + "x86_64-windows-gnu", + "aarch64-windows-gnu", + "x86-windows-gnu", + "x86_64-freebsd", + "x86_64-netbsd", + "wasm32-freestanding", + "wasm32-wasi", + "x86_64-linux-musl", + "aarch64-linux-musl", + "aarch64-linux-android", + "aarch64-ios", + }, +}