Skip to content

runtime/debug: populate BuildInfo so ReadBuildInfo works - #5592

Open
0pcom wants to merge 2 commits into
tinygo-org:devfrom
0magnet:debug-buildinfo-vcs
Open

runtime/debug: populate BuildInfo so ReadBuildInfo works#5592
0pcom wants to merge 2 commits into
tinygo-org:devfrom
0magnet:debug-buildinfo-vcs

Conversation

@0pcom

@0pcom 0pcom commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

debug.ReadBuildInfo() returns ok=false under TinyGo, so anything that reports its own version — a --version flag, a crash handler, a metric — has nothing to read.

The information already exists. go list reports module paths and versions for the loaded packages, and the standard toolchain stamps the same data into runtime/debug.modinfo, a plain string global that runtime/debug parses back into a *BuildInfo. This fills that global the same way, so the existing parsing path does the rest.

Four pieces, because the data has to travel:

  • loader — keep the module Version that go list already returns and the struct was discarding
  • builder — assemble the modinfo string and set the global, unless -ldflags="-X runtime/debug.modinfo=..." already did
  • src/runtime/debug — parse it, which is where ReadBuildInfo reads from
  • go.modgolang.org/x/mod, for module.Check and semver validation of what goes into the string

Skipped in GOPATH mode and when the main package isn't in a module, where there's nothing to report.

Verified: a module built with this prints its own path and version from ReadBuildInfo (path: bitest, main: bitest (devel)), where it previously reported nothing available.

@b0ch3nski

Copy link
Copy Markdown
Contributor

This PR updates all submodules - unintentionally I guess?

debug.ReadBuildInfo() returns ok=false under TinyGo, so anything that
reports its own version — a --version flag, a crash handler, a metric —
has nothing to read.

The information already exists: `go list` reports module paths and
versions for the loaded packages, and the standard toolchain stamps the
same data into runtime/debug.modinfo, a plain string global that
runtime/debug parses back into a *BuildInfo. This fills that global the
same way.

Four pieces, because the data has to travel:

  - loader: keep the module Version that `go list` already returns and
    the struct was discarding.
  - builder: assemble the modinfo string and set the global, unless
    -ldflags="-X runtime/debug.modinfo=..." already did.
  - src/runtime/debug: parse it, which is where ReadBuildInfo reads from.
  - go.mod: golang.org/x/mod, for module.Check and semver validation of
    what goes into the string.

Skipped in GOPATH mode and when the main package is not in a module,
where there is nothing to report.

Verified: a module built with this prints its own path and version from
ReadBuildInfo, where it previously reported nothing available.
@0pcom

0pcom commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Correct, entirely unintentional — thanks for catching it.

The branch had picked up submodule pointer bumps for lib/cmsis-svd, lib/mingw-w64, lib/nrfx, lib/picolibc, lib/wasi-cli, lib/wasi-libc and src/net. The src/net one was the worst of them: it pointed at a fork, which had no business being in an upstream PR at all.

All seven are gone; the diff is now only builder/build.go, loader/loader.go, src/runtime/debug/debug.go and go.mod. The go.mod change is intended — golang.org/x/mod moves from indirect to direct because the new code imports it for module.Check and semver validation.

Also rebased on dev, which merged cleanly with c33682c apart from an import that both sides added to; ./builder compiles.

@0pcom
0pcom force-pushed the debug-buildinfo-vcs branch from 86d1329 to e053e7f Compare August 16, 2026 19:11
@dgryski

dgryski commented Aug 19, 2026

Copy link
Copy Markdown
Member

This needs a test.

Adds testdata/buildinfo.go to the compiler test list.

The output has to be identical on every machine, so nothing here prints a
version, a module path or a checksum the build happens to have. What is checked
is either derived — that ReadBuildInfo always succeeds, that it reports a
toolchain version even with no module information embedded, and that the
version names the compiler — or comes from a fixed module string parsed in the
test.

That string covers the four line kinds and a replacement, so the parse, the
round trip back through BuildInfo.String, and the shapes that must be rejected
are all exercised.

The distinction the last group draws is the one worth having: a line whose
prefix is not a known kind is skipped, which is what upstream does and what
lets an older parser read a newer module string, while a known kind with the
wrong number of columns is an error — ReadBuildInfo falls back to reporting
just the toolchain version when that happens.
@0pcom

0pcom commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Added testdata/buildinfo.go and registered it in the compiler test list.

The constraint is that the golden output has to be identical on every machine, so nothing printed is a version, a module path or a checksum the build happens to have. What is asserted is either derived — ReadBuildInfo always succeeds, it reports a toolchain version even with no module info embedded, and that version names the compiler — or comes from a fixed module string the test parses, covering the four line kinds plus a replacement, the round trip back through BuildInfo.String, and the inputs that must be rejected.

One distinction in there is worth calling out, because writing the test is what settled it: a line whose prefix is not a known kind is skipped rather than rejected — matching upstream, and what lets an older parser read a newer module string — while a known kind with the wrong column count is an error, which is when ReadBuildInfo falls back to reporting just the toolchain version. My first draft asserted the opposite and was wrong.

I could not run the full go test matrix locally: building the compiler here wants LLVM 20 and this machine has 22. I ran the program directly against this branch's src/ with a matching tinygo build, confirmed the output is byte-identical across runs, and confirmed it compiles for wasip1 and wasm. CI is what will confirm the rest.

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.

3 participants