C (and C++*) precompiled dependencies using Conan

C (and C++*) precompiled dependencies using Conan


Hi!

I’m a maintainer for the Conan C and C++ package manager, but I’m also involved in the support of Zig in Compiler Explorer since the begining, and for some time now (ever since CE added library support with the args library) I though it would be cool to be able to to have an easier way to use precompiled C libraries in Zig code (C++ can be harder to interop as I learned when implementing this*), as right now the main way is to have zig compile the code itself, so I set out in trying to find if something better was possible.

The result currently lives under a PR in the Conan repository, where a usual Conan flow generates enough info for Zig to consume most C libraries with the usual zig build.

I’m showcasing this looking for feedback/insight into the implementation of the Conan-generated Zig helpers, as Conan would love to support Zig, as it has always been a very interesting project to support :). The files are Jinja2 templates in the conan/tools/zig/zigdeps.py file, or available by running the example below. It currently has some limitations regarding libraries that require specific flags, but those are the minority in Conan Center Index (the canonical repository for dependencies in Conan) after a cursory search.

* C++ support is limited to libraries that expose headers that can be consumed using C, as most C++ features are not mappeable in Zig

Example usage

Having a conanfile.txt such as:

[requires]
openssl/3.5.4

[generators]
ZigDeps

With a Zig code that simply hashes some contents (LLM generated for example purposes):

const std = @import("std");

// ZigDeps put OpenSSL's include directories on this
// module, so @cInclude resolves without any path being written here.
const ssl = @cImport({
    @cInclude("openssl/evp.h");
    @cInclude("openssl/crypto.h");
});

pub fn main() !void {
    const msg = "conan + zig";

    var digest: [ssl.EVP_MAX_MD_SIZE]u8 = undefined;
    var len: c_uint = 0;

    const ctx = ssl.EVP_MD_CTX_new() orelse return error.OpenSslFailed;
    defer ssl.EVP_MD_CTX_free(ctx);

    if (ssl.EVP_DigestInit_ex(ctx, ssl.EVP_sha256(), null) != 1) return error.OpenSslFailed;
    if (ssl.EVP_DigestUpdate(ctx, msg, msg.len) != 1) return error.OpenSslFailed;
    if (ssl.EVP_DigestFinal_ex(ctx, &digest, &len) != 1) return error.OpenSslFailed;

    std.debug.print("{s}\n", .{std.mem.span(ssl.OpenSSL_version(ssl.OPENSSL_VERSION))});
    std.debug.print("sha256(\"{s}\") = ", .{msg});
    for (digest[0..len]) |b| std.debug.print("{x:0>2}", .{b});
    std.debug.print("\n", .{});
}

And a simple build.zig

const std = @import("std");
const conan = @import("conan_zig_deps/conan_setup.zig");  // Conan generated file

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // A plain Zig module - no C or C++ sources of our own.
    const mod = b.createModule(.{
        .root_source_file = b.path("main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // Only the "crypto" component is needed. Its own requires - openssl::crypto ->
    // zlib::zlib - are followed automatically, so zlib is linked without naming it.
    conan.linkDependency(mod, "openssl::crypto");

    const exe = b.addExecutable(.{ .name = "digest", .root_module = mod });
    b.installArtifact(exe);

    const run = b.addRunArtifact(exe);
    b.step("run", "Run the Zig program").dependOn(&run.step);
}

Normal workflows of Conan and Zig produce a valid binary:

$ conan install -b=missing
$ zig build run

OpenSSL 3.5.4 30 Sep 2025
sha256("conan + zig") = c69d96afb1f7a8ea85d27a29245f1a31bb3e0026de72f0e4f762ad93fac142e6

More examples can be found in the PR comments

Supported Zig versions

This was developed using Zig 0.17, some of the APIs used by the Conan generated helpers are missing in older versions.

AI / LLM usage disclosure

Developed by humans, tests and code comments were generated using Claude, and it was tasked with answering some of the Zig internals questions that popped up while developing the integration. It also did a final cleanup of the code (providing some bugfixes) after the implementation was considered finished, and it helped fill the gaps and wording of example usage sketchs that were provided to it.

4 Likes

Hello. Interesting project.

Some questions:

  1. How does conan maintain ABI compatiblity? What libc and libstdc++ are the prebuilt binaries compiled against?
  2. Is this meant to be only used with zig programs? Build.zig can also be used by C/C++ projects.

On a side note. I found the comment on a PR in conan repo very hard to read, as it seemed mostly a dump from a LLM.

Thanks!

  1. How does conan maintain ABI compatiblity? What libc and libstdc++ are the prebuilt binaries compiled against?

Packages are generated following a profile, and locally/in a CI those can be set to use whatever the project requires. For C++, the canonical repository for dependencies (Conan Center Index) builds them with libc++ in Macos, libstdc++11 in Linux , and msvc’s in Windows, but users are able to re-compile locally if different configurations are desired.

Is this meant to be only used with zig programs? Build.zig can also be used by C/C++ projects.

This would in fact be able to be used by C/C++ projects too, yes. The idea would be similar and this is tested in the test suite for the PR, so we expect this to be a valid workflow too

I found the comment on a PR in conan repo very hard to read, as it seemed mostly a dump from a LLM.

Yes, as the implementation is not final, not much time was provided to have solid wording in the examples/docs, with the expectation that things might change before being merged. I have updated the main post with a full example so that reading it is not required to see it working, let me know if that helps :slight_smile:

This may be a problem for projects that use zig’s builtin c++ std which is LLVM’s libc++. This integration could provide a custom libc.txt file so you can link to compatible C/C++ libs instead of zig’s one in this case.

1 Like