How to build clang and libc++ for macOS and iOS devices?

I’ve been building Clang from source and using it to compile things on my host system (macOS, x86_64). Now I’d like to build llvm/clang/libc++ so I can create iOS apps as well. I tried:

cmake -S llvm -B build -G Ninja 

That last -D option was a guess. I get the error below.

Is there a CMake invocation that will allow me to build:

  1. A clang that can compile binaries for macOS and iOS devices (x86, arm)
  2. The libraries like libc++.a that I can statically link to deploy apps to those devices.

Thank you.

CMake Error at runtimes/CMakeLists.txt:54 (message):
  compiler-rt for Darwin builds for all platforms and architectures using a
  single configuration.  Specify only a single darwin triple (e.g.
  x86_64-apple-darwin) in your targets list (and not a triple for a specific
  platform such as macos).  You can use variables such as
  COMPILER_RT_ENABLE_IOS and DARWIN_ios_ARCHS to control the specific
  platforms and architectures to build.  Set RUNTIMES_BUILD_ALLOW_DARWIN to
  allow a single darwin triple.
Call Stack (most recent call first):
  runtimes/CMakeLists.txt:438 (check_apple_target)

Just reading that message it seems that you should give one triple to RUNTIME_TARGETS and the result will be all possible configurations for Darwin. Is that not what you get when you try that?

I think it’s basically saying that the Darwin build bucks the trend of the runtimes build, which is for the user to specify individual targets. So you must opt into that behaviour for Darwin if you want it (and here you don’t want to opt in).

If I use one triple there and try the other settings in the error message, I get something that builds, but I still don’t see a libc++ anywhere built for ARM. It’s tough to just guess, because it takes about an hour to build this on my laptop.

% cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" 
% ninja -C build
% file build/lib/libc++.1.0.dylib 
build/lib/libc++.1.0.dylib: Mach-O 64-bit dynamically linked shared library x86_64 

I think you do not want -DRUNTIMES_BUILD_ALLOW_DARWIN=ON because that’s causing it to build just for the single triple, as opposed to all supported Darwin architectures.

The original commit might explain it better: ⚙ D86313 [runtimes] Allow LLVM_BUILTIN_TARGETS to include Darwin

If I leave that off, then the cmake command fails, with the error in my original post.

Then I have misread that help text then, and without a Mac to try this I’m no further help.

@smeenai Do you by any chance remember the logic here?

1 Like

I think those error messages were about the compiler-rt component, which is not libc++. I did get multi-architecture builds of what I think are the compiler-rt libraries. So maybe all I need now is to find out how to do the same thing with libc++.

% ls build/lib/clang/17/lib/darwin/                                  

@tamas might know the magic incantation for this. If I remember correctly you shouldn’t use runtimes at all for Darwin.

1 Like

I tried rerunning my command above with -DLLVM_RUNTIME_TARGETS="arm64-apple-darwin" and it ended up making an libc++ that looks like it’s for ARM, but leads to errors like the one below.

I’m going to stop guessing now, because every time I try something, it’s hour of CPU fans on my MacBook, rebuilding everything.

ld: in /Dev/llvm-project/build/lib/libc++experimental.a(memory_resource.cpp.o), building for iOS, 
  but linking in object file built for macOS,
  file '/Dev/llvm-project/build/lib/libc++experimental.a' for architecture arm64

We have:


IIRC CMake currently warns about using libcxx in LLVM_ENABLE_PROJECTS, but it works, so I’m not changing that until it breaks. I don’t know if that warning is intended to be shown on Darwin.

Not all of these will be relevant to everyone and frankly, some of this is probably legacy from our point of view as well. Make sure to use a fresh build dir when changing these options. Let me know how it goes, happy to help further.

1 Like

Thanks! Before I try this, let me try to understand better. You don’t have ‘clang’ in LLVM_ENABLE_PROJECTS. Did you run another build first to create clang, and you’ve got your PATH set somehow to use that new clang for this build here with compiler-rt;libcxx?

Sorry, I totally messed up (I was reading our python code that builds the toolchain). In my CMake cache I have:


and yes, LLVM_ENABLE_RUNTIMES is empty.

And you also didn’t use LLVM_RUNTIME_TARGETS nor LLVM_TARGETS_TO_BUILD? If so, I guess you are triggering the x86+ARM capabilities from that CMAKE_OSX_ARCHITECTURES setting.

If this works I’m going to file some bugs about the documentation. :slight_smile:

LLVM_RUNTIME_TARGETS is not even in the cache, LLVM_TARGETS_TO_BUILD is x86;ARM;AArch64;NVPTX, but I don’t set that explicitly either.

I don’t claim that this is the right way to to build it, just that it works. Compared to all other targets, the Darwin configuration we need to do is minuscule (probably because the defaults can be reasonably good due to Apple being a singular platform with little variation in hardware and software capabilities).

Okay, I finally got a cmake command to work, and in about 60 minutes I’ll know if the build worked.

It’s an error not a warning for me. Maybe your Python scripts working some other magic?

So I used -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;compiler-rt" and moved those three out of -DLLVM_ENABLE_PROJECTS.

% cmake -S llvm -B build -G Ninja 
  -DCMAKE_LINKER="/usr/bin/ld" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=ld" 


CMake Error at CMakeLists.txt:139 (MESSAGE):
  libcxx isn't a known project:
  Did you mean to enable it as a runtime in LLVM_ENABLE_RUNTIMES?

-- Configuring incomplete, errors occurred!
rob@RobertsMBP llvm-project % 

I appreciate the help. I feel like I’m off the beaten path here. If you don’t mind sharing, what’s your motive for building clang from source like this? In my case I wanted to try the latest features in C++, and didn’t want to wait for Apple to release them with Xcode.

Could be because I was still building LLVM15 up until now.

My impression is that most paths in this area are “not very beaten” at all, so don’t feel like you are lost - it’s just the wilderness :sweat_smile: Most folks don’t build compilers, since it’s much easier to use a prebuilt toolchain. This is especially true in the case of Apple targets (double especially because I recently learned that some things in LLVM can’t even be compiled against the public SDK).

If you don’t mind sharing, what’s your motive for building clang from source like this?

In my case, I’m maintaining this toolchain because our product (Plex Media Server) needs to target a whopping 24 configurations (OS x Arch combinations, and a few others), many of which are not readily available as targets in prebuilt toolchains. Building our toolchain allows minimizing the maintenance cost between different compilers & STLs, as well using our musl fork that we can use to produce fully portable dynamically linked executables on Linux. We also run PGO using our own codebase. Access to latest features is a nice plus, although we don’t need bleeding edge. We do still use MSVC on Windows, but I hope moving to clang soon there (but maybe not libc++, we’ll see). (This toolchain builder was originally created by @tobiashieta BTW, so that’s why he had a hunch that I might have an answer :stuck_out_tongue:)

1 Like

So it completed a build without an error, but it looks like it failed to produce libc++ as desired. It produced a bunch of fat binaries for executables (not what I need), but libc++ is x86_64 only, and strangely libc++.a is completely missing. That’s the first time that happened, so perhaps it’s something about your cmake command that I copied without completely understanding it. (Eg: -DLIBCXX_INSTALL_LIBRARY=OFF, or that long list for -DLLVM_DISTRIBUTION_COMPONENTS)

% file build/bin/clang
build/bin/clang: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64]
build/bin/clang (for architecture x86_64):	Mach-O 64-bit executable x86_64
build/bin/clang (for architecture arm64):	Mach-O 64-bit executable arm64

% file build/lib/libc++*
build/lib/libc++.1.0.dylib:     Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++.1.dylib:       Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++.dylib:         Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++abi.1.0.dylib:  Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++abi.1.dylib:    Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++abi.a:          current ar archive
build/lib/libc++abi.dylib:      Mach-O 64-bit dynamically linked shared library x86_64
build/lib/libc++experimental.a: current ar archive

I unpacked libc++experimental.a and it’s .o files are x86_64 only.

Here was my original cmake command, before I said ninja -C build.

 cmake -S llvm -B build -G Ninja 
   -DCMAKE_LINKER="/usr/bin/ld" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=ld" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=ld" 
   -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-apple-darwin -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi;compiler-rt" 

If you want a static libc++, you will probably need to pass -DLIBCXX_ENABLE_STATIC=ON as well (but I’m not certain that it is in a usable state on iOS for example).

As for the universal binaries, it might be difficult to convince the build not to do that. It is very much hardwired to do so (and you will have a better time if you allow it to). Finally, you might want to build the install-distribution-stripped or install-distribution targets.

There are like 800~ish options and cache variables in the LLVM CMake that you can pass to CMake in order to customize your build. Naturally, only a small subset of the possible configurations are actually built by people in world (let alone the LLVM project itself), so anything that differs from the default might be a painful (but not necessarily impossible) endeavor. My impression is that the defaults are especially important on Darwin where Apple dictates the way.