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.