github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/dist/buildtool.go (about) 1 // Copyright 2015 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 // Build toolchain using Go bootstrap version. 6 // 7 // The general strategy is to copy the source files we need into 8 // a new GOPATH workspace, adjust import paths appropriately, 9 // invoke the Go bootstrap toolchains go command to build those sources, 10 // and then copy the binaries back. 11 12 package dist 13 14 import ( 15 "fmt" 16 "os" 17 "path/filepath" 18 "regexp" 19 "strings" 20 ) 21 22 // bootstrapDirs is a list of directories holding code that must be 23 // compiled with the Go bootstrap toolchain to produce the bootstrapTargets. 24 // All directories in this list are relative to and must be below $GOROOT/src. 25 // 26 // The list has two kinds of entries: names beginning with cmd/ with 27 // no other slashes, which are commands, and other paths, which are packages 28 // supporting the commands. Packages in the standard library can be listed 29 // if a newer copy needs to be substituted for the Go bootstrap copy when used 30 // by the command packages. Paths ending with /... automatically 31 // include all packages within subdirectories as well. 32 // These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big. 33 var bootstrapDirs = []string{ 34 "cmp", 35 "cmd/asm", 36 "github.com/go-asm/go/cmd/asm/...", 37 "cmd/cgo", 38 "cmd/compile", 39 "github.com/go-asm/go/cmd/compile/...", 40 "github.com/go-asm/go/cmd/archive", 41 "github.com/go-asm/go/cmd/bio", 42 "github.com/go-asm/go/cmd/codesign", 43 "github.com/go-asm/go/cmd/dwarf", 44 "github.com/go-asm/go/cmd/edit", 45 "github.com/go-asm/go/cmd/gcprog", 46 "github.com/go-asm/go/cmd/goobj", 47 "github.com/go-asm/go/cmd/notsha256", 48 "github.com/go-asm/go/cmd/obj/...", 49 "github.com/go-asm/go/cmd/objabi", 50 "github.com/go-asm/go/cmd/pkgpath", 51 "github.com/go-asm/go/cmd/quoted", 52 "github.com/go-asm/go/cmd/src", 53 "github.com/go-asm/go/cmd/sys", 54 "cmd/link", 55 "github.com/go-asm/go/cmd/link/...", 56 "compress/flate", 57 "compress/zlib", 58 "container/heap", 59 "debug/dwarf", 60 "debug/elf", 61 "debug/macho", 62 "debug/pe", 63 "go/build/constraint", 64 "go/constant", 65 "go/version", 66 "github.com/go-asm/go/abi", 67 "github.com/go-asm/go/coverage", 68 "github.com/go-asm/go/cmd/cov/covcmd", 69 "github.com/go-asm/go/bisect", 70 "github.com/go-asm/go/buildcfg", 71 "github.com/go-asm/go/goarch", 72 "github.com/go-asm/go/godebugs", 73 "github.com/go-asm/go/goexperiment", 74 "github.com/go-asm/go/goroot", 75 "github.com/go-asm/go/gover", 76 "github.com/go-asm/go/goversion", 77 // github.com/go-asm/go/lazyregexp is provided by Go 1.17, which permits it to 78 // be imported by other packages in this list, but is not provided 79 // by the Go 1.17 version of gccgo. It's on this list only to 80 // support gccgo, and can be removed if we require gccgo 14 or later. 81 "github.com/go-asm/go/lazyregexp", 82 "github.com/go-asm/go/pkgbits", 83 "github.com/go-asm/go/platform", 84 "github.com/go-asm/go/profile", 85 "github.com/go-asm/go/race", 86 "github.com/go-asm/go/saferio", 87 "github.com/go-asm/go/syscall/unix", 88 "github.com/go-asm/go/types/errors", 89 "github.com/go-asm/go/unsafeheader", 90 "github.com/go-asm/go/xcoff", 91 "github.com/go-asm/go/zstd", 92 "math/bits", 93 "sort", 94 } 95 96 // File prefixes that are ignored by go/build anyway, and cause 97 // problems with editor generated temporary files (#18931). 98 var ignorePrefixes = []string{ 99 ".", 100 "_", 101 "#", 102 } 103 104 // File suffixes that use build tags introduced since Go 1.17. 105 // These must not be copied into the bootstrap build directory. 106 // Also ignore test files. 107 var ignoreSuffixes = []string{ 108 "_test.s", 109 "_test.go", 110 // Skip PGO profile. No need to build toolchain1 compiler 111 // with PGO. And as it is not a text file the import path 112 // rewrite will break it. 113 ".pgo", 114 } 115 116 var tryDirs = []string{ 117 "sdk/go1.17", 118 "go1.17", 119 } 120 121 func bootstrapBuildTools() { 122 goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP") 123 if goroot_bootstrap == "" { 124 home := os.Getenv("HOME") 125 goroot_bootstrap = pathf("%s/go1.4", home) 126 for _, d := range tryDirs { 127 if p := pathf("%s/%s", home, d); isdir(p) { 128 goroot_bootstrap = p 129 } 130 } 131 } 132 xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap) 133 134 mkbuildcfg(pathf("%s/src/github.com/go-asm/go/buildcfg/zbootstrap.go", goroot)) 135 mkobjabi(pathf("%s/src/github.com/go-asm/go/cmd/objabi/zbootstrap.go", goroot)) 136 137 // Use $GOROOT/pkg/bootstrap as the bootstrap workspace root. 138 // We use a subdirectory of $GOROOT/pkg because that's the 139 // space within $GOROOT where we store all generated objects. 140 // We could use a temporary directory outside $GOROOT instead, 141 // but it is easier to debug on failure if the files are in a known location. 142 workspace := pathf("%s/pkg/bootstrap", goroot) 143 xremoveall(workspace) 144 xatexit(func() { xremoveall(workspace) }) 145 base := pathf("%s/src/bootstrap", workspace) 146 xmkdirall(base) 147 148 // Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths. 149 writefile("module bootstrap\ngo 1.20\n", pathf("%s/%s", base, "go.mod"), 0) 150 for _, dir := range bootstrapDirs { 151 recurse := strings.HasSuffix(dir, "/...") 152 dir = strings.TrimSuffix(dir, "/...") 153 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 154 if err != nil { 155 fatalf("walking bootstrap dirs failed: %v: %v", path, err) 156 } 157 158 name := filepath.Base(path) 159 src := pathf("%s/src/%s", goroot, path) 160 dst := pathf("%s/%s", base, path) 161 162 if info.IsDir() { 163 if !recurse && path != dir || name == "testdata" { 164 return filepath.SkipDir 165 } 166 167 xmkdirall(dst) 168 if path == "cmd/cgo" { 169 // Write to src because we need the file both for bootstrap 170 // and for later in the main build. 171 mkzdefaultcc("", pathf("%s/zdefaultcc.go", src)) 172 mkzdefaultcc("", pathf("%s/zdefaultcc.go", dst)) 173 } 174 return nil 175 } 176 177 for _, pre := range ignorePrefixes { 178 if strings.HasPrefix(name, pre) { 179 return nil 180 } 181 } 182 for _, suf := range ignoreSuffixes { 183 if strings.HasSuffix(name, suf) { 184 return nil 185 } 186 } 187 188 text := bootstrapRewriteFile(src) 189 writefile(text, dst, 0) 190 return nil 191 }) 192 } 193 194 // Set up environment for invoking Go bootstrap toolchains go command. 195 // GOROOT points at Go bootstrap GOROOT, 196 // GOPATH points at our bootstrap workspace, 197 // GOBIN is empty, so that binaries are installed to GOPATH/bin, 198 // and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty, 199 // so that Go bootstrap toolchain builds whatever kind of binary it knows how to build. 200 // Restore GOROOT, GOPATH, and GOBIN when done. 201 // Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH, 202 // because setup will take care of those when bootstrapBuildTools returns. 203 204 defer os.Setenv("GOROOT", os.Getenv("GOROOT")) 205 os.Setenv("GOROOT", goroot_bootstrap) 206 207 defer os.Setenv("GOPATH", os.Getenv("GOPATH")) 208 os.Setenv("GOPATH", workspace) 209 210 defer os.Setenv("GOBIN", os.Getenv("GOBIN")) 211 os.Setenv("GOBIN", "") 212 213 os.Setenv("GOOS", "") 214 os.Setenv("GOHOSTOS", "") 215 os.Setenv("GOARCH", "") 216 os.Setenv("GOHOSTARCH", "") 217 218 // Run Go bootstrap to build binaries. 219 // Use the math_big_pure_go build tag to disable the assembly in math/big 220 // which may contain unsupported instructions. 221 // Use the purego build tag to disable other assembly code, 222 // such as in github.com/go-asm/go/cmd/notsha256. 223 cmd := []string{ 224 pathf("%s/bin/go", goroot_bootstrap), 225 "install", 226 "-tags=math_big_pure_go compiler_bootstrap purego", 227 } 228 if vflag > 0 { 229 cmd = append(cmd, "-v") 230 } 231 if tool := os.Getenv("GOBOOTSTRAP_TOOLEXEC"); tool != "" { 232 cmd = append(cmd, "-toolexec="+tool) 233 } 234 cmd = append(cmd, "bootstrap/cmd/...") 235 run(base, ShowOutput|CheckExit, cmd...) 236 237 // Copy binaries into tool binary directory. 238 for _, name := range bootstrapDirs { 239 if !strings.HasPrefix(name, "cmd/") { 240 continue 241 } 242 name = name[len("cmd/"):] 243 if !strings.Contains(name, "/") { 244 copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec) 245 } 246 } 247 248 if vflag > 0 { 249 xprintf("\n") 250 } 251 } 252 253 var ssaRewriteFileSubstring = filepath.FromSlash("src/github.com/go-asm/go/cmd/compile/ssa/rewrite") 254 255 // isUnneededSSARewriteFile reports whether srcFile is a 256 // src/github.com/go-asm/go/cmd/compile/ssa/rewriteARCHNAME.go file for an 257 // architecture that isn't for the given GOARCH. 258 // 259 // When unneeded is true archCaps is the rewrite base filename without 260 // the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc. 261 func isUnneededSSARewriteFile(srcFile, goArch string) (archCaps string, unneeded bool) { 262 if !strings.Contains(srcFile, ssaRewriteFileSubstring) { 263 return "", false 264 } 265 fileArch := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(srcFile), "rewrite"), ".go") 266 if fileArch == "" { 267 return "", false 268 } 269 b := fileArch[0] 270 if b == '_' || ('a' <= b && b <= 'z') { 271 return "", false 272 } 273 archCaps = fileArch 274 fileArch = strings.ToLower(fileArch) 275 fileArch = strings.TrimSuffix(fileArch, "splitload") 276 fileArch = strings.TrimSuffix(fileArch, "latelower") 277 if fileArch == goArch { 278 return "", false 279 } 280 if fileArch == strings.TrimSuffix(goArch, "le") { 281 return "", false 282 } 283 return archCaps, true 284 } 285 286 func bootstrapRewriteFile(srcFile string) string { 287 // During bootstrap, generate dummy rewrite files for 288 // irrelevant architectures. We only need to build a bootstrap 289 // binary that works for the current gohostarch. 290 // This saves 6+ seconds of bootstrap. 291 if archCaps, ok := isUnneededSSARewriteFile(srcFile, gohostarch); ok { 292 return fmt.Sprintf(`%spackage ssa 293 294 func rewriteValue%s(v *Value) bool { panic("unused during bootstrap") } 295 func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") } 296 `, generatedHeader, archCaps, archCaps) 297 } 298 299 return bootstrapFixImports(srcFile) 300 } 301 302 func bootstrapFixImports(srcFile string) string { 303 text := readfile(srcFile) 304 if !strings.Contains(srcFile, "/cmd/") && !strings.Contains(srcFile, `\cmd\`) { 305 text = regexp.MustCompile(`\bany\b`).ReplaceAllString(text, "interface{}") 306 } 307 lines := strings.SplitAfter(text, "\n") 308 inBlock := false 309 for i, line := range lines { 310 if strings.HasPrefix(line, "import (") { 311 inBlock = true 312 continue 313 } 314 if inBlock && strings.HasPrefix(line, ")") { 315 inBlock = false 316 continue 317 } 318 if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) || 319 inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"") || strings.HasPrefix(line, "\texec \"") || strings.HasPrefix(line, "\trtabi \"")) { 320 line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1) 321 for _, dir := range bootstrapDirs { 322 if strings.HasPrefix(dir, "cmd/") { 323 continue 324 } 325 line = strings.Replace(line, `"`+dir+`"`, `"bootstrap/`+dir+`"`, -1) 326 } 327 lines[i] = line 328 } 329 } 330 331 lines[0] = generatedHeader + "// This is a bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0] 332 333 return strings.Join(lines, "") 334 }