golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/gorebuild/build.go (about) 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(), 144 "GOROOT="+goroot, 145 "GOROOT_BOOTSTRAP="+bdir, 146 "GOTOOLCHAIN=local", // keep bootstraps honest 147 148 // Clear various settings that would leak into defaults 149 // in the toolchain and change the generated binaries. 150 // These are unlikely to be set to begin with, except 151 // maybe $CC and $CXX, but if they are, the failures would 152 // be mysterious. 153 "CC=", 154 "CC_FOR_TARGET=", 155 "CGO_ENABLED=", 156 "CXX=", 157 "CXX_FOR_TARGET=", 158 "GO386=", 159 "GOAMD64=", 160 "GOARM=", 161 "GOBIN=", 162 "GOEXPERIMENT=", 163 "GOMIPS64=", 164 "GOMIPS=", 165 "GOPATH=", 166 "GOPPC64=", 167 "GOROOT_FINAL=", 168 "GO_EXTLINK_ENABLED=", 169 "GO_GCFLAGS=", 170 "GO_LDFLAGS=", 171 "GO_LDSO=", 172 "PKG_CONFIG=", 173 ) 174 cmd.Env = append(cmd.Env, env...) 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 }