Building Distributable Toolchains for Apple Silicon

I'm building a distributable Swift toolchain using build-toolchain, with updated presets to drop cross-compilation (we're fully on Apple Silicon).

The toolchain builds fine, installs cleanly in Xcode, and works. However, I hit a linker error for iOS simulator builds:

libclang_rt.profile_iossim.a not found

As a workaround, I’ve been copying libclang_rt.profile_iossim.a from Xcode’s default toolchain and that gives me a successful build.

Questions:

  1. How can I ensure libclang_rt.profile_iossim.a is bundled as part of the toolchain build?
    compiler-rt is already included in install_components, but it seems additional configuration might be needed to include this runtime archive.

  2. Are there other presets or install components needed to more closely match Xcode’s default toolchain? I’m aiming for a fully distributable toolchain - any guidance on additional config/presets to align more closely with what Xcode toolchains provide out of the box would be super helpful.

apple_silicon_production_presets.ini

[preset: apple_silicon_production]

swift-install-components=autolink-driver;back-deployment;compiler;clang-resource-dir-symlink;libexec;stdlib;sdk-overlay;static-mirror-lib;toolchain-tools;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers
llvm-install-components=llvm-ar;llvm-ranlib;llvm-cov;llvm-profdata;IndexStore;clang;clang-resource-headers;compiler-rt;clangd;dsymutil;LTO;clang-features-file;lld

lit-args=-v
build-ninja

no-assertions

swift-stdlib-build-type=RelWithDebInfo
swift-stdlib-enable-assertions=false

swift-darwin-supported-archs=arm64
stdlib-deployment-targets=macosx-arm64
swift-primary-variant-sdk=OSX
swift-primary-variant-arch=arm64

ios
tvos
watchos
xros

libcxx
lldb
llbuild
swiftpm
swift-driver
swift-inspect
swiftsyntax
swift-testing
swiftformat
playgroundsupport
indexstore-db
sourcekit-lsp
swiftdocc

release-debuginfo

lldb-use-system-debugserver
lldb-build-type=Release
build-swift-stdlib-unittest-extra


# When building for an Xcode toolchain, don't copy the Swift Resource/ directory
# into the LLDB.framework. LLDB.framework will be installed alongside a Swift
# compiler, so LLDB should use its resource directory directly.
# Also, to reduce the size of the final toolchain, limit debug info to be
# line-tables only.
extra-cmake-options=
   -DLLDB_FRAMEWORK_COPY_SWIFT_RESOURCES=0
   -DCMAKE_C_FLAGS="-gline-tables-only"
   -DCMAKE_CXX_FLAGS="-gline-tables-only"

extra-dsymutil-args="--verify-dwarf=none"

install-llvm
install-static-linux-config
install-swift
install-lldb
install-swift-testing-macros
install-llbuild
install-swiftpm
install-swift-driver
install-swiftsyntax
install-swift-testing
install-playgroundsupport
install-sourcekit-lsp
install-swiftformat
install-swiftdocc

darwin-install-extract-symbols
no-swift-stdlib-assertions

skip-test-swift
skip-test-swiftpm
skip-test-swift-driver
skip-test-llbuild
skip-test-lldb
skip-test-cmark
skip-test-playgroundsupport
skip-test-swiftsyntax
skip-test-swiftformat
skip-test-skstresstester
skip-test-swiftdocc
skip-test-osx
skip-build-benchmarks

#Path to the root of the installation filesystem.
install-destdir=%(install_destdir)s

# Path to the .tar.gz package we would create.
installable-package=%(installable_package)s
# Path where debug symbols will be installed.
install-symroot=%(install_symroot)s

# Path where the compiler, the runtime and the standard libraries will be
# installed.
install-prefix=%(install_toolchain_dir)s/usr

# Info.plist
darwin-toolchain-bundle-identifier=%(darwin_toolchain_bundle_identifier)s
darwin-toolchain-display-name=%(darwin_toolchain_display_name)s
darwin-toolchain-display-name-short=%(darwin_toolchain_display_name_short)s
darwin-toolchain-name=%(darwin_toolchain_xctoolchain_name)s
darwin-toolchain-version=%(darwin_toolchain_version)s
darwin-toolchain-alias=%(darwin_toolchain_alias)s
darwin-toolchain-require-use-os-runtime=0

Even Apple’s toolchains have been incompatible with iOS (and all the other non macOS Apple platforms) for 5+ years now. Until the missing components are shipped with them, or they can use the right bits from existing Xcode installs, we won’t be able to use toolchains with those platforms.

I flipped -DCOMPILER_RT_ENABLE_IOS to True (link) and made a small tweak in swift/llvm-project. This gave us all the required clang dylibs (profile, asan, tsan, ubsan etc for iOS). That was enough to get a proper toolchain (supporting iOS + macOS) - no need to copy anything from Xcode’s default toolchain (atleast for now :crossed_fingers:)

That mostly answers my first question. If anyone can confirm whether these presets are close to what’s used for production-grade toolchains (even just for macOS - iOS likely follows a similar path), that'd ensure we are on the right track. (for eg: add this preset/turn this flag for this optimization or remove this preset etc)

Even Apple’s toolchains have been incompatible with iOS

Thanks, it's reassuring to know I’m not the only one running into this. While the official downloadable toolchains do have some support for iOS (though primarily for macOS), I can imagine they don’t include complete support for all platforms - maybe because there are workarounds available, or to keep distribution and CI simpler/faster. But the nice part is: since everything’s opensource, we can build equivalent toolchains locally. Not ideal but it’s still great that we have that option.

Also, if anyone’s come across other issues or had to manually pull in additional pieces, please feel free to share.

I didn't realize it would be quite that easy. It's been broken for years so I assumed it used something private to Apple rather than some flag just not being enabled. Can you use the result to build and run iOS apps in the sim and / or on device? @mishal_shah it looks like this was disabled 9 (?!) years ago due to a build issue, but apparently it may work now, so can we get it reenabled? Swift is missing a huge amount of prerelease testing by not being able to build for any other Apple platform.

Can you use the result to build and run iOS apps in the sim and / or on device?

Yes, I tested it on the simulator and everything builds fine - didn't have to copy anything from the default toolchain. I’ll continue testing tomorrow, including additional scenarios and testing on device.

However, I ran into a warning for a framework target that had Explicit Module Builds enabled. It looks like the build system isn’t honoring the libclang.dylib from the custom toolchain and is only checking in the default Xcode toolchain. I think this will also show up for pure macOS targets.

Explicit modules is enabled but could not resolve libclang.dylib, continuing with explicit modules disabled.

Candidate '/Applications/Xcode.16.1.0.16B40.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib' skipped because it did not match the configured compiler

I had to set CLANG_EXPLICIT_MODULES_LIBCLANG_PATH in the build settings to point to libclang.dylib from the custom toolchain, and that got rid of the warning(and didn't disable explicit modules). Really glad swift-build is open source!

I'm linking a thread on a related topic: Open Source Toolchain for iOS, tvOS, etc

While experimenting with these flags, I discovered that enabling compiler_rt support is straightforward on iOS but not on tvOS or watchOS. The SDKs for these platforms from Xcode mark a lot of the functions used by compiler_rt "unavailable" - compiler_rt build for watchOS/tvOS · Issue #125309 · llvm/llvm-project · GitHub