Xcode 26 introduced COMPILATION_CACHE_ENABLE_CACHING to enable LLVM CAS and support the Plugin format. However, it was found during the process that many artifacts still rely on local absolute paths, such as xxx-Bridging-Header.h. During compilation, ChainedBridgingHeader.h is generated, which contains the absolute path of xxx-Bridging-Header.h. Ultimately, this leads to compilation caches not being reused across different machines
You are correct that COMPILATION_CACHE_ENABLE_CACHING
is the Xcode setting that is designed to use local caching only. There are more works to do to make distributed caching working properly. This is an ongoing process, and the solution to the absolute paths you mentioned is to prefix mapped them into a canonical location. Of course there are more works to do to ensure seamless development experience, for example, proper debugging support.
If you want to try it out in its early form, feel free to share your configuration here and we can discuss how to help you set up distributed caching.
Thank you for your reply,Please let me describe my configuration, My sample project is a mixed-language project using Objective-C and Swift. I use the following relevant configuration,And I have implemented a Cache Server myself to monitor /tmp/cc, and this server is functioning normally。
OTHER_SWIFT_FLAGS,I use it to –scanner-prefix-map avoid the impact of absolute paths of source files on the cache。
When I have two identical copies of code in different directories, the caches between them cannot be shared, especially for Swift, which is affected by the aforementioned ChainedBridgingHeader.h
Since you seem to know about the new caching, can you explain exactly what it enables? If it's just a local cache for now, what does it provide that the previous compiler artifacts didn't? In my testing it doesn't seem to increase overall clean or incremental build performance, so is there some other purpose?
You went very far to enable distributed caching! That is very cool that you are able to do it without real documentation for it yet. There is a Xcode configuration SWIFT_ENABLE_PREFIX_MAPPING=YES
that can help you setup the mapping and since you are a mixed project CLANG_ENABLE_PREFIX_MAPPING=YES
should be used as well.
There is currently some limitation in the Xcode 26 release that the module cache directories cannot be different between two machines, and there might be some false negatives on caching when using swift macros. If you have problem with ChaingedBridgingHeader with caching, can you file an issue on GitHub with a reproducer? I assume it should be fairly reproducible with a reduced Xcode project and I have happy to take a look and fix the issue.
The main benefits are:
- You can switch branches with fast rebuilds
- If you have some package dependencies that you don’t really touch, you can get fast rebuild between clean builds
- If you have a CI system that needs to perform lots of incremental clean builds, it will be helpful as well.
If you find other good use cases and would like to get that supported, please report them as well.
This isn't already the case? AFAIK, switching branches doesn't affect the build products in DerivedData in any way.
This may be a big benefit, interesting! I'll test that case and see if there's a difference.
Since we can't share between machines yet, you mean something like doing an xcodebuild
, an xcodebuild ... clean
, and then another xcodebuild
, will use some cached products?
If much of this content persists between Xcode's "Clean Build...", how do we clear this cache?
I mean switching to a previous built branch will result in cache hit. All build products from previous branch can be invalidated after branch switching.
From Xcode:
Settings → Location → Compilation Cache (click circled i on the right) → Clear Cache
Okay, please give me some time to set up a Demo project
SWIFT_ENABLE_PREFIX_MAPPING and CLANG_ENABLE_PREFIX_MAPPING are merely wrappers for some prefix-maps. As shown in the figure above, I have already set up some prefix-maps.
This is a demo project I built, GitHub - Coder-Star/cache_test . Allow me to briefly explain the phenomenon. When you copy this project twice and place them in different projects, compile one first to generate the corresponding cache, and then compile it again, you can reuse the cache 100% of the time.
Then, when you compile the project in another directory, you will find that the reuse rate is not 100%, but 80%. This is because the ChaingedBridgingHeader stores absolute paths internally, which prevents the file from being cached. Consequently, all Swift files that use this file will also fail to be cached, while OC files can be cached.
Additionally, my Xcode version is Xcode 26 beta 2
Thanks for reporting. I can reproduce. Here is an issue I created: [Caching] Swift caching with prefix mapping doesn't work when having a bridging header · Issue #84088 · swiftlang/swift · GitHub
May I ask if this issue will be resolved in the next version?
Distributed caching will be the next focus for swift caching build, presumably for swift 6.3. There are lots of work needs to be done to make distributed caching as good as local caching, including the issue you filed.
Please follow the issue for the progress! If you need to have a workaround, maybe copy the bridging header into a deterministic path (like /tmp/bridging.h
) and set that in Xcode project.
This cache's improvements are quite impressive! In a larger app, ~1500 Swift files, using swift-syntax
and quite a few macros, but no prebuilt for Xcode 26, the "clean" build time drops from ~170s to ~61s! And that's even with security scanner software taking 300% of an M2 Max!
Is there any documentation about the contents of this cache, and how it varies from the existing build product cache?
Unfortunately, without this new build cache, Xcode 26 is a rather severe regression from Xcode 16.4, especially for incremental builds. For clean builds (no cache, no prebuilt swift-syntax), it regressed about 30%, 120s -> 160s, and for incremental builds it regressed from 36s to 74s (most of the time in incremental builds is spent in "Emit Swift Module" redundantly reexpanding every macro, so will get much better when prebuilts are available). Once the cache is seeded, clean builds are consistently faster, but incremental builds remain regressed.
One step forward, one step back I guess.
Is there any guidance on what directories we should capture and restore when using this on CI? I assume at a minimum DerivedData/CompilationCache.noindex
?
Thanks for the feedback and speed up when using caching.
You can file an issue with a reproducer for the build regressions. For incremental build, you need to emit the module for debugging purposes and unfortunately that usually means regenerate the module. For older swift compiler version, you might use a split module and merge in the end. That path has been obsoleted because merge module is not sound in general and can lead to bad modules. But an issue tracking the build performance issue you are seeing is always welcomed and can guide future build performance optimizations.
Yes, that is the cache directory and is figured using the setting I mentioned above.
From Xcode:
Settings → Location → Compilation Cache
While you can copy (save and restore) the directory, is not designed for that use case and I can totally imagine save and restore takes longer than the actual compilation. Please benchmark before doing that.
It looks like I’m just getting cache misses even when I restore this, so something else is afoot (probably the bridging header issue above). I’ll subscribe to updates for that issue — thanks, Steven!