Help with including C header

I’m trying to learn zig and I wanted to test a single function for learning purposes even though it’s not really required I wanted to know that its doing what I expected

const std = @import("std");
const c = @cImport(@cInclude("include/miniaudio.h"));

const Playlist = struct {
    songs: std.ArrayList([]const u8), // Dynamic array for song file paths
    current_index: usize, // Current song index
    paused: bool, // Flag to check if the song is paused
    context: c.ma_context, // Miniaudio context
    device: c.ma_device, // Miniaudio device for audio playback
    sound: c.ma_sound, // Miniaudio sound
};

// Initialise the playlist with default values
fn initPlaylist() Playlist {
    return Playlist{
        .song = std.ArrayList([]const u8).init(std.heap.page_allocator),
        .current_index = 0,
        .paused = false,
        .context = undefined,
        .device = undefined,
        .sound = undefined,
    };
}

test "test initPlaylist" {
    var playlist = initPlaylist();
    const expect = std.testing.expect;

    // Check that the songs array is initialised and empty
    try expect(playlist.songs.items.len == 0);

    // Check the current index is initialised and 0
    try expect(playlist.current_index == 0);

    // Check the paused flag is initialised and false
    try expect(playlist.paused == false);

    // Check that the context is initialized to undefined
    try expect(playlist.context == undefined);

    // Check that the device is initialized to undefined
    try expect(playlist.device == undefined);

    // Check that the sound is initialized to undefined
    try expect(playlist.sound == undefined);

    // Clean up to prevent a memory leak
    playlist.songs.deinit();
}

Note I’ve tried the following

const c = @cImport({
    @cInclude("include/miniaudio.h");
});

And

const c = @cImport({
    @cInclude("miniaudio.h");
});

I keep getting the same error

Build.zig:

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.
    const target = b.standardTargetOptions(.{});

    // Standard release options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
    const mode = b.standardReleaseOptions();

    const exe = b.addExecutable("zig-music", "src/main.zig");
    exe.setTarget(target);
    exe.setBuildMode(mode);
    exe.install();

    exe.addIncludePath("include");
    // exe.linkSystemLibrary("c");

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    const exe_tests = b.addTest("src/main.zig");
    exe_tests.setTarget(target);
    exe_tests.setBuildMode(mode);
    exe_tests.addIncludePath("include");

    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&exe_tests.step);
}

Project:

tree .
.
├── build.zig
├── include
│   └── miniaudio.h
├── src
│   └── main.zig

Error when running zig build test:

zig build test
/tmp/zig-music/src/main.zig:2:11: error: C import failed
const c = @cImport(@cInclude("miniaudio.h"));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/zig-music/src/main.zig:2:11: note: libc headers not available; compilation does not link against libc
referenced by:
    Playlist: /tmp/zig-music/src/main.zig:8:14
    Playlist: /tmp/zig-music/src/main.zig:4:18
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

error: test...
error: The following command exited with error code 1:
/usr/bin/zig test /tmp/zig-music/src/main.zig --cache-dir /tmp/zig-music/zig-cache --global-cache-dir ~/.cache/zig --name test -I /tmp/zig-music/include --enable-cache 
error: the following build command failed with exit code 1:
/tmp/zig-music/zig-cache/o/b88a9267b8f4ddb25693e550dad2dcfb/build /usr/bin/zig /tmp/zig-music /tmp/zig-music/zig-cache ~/.cache/zig test

I’m using Void Linux

zig env
{
 "zig_exe": "/usr/bin/zig",
 "lib_dir": "/usr/lib/zig",
 "std_dir": "/usr/lib/zig/std",
 "global_cache_dir": "~/.cache/zig",
 "version": "0.10.1",
 "target": "x86_64-linux.6.6.31...6.6.31-gnu.2.39"

After actually looking at the output of zig env I’m wondering is this because of the version of zig installed?

I am happy to grab the latest version and try again, but that isn’t going to help my understanding of why this error is happening.

Thank you.

To link against libc call linkLibC in your build.zig file:

    ...
    exe.addIncludePath("include");
    exe.linkLibC();
    ...

Hi,

Thanks for your reply I’ve updated my build.zig file with your suggestion but I’m still getting the error

rm -fr ~/.cache/zig zig-cache
 zig build test
/tmp/zig-music/src/main.zig:3:11: error: C import failed
const c = @cImport({
          ^~~~~~~~
/tmp/zig-music/src/main.zig:3:11: note: libc headers not available; compilation does not link against libc
referenced by:
    Playlist: /tmp/zig-music/src/main.zig:11:14
    Playlist: /tmp/zig-music/src/main.zig:7:18
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

error: test...
error: The following command exited with error code 1:
/usr/bin/zig test /tmp/zig-music/src/main.zig --cache-dir /tmp/zig-music/zig-cache --global-cache-dir ~/.cache/zig --name test -I /tmp/zig-music/include --enable-cache 
error: the following build command failed with exit code 1:
/tmp/zig-music/zig-cache/o/619efab48cd1e5dc6b813a0d765eed5b/build /usr/bin/zig /tmp/zig-music /tmp/zig-music/zig-cache ~/.cache/zig test

I am Sorry, I thought that you run zig build run.
For zig build test to work exe_tests.linkLibC(); is also required.

I included the change and I’m still getting the same error.

zig version 0.10.1!
Why don’t you use 0.12.0?

1 Like

its what came in the repo. I’m trying 0.12 now and getting more or less the same error, before I ask for help again I just want to try a few more things first.

I think the further errors might be about a missing define that triggers the implementation to be included. Also take a look at this topic:

1 Like

I’ve updated things with the current suggestions I’ve also changed my build.zig

Current output with Zig 0.12

../zig-linux-x86_64-0.12.0/zig build test --summary all
test
└─ run test
   └─ zig test Debug native 2 errors
src/main.zig:2:11: error: C import failed
const c = @cImport(@cInclude("miniaudio.h"));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    Playlist: src/main.zig:11:14
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
/tmp/zig-music-12/zig-cache/o/bfac3a33fa7631616caaccd410df7966/cimport.h:1:10: error: 'miniaudio.h' file not found
#include <miniaudio.h>
         ^
error: the following command failed with 2 compilation errors:
/home/kai/tmp/zig-linux-x86_64-0.12.0/zig test -ODebug -Mroot=/tmp/zig-music-12/src/main.zig -lc --cache-dir /tmp/zig-music-12/zig-cache --global-cache-dir ~/.cache/zig --name test --listen=- 
Build Summary: 2/5 steps succeeded; 1 failed; 1/1 tests passed
test transitive failure
├─ run test 1 passed 689us MaxRSS:1M
│  └─ zig test Debug native success 3s MaxRSS:190M
└─ run test transitive failure
   └─ zig test Debug native 2 errors
error: the following build command failed with exit code 1:
/tmp/zig-music-12/zig-cache/o/a288c827df7bff6a6ef80f8441abca6b/build /tmp/zig-linux-x86_64-0.12.0/zig /tmp/zig-music-12 /tmp/zig-music-12/zig-cache ~/.cache/zig --seed 0x9048192a -Z5efebc500ea82f36 test --summary all
 tree .
.
├── build.zig
├── build.zig.zon
├── include
│   ├── miniaudio.c
│   └── miniaudio.h
├── src
│   ├── main.zig
│   └── root.zig

Cat include/miniaudio.c

#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"

Build file:

const std = @import("std");

// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.
    const target = b.standardTargetOptions(.{});

    // Standard optimization options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
    // set a preferred release mode, allowing the user to decide how to optimize.
    const optimize = b.standardOptimizeOption(.{});

    const lib = b.addStaticLibrary(.{
        .name = "zig-music-12",
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
    });

    // This declares intent for the library to be installed into the standard
    // location when the user invokes the "install" step (the default step when
    // running `zig build`).
    b.installArtifact(lib);

    const exe = b.addExecutable(.{
        .name = "zig-music-12",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    exe.linkLibC();
    exe.addIncludePath(.{ .path = "include" });
    // exe.addCSourceFiles(&[_][]const u8{"include/miniaudio.c"}, &[_][]const u8{ "-g", "-O3" });
    exe.addCSourceFile(std.Build.Module.CSourceFile{ .file = .{ .path = "include/miniaudio.c" }, .flags = &[_][]const u8{ "-g", "-O3" } });

    // Add the miniaudio C source file and include directory
    // exe.addCSourceFile("include/miniaudio.c");

    // This declares intent for the executable to be installed into the
    // standard location when the user invokes the "install" step (the default
    // step when running `zig build`).
    b.installArtifact(exe);

    // This *creates* a Run step in the build graph, to be executed when another
    // step is evaluated that depends on it. The next line below will establish
    // such a dependency.
    const run_cmd = b.addRunArtifact(exe);

    // By making the run step depend on the install step, it will be run from the
    // installation directory rather than directly from within the cache directory.
    // This is not necessary, however, if the application depends on other installed
    // files, this ensures they will be present and in the expected location.
    run_cmd.step.dependOn(b.getInstallStep());

    // This allows the user to pass arguments to the application in the build
    // command itself, like this: `zig build run -- arg1 arg2 etc`
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    // This creates a build step. It will be visible in the `zig build --help` menu,
    // and can be selected like this: `zig build run`
    // This will evaluate the `run` step rather than the default, which is "install".
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // Creates a step for unit testing. This only builds the test executable
    // but does not run it.
    const lib_unit_tests = b.addTest(.{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

    const exe_unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    exe_unit_tests.linkLibC();

    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

    // Similar to creating the run step earlier, this exposes a `test` step to
    // the `zig build --help` menu, providing a way for the user to request
    // running the unit tests.
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_lib_unit_tests.step);
    test_step.dependOn(&run_exe_unit_tests.step);
}

Use addIncludePath to add the include path to the file miniaudio.h

exe_unit_tests.addIncludePath(.{ .path = "include" });
1 Like

That looks like it worked! Thank you!

I spoke too soon :frowning:

~/tmp/zig-linux-x86_64-0.12.0/zig build test --summary all
test
└─ run test
   └─ zig test Debug native 2 errors
src/main.zig:2:11: error: C import failed
const c = @cImport(@cInclude("miniaudio.h"));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.zig:2:11: note: libc headers not available; compilation does not link against libc
referenced by:
    Playlist: src/main.zig:11:14
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
/tmp/zig-music-12/zig-cache/o/29d3046096a5a1923f7023b9e81e3da1/cimport.h:1:10: error: 'miniaudio.h' file not found
#include <miniaudio.h>

EDIT:
Anyone else in the future comes across this problem do the marked solution PLUS add the following to both exe_unit_tests and exe in the bulid.zig

    const exe_unit_tests = b.addTest(.{
       ...
        .link_libc = true,
    });

3 Likes

this is the type of stuff i give up on most languages, it says in the docs or somewhere else it supports a certain feature, and it turns into a nightmare.

i had this with a lot of other languages too, funny enough, even do i hate rust, i never had this type of problems with it, it just worked

right now im trying to make my own godot engine plugin with gdextension, and im trying my best to avoid using c++ XD, so i tried all the available language bindings, including zig, but the only one so far i was able to get to work is rust

I feel your pain. Hope this helps. If it does, give me a like.

TLDR;

Your code

zig test main.zig -lc -I ../src/
All 1 tests passed.

LONG

  1. Download

I downloaded miniaudio.h from https://miniaud.io/
I downloaded zig13 which is marked as stable ie zig version 0.13.0
I downloaded your code from your first post

  1. Setup

I created a subdirectory and changed to it
I ran #> zig init
This created a src directory which I changed to
I copied miniaudio.h to src
I copied your code to src

  1. Initial test run and issues

zig test main.zig -lc -I …/src/

I got errors about songs not having s on them and undefined not being allowed. So I added s
and // out the all offending code.

Here is the diff file

diff main.zig main.zig_original
16c16
<         .songs = std.ArrayList([]const u8).init(std.heap.page_allocator),
---
>         .song = std.ArrayList([]const u8).init(std.heap.page_allocator),
39c39
<     //try expect(playlist.context == undefined);
---
>     try expect(playlist.context == undefined);
42c42
<     //try expect(playlist.device == undefined);
---
>     try expect(playlist.device == undefined);
45c45
<     //try expect(playlist.sound == undefined);
---
>     try expect(playlist.sound == undefined);
  1. rerun

zig test main.zig -lc -I ../src/
All 1 tests passed.

Here is my version of your code.

const std = @import("std");
const c = @cImport(@cInclude("miniaudio.h"));

const Playlist = struct {
    songs: std.ArrayList([]const u8), // Dynamic array for song file paths
    current_index: usize, // Current song index
    paused: bool, // Flag to check if the song is paused
    context: c.ma_context, // Miniaudio context
    device: c.ma_device, // Miniaudio device for audio playback
    sound: c.ma_sound, // Miniaudio sound
};

// Initialise the playlist with default values
fn initPlaylist() Playlist {
    return Playlist{
        .songs = std.ArrayList([]const u8).init(std.heap.page_allocator),
        .current_index = 0,
        .paused = false,
        .context = undefined,
        .device = undefined,
        .sound = undefined,
    };
}

test "test initPlaylist" {
    var playlist = initPlaylist();
    const expect = std.testing.expect;

    // Check that the songs array is initialised and empty
    try expect(playlist.songs.items.len == 0);

    // Check the current index is initialised and 0
    try expect(playlist.current_index == 0);

    // Check the paused flag is initialised and false
    try expect(playlist.paused == false);

    // Check that the context is initialized to undefined
    //try expect(playlist.context == undefined);

    // Check that the device is initialized to undefined
    //try expect(playlist.device == undefined);

    // Check that the sound is initialized to undefined
    //try expect(playlist.sound == undefined);

    // Clean up to prevent a memory leak
    playlist.songs.deinit();
}