| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Build toolchain using Go bootstrap version. |
| // |
| // The general strategy is to copy the source files we need into |
| // a new GOPATH workspace, adjust import paths appropriately, |
| // invoke the Go bootstrap toolchains go command to build those sources, |
| // and then copy the binaries back. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| "regexp" |
| "strings" |
| ) |
| |
| // bootstrapDirs is a list of directories holding code that must be |
| // compiled with the Go bootstrap toolchain to produce the bootstrapTargets. |
| // All directories in this list are relative to and must be below $GOROOT/src. |
| // |
| // The list has two kinds of entries: names beginning with cmd/ with |
| // no other slashes, which are commands, and other paths, which are packages |
| // supporting the commands. Packages in the standard library can be listed |
| // if a newer copy needs to be substituted for the Go bootstrap copy when used |
| // by the command packages. Paths ending with /... automatically |
| // include all packages within subdirectories as well. |
| // These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big. |
| var bootstrapDirs = []string{ |
| "cmp", |
| "cmd/asm", |
| "cmd/asm/internal/...", |
| "cmd/cgo", |
| "cmd/compile", |
| "cmd/compile/internal/...", |
| "cmd/internal/archive", |
| "cmd/internal/bio", |
| "cmd/internal/codesign", |
| "cmd/internal/dwarf", |
| "cmd/internal/edit", |
| "cmd/internal/gcprog", |
| "cmd/internal/goobj", |
| "cmd/internal/notsha256", |
| "cmd/internal/obj/...", |
| "cmd/internal/objabi", |
| "cmd/internal/pkgpath", |
| "cmd/internal/quoted", |
| "cmd/internal/src", |
| "cmd/internal/sys", |
| "cmd/link", |
| "cmd/link/internal/...", |
| "compress/flate", |
| "compress/zlib", |
| "container/heap", |
| "debug/dwarf", |
| "debug/elf", |
| "debug/macho", |
| "debug/pe", |
| "go/build/constraint", |
| "go/constant", |
| "go/version", |
| "internal/abi", |
| "internal/coverage", |
| "cmd/internal/cov/covcmd", |
| "internal/bisect", |
| "internal/buildcfg", |
| "internal/goarch", |
| "internal/godebugs", |
| "internal/goexperiment", |
| "internal/goroot", |
| "internal/gover", |
| "internal/goversion", |
| // internal/lazyregexp is provided by Go 1.17, which permits it to |
| // be imported by other packages in this list, but is not provided |
| // by the Go 1.17 version of gccgo. It's on this list only to |
| // support gccgo, and can be removed if we require gccgo 14 or later. |
| "internal/lazyregexp", |
| "internal/pkgbits", |
| "internal/platform", |
| "internal/profile", |
| "internal/race", |
| "internal/saferio", |
| "internal/syscall/unix", |
| "internal/types/errors", |
| "internal/unsafeheader", |
| "internal/xcoff", |
| "internal/zstd", |
| "math/bits", |
| "sort", |
| } |
| |
| // File prefixes that are ignored by go/build anyway, and cause |
| // problems with editor generated temporary files (#18931). |
| var ignorePrefixes = []string{ |
| ".", |
| "_", |
| "#", |
| } |
| |
| // File suffixes that use build tags introduced since Go 1.17. |
| // These must not be copied into the bootstrap build directory. |
| // Also ignore test files. |
| var ignoreSuffixes = []string{ |
| "_test.s", |
| "_test.go", |
| // Skip PGO profile. No need to build toolchain1 compiler |
| // with PGO. And as it is not a text file the import path |
| // rewrite will break it. |
| ".pgo", |
| } |
| |
| var tryDirs = []string{ |
| "sdk/go1.17", |
| "go1.17", |
| } |
| |
| func bootstrapBuildTools() { |
| goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP") |
| if goroot_bootstrap == "" { |
| home := os.Getenv("HOME") |
| goroot_bootstrap = pathf("%s/go1.4", home) |
| for _, d := range tryDirs { |
| if p := pathf("%s/%s", home, d); isdir(p) { |
| goroot_bootstrap = p |
| } |
| } |
| } |
| xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap) |
| |
| mkbuildcfg(pathf("%s/src/internal/buildcfg/zbootstrap.go", goroot)) |
| mkobjabi(pathf("%s/src/cmd/internal/objabi/zbootstrap.go", goroot)) |
| |
| // Use $GOROOT/pkg/bootstrap as the bootstrap workspace root. |
| // We use a subdirectory of $GOROOT/pkg because that's the |
| // space within $GOROOT where we store all generated objects. |
| // We could use a temporary directory outside $GOROOT instead, |
| // but it is easier to debug on failure if the files are in a known location. |
| workspace := pathf("%s/pkg/bootstrap", goroot) |
| xremoveall(workspace) |
| xatexit(func() { xremoveall(workspace) }) |
| base := pathf("%s/src/bootstrap", workspace) |
| xmkdirall(base) |
| |
| // Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths. |
| writefile("module bootstrap\ngo 1.20\n", pathf("%s/%s", base, "go.mod"), 0) |
| for _, dir := range bootstrapDirs { |
| recurse := strings.HasSuffix(dir, "/...") |
| dir = strings.TrimSuffix(dir, "/...") |
| filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { |
| if err != nil { |
| fatalf("walking bootstrap dirs failed: %v: %v", path, err) |
| } |
| |
| name := filepath.Base(path) |
| src := pathf("%s/src/%s", goroot, path) |
| dst := pathf("%s/%s", base, path) |
| |
| if info.IsDir() { |
| if !recurse && path != dir || name == "testdata" { |
| return filepath.SkipDir |
| } |
| |
| xmkdirall(dst) |
| if path == "cmd/cgo" { |
| // Write to src because we need the file both for bootstrap |
| // and for later in the main build. |
| mkzdefaultcc("", pathf("%s/zdefaultcc.go", src)) |
| mkzdefaultcc("", pathf("%s/zdefaultcc.go", dst)) |
| } |
| return nil |
| } |
| |
| for _, pre := range ignorePrefixes { |
| if strings.HasPrefix(name, pre) { |
| return nil |
| } |
| } |
| for _, suf := range ignoreSuffixes { |
| if strings.HasSuffix(name, suf) { |
| return nil |
| } |
| } |
| |
| text := bootstrapRewriteFile(src) |
| writefile(text, dst, 0) |
| return nil |
| }) |
| } |
| |
| // Set up environment for invoking Go bootstrap toolchains go command. |
| // GOROOT points at Go bootstrap GOROOT, |
| // GOPATH points at our bootstrap workspace, |
| // GOBIN is empty, so that binaries are installed to GOPATH/bin, |
| // and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty, |
| // so that Go bootstrap toolchain builds whatever kind of binary it knows how to build. |
| // Restore GOROOT, GOPATH, and GOBIN when done. |
| // Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH, |
| // because setup will take care of those when bootstrapBuildTools returns. |
| |
| defer os.Setenv("GOROOT", os.Getenv("GOROOT")) |
| os.Setenv("GOROOT", goroot_bootstrap) |
| |
| defer os.Setenv("GOPATH", os.Getenv("GOPATH")) |
| os.Setenv("GOPATH", workspace) |
| |
| defer os.Setenv("GOBIN", os.Getenv("GOBIN")) |
| os.Setenv("GOBIN", "") |
| |
| os.Setenv("GOOS", "") |
| os.Setenv("GOHOSTOS", "") |
| os.Setenv("GOARCH", "") |
| os.Setenv("GOHOSTARCH", "") |
| |
| // Run Go bootstrap to build binaries. |
| // Use the math_big_pure_go build tag to disable the assembly in math/big |
| // which may contain unsupported instructions. |
| // Use the purego build tag to disable other assembly code, |
| // such as in cmd/internal/notsha256. |
| cmd := []string{ |
| pathf("%s/bin/go", goroot_bootstrap), |
| "install", |
| "-tags=math_big_pure_go compiler_bootstrap purego", |
| } |
| if vflag > 0 { |
| cmd = append(cmd, "-v") |
| } |
| if tool := os.Getenv("GOBOOTSTRAP_TOOLEXEC"); tool != "" { |
| cmd = append(cmd, "-toolexec="+tool) |
| } |
| cmd = append(cmd, "bootstrap/cmd/...") |
| run(base, ShowOutput|CheckExit, cmd...) |
| |
| // Copy binaries into tool binary directory. |
| for _, name := range bootstrapDirs { |
| if !strings.HasPrefix(name, "cmd/") { |
| continue |
| } |
| name = name[len("cmd/"):] |
| if !strings.Contains(name, "/") { |
| copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec) |
| } |
| } |
| |
| if vflag > 0 { |
| xprintf("\n") |
| } |
| } |
| |
| var ssaRewriteFileSubstring = filepath.FromSlash("src/cmd/compile/internal/ssa/rewrite") |
| |
| // isUnneededSSARewriteFile reports whether srcFile is a |
| // src/cmd/compile/internal/ssa/rewriteARCHNAME.go file for an |
| // architecture that isn't for the given GOARCH. |
| // |
| // When unneeded is true archCaps is the rewrite base filename without |
| // the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc. |
| func isUnneededSSARewriteFile(srcFile, goArch string) (archCaps string, unneeded bool) { |
| if !strings.Contains(srcFile, ssaRewriteFileSubstring) { |
| return "", false |
| } |
| fileArch := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(srcFile), "rewrite"), ".go") |
| if fileArch == "" { |
| return "", false |
| } |
| b := fileArch[0] |
| if b == '_' || ('a' <= b && b <= 'z') { |
| return "", false |
| } |
| archCaps = fileArch |
| fileArch = strings.ToLower(fileArch) |
| fileArch = strings.TrimSuffix(fileArch, "splitload") |
| fileArch = strings.TrimSuffix(fileArch, "latelower") |
| if fileArch == goArch { |
| return "", false |
| } |
| if fileArch == strings.TrimSuffix(goArch, "le") { |
| return "", false |
| } |
| return archCaps, true |
| } |
| |
| func bootstrapRewriteFile(srcFile string) string { |
| // During bootstrap, generate dummy rewrite files for |
| // irrelevant architectures. We only need to build a bootstrap |
| // binary that works for the current gohostarch. |
| // This saves 6+ seconds of bootstrap. |
| if archCaps, ok := isUnneededSSARewriteFile(srcFile, gohostarch); ok { |
| return fmt.Sprintf(`%spackage ssa |
| |
| func rewriteValue%s(v *Value) bool { panic("unused during bootstrap") } |
| func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") } |
| `, generatedHeader, archCaps, archCaps) |
| } |
| |
| return bootstrapFixImports(srcFile) |
| } |
| |
| func bootstrapFixImports(srcFile string) string { |
| text := readfile(srcFile) |
| if !strings.Contains(srcFile, "/cmd/") && !strings.Contains(srcFile, `\cmd\`) { |
| text = regexp.MustCompile(`\bany\b`).ReplaceAllString(text, "interface{}") |
| } |
| lines := strings.SplitAfter(text, "\n") |
| inBlock := false |
| for i, line := range lines { |
| if strings.HasPrefix(line, "import (") { |
| inBlock = true |
| continue |
| } |
| if inBlock && strings.HasPrefix(line, ")") { |
| inBlock = false |
| continue |
| } |
| if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) || |
| inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"") || strings.HasPrefix(line, "\texec \"") || strings.HasPrefix(line, "\trtabi \"")) { |
| line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1) |
| for _, dir := range bootstrapDirs { |
| if strings.HasPrefix(dir, "cmd/") { |
| continue |
| } |
| line = strings.Replace(line, `"`+dir+`"`, `"bootstrap/`+dir+`"`, -1) |
| } |
| lines[i] = line |
| } |
| } |
| |
| lines[0] = generatedHeader + "// This is a bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0] |
| |
| return strings.Join(lines, "") |
| } |