Skip to content

Commit a448232

Browse files
committed
cmd/gorebuild: add tool to reproduce posted Go binaries
This command rebuilds or verifies all the artifacts posted on go.dev/dl for the latest supported releases (the last patch of the last two major releases, plus the most recent release candidate if we're approaching a new release). It is meant to be run by the Go team to update a status page that can be linked from reproducible-builds.org, but it is also meant to be run by anyone who wants to "trust but verify" the status page themselves. For golang/go#57120. For golang/go#58884. Change-Id: I80a70275c1821a66b6219d24f29c2d11bfe464a8 Reviewed-on: https://go-review.googlesource.com/c/build/+/513975 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Russ Cox <[email protected]>
1 parent c449222 commit a448232

File tree

9 files changed

+2138
-0
lines changed

9 files changed

+2138
-0
lines changed

cmd/gorebuild/build.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"runtime"
13+
"strings"
14+
)
15+
16+
// BootstrapVersion returns the Go bootstrap version required
17+
// for the named version of Go. If the version needs no bootstrap
18+
// (that is, if it's before Go 1.5), BootstrapVersion returns an empty version.
19+
func BootstrapVersion(version string) (string, error) {
20+
if Compare(version, Go(1, 5)) < 0 {
21+
return "", nil
22+
}
23+
if Compare(version, Go(1, 20)) < 0 {
24+
return Go(1, 4), nil
25+
}
26+
if Compare(version, Go(1, 22)) < 0 {
27+
return Go(1, 17), nil
28+
}
29+
if Compare(version, Go(1, 1000)) > 0 {
30+
return "", fmt.Errorf("invalid version %q", version)
31+
}
32+
for i := 24; ; i += 2 {
33+
if Compare(version, Go(1, i)) < 0 {
34+
// 1.24 will switch to 1.22; before that we used 1.20
35+
// 1.26 will switch to 1.24; before that we used 1.22
36+
// ...
37+
return Go(1, i-4), nil
38+
}
39+
}
40+
}
41+
42+
// BootstrapDir returns the name of a directory containing the GOROOT
43+
// for a fully built bootstrap toolchain with the given version.
44+
func (r *Report) BootstrapDir(version string) (dir string, err error) {
45+
for _, b := range r.Bootstraps {
46+
if b.Version == version {
47+
return b.Dir, b.Err
48+
}
49+
}
50+
51+
dir = filepath.Join(r.Work, "bootstrap-"+version)
52+
b := &Bootstrap{
53+
Version: version,
54+
Dir: dir,
55+
Err: fmt.Errorf("bootstrap %s cycle", version),
56+
}
57+
b.Log.Name = "bootstrap " + version
58+
r.Bootstraps = append(r.Bootstraps, b)
59+
60+
defer func() {
61+
if err != nil {
62+
b.Log.Printf("%v", err)
63+
err = fmt.Errorf("bootstrap %s: %v", version, err)
64+
}
65+
b.Err = err
66+
}()
67+
68+
if r.Full {
69+
return b.Dir, r.BootstrapBuild(b, version)
70+
}
71+
return b.Dir, r.BootstrapPrebuilt(b, version)
72+
}
73+
74+
// BootstrapPrebuilt downloads a prebuilt toolchain.
75+
func (r *Report) BootstrapPrebuilt(b *Bootstrap, version string) error {
76+
for _, dl := range r.dl {
77+
if strings.HasPrefix(dl.Version, version+".") {
78+
b.Log.Printf("using %s binary distribution for %s", dl.Version, version)
79+
version = dl.Version
80+
break
81+
}
82+
}
83+
84+
url := "https://go.dev/dl/" + version + "." + runtime.GOOS + "-" + runtime.GOARCH + ".tar.gz"
85+
unpack := UnpackTarGz
86+
if runtime.GOOS == "windows" {
87+
url = strings.TrimSuffix(url, ".tar.gz") + ".zip"
88+
unpack = UnpackZip
89+
}
90+
91+
arch, err := Get(&b.Log, url)
92+
if err != nil {
93+
return err
94+
}
95+
if err := unpack(b.Dir, arch); err != nil {
96+
return err
97+
}
98+
b.Dir = filepath.Join(b.Dir, "go")
99+
return nil
100+
}
101+
102+
// BootstrapBuild builds the named bootstrap toolchain and returns
103+
// the directory containing the GOROOT for the build.
104+
func (r *Report) BootstrapBuild(b *Bootstrap, version string) error {
105+
tgz, err := GerritTarGz(&b.Log, "go", "refs/heads/release-branch."+version)
106+
if err != nil {
107+
return err
108+
}
109+
if err := UnpackTarGz(b.Dir, tgz); err != nil {
110+
return err
111+
}
112+
return r.Build(&b.Log, b.Dir, version, nil, nil)
113+
}
114+
115+
// Build runs a Go make.bash/make.bat/make.rc in the named goroot
116+
// which contains the named version of Go,
117+
// with the additional environment and command-line arguments.
118+
// The returned error is not logged.
119+
// If an error happens during the build, the full output is logged to log,
120+
// but the returned error simply says "make.bash in <goroot> failed".
121+
func (r *Report) Build(log *Log, goroot, version string, env, args []string) error {
122+
bver, err := BootstrapVersion(version)
123+
if err != nil {
124+
return err
125+
}
126+
var bdir string
127+
if bver != "" {
128+
bdir, err = r.BootstrapDir(bver)
129+
if err != nil {
130+
return err
131+
}
132+
}
133+
134+
make := "./make.bash"
135+
switch runtime.GOOS {
136+
case "windows":
137+
make = "./make.bat"
138+
case "plan9":
139+
make = "./make.rc"
140+
}
141+
cmd := exec.Command(make, args...)
142+
cmd.Dir = filepath.Join(goroot, "src")
143+
cmd.Env = append(os.Environ(), env...)
144+
cmd.Env = append(cmd.Env,
145+
"GOROOT="+goroot,
146+
"GOROOT_BOOTSTRAP="+bdir,
147+
"GOTOOLCHAIN=local", // keep bootstraps honest
148+
149+
// Clear various settings that would leak into defaults
150+
// in the toolchain and change the generated binaries.
151+
// These are unlikely to be set to begin with, except
152+
// maybe $CC and $CXX, but if they are, the failures would
153+
// be mysterious.
154+
"CC=",
155+
"CC_FOR_TARGET=",
156+
"CGO_ENABLED=",
157+
"CXX=",
158+
"CXX_FOR_TARGET=",
159+
"GO386=",
160+
"GOAMD64=",
161+
"GOARM=",
162+
"GOBIN=",
163+
"GOEXPERIMENT=",
164+
"GOMIPS64=",
165+
"GOMIPS=",
166+
"GOPATH=",
167+
"GOPPC64=",
168+
"GOROOT_FINAL=",
169+
"GO_EXTLINK_ENABLED=",
170+
"GO_GCFLAGS=",
171+
"GO_LDFLAGS=",
172+
"GO_LDSO=",
173+
"PKG_CONFIG=",
174+
)
175+
log.Printf("running %s env=%v args=%v\nGOROOT=%s\nGOROOT_BOOTSTRAP=%s\n",
176+
make, env, args, goroot, bdir)
177+
out, err := cmd.CombinedOutput()
178+
if err != nil {
179+
log.Printf("%s: %s\n%s", make, err, out)
180+
return fmt.Errorf("%s in %s failed", make, goroot)
181+
}
182+
log.Printf("%s completed:\n%s", make, out)
183+
return nil
184+
}

0 commit comments

Comments
 (0)