Tuist Cache#
Tuist Cache (binary cache) is an EE feature that pre-builds targets into XCFrameworks (or bundles/macros) and stores them in a local or remote cache. On subsequent tuist generate / tuist build / tuist test invocations, cached binaries replace source targets, cutting build times.
The implementation lives under #if canImport(TuistCacheEE) throughout the codebase.
Cache Warm Workflow#
tuist cache warm is the entry point for populating the cache. The orchestrator is CacheWarmCommandService.
Two-phase generation#
-
Preload phase β calls
generatorFactory.binaryCacheWarmingPreload(...), which loads the graph and fetches any already-cached remote binaries into the local cache without writing an Xcode project .ExplicitDependencyGraphMapperis explicitly excluded from this generator so framework search paths can be normalized before hashing . -
Warming phase β after target selection and hashing, calls
generatorFactory.binaryCacheWarming(...)to produce an Xcode project that builds only targets that missed the cache .ExplicitDependencyGraphMapperis also excluded here .
xcodebuild invocations#
For each Binaries-Cache-<Platform> scheme, xcodeBuildController.build(...) is called twice β once for simulator, once for device β with these key settings :
| Setting | Value |
|---|---|
SKIP_INSTALL | NO |
DEBUG_INFORMATION_FORMAT | dwarf |
STRIP_INSTALLED_PRODUCT | YES |
SWIFT_SERIALIZE_DEBUGGING_OPTIONS | NO |
ONLY_ACTIVE_ARCH | NO |
CODE_SIGN_IDENTITY / CODE_SIGNING_* | disabled |
COMPILER_INDEX_STORE_ENABLE | NO |
For release configurations, GCC_INSTRUMENT_PROGRAM_FLOW_ARCS and CLANG_ENABLE_CODE_COVERAGE are additionally forced to NO to prevent App Store rejection .
A separate Binaries-Cache-Catalyst scheme handles Mac Catalyst slices . Bundles (Bundles-Cache-*) and macros (Macros-Cache-*) are built with similar settings but without the simulator+device split.
After building, XCFrameworks are assembled with xcodebuild -create-xcframework, run with bounded concurrency capped at min(activeProcessorCount, 8) .
Graph Mapper Ordering#
The mapper pipeline is assembled by CacheGraphMapperFactory (EE-only). Order matters: hashing must see the same normalized graph that warm used; binary replacement must happen before framework search paths are computed.
binaryCacheWarming pipeline #
ForeignBuildGraphMapperβModuleMapMapperβExternalProjectsPlatformNarrowerGraphMapperGenerateCacheableSchemesGraphMapperβ inserted immediately after the narrower so it sees platform-narrowed destinationsPruneOrphanExternalTargetsGraphMapperβTreeShakePrunedTargetsGraphMapperUpdateWorkspaceProjectsGraphMapperβStaticXCFrameworkModuleMapGraphMapperβStaticXCFrameworkAppIntentsMetadataGraphMapperCacheHashingGraphMapper+FocusTargetsGraphMappers(when specific targets are requested) β snapshots the normalized graph before focus/replacementTargetsToCacheBinariesGraphMapperβ replaces already-cached transitive dependencies with binariesTreeShakePrunedTargetsGraphMapperβStaticXCFrameworkModuleMapGraphMapperβStaticXCFrameworkAppIntentsMetadataGraphMapperFrameworkSearchPathsGraphMapperβ runs after binary replacement so it sees precompiled XCFrameworksForeignBuildSideEffectGraphMapperβAutogeneratedWorkspaceSchemeGraphMapper
binaryCacheWarmingPreload pipeline #
Same base mappers but without GenerateCacheableSchemesGraphMapper, CacheHashingGraphMapper, or TargetsToCacheBinariesGraphMapper. FrameworkSearchPathsGraphMapper is appended at the end so generated framework search paths are present when hashes are computed.
Hash Alignment Between Warm and Generate#
A mismatch between the hash computed at warm time and the hash requested at generate/build time is the root cause of cache misses even when sources haven't changed.
The problem (fixed in #11616): FrameworkSearchPathsGraphMapper (introduced in #11054) injects generated framework search paths into targetSettings. Cache warm included these in its hash; unfocused tuist generate --cache-profile all-possible did not preserve that same normalized state before requesting binaries, so hashes diverged.
The fix: CacheHashingGraphMapper is now inserted before TargetsToCacheBinariesGraphMapper in both the generation and build paths. It stores a snapshot of the normalized graph in environment.initialGraphWithSources . The normalization mappers it runs β FrameworkSearchPathsGraphMapper, ForeignBuildSideEffectGraphMapper, and (optionally) AutogeneratedWorkspaceSchemeGraphMapper β are the same ones used by warm, ensuring both sides derive cache keys from an identical graph .
In the generation path, shouldPreserveHashingGraph is true when cacheProfile != .none && !includedTargets.isEmpty; in that case the hashing graph is preserved inside the focused-targets pipeline rather than appended afterward .
Compiler Flag Propagation (XcodeCacheSettingsProjectMapper)#
XcodeCacheSettingsProjectMapper adds Xcode compilation-caching build settings when generationOptions.enableCaching is true .
COMPILATION_CACHE_ENABLE_CACHING = YESis always set.- Kura path (remote CAS via plugin): sets
COMPILATION_CACHE_ENABLE_PLUGIN,COMPILATION_CACHE_PLUGIN_PATH, and appends-cas-plugin-option tuist-instance=<fullHandle>(andtuist-upload=falseif uploads are disabled) toOTHER_SWIFT_FLAGS. Clang/ObjC is never routed through the plugin β only Swift gets remote compilation caching. - Legacy path: sets
COMPILATION_CACHE_REMOTE_SERVICE_PATHto the daemon unix-socket installed bytuist setup cache. - If kura is enabled but the bundled
libtuist_cas_plugin.dylibis absent, the build silently falls back to local-only caching with a warning .
ModuleMapMapper (separate from the above) converts MODULEMAP_FILE settings into -fmodule-map-file compiler flags on OTHER_SWIFT_FLAGS and OTHER_CFLAGS, generating a combined module map per target to avoid "Argument list too long" errors .
Key Source Files#
| File | Purpose |
|---|---|
CacheWarmCommandService.swift | Top-level orchestrator: loads graph, hashes, builds, stores |
GeneratorFactory.swift | CacheGeneratorFactory.binaryCacheWarming/Preload |
GraphMapperFactory.swift | Mapper pipeline assembly for all cache flows |
CacheHashingGraphMapper.swift | Snapshot of normalized graph for stable hashing |
XcodeCacheSettingsProjectMapper.swift | Compilation caching build settings injection |
ModuleMapMapper.swift | MODULE_MAP β -fmodule-map-file compiler flag propagation |