diff --git a/internal/linker/linker.go b/internal/linker/linker.go index d02ca8f..e043433 100644 --- a/internal/linker/linker.go +++ b/internal/linker/linker.go @@ -156,7 +156,11 @@ func cachePath(cacheDir string) (string, error) { } func getCurrentVersion(goVersion, patchesVer string) string { - return goVersion + " " + patchesVer + // Note that we assume that if a Go toolchain reports itself as e.g. go1.24.1, + // it really is that upstream Go version with no alterations or edits. + // If any modifications are made, it should report itself as e.g. go1.24.1-corp. + // The alternative would be to use the content ID hash of the cmd/link binary. + return goVersion + " " + patchesVer + "\n" } func checkVersion(linkerPath, goVersion, patchesVer string) (bool, error) { diff --git a/main_test.go b/main_test.go index 4ae2a1f..8898887 100644 --- a/main_test.go +++ b/main_test.go @@ -83,6 +83,13 @@ func TestScript(t *testing.T) { // Use testdata/mod as our module proxy. env.Setenv("GOPROXY", proxyURL) + // gotoolchain.txtar is one test which wants to reuse GOMODCACHE. + out, err := exec.Command("go", "env", "GOMODCACHE").Output() + if err != nil { + return err + } + env.Setenv("HOST_GOMODCACHE", strings.TrimSpace(string(out))) + // We use our own proxy, so avoid sum.golang.org. env.Setenv("GONOSUMDB", "*") @@ -117,7 +124,7 @@ func TestScript(t *testing.T) { Condition: func(cond string) (bool, error) { switch cond { case "cgo": - out, err := exec.Command("go", "env", "CGO_ENABLED").CombinedOutput() + out, err := exec.Command("go", "env", "CGO_ENABLED").Output() if err != nil { return false, err } @@ -138,6 +145,7 @@ func TestScript(t *testing.T) { "generate-literals": generateLiterals, "setenvfile": setenvfile, "grepfiles": grepfiles, + "setup-go": setupGo, }, UpdateScripts: *update, RequireExplicitExec: true, @@ -383,6 +391,25 @@ func grepfiles(ts *testscript.TestScript, neg bool, args []string) { } } +func setupGo(ts *testscript.TestScript, neg bool, args []string) { + if neg || len(args) != 1 { + ts.Fatalf("usage: setup-go version") + } + // Download the version of Go specified as an argument, cache it in GOMODCACHE, + // and get its GOROOT directory inside the cache so we can use it. + cmd := exec.Command("go", "env", "GOROOT") + cmd.Env = append(cmd.Environ(), "GOTOOLCHAIN="+args[0]) + out, err := cmd.Output() + ts.Check(err) + + goroot := strings.TrimSpace(string(out)) + + ts.Setenv("PATH", filepath.Join(goroot, "bin")+string(os.PathListSeparator)+ts.Getenv("PATH")) + // Remove GOROOT from the environment, as it is unnecessary and gets in the way + // when we want to test GOTOOLCHAIN upgrades, which will need different GOROOTs. + ts.Setenv("GOROOT", "") +} + func TestSplitFlagsFromArgs(t *testing.T) { t.Parallel() tests := []struct { diff --git a/testdata/script/gotoolchain.txtar b/testdata/script/gotoolchain.txtar new file mode 100644 index 0000000..e4dee89 --- /dev/null +++ b/testdata/script/gotoolchain.txtar @@ -0,0 +1,37 @@ +# Test that garble works with transparent upgrades via GOTOOLCHAIN. +# We want to use the real GOPROXY so that we can download the newer +# toolchain, and we use the host's GOMODCACHE so we can reuse it. +setup-go go1.23.7 +env GOPROXY=proxy.golang.org +env GOMODCACHE=${HOST_GOMODCACHE} +# The bug below is about a badly patched cmd/link, so we need a separate +# GARBLE_CACHE to not allow reusing the host's working patched linker. +env GARBLE_CACHE=${WORK}/.garble-cache + +# Verify that we are using an older version of Go. +exec go version +stdout 'go version go1\.23\.7' + +cd mod + +# The builds inside the module use the upgraded toolchain. + +exec go run . +stderr 'hello from go1\.24\.1' + +# TODO: fix this. +! exec garble run . +stderr 'linked object header mismatch' + +-- mod/go.mod -- +module test + +go 1.24.1 +-- mod/main.go -- +package main + +import "runtime" + +func main() { + println("hello from", runtime.Version()) +}