Skip to content

cmd/loopvec-toolexec: give the same archive on every build - #11

Merged
mauri870 merged 1 commit into
mauri870:masterfrom
thesyncim:toolexec-reproducible
Nov 3, 2026
Merged

mauri870 merged 1 commit into
mauri870:masterfrom
thesyncim:toolexec-reproducible

Conversation

@thesyncim

@thesyncim thesyncim commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

The wrapper wrote rewritten files to a fresh os.MkdirTemp directory under the go command's work directory. File names end up in the export data, and the go command only trims each compile's own object directory. So every build of a rewritten package gave a different archive, with a random absolute path in it.

That breaks builds where the go command compiles one package twice with the same inputs. With PGO, go test can compile a dependency once per test binary, then compile an importer against one copy and link the other. In goav1, whose cmd/qualitybench has a default.pgo, go test ./... failed with master's wrapper:

link: fingerprint mismatch: github.com/thesyncim/goav1 has 9bcecb47ca806e38, import from github.com/thesyncim/goav1/cmd/qualitybench expecting 224b81cee36e8b4e

The rewritten files now go in loopvec/ inside the object directory, next to the importcfg the go command writes there. That's still a separate place for each compile, and the -trimpath "$WORK/bNNN=>" that the go command always passes reduces it to loopvec/x.go. After this, the two archives in that build have the same content ID, and the test binary links.

toolexec_reproducible.txt builds a package with two empty caches and compares the archives. It fails without the change. -trimpath builds were already fine.

The wrapper wrote the rewritten files to a new os.MkdirTemp directory
under the go command's work directory. The compiler records file names
in the export data, and the go command trims only the compile's own
object directory, so every build of a rewritten package produced a
different archive, with a random absolute path in it.

That fails builds where the go command compiles a package twice with
the same inputs. With PGO, go test can compile a dependency once for
each test binary that needs it, then compile an importer against one
copy and link the other. In github.com/thesyncim/goav1, whose
cmd/qualitybench has a default.pgo, go test ./... fails with:

  link: fingerprint mismatch: github.com/thesyncim/goav1 has
  9bcecb47ca806e38, import from github.com/thesyncim/goav1/cmd/qualitybench
  expecting 224b81cee36e8b4e

The rewritten files now go in a loopvec directory inside the object
directory, next to the importcfg the go command writes there. That is
still a separate place for each compile, and the -trimpath the go
command passes reduces it to loopvec/x.go.
@thesyncim
thesyncim force-pushed the toolexec-reproducible branch from 6a5db5b to 0eb9b61 Compare November 1, 2026 13:36
@mauri870
mauri870 merged commit f3d6b5c into mauri870:master Nov 3, 2026
1 of 2 checks passed
mauri870 added a commit that referenced this pull request Nov 3, 2026
`toolexec_reproducible.txt` checks that two separate builds of a
rewritten package give the same archive. It doesn't cover the failure
that motivated #11. With PGO, `go test` compiles a package once for a
main package's binary and again for its test binary. It then compiles
the test against one copy and links the other, and the linker reports a
fingerprint mismatch when the two differ.

`toolexec_pgo.txt` reproduces that. A main package with a `default.pgo`
has a test that imports the rewritten package, so the test binary gets a
PGO copy of its own. The profile is written by a small program inside
the script, because a CPU profile with no samples is enough to turn PGO
on. Without #11, `go test ./...` fails with the same link error seen in
goav1:

```
link: fingerprint mismatch: example.com/app/vec has d0f77d59987fdb10, import from example.com/app/cmd/bench expecting 205082f863ae44a6
```

`toolexec_reproducible.txt` is also brought in line with the other
toolexec scripts:

- it checks the wrapper's log against a `log.want` section;
- it checks that both builds print nothing;
- it limits the log to the package under test with
`LOOPVEC_TOOLEXEC_LOG_PKGS`.

Tested locally:

- With #11's `compile.go` reverted, both scripts fail. The PGO one fails
with the link error above, and the reproducible one at `cmp a1.a a2.a`.
- With #11 in place, both pass, with the same result on three repeated
runs.
- `make test` and `make test-qemu-arm64` pass.
- `-update` leaves both scripts unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants