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 sizePlace 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 = falseOr configure via environment:
export CCACHE_NOHASHDIR=1To confirm it’s active:
ccache -p | grep hash_dirExpected output:
hash_dir = false- Build and Verify
CcacheIs Working
Reset ccache stats:
ccache -zBuild a target:
source build/envsetup.sh
lunch aosp_arm64-trunk_staging-userdebug
mCheck the cache stats:
ccache -sYou 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 = 1AppArmor 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"Did this guide save you time?
Support this site