How to enable build.zig also emit header file?

I am following the example in Documentation - The Zig Programming Language (ziglang.org)

with

zig build-lib -femit-h mathtest.zig

it is easy to generate mathtest.h

but when following

// build_c.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const lib = b.addSharedLibrary(.{
        .name = "mathtest",
        .root_source_file = .{ .path = "mathtest.zig" },
        .version = .{ .major = 1, .minor = 0, .patch = 0 },
    });
    const exe = b.addExecutable(.{
        .name = "test",
    });
    exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99"} });
    exe.linkLibrary(lib);
    exe.linkSystemLibrary("c");

    b.default_step.dependOn(&exe.step);

    const run_cmd = exe.run();

    const test_step = b.step("test", "Test the program");
    test_step.dependOn(&run_cmd.step);
}

where should I find generated mathtest.h??

getEmmitedH adds -femit-h and returns the path to the generated header file.

_ = lib.getEmmitedH();
2 Likes

Is this still true? I can’t seem to figure out how to get it to generate it.

    const h_path = lib.getEmittedH();
    std.debug.print("h_path = {s}\n", .{h_path.generated.file.getPath()});

results in no file and this error

thread 12129 panic: getPath() was called on a GeneratedFile that wasn't built yet. Is there a missing Step dependency on step 'zig build-lib xlatr_plugin_c_c11 Debug native'?

Yes, it is.

The returned path is a LazyPath that gets its value later.

I’m not finding the file in zig-out with the shared library. I mean after removing the debug print.

And I’m looking through the build code and not finding where it is setting the emit_h.
Where should the file show up?

pub fn getEmittedH(compile: *Compile) LazyPath {
    assert(compile.kind != .exe and compile.kind != .@"test");
    return compile.getEmittedFileGeneric(&compile.generated_h);
}

fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath {
    if (output_file.*) |file| return .{ .generated = .{ .file = file } };
    const arena = compile.step.owner.allocator;
    const generated_file = arena.create(GeneratedFile) catch @panic("OOM");
    generated_file.* = .{ .step = &compile.step };
    output_file.* = generated_file;
    return .{ .generated = .{ .file = generated_file } };
}

-femit-h is currently disabled by default in the build system and won’t get installed to zig-out. You might be able to do something like

const installed_lib = b.addInstallArtifact(lib, .{});
installed_lib.emitted_h = lib.getEmittedH();
b.getInstallStep().dependOn(&installed_lib.step);

to get it installed (you may also need to explicitly specify an h_dir override in your call to addInstallArtifact), but it may or may not work because -femit-h is currently a bit broken. See resurrect emit-h · Issue #9698 · ziglang/zig · GitHub for more details.

Thanks for the lead …

I’m not sure why zig seems to be anti-shared libraries and wants you to recompile everything from source (link with addModule). Might be a C ABI issue but seems like there should be a Zig ABI that supports things like tagged unions and error unions. rant over

Here is the error that I got from that. I will keep working on it. I may have to just get build to run build from the command line if nothing else works.

error: unable to update file from '/home/.../.zig-cache/o/d6ee426166d3e5d2639aa583e484de65/xlatr_plugin_c_c11.h' to '/home/.../zig-out/include/xlatr_plugin_c_c11.h': FileNotFound

I’m also unable to find a way to make -femit-h work, either with zig build or zig build-lib. I have a small zig library I developed with the intent to provide a C interface but it looks like making the header file will be done by hand.

This seems like an important use case for zig at least according to the language reference so it will be good when it becomes a priority again.

Currently it does not work with the latest master, but it works with zig 0.13.0.

❯ zig version
0.13.0
❯ cat test.zig
export fn foo() c_int {
    return 42;
}
❯ zig build-lib -femit-h test.zig
❯ cat test.h
#include "zig.h"
zig_extern int foo(void);

Thank you, I did not think to try 0.13, that’s very good to know!