Problem
go test command synthesises a _testmain.go file that wires the user's Test*/Benchmark*/etc functions into testing.MainStart.
The generator lives inline in cmd/go/internal/load/test.go. There is no standalone cmd/testmain binary.
I maintain the Buck2 fork. Keeping it in sync with upstream testing internals across Go releases is recurring work, and the fork has accumulated subtle behavioral differences from go test. A shared standalone generator would let both Buck2 and rules_go drop their forks and stay aligned with cmd/go.
Proposed Change
Add a cmd/testmain binary, pre-built like other Go tools, that cmd/go execs via base.Tool("testmain"), replacing the in-process call to cmd/go/internal/load.formatTestmain.
Buck2 and rules_go can adopt it via cmd/testmain call.
CLI Sketch
The tool reads test sources and writes the generated _testmain.go:
go tool testmain [flags] [@responsefile]
-o FILE output file (use "-" for stdout)
-importpath PATH package import path
-modulepath PATH module path (optional)
-testfile FILE internal test source (repeatable)
-xtestfile FILE external test source (repeatable, package foo_test)
-covermode MODE "" | "set" | "count" | "atomic"
-coverpkg PATH resolved cover-package import path (repeatable)
-coverpath PATTERN user-typed -coverpkg pattern (repeatable, banner)
@responsefile GCC-style, via cmd/internal/objabi.Flagparse
Integration
-
cmd/go: Switch formatTestmain to a subprocess call.
-
Buck2 (out of scope): Drop prelude/go/tools/testmaingen.go and exec cmd/testmain. Buck handles its working-directory requirement (resources colocated with the binary) on the Buck side.
-
rules_go (out of scope): Drop go/tools/builders/generate_test_main.go and exec cmd/testmain. rules_go has additional features the standard generator does not provide (Bazel test protocol — TESTBRIDGE_*, sharding, XML_OUTPUT_FILE, TEST_TIMEOUT — and lcov coverage output). They live in the generated main() today, landing them elsewhere will likely need new extension points in cmd/testmain or in testing/internal/testdeps, scoped by the rules_go maintainers.
Migration is opt-in. Existing forks keep working.
cmd/testmain is stricter about test-function signatures than Buck and rules_go are today; the upstream rules will be enforced once they migrate.
Had a great chat about this with @aclements and @matloob at GopherCon, felt like we were on the same page.
Problem
go testcommand synthesises a_testmain.gofile that wires the user'sTest*/Benchmark*/etc functions intotesting.MainStart.The generator lives inline in
cmd/go/internal/load/test.go. There is no standalonecmd/testmainbinary.buck2/prelude/go/tools/testmaingen.gorules_go/go/tools/builders/generate_test_main.goI maintain the Buck2 fork. Keeping it in sync with upstream
testinginternals across Go releases is recurring work, and the fork has accumulated subtle behavioral differences fromgo test. A shared standalone generator would let both Buck2 and rules_go drop their forks and stay aligned withcmd/go.Proposed Change
Add a
cmd/testmainbinary, pre-built like other Go tools, thatcmd/goexecs viabase.Tool("testmain"), replacing the in-process call tocmd/go/internal/load.formatTestmain.Buck2 and rules_go can adopt it via
cmd/testmaincall.CLI Sketch
The tool reads test sources and writes the generated
_testmain.go:Integration
cmd/go: Switch
formatTestmainto a subprocess call.Buck2 (out of scope): Drop
prelude/go/tools/testmaingen.goand execcmd/testmain. Buck handles its working-directory requirement (resources colocated with the binary) on the Buck side.rules_go (out of scope): Drop
go/tools/builders/generate_test_main.goand execcmd/testmain. rules_go has additional features the standard generator does not provide (Bazel test protocol —TESTBRIDGE_*, sharding,XML_OUTPUT_FILE,TEST_TIMEOUT— and lcov coverage output). They live in the generatedmain()today, landing them elsewhere will likely need new extension points incmd/testmainor intesting/internal/testdeps, scoped by the rules_go maintainers.Migration is opt-in. Existing forks keep working.
cmd/testmainis stricter about test-function signatures than Buck and rules_go are today; the upstream rules will be enforced once they migrate.Had a great chat about this with @aclements and @matloob at GopherCon, felt like we were on the same page.