Can you bring your own C compiler with the zig build system?

I’m considering using the zig build system for a C project where I want to do some non trivial things at compile time and have cross compilation support. It seems like the perfect fit, but I’d also like to allow users to bring their own C compiler if they already have a C compiler for their target system with potentially better support than zig cc. Is it possible to override the C compiler?

Hello @L9QHFnQ5wg, welcome to the forum.

The answer is yes, you can call any c compiler from the zig build system.
But don’t expect the zig toolkit cross compilation benefits when you only use it for building.

To understand the zig toolkit cross compile benefits you need to try to cross compile the following trivial C code. Try to cross compile it using any toolkit, to a machine with different operating system and/or different architecture.

#include <stdio.h>

int main()
    printf("Hello World\n");
    return 0;

With zig you just need to download one toolkit and you can compile to: Windows, macOS or Linux for any supported CPU.

zig cc hello.c -target aarch64-linux-musl
zig cc hello.c -target aarch64-linux-gnu
zig cc hello.c -target x86_64-windows-msvc

With clang you can compile for the same OS only and different architecture and you cannot link, because either you are missing the other OS include file or the other OS and/or architecture libraries.

With gcc you need a different toolkit for each target, and you are going to have the same problems with clang.

Go and rust languages have wonderful cross compile functionality unless you need a C library, then your best bet is to just use the zig toolkit to help them cross compile.
rust: Zig Makes Rust Cross-compilation Just Work · Um, actually...
go: Zig Makes Go Cross Compilation Just Work - DEV Community
why: `zig cc`: a Powerful Drop-In Replacement for GCC/Clang - Andrew Kelley

1 Like

Good to know, can you share how you’d change the compiler in the build.zig while still defaulting to using zig?

You can call the C compiler using addSystemCommand

const cc = b.addSystemCommand(&.{

You are going to loose the caching and the dependency handling.

If you call zig build --verbose zig displays the invocations of zig cc and zig ld.
You can have back some of the missing functionality by copying some options from the zig cc invocations to your cc calls.

Oh, hm… that isn’t as ergonomic as I was hoping. There is nothing like CC=my-compiler that will essentially replace the zig cc calls? Or no API in std.Build that will let you replace zig cc?

No, there is no easy way.
And the reason is that you get .h .c dependency handling and caching.
It’s like having builtin ccache and makefile dependency building.

Are you afraid of the risk to use zig cc, or do you expect to have some c source that zig cc cannot compile?
If you are afraid, it is a project to change to a different build system, but you can use zig and you don’t have to pay the cost up front.
If you think that some c cannot compile, just compile it with another platform compiler as a library and link to it.

Also note that zig cc is clang:

zig cc --version
clang version 17.0.6 ( 4c78aa1bba84dbd324e178932cd52221417f63da)

I’m a bit afraid of locking to a specific C compiler. I know zig cc is clang and it is highly likely it’ll support all targets I’m interested in; I’m just trying to see if there is an out in case I’m wrong there. Thanks for your help

Before zig cc, that used to be how it worked. Today, it does not work that way, in order to make zig projects more portable and build from source reliably. However, in the future, it will be possible again, to meet the goal of the zig build system being a general-purpose build system. To compete with the established players such as CMake, it needs to be able to drive a system C/C++ toolchain. Probably, the --system flag will enable the “system C/C++ toolchain” mode in addition to the other stuff it already does.

Long-term, the ability to compile C/C++ code will be externalized into a package fetched by the build system. So there will effectively be a choice:

  • fetch a clang package to do the compilation (default)
  • rely on system tooling (--system flag)

Of course you can always do something more advanced by using Run steps, as @dimdin pointed out.