From 178e8b8f57568e4c111eb2a249c726db5f79766d Mon Sep 17 00:00:00 2001 From: Noah Baertsch Date: Tue, 30 Dec 2025 16:13:38 -0500 Subject: [PATCH 1/3] update for zig 0.15.2 --- build.zig | 36 ++++++++++++++++++++++++++++-------- build.zig.zon | 2 +- src/cancel_handle.zig | 2 +- src/compiled_plugin.zig | 2 +- src/current_plugin.zig | 2 +- src/ffi.zig | 3 --- src/function.zig | 2 +- src/main.zig | 2 +- src/plugin.zig | 20 +++++++++++++++++--- test.zig | 14 +++++++------- 10 files changed, 58 insertions(+), 27 deletions(-) delete mode 100644 src/ffi.zig diff --git a/build.zig b/build.zig index 1be3ea8..1d75b18 100644 --- a/build.zig +++ b/build.zig @@ -13,22 +13,40 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + // Generate C bindings from extism.h using translateC + const translate_c = b.addTranslateC(.{ + .root_source_file = .{ .cwd_relative = "/usr/local/include/extism.h" }, + .target = target, + .optimize = optimize, + .link_libc = true, + }); + + // Create a module from the generated bindings + const extism_c = translate_c.createModule(); + const extism_module = b.addModule("extism", .{ .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "extism_c", .module = extism_c }, + }, }); extism_module.addIncludePath(.{ .cwd_relative = "/usr/local/include" }); extism_module.addLibraryPath(.{ .cwd_relative = "/usr/local/lib" }); var tests = b.addTest(.{ .name = "Library Tests", - .root_source_file = b.path("test.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("test.zig"), + .target = target, + .optimize = optimize, + }), }); tests.root_module.addImport("extism", extism_module); - tests.linkLibC(); - tests.linkSystemLibrary("extism"); + tests.root_module.link_libc = true; + tests.root_module.linkSystemLibrary("extism", .{}); const tests_run_step = b.addRunArtifact(tests); const test_step = b.step("test", "Run library tests"); @@ -36,9 +54,11 @@ pub fn build(b: *std.Build) void { var example = b.addExecutable(.{ .name = "Example", - .root_source_file = b.path("examples/basic.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("examples/basic.zig"), + .target = target, + .optimize = optimize, + }), }); example.root_module.addImport("extism", extism_module); diff --git a/build.zig.zon b/build.zig.zon index 921329b..712ca7f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .extism, .version = "1.0.0-rc3", - .minimum_zig_version = "0.14.0", + .minimum_zig_version = "0.15.2", .fingerprint = 0xb41364f00afbe9db, .paths = .{""}, } diff --git a/src/cancel_handle.zig b/src/cancel_handle.zig index 9bfddea..5aabaad 100644 --- a/src/cancel_handle.zig +++ b/src/cancel_handle.zig @@ -1,4 +1,4 @@ -const c = @import("ffi.zig"); +const c = @import("extism_c"); const Self = @This(); handle: ?*const c.ExtismCancelHandle, diff --git a/src/compiled_plugin.zig b/src/compiled_plugin.zig index 1e2fd1d..20b8175 100644 --- a/src/compiled_plugin.zig +++ b/src/compiled_plugin.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Manifest = @import("manifest.zig").Manifest; const Function = @import("function.zig"); const CancelHandle = @import("cancel_handle.zig"); -const c = @import("ffi.zig"); +const c = @import("extism_c"); const Self = @This(); diff --git a/src/current_plugin.zig b/src/current_plugin.zig index 36cbdca..8fd3a0a 100644 --- a/src/current_plugin.zig +++ b/src/current_plugin.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const c = @import("ffi.zig"); +const c = @import("extism_c"); c_currplugin: *c.ExtismCurrentPlugin, diff --git a/src/ffi.zig b/src/ffi.zig deleted file mode 100644 index 81a2f4a..0000000 --- a/src/ffi.zig +++ /dev/null @@ -1,3 +0,0 @@ -pub usingnamespace @cImport({ - @cInclude("extism.h"); -}); diff --git a/src/function.zig b/src/function.zig index 4b86f9d..0ac8993 100644 --- a/src/function.zig +++ b/src/function.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const c = @import("ffi.zig"); +const c = @import("extism_c"); const Self = @This(); c_func: ?*c.ExtismFunction, diff --git a/src/main.zig b/src/main.zig index 96f83b2..b10a8f6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,6 +1,6 @@ const std = @import("std"); const testing = std.testing; -pub const c = @import("ffi.zig"); +pub const c = @import("extism_c"); pub const Plugin = @import("plugin.zig"); pub const CompiledPlugin = @import("compiled_plugin.zig"); diff --git a/src/plugin.zig b/src/plugin.zig index 2f51671..fa5ec4f 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -1,8 +1,22 @@ const std = @import("std"); const Manifest = @import("manifest.zig").Manifest; const Function = @import("function.zig"); + +// Helper function for JSON stringification in Zig 0.15+ +fn jsonStringifyAlloc(allocator: std.mem.Allocator, value: anytype) ![]u8 { + var out: std.io.Writer.Allocating = .init(allocator); + defer out.deinit(); + + var stringify: std.json.Stringify = .{ + .writer = &out.writer, + .options = .{ .emit_null_optional_fields = false }, + }; + try stringify.write(value); + + return allocator.dupe(u8, out.written()); +} const CancelHandle = @import("cancel_handle.zig"); -const c = @import("ffi.zig"); +const c = @import("extism_c"); const CompiledPlugin = @import("compiled_plugin.zig"); const Self = @This(); @@ -43,7 +57,7 @@ pub fn init(allocator: std.mem.Allocator, data: []const u8, functions: []const F /// Create a new plugin from the given manifest pub fn initFromManifest(allocator: std.mem.Allocator, manifest: Manifest, functions: []const Function, wasi: bool) !Self { - const json = try std.json.stringifyAlloc(allocator, manifest, .{ .emit_null_optional_fields = false }); + const json = try jsonStringifyAlloc(allocator, manifest); defer allocator.free(json); return init(allocator, json, functions, wasi); } @@ -107,7 +121,7 @@ pub fn callWithContext(self: *Self, function_name: []const u8, input: []const u8 /// Set configuration values pub fn setConfig(self: *Self, allocator: std.mem.Allocator, config: std.json.ArrayHashMap([]const u8)) !void { - const config_json = try std.json.stringifyAlloc(allocator, config, .{ .emit_null_optional_fields = false }); + const config_json = try jsonStringifyAlloc(allocator, config); defer allocator.free(config_json); _ = c.extism_plugin_config(self.ptr, config_json.ptr, @as(u64, config_json.len)); } diff --git a/test.zig b/test.zig index 72184d2..32b3b3d 100644 --- a/test.zig +++ b/test.zig @@ -6,7 +6,7 @@ const CurrentPlugin = sdk.CurrentPlugin; const Function = sdk.Function; const manifest = sdk.manifest; -export fn hello_world(plugin_ptr: ?*sdk.c.ExtismCurrentPlugin, inputs: [*c]const sdk.c.ExtismVal, n_inputs: u64, outputs: [*c]sdk.c.ExtismVal, n_outputs: u64, user_data: ?*anyopaque) callconv(.C) void { +export fn hello_world(plugin_ptr: ?*sdk.c.ExtismCurrentPlugin, inputs: [*c]const sdk.c.ExtismVal, n_inputs: u64, outputs: [*c]sdk.c.ExtismVal, n_outputs: u64, user_data: ?*anyopaque) callconv(.c) void { std.debug.print("Hello from Zig!\n", .{}); const str_ud = @as([*:0]const u8, @ptrCast(user_data orelse unreachable)); std.debug.print("User data: {s}\n", .{str_ud}); @@ -41,12 +41,12 @@ test "Single threaded tests" { var plugin = try Plugin.initFromManifest(testing.allocator, man, &[_]Function{f}, true); defer plugin.deinit(); - std.debug.print("\nregister loaded plugin: {}\n", .{std.fmt.fmtDuration(wasm_start.read())}); + std.debug.print("\nregister loaded plugin: {}\n", .{wasm_start.read()}); const repeat = 1182; const input = "aeiouAEIOU____________________________________&smtms_y?" ** repeat; var data = try plugin.call("count_vowels", input); try testing.expectEqualStrings("{\"count\": 11820}", data); - std.debug.print("register plugin + function call: {}, sent input size: {} bytes\n", .{ std.fmt.fmtDuration(wasm_start.read()), input.len }); + std.debug.print("register plugin + function call: {}, sent input size: {} bytes\n", .{ wasm_start.read(), input.len }); var ctx: u64 = 12345; data = try plugin.callWithContext("count_vowels", input, @ptrCast(&ctx)); try testing.expectEqualStrings("{\"count\": 11820}", data); @@ -74,8 +74,8 @@ test "Single threaded tests" { native_elapsed += native_start.read(); } const native_avg = native_elapsed / i; - std.debug.print("native function call (avg, N = {}): {}\n", .{ i, std.fmt.fmtDuration(native_avg) }); - std.debug.print("wasm function call (avg, N = {}): {}\n", .{ i, std.fmt.fmtDuration(wasm_avg) }); + std.debug.print("native function call (avg, N = {}): {}\n", .{ i, native_avg }); + std.debug.print("wasm function call (avg, N = {}): {}\n", .{ i, wasm_avg }); } test "Multi threaded tests" { @@ -135,7 +135,7 @@ test "Plugin Cancellation" { var handle = plugin.cancelHandle(); const S = struct { fn _test(h: *sdk.CancelHandle) void { - std.time.sleep(1 * std.time.ns_per_s); + std.Thread.sleep(1 * std.time.ns_per_s); _ = h.cancel(); } }; @@ -144,5 +144,5 @@ test "Plugin Cancellation" { const output = plugin.call("infinite_loop", "abc123"); const call_end = call_start.read(); try std.testing.expectError(error.PluginCallFailed, output); - std.debug.print("Cancelled plugin ran for {}\n", .{std.fmt.fmtDuration(call_end)}); + std.debug.print("Cancelled plugin ran for {}\n", .{call_end}); } From 898eb993014d09654cb8163f18dc03ec26963d60 Mon Sep 17 00:00:00 2001 From: Noah Baertsch Date: Tue, 30 Dec 2025 16:34:52 -0500 Subject: [PATCH 2/3] bump github actions zig version and migrate to mlugg/setup-zig --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1f0064e..dd249ac 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - zig_version: ["0.14.0"] # eventually use multiple versions once stable + zig_version: ["0.15.2"] # eventually use multiple versions once stable rust: - stable steps: @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v3 - uses: ./.github/actions/libextism - name: Setup Zig env - uses: goto-bus-stop/setup-zig@v2 + uses: https://github.com/mlugg/setup-zig@v2 with: version: ${{ matrix.zig_version }} - name: Test Zig Host SDK From 879a7ac52d08d5388cbc40da27ac254705c4613d Mon Sep 17 00:00:00 2001 From: Noah Baertsch Date: Tue, 30 Dec 2025 17:09:52 -0500 Subject: [PATCH 3/3] remove unnecesary helper function --- src/plugin.zig | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/plugin.zig b/src/plugin.zig index fa5ec4f..943f0d9 100644 --- a/src/plugin.zig +++ b/src/plugin.zig @@ -1,20 +1,6 @@ const std = @import("std"); const Manifest = @import("manifest.zig").Manifest; const Function = @import("function.zig"); - -// Helper function for JSON stringification in Zig 0.15+ -fn jsonStringifyAlloc(allocator: std.mem.Allocator, value: anytype) ![]u8 { - var out: std.io.Writer.Allocating = .init(allocator); - defer out.deinit(); - - var stringify: std.json.Stringify = .{ - .writer = &out.writer, - .options = .{ .emit_null_optional_fields = false }, - }; - try stringify.write(value); - - return allocator.dupe(u8, out.written()); -} const CancelHandle = @import("cancel_handle.zig"); const c = @import("extism_c"); const CompiledPlugin = @import("compiled_plugin.zig"); @@ -57,7 +43,7 @@ pub fn init(allocator: std.mem.Allocator, data: []const u8, functions: []const F /// Create a new plugin from the given manifest pub fn initFromManifest(allocator: std.mem.Allocator, manifest: Manifest, functions: []const Function, wasi: bool) !Self { - const json = try jsonStringifyAlloc(allocator, manifest); + const json = try std.json.Stringify.valueAlloc(allocator, manifest, .{ .emit_null_optional_fields = false }); defer allocator.free(json); return init(allocator, json, functions, wasi); } @@ -121,7 +107,7 @@ pub fn callWithContext(self: *Self, function_name: []const u8, input: []const u8 /// Set configuration values pub fn setConfig(self: *Self, allocator: std.mem.Allocator, config: std.json.ArrayHashMap([]const u8)) !void { - const config_json = try jsonStringifyAlloc(allocator, config); + const config_json = try std.json.Stringify.valueAlloc(allocator, config, .{ .emit_null_optional_fields = false }); defer allocator.free(config_json); _ = c.extism_plugin_config(self.ptr, config_json.ptr, @as(u64, config_json.len)); }