第 2 章 构建缓存与本地缓存:.buildCache 目录结构、Gradle 缓存路径配置、缓存目录权限问题

2.1 Gradle 缓存机制与 .buildCache 目录概述

Gradle 的缓存体系分为 本地构建缓存(Local Build Cache)和 远程构建缓存(Remote Build Cache)。本章聚焦于本地缓存,其物理载体为项目根目录下的 .gradle/build-cache 文件夹(Gradle 6.0+ 默认启用)。每次任务输出命中缓存时,Gradle 直接从该目录恢复产物,跳过任务执行,从而显著缩短增量构建时间。

缓存内容以散列键(Hash Key)为索引存储,每条缓存记录包含任务输入快照、输出文件及其元数据。目录权限直接影响多用户环境下的缓存共享与命中率。

Gradle 本地构建缓存目录结构示意 ~/.gradle/caches/ build-cache/ cache.properties build-cache-1/ build-cache-2/ a9b2c3d4.../ e5f6g7h8.../ 说明: • build-cache 目录位于 Gradle 用户级缓存目录下(Linux: ~/.gradle/caches/,Windows: %USERPROFILE%\.gradle\caches\)。 • 每个构建任务输出以输入哈希为文件夹名存储,文件权限默认为 755(目录)和 644(文件)。 • cache.properties 记录本地缓存容量上限、清理策略等元数据。

2.2 默认缓存目录结构与子目录含义

Gradle 6.0+ 的本地构建缓存默认存储在 ~/.gradle/caches/build-cache-1/(旧版本为 build-cache)。其内部结构如下:

子目录 / 文件用途典型生命周期
build-cache-1/<hash>/存储单个任务输出的压缩存档(.tar 格式)及元数据根据 LRU 策略或容量上限清除
cache.properties本地缓存配置(cacheSize、removeUnusedEntriesAfterDays)配置文件,手动修改后生效
cachedir.lock文件锁,防止多进程同时操作缓存目录进程持有期间存在
trash/临时存放即将删除的缓存条目,重启时清理(Gradle 8.0+)构建结束后清理

2.3 缓存路径配置方法

Gradle 提供三层配置机制,优先级由低到高:settings.gradle 配置脚本、系统属性、环境变量。

2.3.1 settings.gradle 配置(推荐)

// settings.gradle.kts
buildCache {
    local {
        isEnabled = true
        // 自定义目录路径
        directory = File(rootDir, ".gradle/my-build-cache")
        // 容量上限,单位 MB
        cacheSizeMB = 2048
        // 超过 7 天未使用的条目自动清除
        removeUnusedEntriesAfterDays = 7
    }
}

2.3.2 系统属性与命令行参数

# 指定缓存目录
./gradlew build -Dorg.gradle.cache.local.directory=/opt/build-cache

# 禁用本地缓存(临时)
./gradlew build -Dorg.gradle.caching.local.enabled=false

2.3.3 环境变量(适用于 CI/CD)

# Linux/Mac
export GRADLE_USER_HOME=/data/gradle
export GRADLE_CACHE_LOCAL_DIR=/mnt/ssd/build-cache

# Windows PowerShell
$env:GRADLE_USER_HOME="D:\GradleCache"
$env:GRADLE_CACHE_LOCAL_DIR="E:\SsdBuildCache"
注意:GRADLE_USER_HOME 会同时影响依赖缓存、wrapper 和构建缓存。若仅需改变缓存目录,优先使用 directory 配置项,而非修改用户根目录。

2.4 缓存目录权限问题

在共享服务器或多用户 CI 环境下,缓存目录的权限配置不当会导致缓存写入失败或缓存未命中。核心原则:所有运行构建的用户/进程必须对缓存目录具有 rwx 权限。

  • 问题场景 1:Permission Denied
    当构建任务尝试写入 ~/.gradle/caches/build-cache-1,但当前用户无写权限时,Gradle 抛出 java.io.FileNotFoundException(权限被拒绝)。
    解决方案:
    # 创建共享缓存组
    sudo groupadd gradle-cache
    sudo usermod -aG gradle-cache $USER
    # 设置目录组权限为 rwx
    sudo chgrp -R gradle-cache /opt/shared-build-cache
    sudo chmod -R g+rwxs /opt/shared-build-cache
  • 问题场景 2:umask 导致文件权限不一致
    若 CI 进程 umask 为 0077,新创建的缓存目录仅对当前用户可读写,其他用户无法读取,导致后续构建缓存未命中(实际内容相同但无法读取)。
    修复方式:在构建脚本中添加 umask 设置:
    // build.gradle.kts
    tasks.register<DefaultTask>("ensureCachePermissions") {
        doLast {
            val cacheDir = gradle.gradleUserHomeDir.resolve("caches").resolve("build-cache-1")
            cacheDir.mkdirs()
            cacheDir.setReadable(true, false) // 全局可读
            cacheDir.setWritable(true, false) // 全局可写
            cacheDir.setExecutable(true, false)
        }
    }
  • 问题场景 3:根用户 vs 普通用户
    Docker 容器内以 root 运行构建,缓存目录属于 root;之后以普通用户身份运行构建时会读取失败。推荐容器内创建非 root 用户并提前 chown:
    RUN useradd -m jenkins && \
        mkdir -p /cache && \
        chown -R jenkins:jenkins /cache
    USER jenkins

2.5 调试与验证缓存目录状态

Gradle 提供以下调试手段:

  1. 启用 info 级别日志
    ./gradlew build --info | grep -E "(Build cache|build-cache)"
    输出示例:
    Storing build cache entry at ~/.gradle/caches/build-cache-1/a1b2c3d4.tar
    Build cache entry for task :compileJava stored
  2. 命令行查看缓存目录
    ls -la ~/.gradle/caches/build-cache-1/
    # 查看单个缓存文件元数据
    tar -tf ~/.gradle/caches/build-cache-1/a1b2c3d4.tar
  3. Gradle 缓存清理工具
    gradle --stop   # 停止所有守护进程
    gradle --gc     # 手动触发垃圾回收和缓存清理
调试技巧:若怀疑缓存未命中由权限导致,可临时将缓存目录权限设为 777 测试。定位问题后务必恢复最小权限原则(755 或 775)。

2.6 常见错误与排查

错误信息根因解决方法
Failed to create directory ~/.gradle/caches/build-cache-1 父目录无写权限或磁盘已满 检查磁盘空间,修改目录所有权或增加容量
Build cache entry from local storage was rejected 缓存文件校验和失败(可能被其他用户部分覆盖) 使用文件锁或专用缓存目录;删除损坏条目
java.nio.file.AccessDeniedException: cachedir.lock 多个 Gradle 守护进程或同一用户多次构建竞争锁 确保 --no-daemon 或等待锁释放;删除残留 .lock 文件

2.7 性能优化建议

  • 使用 SSD 或 tmpfs 挂载缓存目录
    将 GRADLE_CACHE_LOCAL_DIR 指向内存文件系统,大幅减少磁盘 I/O:
    # Linux tmpfs(占内存,建议 2-4GB)
    sudo mount -t tmpfs -o size=4G tmpfs /mnt/build-cache
    # 在 settings.gradle 中指定
    buildCache { local { directory = File("/mnt/build-cache") } }
  • 调整缓存容量与存活时间
    默认 cacheSizeMB=1024(1GB),removeUnusedEntriesAfterDays=7。对于大型项目,可增至 4GB 并延长存活周期:
    buildCache {
        local {
            cacheSizeMB = 4096
            removeUnusedEntriesAfterDays = 30
        }
    }
  • 避免缓存目录 NFS 挂载
    NFS 延迟高且锁机制不完善,极易导致缓存损坏。优先本地 SSD 或分布式缓存服务(如 S3 后端)。
  • CI 场景定期清理
    在 CI 流水线中增加定时任务删除超过 30 天未访问的缓存:
    find /cache/build-cache-1 -type d -atime +30 -exec rm -rf {} +
注意事项:修改缓存目录权限时,需同时考虑 cachedir.lock 文件锁的访问。若将目录设为 777,锁文件可能被恶意进程占用,建议使用组权限(775)配合同一个用户组运行所有构建进程。

通过本章学习,你已经掌握本地构建缓存的磁盘结构、配置路径、权限管理及常见故障排查。下一部分将深入远程缓存协议与云端存储策略。