Hacker News new | past | comments | ask | show | jobs | submit login
Using Zig as cross-platform C toolchain (ruoyusun.com)
196 points by insraq on Feb 28, 2022 | hide | past | favorite | 38 comments

"So, a clang wrapper?" is a common thought, so here's how Zig differs from clang out of the box:

* Links MachO binaries for Apple Silicon via the custom zld linker it ships. LLVM cannot do this currently.

* Provides (deduplicated) libc headers for pretty much every platform, including macOS and glibc/musl. https://github.com/ziglang/zig/tree/master/lib/libc/include

* Provides a libc implementation (libSystem for macOS, musl and glibc, mingw for Windows, and WASI)

* Deals with lots of the deep depths of hell, like enabling you to target any version of glibc out of the box by building symbol mappings: https://github.com/ziglang/glibc-abi-tool/

And that doesn't mention the most important part, IMO, which is that it lets you cross compile _out of the box_. No fiddling with sysroots, system packages, etc. to get a cross compiling toolchain working.

Does it also work with C++? If so, it seems like a very good alternative compared to the MSVC + clang-cl toolchain in Windows (which you need to install gigabytes of libraries before doing any development).

Also, does it also work with build systems like CMake flawlessly? (I know that you can use Zig as a build system, but I would still want to leverage the ecosystem of C++ libraries compatible with CMake.)

Best to try it to figure it out. I tried compiling a trivial main function with `zig c++` (without cross compile) and the produced binary crashed when run. I think it's far more battle tested for C. But I look forward to C++ being improved as well.

Yes, Zig is also a complete C++ and ObjC/C++ toolchain. For some operating system it also comes with system headers (e.g. for building Win32 or macOS applications).

It should be possible to use Zig as clang replacement in traditional C/C++ build systems, just by replacing the compiler invocation with 'zig cc', although I heard this sometimes causes subtle problems because of the space between 'zig' and 'cc', so it probably needs to be wrapped with a script file or maybe alias.

> Does it also work with C++?

It wraps clang, so it supports whatever clang supports, C++ included.

> Also, does it also work with build systems like CMake flawlessly?

I haven't personally tried it, but I'd expect that if all else fails you could add a step in build.zig to run CMake and then link against whatever library you just built.

Does this include libc++ (compiled shared library of the STL complete with headers and debug symbols?) Although I hate most parts of the STL I still find myself using some parts of it (std::vector, std::string, <algorithm>, <type_traits>, etc.). Maybe I can link libc++ as an external library but then that's too much busywork needed for Zig to be a complete C++ dev environment out-of-the-box.

As an experiment a while back, I had tried in on a small project of mine that uses std::vector, std::string, and <algorithm> (though not a whole lot else). It built, ran, and passed my test suite.

I haven't actually used it myself, but I believe yes to both. It wraps clang, and while it's not quite as simple as "just a wrapper" it should be able to do anything that clang can do.

Repeating my other comment, but I'm also interested if the STL also works out-of-the-box (is libc++ included?). Without it you can't really say it supports C++.

Can't find a definitive answer to this, but this merged PR looks like it might implement support for this: https://github.com/ziglang/zig/issues/4786

That's good news. I might try using it someday...

> enabling you to target any version of glibc out of the box by building symbol mappings: https://github.com/ziglang/glibc-abi-tool/

This would be huge. How can I tell zig cc to use a particular glibc version though?

For example:

zig cc -target x86_64-linux-gnu.2.28

That's very cool. While searching for it, I only found an older blog post where it said that internally it can do that, but there wasn't an option yet.

For anyone else trying this, it seems like Autotools/configure doesn't like CC with arguments, creating a wrapper script works though.

And the whole thing is a (much) smaller download than Clang, somehow.

I gave it a try and was very impressed.

Not disagreeing with the rest, but LLD can link for Apple Silicon as well, and you could use Clang with zld as well if you wanted.

TIL about clang's -fsanitize=undefined, and immediately discovered and corrected a handful of subtle bugs lurking in one of my projects. Thanks, Zig, and thanks, Clang.

Crazy how often this catches undefined behavior. Compiling GLFW with Zig we've found like ~4 separate UB issues, one I described here[0]

Wish it could default to on with clang, but probably it'd break too many things. Really wonder how many severe issues we'd find just from turning this on by default everywhere, though.

[0] https://devlog.hexops.com/2021/perfecting-glfw-for-zig-and-f...

UBSAN adds a lot of runtime overhead. Turning it on my default would mean most software built with clang (or GCC, which has also had it for years) would suddenly be dog slow, like MSVCC in debug mode. Few developers have any idea how to use their toolchain correctly, so the defaults need to be chosen wisely.

I recently started testing a project with -fsanitize=integer as well. It'll report a few potential issues that -fsanitize=undefined won't, like left shifting a 1-bit off the end.

GCC supports many of the same sanitizers as well, since they were originally developed for GCC. We use GNU's ubsan and asan in our automated test suite.

There's a great article about using Zig as a cross-platform C toolchain for compiling CGo, to bring easy cross-platform compilation to it, which is usually a pain: https://dev.to/kristoff/zig-makes-go-cross-compilation-just-...


that’s… a great idea.

I recently learned that Clang supports this kind of cross-compiling out of the box. https://mcilloni.ovh/2021/02/09/cxx-cross-clang/

The main difference is that Clang does not ship with headers/libraries for different platforms, as Zig appears to do. You need to give Clang a "sysroot" -- a path that has the headers/libraries for the platform you want to compile for.

If you create a bunch of sysroots for various architectures, you can do some pretty "easy" cross-compiling with just a single compiler binary. Docker can be a nice way of packaging up these sysroots (especially combined with Docker images like manylinux: https://github.com/pypa/manylinux). Gone are the days when you had to build a separate GCC cross-compiler for each platform you want to target.

Yes, for instance for ossia score, the sequencer I'm working on which does some runtime compiling of c++ through clang & llvmjit, I ship a sdk for each platform with all the libc, libc++ headers. What a pain it was !

My scripts are the create-sdk-... Here if that can be useful to anyone: https://github.com/ossia/score/tree/master/ci and the SDKs are available at https://github.com/ossia/sdk

Zig's cross-compilation tooling and C interop is really impressive. I love to see projects that put so much emphasis on solving real-world practical problems like this and implement it so well.

It makes me wonder whether cross-language tooling could be made to work more broadly. It would be awesome if you could have one compiler that would seamlessly compile zig/C/C++/Rust, and perhaps Fortran/ADA too.

"...an absolute joy to work with" I agree. I used zig to make a game in WASM and had a blast. I also had some spare Arduino Nanos laying around and so I build a simple firmware in zig to test it. It worked great, so you can use zig for your embedded projects as well.

I'm almost sold, I checked zig briefly at its home page in the past and never used it. the cross platform build(similar to golang and rust) for c is very interesting.

what about the mac-os(or linux,etc) native libraries on windows where you're building the demo code? The 60MB zig compiler and native clang on Windows will not have them, how is that done? Is it totally on clang side?

Apple does not allow to use their SDK on non-Apple hardware. So if you need to use MacOS headers and libraries, the hardware where the compiler runs must be from Apple. Apple has no problem if you run Linux on that and cross-compile from it to Mac.

At work we could not for that reason use the same compilation server setup to cross-compile for Linux, MacOS, Windows and ended up with a separated MacPro box for the compilation farm.

EDIT: cross-compilation from Windows works after one gets Windows SDK and Visual Studio libraries (the later is not free, but free alternatives often are sufficient). The biggest headache is that Windows headers assume a case-insensitive file system and includes, for example, windows.h when the file is Windows.h. One can either use case-insensitive mount option for ext4 or symlink relevant files to the proper case.

Note however Zig actually provides libSystem.tbd stubs out of the box, so you can cross-compile from any OS to macOS too as long as you only depend on libSystem (the macOS libc.)

If you need the rest of the Apple SDK, then you'd need a sysroot (and face the challenges you described.)

VS community edition is free (but not an option for commercial work).

Another Clang-specific way to handle the case insensitivity is by making use of a virtual file system (VFS) overlay. https://github.com/llvm/llvm-project/blob/4976d1fe58f89169ef... demonstrates how that works. (There's no linker equivalent that I'm aware of, so you do have to use the symlinks or something like ciopfs for that; https://github.com/llvm/llvm-project/blob/4976d1fe58f89169ef... is an example of symlink generation.)

I'm really liking the enthusiasm about zig these days. Kinda mirrors what first had when it was starting to become popular

Zig can also target WebAssembly (standalone and WASI), so you don't need to download and install yet another LLVM toolchain, or fiddle with libclang_rt.builtins-wasm32.a.

So basically a clang wrapper?

Its caching system is also smart enough to handle #include dependencies, so for simple C/C++ projects, it can also function as a make alternative (without having to write a Makefile).

Plus, it makes cross-compilation really easy.

Now that's interesting. This is something that breaks go's cache (for cgo).

At first glance, yeah probably. But the advantage with zig is that it has c interoperability and the concept around the programmable build system (build.zig)

It's a clang wrapper that makes clang just work for cross-compilation.

In addition to being its own programming language and build system as well.

Applications are open for YC Summer 2023

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact