ccache is a powerful compiler cache that speeds up Android builds by avoiding redundant compilation. AOSP 15 fully supports it when configured correctly — no changes to the build system are necessary.
This guide walks you through enabling ccache in a reliable and portable way.
Prerequisite
You need a working ccache (4.x+). If your distro’s package is old or you want a reproducible, manpage-included build, follow this:
- Enable
Ccachein Your Environment
Add the following to your shell or build script:
export USE_CCACHE=1
export CCACHE_DIR=~/.cache/ccache
export CCACHE_EXEC=$(which ccache)
ccache -M 100G # Optional: set maximum cache size
Place these lines in .bashrc, .zshrc, or build/envsetup.sh if needed.
- Improve Cache Sharing Across Directories (Optional)
If you build the same source code from different paths (e.g. different branches or clones), you can make ccache ignore those differences in its hashes:
Configure via file:
# ~/.cache/ccache/ccache.conf
hash_dir = false
Or configure via environment:
export CCACHE_NOHASHDIR=1
To confirm it’s active:
ccache -p | grep hash_dir
Expected output:
hash_dir = false
- Build and Verify
CcacheIs Working
Reset ccache stats:
ccache -z
Build a target:
source build/envsetup.sh
lunch aosp_arm64-trunk_staging-userdebug
m
Check the cache stats:
ccache -s
You should see output like:
Cacheable calls: 73517 / 78098 (94.13%)
Hits: 73517 / 73517 (100.0%)
Direct: 73492 / 73517 (99.97%)
Preprocessed: 25 / 73517 ( 0.03%)
Misses: 0 / 73517 ( 0.00%)
Uncacheable calls: 4581 / 78098 ( 5.87%)
Local storage:
Cache size (GB): 37.8 / 60.0 (62.96%)
Hits: 73517 / 73517 (100.0%)
Misses: 0 / 73517 ( 0.00%)
Option: Building inside AOSP’s nsjail on Ubuntu 24.04 (kernel 6.8)
When building inside nsjail on Ubuntu 24.04 with:
kernel.apparmor_restrict_unprivileged_userns = 1
AppArmor and the jail can block or hide your ccache directory, causing “permission denied” or zero hits.
Background, diagnosis, and minimal allow-list rationale:
Make the ccache directory visible inside the jail
If you’ve applied this small Soong change to build/soong/ui/build/sandbox_linux.go, you can inject extra bind mounts at runtime:
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 95b71a794..222828a62 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -280,6 +280,22 @@ func (c *Cmd) wrapSandbox() {
sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}")
}
+
+ // Allow extra bind mounts via environment variable.
+ // ANDROID_NSJAIL_EXTRA_BINDS is a comma-separated list where each item can be:
+ // /src/path
+ // /src/path:/dst/path
+ // /src/path:/dst/path:1 // (1 = read-only in nsjail)
+ if extra := os.Getenv("ANDROID_NSJAIL_EXTRA_BINDS"); extra != "" {
+ for _, spec := range strings.Split(extra, ",") {
+ spec = strings.TrimSpace(spec)
+ if spec == "" {
+ continue
+ }
+ sandboxArgs = append(sandboxArgs, "-B", spec)
+ }
+ }
+
// Mount srcDir RW allowlists as Read-Write
if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
Then, before building, bind your cache into the jail (same path, read-write):
export ANDROID_NSJAIL_EXTRA_BINDS="$CCACHE_DIR"
