Skip to content

proposal: cmd/go,cmd/testmain: extract _testmain.go generator into a standalone tool #79240

Description

@podtserkovskiy

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoCommandcmd/goProposalToolProposalIssues describing a requested change to a Go tool or command-line program.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions