If you already have some C library installed and you want to write a single Zig file that imports and calls some functions from it, you can still do that:
// script.zig
const std = @import("std");
pub fn main() void {
const c = @import("c");
const log = std.log.scoped(.main);
log.info("running '{s}'", .{@src().file});
log.info("imported {d} declarations from c", .{std.meta.declarations(c).len});
log.info("strerror(0) = {s}", .{c.strerror(0)}); // for demo purposes
}
// the rest below
./zig-0.16.0/zig build --build-file script.zig
info(main): running 'script.zig'
info(main): imported 602 declarations from c
info(main): strerror(0) = Success
The “trick” is that there’s nothing stopping you from having both main and build in the same file.
script.zig
// the rest
pub fn build(b: *std.Build) void {
const src = @src();
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const headers =
\\#include <string.h>
;
const write_tmp_file = b.addWriteFiles();
const tmp_file = write_tmp_file.add(src.module ++ ".h", headers);
const translate_c = b.addTranslateC(.{
.optimize = optimize,
.target = target,
.root_source_file = tmp_file,
});
const exe = b.addExecutable(.{
.name = src.module,
.root_module = b.createModule(.{
.root_source_file = b.path(src.file),
.target = target,
.optimize = optimize,
.imports = &.{.{
.name = "c",
.module = translate_c.createModule(),
}},
}),
});
const run_cmd = b.addRunArtifact(exe);
b.getInstallStep().dependOn(&run_cmd.step);
if (b.args) |args| {
run_cmd.addArgs(args);
}
}
I agree it isn’t exactly super convenient now, but it is still possible.
(The pattern is from the “cursed Zig” thread, but hey, if it’s relevant and useful…)