From a8af66bc5903c962a652b849f84a54a35bc616da Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Sat, 24 Oct 2026 00:49:02 -0700 Subject: [PATCH] compiler: share artifacts across build directories The shared cache identity named each protobuf binding root by its key path. A root outside every module of the graph keys by its path from the request directory, so the same program built from two directories at different depths, as bldr does for its js and web/js/wasm platforms, shared no artifact: every key differed. The identity no longer names binding roots. They change output only through the bindings a package reads, which protobufSideInputs already keys per package. The replaced checkout test now builds its second checkout two levels deeper with a binding root outside the graph. Signed-off-by: Christian Stewart --- compiler/compiler-cache.go | 10 ++++------ compiler/compiler-cache_test.go | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/compiler-cache.go b/compiler/compiler-cache.go index c47c09e57..e9c679c3e 100644 --- a/compiler/compiler-cache.go +++ b/compiler/compiler-cache.go @@ -946,17 +946,15 @@ func (o *compilerCacheKeyOwner) keyedGoMod(file string, data []byte) []byte { // sharedIdentity digests the schema, the compiler identity and the request. // Override directories count by content, which decides the override facts the -// parity check reads. +// parity check reads. Protobuf binding roots count only through the bindings +// each package reads (protobufSideInputs): a root outside every module would +// key by its path from the request directory, which differs per build +// directory, so programs that share packages would share no artifact. func (o *compilerCacheKeyOwner) sharedIdentity() string { var b strings.Builder writeKeyField(&b, "schema", compilerCacheSchema) writeCompilerIdentity(&b) req := o.req - if req.ProtobufTypeScriptBinding { - for _, root := range req.AdditionalBindingRoots { - writeKeyField(&b, "protobuf-binding-root", o.keyPath(root)) - } - } for _, flag := range goScriptBuildFlags(req.BuildFlags) { writeKeyField(&b, "build-flag", flag) } diff --git a/compiler/compiler-cache_test.go b/compiler/compiler-cache_test.go index c3ff21848..d279e6316 100644 --- a/compiler/compiler-cache_test.go +++ b/compiler/compiler-cache_test.go @@ -119,23 +119,26 @@ func TestCompilePackagesCacheReplaysAcrossCheckouts(t *testing.T) { // TestCompilePackagesCacheReplaysAcrossReplacedCheckouts pins that a go.mod // replacing a module with an absolute directory keys that directory by its -// module, so a generated module in a second checkout still replays. +// module, so a generated module in a second checkout still replays. The second +// module sits deeper, as a build tool's per platform directory does, and both +// name a protobuf binding root that holds no package of the graph. func TestCompilePackagesCacheReplaysAcrossReplacedCheckouts(t *testing.T) { cacheRoot := filepath.Join(t.TempDir(), "cache") - checkout := func(outputName string) { + config := Config{ProtobufTypeScriptBinding: true, AdditionalBindingRoots: []string{t.TempDir()}} + checkout := func(outputName, appPath string) { root := writePackageGraphFixture(t, map[string]string{ - "lib/go.mod": "module example.test/lib\n\ngo 1.25.3\n", - "lib/lib.go": "package lib\nconst Name = \"lib\"\n", - "app/main.go": "package main\n\nimport \"example.test/lib\"\n\nfunc main() { println(lib.Name) }\n", + "lib/go.mod": "module example.test/lib\n\ngo 1.25.3\n", + "lib/lib.go": "package lib\nconst Name = \"lib\"\n", + appPath + "/main.go": "package main\n\nimport \"example.test/lib\"\n\nfunc main() { println(lib.Name) }\n", }) - appDir := filepath.Join(root, "app") + appDir := filepath.Join(root, appPath) writeFixtureFile(t, appDir, "go.mod", "module example.test/app\n\ngo 1.25.3\n\nrequire example.test/lib v0.0.0\n\nreplace example.test/lib => "+filepath.Join(root, "lib")+"\n") - compileCacheFixture(t, appDir, filepath.Join(t.TempDir(), outputName), cacheRoot) + compileCacheFixtureConfig(t, config, appDir, filepath.Join(t.TempDir(), outputName), cacheRoot) } - checkout("first") + checkout("first", "app") manifests := countCacheManifests(t, cacheRoot) - checkout("second") + checkout("second", "web/js/app") if got := countCacheManifests(t, cacheRoot); got != manifests { t.Fatalf("cache manifests after second checkout = %d, want %d", got, manifests) }