From 39afa97e57b9bfb711f18591197c1d496bf0d93c Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Sun, 10 May 2026 01:38:50 +0800 Subject: [PATCH] chore: update Zig support to 0.16.0 and keep build compatible - Update CI workflow to test against Zig 0.16.0 and use new setup-zig action - Refactor build.zig for compatibility with Zig 0.14/0.15/0.16 APIs - Route C source, link, and include operations through root_module to adapt to Zig 0.16 build changes - Fix example build to work with both old and new Zig std APIs for Dir/Io --- .github/workflows/zig.yml | 4 +- build.zig | 166 +++++++++++++++++++++++--------------- 2 files changed, 101 insertions(+), 69 deletions(-) diff --git a/.github/workflows/zig.yml b/.github/workflows/zig.yml index e563a396a..9cf1f6d7c 100644 --- a/.github/workflows/zig.yml +++ b/.github/workflows/zig.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - version: [0.14.0, 0.15.1] + version: [0.14.0, 0.15.1, 0.16.0] fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -18,7 +18,7 @@ jobs: xcode-version: latest-stable - uses: actions/checkout@v4 - name: Setup Zig - uses: goto-bus-stop/setup-zig@v2 + uses: mlugg/setup-zig@v2 with: version: ${{ matrix.version }} - name: Build static library diff --git a/build.zig b/build.zig index 1bc8dc59e..412329f3b 100644 --- a/build.zig +++ b/build.zig @@ -81,6 +81,10 @@ fn addLinkerFlags( const is_windows = webui_target.os.tag == .windows; const is_darwin = webui_target.os.tag == .macos; const debug = webui.root_module.optimize.? == .Debug; + // In Zig 0.16, methods like addCSourceFile/linkLibC/addIncludePath/linkSystemLibrary/ + // linkFramework/addCMacro were removed from *Compile and live only on *Module. + // Routing every call through `mod` keeps the build script compatible with 0.14/0.15/0.16. + const mod = webui.root_module; // Prepare compiler flags. const no_tls_flags: []const []const u8 = &.{"-DNO_SSL"}; @@ -93,24 +97,24 @@ fn addLinkerFlags( }; if (debug and enable_webui_log) { - webui.root_module.addCMacro("WEBUI_LOG", ""); + mod.addCMacro("WEBUI_LOG", ""); } - webui.addCSourceFile(.{ + mod.addCSourceFile(.{ .file = b.path("src/webui.c"), .flags = if (enable_tls) tls_flags else no_tls_flags, }); // Add Win32 WebView2 C++ support on Windows if (is_windows) { - webui.addCSourceFile(.{ + mod.addCSourceFile(.{ .file = b.path("src/webview/win32_wv2.cpp"), .flags = if (enable_tls) tls_flags else no_tls_flags, }); - webui.linkLibCpp(); + mod.link_libcpp = true; } const civetweb_debug = debug and debug_dependencies.contains(.civetweb); - webui.addCSourceFile(.{ + mod.addCSourceFile(.{ .file = b.path("src/civetweb/civetweb.c"), .flags = if (enable_tls and !civetweb_debug) civetweb_flags ++ tls_flags ++ .{"-DNDEBUG"} @@ -121,34 +125,34 @@ fn addLinkerFlags( else civetweb_flags ++ .{"-DUSE_WEBSOCKET"} ++ no_tls_flags, }); - webui.linkLibC(); - webui.addIncludePath(b.path("include")); + mod.link_libc = true; + mod.addIncludePath(b.path("include")); webui.installHeader(b.path("include/webui.h"), "webui.h"); if (is_darwin) { - webui.addCSourceFile(.{ + mod.addCSourceFile(.{ .file = b.path("src/webview/wkwebview.m"), .flags = &.{}, }); - webui.linkFramework("Cocoa"); - webui.linkFramework("WebKit"); + mod.linkFramework("Cocoa", .{}); + mod.linkFramework("WebKit", .{}); } else if (is_windows) { - webui.linkSystemLibrary("ws2_32"); - webui.linkSystemLibrary("ole32"); + mod.linkSystemLibrary("ws2_32", .{}); + mod.linkSystemLibrary("ole32", .{}); if (webui_target.abi == .msvc) { - webui.linkSystemLibrary("Advapi32"); - webui.linkSystemLibrary("Shell32"); - webui.linkSystemLibrary("user32"); + mod.linkSystemLibrary("Advapi32", .{}); + mod.linkSystemLibrary("Shell32", .{}); + mod.linkSystemLibrary("user32", .{}); } if (enable_tls) { - webui.linkSystemLibrary("bcrypt"); + mod.linkSystemLibrary("bcrypt", .{}); } } if (enable_tls) { - webui.linkSystemLibrary("ssl"); - webui.linkSystemLibrary("crypto"); + mod.linkSystemLibrary("ssl", .{}); + mod.linkSystemLibrary("crypto", .{}); } - for (webui.root_module.link_objects.items) |lo| { + for (mod.link_objects.items) |lo| { switch (lo) { .c_source_file => |csf| { log(.debug, .WebUI, "{s} linker flags:\n", .{ @@ -165,60 +169,88 @@ fn addLinkerFlags( fn build_examples(b: *Build, webui: *Compile) !void { const build_examples_step = b.step("examples", "builds the library and its examples"); - const target = webui.root_module.resolved_target.?; - const optimize = webui.root_module.optimize.?; - const examples_path = b.path("examples/C").getPath(b); - var examples_dir = std.fs.cwd().openDir( - examples_path, - .{ .iterate = true }, - ) catch |e| switch (e) { - // Do not attempt building examples if directory does not exist. - error.FileNotFound => return, - else => return e, - }; - defer examples_dir.close(); + // Iterate examples/C. Zig 0.16 removed std.fs.cwd() and reworked Dir/Iterator + // to require an Io instance, so split the open+iterate path by version while + // sharing the per-example wiring below. + if (comptime builtin.zig_version.minor >= 16) { + const io = b.graph.io; + var examples_dir = b.build_root.handle.openDir( + io, + "examples/C", + .{ .iterate = true }, + ) catch |e| switch (e) { + error.FileNotFound => return, + else => return e, + }; + defer examples_dir.close(io); - var paths = examples_dir.iterate(); - while (try paths.next()) |val| { - if (val.kind != .directory) { - continue; + var paths = examples_dir.iterate(); + while (try paths.next(io)) |val| { + if (val.kind != .directory) continue; + try add_example(b, webui, build_examples_step, val.name); } - const example_name = val.name; + } else { + const examples_path = b.path("examples/C").getPath(b); + var examples_dir = std.fs.cwd().openDir( + examples_path, + .{ .iterate = true }, + ) catch |e| switch (e) { + error.FileNotFound => return, + else => return e, + }; + defer examples_dir.close(); + + var paths = examples_dir.iterate(); + while (try paths.next()) |val| { + if (val.kind != .directory) continue; + try add_example(b, webui, build_examples_step, val.name); + } + } +} + +fn add_example( + b: *Build, + webui: *Compile, + build_examples_step: *Build.Step, + example_name: []const u8, +) !void { + const target = webui.root_module.resolved_target.?; + const optimize = webui.root_module.optimize.?; - const exe = b.addExecutable(if (builtin.zig_version.minor == 14) .{ - .name = example_name, + const exe = b.addExecutable(if (builtin.zig_version.minor == 14) .{ + .name = example_name, + .target = target, + .optimize = optimize, + } else .{ + .name = example_name, + .root_module = b.createModule(.{ .target = target, .optimize = optimize, - } else .{ - .name = example_name, - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - .pic = true, - }), - }); - const path = try std.fmt.allocPrint(b.allocator, "examples/C/{s}/main.c", .{example_name}); - defer b.allocator.free(path); - - exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); - exe.linkLibrary(webui); - - const exe_install = b.addInstallArtifact(exe, .{}); - const exe_run = b.addRunArtifact(exe); - const step_name = try std.fmt.allocPrint(b.allocator, "run_{s}", .{example_name}); - defer b.allocator.free(step_name); - const step_desc = try std.fmt.allocPrint(b.allocator, "run example {s}", .{example_name}); - defer b.allocator.free(step_desc); - - const cwd = try std.fmt.allocPrint(b.allocator, "src/examples/{s}", .{example_name}); - defer b.allocator.free(cwd); - exe_run.setCwd(b.path(cwd)); - - exe_run.step.dependOn(&exe_install.step); - build_examples_step.dependOn(&exe_install.step); - b.step(step_name, step_desc).dependOn(&exe_run.step); - } + .pic = true, + }), + }); + const path = try std.fmt.allocPrint(b.allocator, "examples/C/{s}/main.c", .{example_name}); + defer b.allocator.free(path); + + // Route through root_module so Zig 0.16 (which removed these methods from *Compile) keeps working. + exe.root_module.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); + exe.root_module.linkLibrary(webui); + + const exe_install = b.addInstallArtifact(exe, .{}); + const exe_run = b.addRunArtifact(exe); + const step_name = try std.fmt.allocPrint(b.allocator, "run_{s}", .{example_name}); + defer b.allocator.free(step_name); + const step_desc = try std.fmt.allocPrint(b.allocator, "run example {s}", .{example_name}); + defer b.allocator.free(step_desc); + + const cwd = try std.fmt.allocPrint(b.allocator, "src/examples/{s}", .{example_name}); + defer b.allocator.free(cwd); + exe_run.setCwd(b.path(cwd)); + + exe_run.step.dependOn(&exe_install.step); + build_examples_step.dependOn(&exe_install.step); + b.step(step_name, step_desc).dependOn(&exe_run.step); } /// Function to runtime-scope log levels based on build flag, for all scopes.