Go Monorepo如何通过go build获取需重构建的目标列表
Go Monorepo 增量构建目标识别方案
方案一:零代码,直接用Go原生工具链(推荐)
Go 1.10 之后内置的构建缓存本身就支持内容级、符号级的依赖校验,完全匹配你列出的增量判定规则,不需要引入任何第三方构建工具,直接用内置命令就能实现:
#!/bin/bash # 遍历所有cmd下的可执行包 for target in ./cmd/...; do # 不实际运行测试,仅触发构建缓存校验 if ! go test -c -o /dev/null "$target" >/dev/null 2>&1; then echo "需要重建: ${target#./}" fi done
原理说明
- 命令里的
go test -c -o /dev/null只会触发目标包及其所有依赖的编译检查,不会执行任何测试逻辑,也不会生成多余文件 - Go原生缓存会比对所有依赖(包括pkg下的共享库)的内容哈希,甚至会做符号引用判定:如果你只修改了共享库里的
add函数,没有用到这个函数的package1会直接命中缓存,不会被标记为需要重建,完全符合你列的5条规则 - CI场景下只需要配置缓存
$GOCACHE目录,就能实现跨流水线任务的增量判定,额外开销极低
方案二:基于go/build标准库编程实现
如果需要把判定逻辑集成到自定义构建工具里,可以直接调用Go标准库的go/build包做依赖遍历,核心逻辑是枚举构建目标、递归收集依赖、比对修改时间,最小实现代码如下:
package main import ( "fmt" "go/build" "os" "path/filepath" ) func main() { // 枚举cmd目录下所有主包 cmdEntries, err := filepath.Glob("./cmd/*") if err != nil { fmt.Fprintf(os.Stderr, "读取cmd目录失败: %v\n", err) os.Exit(1) } for _, entry := range cmdEntries { pkg, err := build.ImportDir(entry, build.IgnoreVendor) if err != nil || !pkg.IsCommand() { continue } // 检查目标二进制是否存在 binPath := filepath.Join("bin", filepath.Base(entry)) binStat, binErr := os.Stat(binPath) needRebuild := binErr != nil // 递归收集所有非标准库依赖的源文件 depSet := map[string]struct{}{} trackDeps(pkg.ImportPath, depSet) // 比对依赖文件修改时间 if !needRebuild { for depFile := range depSet { depStat, err := os.Stat(depFile) if err != nil || depStat.ModTime().After(binStat.ModTime()) { needRebuild = true break } } } if needRebuild { fmt.Println(pkg.ImportPath) } } } // 递归收集指定包的所有Go源文件依赖 func trackDeps(importPath string, collected map[string]struct{}) { pkg, err := build.Import(importPath, ".", 0) if err != nil { return } // 记录当前包的所有源文件 for _, f := range pkg.GoFiles { absPath := filepath.Join(pkg.Dir, f) collected[absPath] = struct{}{} } // 递归处理非标准库依赖 for _, dep := range pkg.Imports { depPkg, err := build.Import(dep, ".", 0) if err != nil || depPkg.Goroot { // 跳过标准库,标准库文件不会随业务代码修改 continue } trackDeps(dep, collected) } }
注:上述代码是文件级依赖判定版本,如果需要实现「修改单个函数仅触发引用该函数的目标重建」的细粒度规则,可以在依赖比对阶段加Go AST解析,判断修改的符号是否被当前目标引用。绝大多数CI场景下,方案一的原生缓存方案已经覆盖了所有边界情况(包括cgo、构建标签、汇编文件等特殊场景),维护成本为0,优先使用。
内容的提问来源于stack exchange,提问作者t3r
相关产品推荐
相关产品推荐

