# How can zls get a configurable system include path for c library?

**URL:** <https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879>\
**Category:** Help\
**Tags:** c, build-system, zls\
**Created:** [July 9, 2025, 11:28am UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879 "2025-07-09T11:28:39Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 9, 2025, 11:28am UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/1 "2025-07-09T11:28:39Z")

</div>

Hi, I’m working on a cross-platform Zig project that builds and links several upstream C libraries dynamically, using `zig build` and a build.zig script. Since I cannot assume fixed include/library paths or system dependencies across platforms (e.g. Linux, MinGW, MSVC), I pass these paths into the build system using custom `-D` options like this:  
**build.zig:**

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const add_include_path = b.option([]std.Build.LazyPath, "add_include_path", "Add Include Path");
    const add_library_path = b.option([]std.Build.LazyPath, "add_library_path", "Add Library Path");
    const add_syslinks = b.option([][]const u8, "add_syslinks", "Add System Links");

    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "my_app",
        .root_source_file = b.path("tests/zig-tests/test.zig"),
        .target = target,
        .optimize = optimize,
    });
    // exe.addSystemIncludePath(.{ .cwd_relative = "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\include" });
    if (add_include_path) |include_paths| {
        for (include_paths) |include_path| {
            exe.addSystemIncludePath(include_path);
        }
    }
    if (add_library_path) |library_paths| {
        for (library_paths) |library_path| {
            exe.addLibraryPath(library_path);
        }
    }
    if (add_syslinks) |syslinks| {
        for (syslinks) |syslink| {
            exe.linkSystemLibrary(syslink);
        }
    }
    exe.linkLibC();
    b.installArtifact(exe);
}

```

**.vscode/settings.json:**

```json
{
    "zig.buildArgs": [
        "-Dadd_include_path=C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\include",
        "-Dadd_library_path=C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\lib",
        "-Dadd_library_path=C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\p\\pcre2\\10.44\\b9cd3e52829b49b5a6277a0f8d46b73b\\lib",
        "-Dadd_library_path=C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\llhttp\\v9.2.1\\f789a618795045469bb19f8380337983\\lib",
        "-Dadd_library_path=C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.1\\64e7237eeea44845a55e4edf99cec725\\lib",
        "-Dadd_syslinks=ole32",
        "-Dadd_syslinks=rpcrt4",
        "-Dadd_syslinks=winhttp",
        "-Dadd_syslinks=ws2_32",
        "-Dadd_syslinks=user32",
        "-Dadd_syslinks=crypt32",
        "-Dadd_syslinks=advapi32",
        "-Dadd_syslinks=git2",
        "-Dadd_syslinks=pcre2-posix",
        "-Dadd_syslinks=pcre2-8",
        "-Dadd_syslinks=llhttp",
        "-Dadd_syslinks=z"
    ]
}

```

This works perfectly fine when building the project with `zig build`. However, zls is unable to see these include paths, which causes problems that, the vscode ziglang plugin cannot find the files to include with `@cImport` and `@cInclude`, even though the build works.  
I understand that `zls` likely doesn’t run `build.zig` with the full options, or can’t observe runtime logic in the build script. But in my use case, is there any way to make `zls` aware of dynamically configured system include paths?

---

<div class="post-metadata">

**Author:** ![typedlambda](https://ziggit.dev/user_avatar/ziggit.dev/typedlambda/32/3613_2.png) [@typedlambda](https://ziggit.dev/u/typedlambda)\
**Post date:** [July 9, 2025, 12:40pm UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/2 "2025-07-09T12:40:20Z")

</div>

If you have build\_on\_save enabled for zls build.zig will be used.  
You could @embedFile a config file, and parse it in the build script, instead of passing arguments.

---

<div class="post-metadata">

**Author:** ![typedlambda](https://ziggit.dev/user_avatar/ziggit.dev/typedlambda/32/3613_2.png) [@typedlambda](https://ziggit.dev/u/typedlambda)\
**Post date:** [July 9, 2025, 12:45pm UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/3 "2025-07-09T12:45:30Z")

</div>

Actually, you can pass build options in a zls.build.json….

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 9, 2025, 2:22pm UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/4 "2025-07-09T14:22:08Z")

</div>

Thank you for your advice. Now it works!  
Here is my example.  
**build.zig:**

```zig
const std = @import("std");
const raw_build_config = @embedFile("build_config.json");

pub fn build(b: *std.Build) void {
    var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena_state.deinit();
    const arena = arena_state.allocator();
    const BuildConfig = struct { add_system_include_paths: [][]const u8, add_library_paths: [][]const u8, link_system_librarys: [][]const u8 };
    const parsed = std.json.parseFromSlice(
        BuildConfig,
        arena,
        raw_build_config,
        .{},
    ) catch |err| {
        std.debug.print("failed to parse build_config.json: {}\n", .{err});
        std.process.exit(1);
    };
    defer parsed.deinit();

    const build_config = parsed.value;

    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "my_app",
        .root_source_file = b.path("tests/zig-tests/test.zig"),
        .target = target,
        .optimize = optimize,
    });
    // exe.addSystemIncludePath(.{ .cwd_relative = "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\include" });
    for (build_config.add_system_include_paths) |system_include_path| {
        exe.addSystemIncludePath(.{ .cwd_relative = system_include_path });
    }
    for (build_config.add_library_paths) |library_path| {
        exe.addLibraryPath(.{ .cwd_relative = library_path });
    }
    for (build_config.link_system_librarys) |system_library| {
        exe.linkSystemLibrary(system_library);
    }
    exe.linkLibC();
    b.installArtifact(exe);

    const exe_check = b.addExecutable(.{
        .name = "my_app",
        .root_source_file = b.path("tests/zig-tests/test.zig"),
        .target = target,
        .optimize = optimize,
    });
    for (build_config.add_system_include_paths) |system_include_path| {
        exe.addSystemIncludePath(.{ .cwd_relative = system_include_path });
    }
    const check = b.step("check", "Check if my_app compiles");
    check.dependOn(&exe_check.step);
}

```

**build\_config.json:**

```json
{
    "add_system_include_paths": [
        "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\include"
    ],
    "add_library_paths": [
        "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\libgit2\\v1.9.1\\ec21fe5c9cd84177a9b2ae7b10364e0e\\lib",
        "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\p\\pcre2\\10.44\\b9cd3e52829b49b5a6277a0f8d46b73b\\lib",
        "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\l\\llhttp\\v9.2.1\\f789a618795045469bb19f8380337983\\lib",
        "C:\\Users\\ly\\AppData\\Local\\.xmake\\packages\\z\\zlib\\v1.3.1\\64e7237eeea44845a55e4edf99cec725\\lib"
    ],
    "link_system_librarys": [
        "ole32",
        "rpcrt4",
        "winhttp",
        "ws2_32",
        "user32",
        "crypt32",
        "advapi32",
        "git2",
        "pcre2-posix",
        "pcre2-8",
        "llhttp",
        "z"
    ]
}

```

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 9, 2025, 2:23pm UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/5 "2025-07-09T14:23:10Z")

</div>

I am still very curious about `zls.build.json`, since I cannot find documentions about this.

---

<div class="post-metadata">

**Author:** ![JacobCrabill](https://ziggit.dev/user_avatar/ziggit.dev/jacobcrabill/32/1316_2.png) [@JacobCrabill](https://ziggit.dev/u/JacobCrabill)\
**Post date:** [July 9, 2025, 4:36pm UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/6 "2025-07-09T16:36:28Z")

</div>

ZLS docs are here: [Per-Build Config - zigtools](https://zigtools.org/zls/configure/per-build/)

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [July 10, 2025, 11:53am UTC](https://ziggit.dev/t/how-can-zls-get-a-configurable-system-include-path-for-c-library/10879/7 "2025-07-10T11:53:54Z")

</div>

Thank you! I learn how it work. In the end, I think an indepedent embed json file to analyse is the correct way to do it, but more complicated. I’m not sure whether the option is a good way, maybe not, though it seems easier and more official.
