# Pitch: Compilation Caching Support in SwiftPM

**URL:** <https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079>\
**Category:** Pitches\
**Tags:** packagemanager, swiftpm\
**Created:** [July 6, 2026, 3:54pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079 "2026-07-06T15:54:02Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![owenv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/owenv/32/6137_2.png) [@owenv](https://forums.swift.org/u/owenv)\
**Post date:** [July 6, 2026, 3:54pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/1 "2026-07-06T15:54:02Z")

</div>

Hi all,

I have a relatively short pitch to add support for configuring clang and swiftc's compilation caching features via SwiftPM:

* * *

# SwiftPM Support for Compilation Caching

- Proposal: SE-NNNN
- Authors: [Owen Voorhees](https://github.com/owenv)
- Implementation: [PR #10246](https://github.com/swiftlang/swift-package-manager/pull/10246)
- Review: TBD

## Introduction

The Swift and Clang compilers have existing support for leveraging a CAS (Content Addressable Store) to cache compilation outputs and replay them in subsequent builds. This proposal introduces support for configuring compilation caching in SwiftPM on a per-build, per-package, or global basis.

## Motivation

During many common workflows, the build of a package may repeat a compile task from an older build with identical inputs. For example:

- Locally switching between two different branches, incrementally rebuilding large parts of the package each time
- Building the same code across several git worktrees, each of which starts with an empty build folder
- Repeatedly building the same code in a CI environment which performs a clean build each time it's invoked

Recompiling sources in these cases is wasteful and reduces development velocity. Furthermore, these are all workflows which do not benefit from traditional incremental builds, which rely on timestamp-based invalidation and reusing intermediates in the build directory. Compilation caching is able to accelerate these workflows by quickly replaying the results of a previous compilation after dependency scanning computes a content-based cache key derived from its inputs (sources, compiler command lines, imported modules, etc.). For example, on a 12-core machine a fully cached clean debug build of SwiftPM is 68% faster than one with an empty cache, a similar build of SwiftSyntax is 75% faster, and a build of Vapor is 67% faster.

Effectively leveraging compilation caching often requires configuring parameters like the cache location, size, and eviction policy to support a particular use case, like local builds or remote CI. As the build system driving the compilers, SwiftPM is in the best position to manage this configuration and expose it to the end user.

## Proposed solution

SwiftPM provides three ways to configure compilation caching:

- Global configuration, via `swift package build-cache configure` using the `--global` modifier. This is stored in a `build-cache-config.json` file in SwiftPM's shared configuration directory (`~/Library/org.swift.swiftpm/configuration/` on macOS, `~/.swiftpm/configuration/` on Linux, etc.).
- Package-local configuration via `swift package build-cache configure`. This is stored in a `build-cache-config.json` file in the package's `.swiftpm/configuration/` directory. This file is included in the default `.gitignore` for new packages created using `swift package init`.
- Per-build configuration via command line options.

Each configuration mechanism has higher precedence compared to the previous. Values set at each level are additive. For example, a user may choose to enable caching globally, and then override only the cache path per-package.

## Detailed design

All three mechanisms support configuring:

- Whether caching is enabled or disabled. By default, caching is opt-in. It is set per-build with `--enable-build-caching`/`--disable-build-caching`, or persistently with `swift package build-cache configure --enable-caching`/`--disable-caching`.
- Enablement of prefix mapping, which canonicalizes paths on compiler command lines and allows multiple instances of a package at different file paths to share cached outputs. By default, enabling caching also enables prefix mapping. It is configured per-build with `--enable-build-cache-prefix-mapping`/`--disable-build-cache-prefix-mapping`, or persistently with `swift package build-cache configure --enable-prefix-mapping`/`--disable-prefix-mapping`.
- The path to the build cache. If caching is enabled but no path is specified, the build cache uses a subdirectory of SwiftPM's default global cache directory (~/Library/Caches/org.swift.swiftpm/ on macOS, ~/.cache/org.swift.swiftpm/ on Linux, etc.). It is set per-build with `--build-cache-path`, or persistently with `swift package build-cache configure --path`.
- A size limit, specified either in absolute terms (e.g. 50G) or as a percentage of available disk space (e.g. 10%). At the end of a build, entries will be evicted from the cache if this limit is exceeded. It is set per-build with `--build-cache-size-limit`, or persistently with `swift package build-cache configure --size-limit`. The default size limit is implementation-defined.
- Enablement of diagnostic remarks which emit information about cache hits and misses in the build log. By default, diagnostic remarks are disabled. They are configurable per-build with `--enable-build-cache-diagnostic-remarks`/`--disable-build-cache-diagnostic-remarks`, or persistently with `swift package build-cache configure --enable-diagnostic-remarks`/`--disable-diagnostic-remarks`.
- The path to a plugin implementing the [LLVM CAS Plugin API](https://github.com/swiftlang/llvm-project/tree/next/llvm/include/llvm-c/CAS), and optionally a unix domain socket path for a [gRPC remote caching service](https://github.com/swiftlang/llvm-project/tree/next/llvm/lib/RemoteCachingService/RemoteCacheProto) to provide to the plugin. These options can be used to integrate SwiftPM build caching with a custom local or distributed cache implementation. The plugin path is set per-build with `--build-cache-plugin-path`, or persistently with `swift package build-cache configure --plugin-path`. The remote service path is set per-build with `--build-cache-remote-service-path`, or persistently with `swift package build-cache configure --remote-service-path`. On macOS, SwiftPM will default to using the CAS plugin from an Xcode toolchain if it is available, allowing the user to specify only a remote service path. A plugin is not required to take advantage of compilation caching. If none is available or provided, the LLVM on-disk CAS implementation will be used.

In SwiftPM's user-facing interface, we consistently use the terminology "build cache" instead of "compilation cache". This is intended to future proof these options so that they could evolve naturally in the future to configure CAS-based caching of additional task types.

In addition to `configure`, the `swift package build-cache` command provides the following subcommands:

- `swift package build-cache get-configuration` prints the effective cache configuration for the current package in an implementation-defined human-readable format.
- `swift package build-cache reset` removes all stored configuration options, returning them to their defaults. Like `configure`, it operates on the package-local configuration by default, or on the global configuration when passed `--global`.
- `swift package build-cache info` reports information about the cache, like its current size, in an implementation-defined human-readable format.
- `swift package build-cache clear` deletes the on-disk build cache for the current package.

## Security

The build cache stores and retrieves compiler outputs from a local (or, via the specified service/plugin, remote) cache. As a result, an adversary with the ability to modify the cache could tamper with compiler outputs and the resulting build artifacts, similar to the capabilities of an adversary with the ability to modify the original source code. Users of build caching in a shared environment like a CI node should keep this in mind when configuring permissions and assessing the risk of adopting the feature.

## Impact on existing packages

None. Compilation caching is an opt in build performance optimization which should not impact outputs.

## Alternatives considered

### Only expose command line options

We could expose compilation caching purely through command line flags to simplify the design by eliminating the `swift package build-cache configure` command. This would likely work well in CI environments, but would limit the usefulness of caching in local builds, where the per-package and global configurations eliminate a lot of tedious command line boilerplate the user would need to add to each `swift build`, likely via a hand-authored script.

## Acknowledgements

Thanks to @cachemeifyoucan, @akyrtzi , and @blangmuir for answering questions and providing feedback on this proposal.

---

<div class="post-metadata">

**Author:** ![Max\_Desiatov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/max_desiatov/32/20654_2.png) [@Max\_Desiatov](https://forums.swift.org/u/Max_Desiatov)\
**Post date:** [July 6, 2026, 5:00pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/2 "2026-07-06T17:00:59Z")

</div>

+1, yes please! Not much to add here, super excited to see this pitched 👏

The only comment I have is on alternatives considered:

> [@owenv](#):
>
> We could expose compilation caching purely through command line flags to simplify the design by eliminating the `swift package build-cache configure` command.

I like this as already pitched separate subcommand. IMO having cache management appended as options/arguments onto some existing command feels weird. `swift package <noun> <verb>` is an established precedent, and I actually prefer a more succinct `swift build-cache`.

---

<div class="post-metadata">

**Author:** ![bkhouri](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bkhouri/32/45895_2.png) [@bkhouri](https://forums.swift.org/u/bkhouri)\
**Post date:** [July 6, 2026, 7:03pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/3 "2026-07-06T19:03:52Z")

</div>

Overall, I support this pitch and and looking forward to it's delivery :)

I just have some minor suggestions with the `swift package build-cache` subcommands. When reading only the command names without their help text, it was unclear to me what some actions performed.

I would propose update the subcommands so they match the `reset`/`clean` subcommands of `swift package`

- `swift package build-cache clean`, performs the actions suggested in the pitches `clear` subcommand
- `swift package build-cache reset`, in addition to the actions suggested in the pitch, it performs the the same action as `clean`
- `swift package build-cache info` and `swift package build-cache get-configuration` should provide an option to return machine readable format (e.g.: `JSON`, `YAML`, etc...), with the default being human-readable (e.g: `text`).
- `swift package build-cache get-configuration` should make the distinction where the setting is configured (e.g.: `global`, `package-local`, `per-build`)

---

<div class="post-metadata">

**Author:** ![anreitersimon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/anreitersimon/32/7269_2.png) [@anreitersimon](https://forums.swift.org/u/anreitersimon)\
**Post date:** [July 6, 2026, 7:54pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/4 "2026-07-06T19:54:12Z")

</div>

Overall this seems great!

Reading this command

`swift package build-cache reset`

I assumed this will delete the caches (Thats what `swift package build-cache clear` does)

For me the distinction between those two commands isnt really clear.

I would propose a more explicit `swift package build-cache reset-configuration`

---

<div class="post-metadata">

**Author:** ![owenv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/owenv/32/6137_2.png) [@owenv](https://forums.swift.org/u/owenv)\
**Post date:** [July 6, 2026, 8:20pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/5 "2026-07-06T20:20:04Z")

</div>

> [@bkhouri](#):
>
> - `swift package build-cache clean`, performs the actions suggested in the pitches `clear` subcommand

This sounds good to me, it's a little clearer IMO

> [@bkhouri](#):
>
> - `swift package build-cache reset`, in addition to the actions suggested in the pitch, it performs the the same action as `clean`

> [@anreitersimon](#):
>
> I would propose a more explicit `swift package build-cache reset-configuration`

I think I prefer naming this something more verbose like "reset-configuration" so it's easier to understand what it's doing rather than combining "delete cache" and "reset configs" into one command

> [@bkhouri](#):
>
> - `swift package build-cache info` and `swift package build-cache get-configuration` should provide an option to return machine readable format (e.g.: `JSON`, `YAML`, etc...), with the default being human-readable (e.g: `text`).

I'm not sure we want to commit to a maintaining a machine-readable format for this, I couldn't think of concrete use cases external tools might have for wanting to read this information. If there are specific use cases I might reconsider.

> [@bkhouri](#):
>
> - `swift package build-cache get-configuration` should make the distinction where the setting is configured (e.g.: `global`, `package-local`, `per-build`)

Not sure it makes sense to have a `per-build` option here as that's ephemeral and determined by the specific command line options passed to the build. An option for reading the global state, ignoring any per-package state might be worth having

---

<div class="post-metadata">

**Author:** ![bkhouri](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bkhouri/32/45895_2.png) [@bkhouri](https://forums.swift.org/u/bkhouri)\
**Post date:** [July 6, 2026, 9:37pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/6 "2026-07-06T21:37:19Z")

</div>

> [@owenv](#):
>
> > [@bkhouri](#):
> >
> > - `swift package build-cache clean`, performs the actions suggested in the pitches `clear` subcommand
> 
> This sounds good to me, it's a little clearer IMO

I wonder whether we should consider using a more verbose name for deleting the caches, something like `swift package build-cache clean-cache` or `clean-caches`

> [@owenv](#):
>
> I think I prefer naming this something more verbose like "reset-configuration" so it's easier to understand what it's doing rather than combining "delete cache" and "reset configs" into one command

👍 to using a more verbose subcommand name of `reset-configuration`.

---

<div class="post-metadata">

**Author:** ![sliemeobn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sliemeobn/32/27698_2.png) [@sliemeobn](https://forums.swift.org/u/sliemeobn)\
**Post date:** [July 7, 2026, 11:58am UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/7 "2026-07-07T11:58:33Z")

</div>

I'm a simple man. I read "faster builds", I upvote: +1

---

<div class="post-metadata">

**Author:** ![owenv](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/owenv/32/6137_2.png) [@owenv](https://forums.swift.org/u/owenv)\
**Post date:** [July 9, 2026, 9:42pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/8 "2026-07-09T21:42:40Z")

</div>

After considering this some more, I've updated the proposal draft at [swift-evolution/proposals/NNNN-swiftpm-compilation-caching.md at owenv/caching · owenv/swift-evolution · GitHub](https://github.com/owenv/swift-evolution/blob/owenv/caching/proposals/NNNN-swiftpm-compilation-caching.md) to:

- Adopt the suggestion from @bkhouri to rename `swift package build-cache clear` to `swift package build-cache clean`. IMO it's easier to remember if we align this command and `swift package clean` since they're conceptually similar
- Adopt the suggestion from @anreitersimon to rename `swift package build-cache reset` to `swift package build-cache reset-configuration`. Since this command probably won't be used often, I think this does a better job of reminding the user it resets the cache configuration rather than resetting/deleting the cache contents.

---

<div class="post-metadata">

**Author:** ![dschaefer2](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dschaefer2/32/46516_2.png) [@dschaefer2](https://forums.swift.org/u/dschaefer2)\
**Post date:** [July 10, 2026, 1:48pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/9 "2026-07-10T13:48:55Z")

</div>

Agreed. `swift package reset` is a weird sort. It almost does the same thing as `clean`. At the end of the day, it deletes scratch directory which is like a super clean, I guess...

---

<div class="post-metadata">

**Author:** ![Finagolfin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/finagolfin/32/34388_2.png) [@Finagolfin](https://forums.swift.org/u/Finagolfin)\
**Post date:** [July 10, 2026, 2:50pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/10 "2026-07-10T14:50:29Z")

</div>

`swift package reset` seems to also do some sort of clean of the source of dependencies, which I have found useful to reset my Swift package's dependencies.

---

<div class="post-metadata">

**Author:** ![Joannis\_Orlandos](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joannis_orlandos/32/732_2.png) [@Joannis\_Orlandos](https://forums.swift.org/u/Joannis_Orlandos)\
**Post date:** [July 22, 2026, 3:46pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/11 "2026-07-22T15:46:58Z")

</div>

I'm very much of favour of this and the design.

---

<div class="post-metadata">

**Author:** ![Alkenso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/alkenso/32/43303_2.png) [@Alkenso](https://forums.swift.org/u/Alkenso)\
**Post date:** [July 24, 2026, 9:23am UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/12 "2026-07-24T09:23:26Z")

</div>

Sounds reasonable and makes sense. Upvote +1

---

<div class="post-metadata">

**Author:** ![moshe-foreflight](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/moshe-foreflight/32/30493_2.png) [@moshe-foreflight](https://forums.swift.org/u/moshe-foreflight)\
**Post date:** [July 24, 2026, 3:01pm UTC](https://forums.swift.org/t/pitch-compilation-caching-support-in-swiftpm/88079/13 "2026-07-24T15:01:35Z")

</div>

Yes please. This is a fantastic addition. I have some questions how this relates to existing similarly named flags, and further pedantry:

> [@owenv](#):
>
> - Global configuration, via `swift package build-cache configure` using the `--global` modifier. This is stored in a `build-cache-config.json` file in SwiftPM's shared configuration directory (`~/Library/org.swift.swiftpm/configuration/` on macOS, `~/.swiftpm/configuration/` on Linux, etc.).
> - Package-local configuration via `swift package build-cache configure`. This is stored in a `build-cache-config.json` file in the package's `.swiftpm/configuration/` directory. This file is included in the default `.gitignore` for new packages created using `swift package init`.

How do we differentiate all this new goodness with persisted configs from pre-existing flags around `-config`s and various caches?

 ![Screenshot 2026-07-24 at 10.51.15 AM](https://global.discourse-cdn.com/swift/original/3X/e/a/ead9570437a2637541e35218f782c82342072fbf.png)

### Less Typing Please?

As promised, further pedantry:

My understanding is the API, as implemented, looks like this:

```swift
swift package build-cache configure
swift package build-cache configure --enable-caching
swift package build-cache configure --disable-caching
swift package build-cache configure --path <path>
swift package build-cache configure --size-limit [bytes|pct]
swift package build-cache configure --enable-diagnostic-remarks
swift package build-cache configure --plugin-path <path>
swift package build-cache configure --remote-service-path <path>
swift package build-cache get-configuration
swift package build-cache reset-configuration
swift package build-cache clean
swift package build-cache info

```

As a developer who wants to type less 🥺 , consider some changes:

```swift
swift package task-cache ... # 2. Alternate naming
swift package task-cache config # 1. Shorter configuration subcommand
swift package task-cache config --enable # 3. Shorter verbs for toggling
swift package task-cache config --disable
[...] # These are fine
swift package task-cache config get # 4. <verb>configuration to <noun> <verb>
swift package task-cache config reset [--global]
swift package task-cache purge # 5. Match other cache verb choice
swift package task-cache stats # 6. Clarify `info` subcommand

```

1. Shorten `configuration` to `config`. The `-config-path` flag provides precedent.
2. Re command naming:

> [@owenv](#):
>
> In SwiftPM's user-facing interface, we consistently use the terminology "build cache" instead of "compilation cache". This is intended to future proof these options so that they could evolve naturally in the future to configure CAS-based caching of additional task types.

1. Would you consider simplifying to `cache` or changing `build-cache` to `task-cache` or `cas-cache`? This is shorter, also matches the intent of future support for other tasks or implementation, respectively. (I just wonder if `<prefix>-cache` is a [pleonasm](https://en.wikipedia.org/wiki/Pleonasm) like "fish" in "tuna fish" or "own" in "my own eyes.")

 ![Existing cache flags](https://global.discourse-cdn.com/swift/original/3X/3/1/319b6a02505b9c798721fffc17d61c8c7a0af9b0.png)

1. Shorten `--<verb>-caching` to `--<verb>` since both are subcommands of `build-cache`.

`swift package task-cache config --enable`

1. Move `get` and `reset` to subcommands of `config[uration]`.
2. Change `purge` to match repo cache equivalent.
3. Consider `stats` instead of `info`, since `info` could arguably refer to information about the config. `stats` implies numeric or usage.
4. I also agree with @bkhouri re output formatting.

> [@bkhouri](#):
>
> - `swift package build-cache info` and `swift package build-cache get-configuration` should provide an option to return machine readable format (e.g.: `JSON`, `YAML`, etc...), with the default being human-readable (e.g: `text`).

+1 to machine-readable, especially if `CI` is considered in the design.

Again, however this lands is awesome. Thank you for putting this together.
