I’m adding a build.zig for the nodejs N-API. To build a Node addon, you compile against a .h and tell the linker to leave those symbols undefined. On Windows, I’ve learned that you need to link against an import library, which you generate from a .def file, which is included in the repo above.
I’m having trouble integrating this step into the build.
I can generate .libs just fine, using b.addSystemCommand(&.{b.graph.zig_exe, "dlltool", ... }). Beyond that, I can’t find anything that works. If I add them (N-API comes with 2 .defs) with addObjFile, the addon crashes when linking against that library, and there are compiler warnings. Zig’s output with --verbose is this (shortened for brevity):
ar rcs node_api.lib obj_container.lib
That seems to be putting the .lib files in an archive rather than their object file contents, and zig ar t seems to confirm that theory. I don’t think that’s right, but I’m not sure. The same thing happens if I use addCSourceFile. Linking the addon against both of these .libs directly from the cache folder does make the addon work, so I know those alone are correct.
Does Zig or LLVM not understand .lib? Are .lib files not able to be “combined”? Or is there no way to addStaticLibrary, so it’s treated as an archive? If there was some way to expose the .lib paths as artifacts, that would be an acceptable work-around too.
build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const node_api_headers = b.dependency("node_api_headers", .{});
const node_api = b.addLibrary(.{
.name = "node_api",
.linkage = .static,
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
})
});
// so that upstream people can call linkLibrary on non-Windows
node_api.addCSourceFile(.{.file = b.path("empty.c"), .flags = &.{}});
node_api.installHeadersDirectory(node_api_headers.path("include"), "", .{});
if (target.result.os.tag == .windows) {
const node_api_lib = dlltool(b, target, node_api_headers.path("def/node_api.def"), "node_api.lib");
const js_native_api_lib = dlltool(b, target, node_api_headers.path("def/js_native_api.def"), "js_native_api.lib");
node_api.addObjectFile(node_api_lib);
node_api.addObjectFile(js_native_api_lib);
}
b.installArtifact(node_api);
}
pub fn dlltool(
b: *std.Build,
target: std.Build.ResolvedTarget,
def_file: std.Build.LazyPath,
lib_file_basename: []const u8
) std.Build.LazyPath {
const exe = b.addSystemCommand(&.{b.graph.zig_exe, "dlltool", "-d", def_file.getPath(b), "-l"});
const lib_file = exe.addOutputFileArg(lib_file_basename);
exe.addArgs(&.{
"-m", switch (target.result.cpu.arch) {
.x86 => "i386",
.x86_64 => "i386:x86-64",
.arm => "arm",
.aarch64 => "arm64",
else => "i386:x86-64",
},
});
return lib_file;
}
build.zig.zon
.{
.name = .node_api,
.version = "0.0.0",
.dependencies = .{
.node_api_headers = .{
.url = "https://github.com/nodejs/node-api-headers/archive/refs/tags/v1.5.0.tar.gz",
.hash = "N-V-__8AAASNAQA0UI0mvQk4LyFEhdFuFBPgH8HSokdFFpnz"
}
},
}
Background: I’m hoping to get a static, cross-compile of node-canvas working with Zig. node-canvas has wasted lot of people’s collective time over the years due to being hard to install.