| // Copyright 2012 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. | |
| package main | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "flag" | |
| "fmt" | |
| "io/ioutil" | |
| "log" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "sort" | |
| "strings" | |
| "sync" | |
| "time" | |
| ) | |
| // Initialization for any invocation. | |
| // The usual variables. | |
| var ( | |
| goarch string | |
| gobin string | |
| gohostarch string | |
| gohostos string | |
| goos string | |
| goarm string | |
| go386 string | |
| gomips string | |
| gomips64 string | |
| goppc64 string | |
| goroot string | |
| goroot_final string | |
| goextlinkenabled string | |
| gogcflags string // For running built compiler | |
| goldflags string | |
| workdir string | |
| tooldir string | |
| oldgoos string | |
| oldgoarch string | |
| exe string | |
| defaultcc map[string]string | |
| defaultcxx map[string]string | |
| defaultcflags string | |
| defaultldflags string | |
| defaultpkgconfig string | |
| defaultldso string | |
| rebuildall bool | |
| defaultclang bool | |
| vflag int // verbosity | |
| ) | |
| // The known architectures. | |
| var okgoarch = []string{ | |
| "386", | |
| "amd64", | |
| "arm", | |
| "arm64", | |
| "mips", | |
| "mipsle", | |
| "mips64", | |
| "mips64le", | |
| "ppc64", | |
| "ppc64le", | |
| "riscv64", | |
| "s390x", | |
| "sparc64", | |
| "wasm", | |
| } | |
| // The known operating systems. | |
| var okgoos = []string{ | |
| "darwin", | |
| "dragonfly", | |
| "illumos", | |
| "ios", | |
| "js", | |
| "linux", | |
| "android", | |
| "solaris", | |
| "freebsd", | |
| "nacl", // keep; | |
| "netbsd", | |
| "openbsd", | |
| "plan9", | |
| "windows", | |
| "aix", | |
| } | |
| // find reports the first index of p in l[0:n], or else -1. | |
| func find(p string, l []string) int { | |
| for i, s := range l { | |
| if p == s { | |
| return i | |
| } | |
| } | |
| return -1 | |
| } | |
| // xinit handles initialization of the various global state, like goroot and goarch. | |
| func xinit() { | |
| b := os.Getenv("GOROOT") | |
| if b == "" { | |
| fatalf("$GOROOT must be set") | |
| } | |
| goroot = filepath.Clean(b) | |
| b = os.Getenv("GOROOT_FINAL") | |
| if b == "" { | |
| b = goroot | |
| } | |
| goroot_final = b | |
| b = os.Getenv("GOBIN") | |
| if b == "" { | |
| b = pathf("%s/bin", goroot) | |
| } | |
| gobin = b | |
| b = os.Getenv("GOOS") | |
| if b == "" { | |
| b = gohostos | |
| } | |
| goos = b | |
| if find(goos, okgoos) < 0 { | |
| fatalf("unknown $GOOS %s", goos) | |
| } | |
| b = os.Getenv("GOARM") | |
| if b == "" { | |
| b = xgetgoarm() | |
| } | |
| goarm = b | |
| b = os.Getenv("GO386") | |
| if b == "" { | |
| b = "sse2" | |
| } | |
| go386 = b | |
| b = os.Getenv("GOMIPS") | |
| if b == "" { | |
| b = "hardfloat" | |
| } | |
| gomips = b | |
| b = os.Getenv("GOMIPS64") | |
| if b == "" { | |
| b = "hardfloat" | |
| } | |
| gomips64 = b | |
| b = os.Getenv("GOPPC64") | |
| if b == "" { | |
| b = "power8" | |
| } | |
| goppc64 = b | |
| if p := pathf("%s/src/all.bash", goroot); !isfile(p) { | |
| fatalf("$GOROOT is not set correctly or not exported\n"+ | |
| "\tGOROOT=%s\n"+ | |
| "\t%s does not exist", goroot, p) | |
| } | |
| b = os.Getenv("GOHOSTARCH") | |
| if b != "" { | |
| gohostarch = b | |
| } | |
| if find(gohostarch, okgoarch) < 0 { | |
| fatalf("unknown $GOHOSTARCH %s", gohostarch) | |
| } | |
| b = os.Getenv("GOARCH") | |
| if b == "" { | |
| b = gohostarch | |
| } | |
| goarch = b | |
| if find(goarch, okgoarch) < 0 { | |
| fatalf("unknown $GOARCH %s", goarch) | |
| } | |
| b = os.Getenv("GO_EXTLINK_ENABLED") | |
| if b != "" { | |
| if b != "0" && b != "1" { | |
| fatalf("unknown $GO_EXTLINK_ENABLED %s", b) | |
| } | |
| goextlinkenabled = b | |
| } | |
| gogcflags = os.Getenv("BOOT_GO_GCFLAGS") | |
| goldflags = os.Getenv("BOOT_GO_LDFLAGS") | |
| cc, cxx := "gcc", "g++" | |
| if defaultclang { | |
| cc, cxx = "clang", "clang++" | |
| } | |
| defaultcc = compilerEnv("CC", cc) | |
| defaultcxx = compilerEnv("CXX", cxx) | |
| defaultcflags = os.Getenv("CFLAGS") | |
| defaultldflags = os.Getenv("LDFLAGS") | |
| b = os.Getenv("PKG_CONFIG") | |
| if b == "" { | |
| b = "pkg-config" | |
| } | |
| defaultpkgconfig = b | |
| defaultldso = os.Getenv("GO_LDSO") | |
| // For tools being invoked but also for os.ExpandEnv. | |
| os.Setenv("GO386", go386) | |
| os.Setenv("GOARCH", goarch) | |
| os.Setenv("GOARM", goarm) | |
| os.Setenv("GOHOSTARCH", gohostarch) | |
| os.Setenv("GOHOSTOS", gohostos) | |
| os.Setenv("GOOS", goos) | |
| os.Setenv("GOMIPS", gomips) | |
| os.Setenv("GOMIPS64", gomips64) | |
| os.Setenv("GOPPC64", goppc64) | |
| os.Setenv("GOROOT", goroot) | |
| os.Setenv("GOROOT_FINAL", goroot_final) | |
| // Use a build cache separate from the default user one. | |
| // Also one that will be wiped out during startup, so that | |
| // make.bash really does start from a clean slate. | |
| os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot)) | |
| // Make the environment more predictable. | |
| os.Setenv("LANG", "C") | |
| os.Setenv("LANGUAGE", "en_US.UTF8") | |
| workdir = xworkdir() | |
| if err := ioutil.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap"), 0666); err != nil { | |
| fatalf("cannot write stub go.mod: %s", err) | |
| } | |
| xatexit(rmworkdir) | |
| tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch) | |
| } | |
| // compilerEnv returns a map from "goos/goarch" to the | |
| // compiler setting to use for that platform. | |
| // The entry for key "" covers any goos/goarch not explicitly set in the map. | |
| // For example, compilerEnv("CC", "gcc") returns the C compiler settings | |
| // read from $CC, defaulting to gcc. | |
| // | |
| // The result is a map because additional environment variables | |
| // can be set to change the compiler based on goos/goarch settings. | |
| // The following applies to all envNames but CC is assumed to simplify | |
| // the presentation. | |
| // | |
| // If no environment variables are set, we use def for all goos/goarch. | |
| // $CC, if set, applies to all goos/goarch but is overridden by the following. | |
| // $CC_FOR_TARGET, if set, applies to all goos/goarch except gohostos/gohostarch, | |
| // but is overridden by the following. | |
| // If gohostos=goos and gohostarch=goarch, then $CC_FOR_TARGET applies even for gohostos/gohostarch. | |
| // $CC_FOR_goos_goarch, if set, applies only to goos/goarch. | |
| func compilerEnv(envName, def string) map[string]string { | |
| m := map[string]string{"": def} | |
| if env := os.Getenv(envName); env != "" { | |
| m[""] = env | |
| } | |
| if env := os.Getenv(envName + "_FOR_TARGET"); env != "" { | |
| if gohostos != goos || gohostarch != goarch { | |
| m[gohostos+"/"+gohostarch] = m[""] | |
| } | |
| m[""] = env | |
| } | |
| for _, goos := range okgoos { | |
| for _, goarch := range okgoarch { | |
| if env := os.Getenv(envName + "_FOR_" + goos + "_" + goarch); env != "" { | |
| m[goos+"/"+goarch] = env | |
| } | |
| } | |
| } | |
| return m | |
| } | |
| // compilerEnvLookup returns the compiler settings for goos/goarch in map m. | |
| func compilerEnvLookup(m map[string]string, goos, goarch string) string { | |
| if cc := m[goos+"/"+goarch]; cc != "" { | |
| return cc | |
| } | |
| return m[""] | |
| } | |
| // rmworkdir deletes the work directory. | |
| func rmworkdir() { | |
| if vflag > 1 { | |
| errprintf("rm -rf %s\n", workdir) | |
| } | |
| xremoveall(workdir) | |
| } | |
| // Remove trailing spaces. | |
| func chomp(s string) string { | |
| return strings.TrimRight(s, " \t\r\n") | |
| } | |
| func branchtag(branch string) (tag string, precise bool) { | |
| log := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch) | |
| tag = branch | |
| for row, line := range strings.Split(log, "\n") { | |
| // Each line is either blank, or looks like | |
| // (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4) | |
| // We need to find an element starting with refs/tags/. | |
| const s = " refs/tags/" | |
| i := strings.Index(line, s) | |
| if i < 0 { | |
| continue | |
| } | |
| // Trim off known prefix. | |
| line = line[i+len(s):] | |
| // The tag name ends at a comma or paren. | |
| j := strings.IndexAny(line, ",)") | |
| if j < 0 { | |
| continue // malformed line; ignore it | |
| } | |
| tag = line[:j] | |
| if row == 0 { | |
| precise = true // tag denotes HEAD | |
| } | |
| break | |
| } | |
| return | |
| } | |
| // findgoversion determines the Go version to use in the version string. | |
| func findgoversion() string { | |
| // The $GOROOT/VERSION file takes priority, for distributions | |
| // without the source repo. | |
| path := pathf("%s/VERSION", goroot) | |
| if isfile(path) { | |
| b := chomp(readfile(path)) | |
| // Commands such as "dist version > VERSION" will cause | |
| // the shell to create an empty VERSION file and set dist's | |
| // stdout to its fd. dist in turn looks at VERSION and uses | |
| // its content if available, which is empty at this point. | |
| // Only use the VERSION file if it is non-empty. | |
| if b != "" { | |
| // Some builders cross-compile the toolchain on linux-amd64 | |
| // and then copy the toolchain to the target builder (say, linux-arm) | |
| // for use there. But on non-release (devel) branches, the compiler | |
| // used on linux-amd64 will be an amd64 binary, and the compiler | |
| // shipped to linux-arm will be an arm binary, so they will have different | |
| // content IDs (they are binaries for different architectures) and so the | |
| // packages compiled by the running-on-amd64 compiler will appear | |
| // stale relative to the running-on-arm compiler. Avoid this by setting | |
| // the version string to something that doesn't begin with devel. | |
| // Then the version string will be used in place of the content ID, | |
| // and the packages will look up-to-date. | |
| // TODO(rsc): Really the builders could be writing out a better VERSION file instead, | |
| // but it is easier to change cmd/dist than to try to make changes to | |
| // the builder while Brad is away. | |
| if strings.HasPrefix(b, "devel") { | |
| if hostType := os.Getenv("META_BUILDLET_HOST_TYPE"); strings.Contains(hostType, "-cross") { | |
| fmt.Fprintf(os.Stderr, "warning: changing VERSION from %q to %q\n", b, "builder "+hostType) | |
| b = "builder " + hostType | |
| } | |
| } | |
| return b | |
| } | |
| } | |
| // The $GOROOT/VERSION.cache file is a cache to avoid invoking | |
| // git every time we run this command. Unlike VERSION, it gets | |
| // deleted by the clean command. | |
| path = pathf("%s/VERSION.cache", goroot) | |
| if isfile(path) { | |
| return chomp(readfile(path)) | |
| } | |
| // Show a nicer error message if this isn't a Git repo. | |
| if !isGitRepo() { | |
| fatalf("FAILED: not a Git repo; must put a VERSION file in $GOROOT") | |
| } | |
| // Otherwise, use Git. | |
| // What is the current branch? | |
| branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD")) | |
| // What are the tags along the current branch? | |
| tag := "devel" | |
| precise := false | |
| // If we're on a release branch, use the closest matching tag | |
| // that is on the release branch (and not on the master branch). | |
| if strings.HasPrefix(branch, "release-branch.") { | |
| tag, precise = branchtag(branch) | |
| } | |
| if !precise { | |
| // Tag does not point at HEAD; add hash and date to version. | |
| tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD")) | |
| } | |
| // Cache version. | |
| writefile(tag, path, 0) | |
| return tag | |
| } | |
| // isGitRepo reports whether the working directory is inside a Git repository. | |
| func isGitRepo() bool { | |
| // NB: simply checking the exit code of `git rev-parse --git-dir` would | |
| // suffice here, but that requires deviating from the infrastructure | |
| // provided by `run`. | |
| gitDir := chomp(run(goroot, 0, "git", "rev-parse", "--git-dir")) | |
| if !filepath.IsAbs(gitDir) { | |
| gitDir = filepath.Join(goroot, gitDir) | |
| } | |
| return isdir(gitDir) | |
| } | |
| /* | |
| * Initial tree setup. | |
| */ | |
| // The old tools that no longer live in $GOBIN or $GOROOT/bin. | |
| var oldtool = []string{ | |
| "5a", "5c", "5g", "5l", | |
| "6a", "6c", "6g", "6l", | |
| "8a", "8c", "8g", "8l", | |
| "9a", "9c", "9g", "9l", | |
| "6cov", | |
| "6nm", | |
| "6prof", | |
| "cgo", | |
| "ebnflint", | |
| "goapi", | |
| "gofix", | |
| "goinstall", | |
| "gomake", | |
| "gopack", | |
| "gopprof", | |
| "gotest", | |
| "gotype", | |
| "govet", | |
| "goyacc", | |
| "quietgcc", | |
| } | |
| // Unreleased directories (relative to $GOROOT) that should | |
| // not be in release branches. | |
| var unreleased = []string{ | |
| "src/cmd/newlink", | |
| "src/cmd/objwriter", | |
| "src/debug/goobj", | |
| "src/old", | |
| } | |
| // setup sets up the tree for the initial build. | |
| func setup() { | |
| // Create bin directory. | |
| if p := pathf("%s/bin", goroot); !isdir(p) { | |
| xmkdir(p) | |
| } | |
| // Create package directory. | |
| if p := pathf("%s/pkg", goroot); !isdir(p) { | |
| xmkdir(p) | |
| } | |
| p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch) | |
| if rebuildall { | |
| xremoveall(p) | |
| } | |
| xmkdirall(p) | |
| if goos != gohostos || goarch != gohostarch { | |
| p := pathf("%s/pkg/%s_%s", goroot, goos, goarch) | |
| if rebuildall { | |
| xremoveall(p) | |
| } | |
| xmkdirall(p) | |
| } | |
| // Create object directory. | |
| // We used to use it for C objects. | |
| // Now we use it for the build cache, to separate dist's cache | |
| // from any other cache the user might have. | |
| p = pathf("%s/pkg/obj/go-build", goroot) | |
| if rebuildall { | |
| xremoveall(p) | |
| } | |
| xmkdirall(p) | |
| xatexit(func() { xremoveall(p) }) | |
| // Create tool directory. | |
| // We keep it in pkg/, just like the object directory above. | |
| if rebuildall { | |
| xremoveall(tooldir) | |
| } | |
| xmkdirall(tooldir) | |
| // Remove tool binaries from before the tool/gohostos_gohostarch | |
| xremoveall(pathf("%s/bin/tool", goroot)) | |
| // Remove old pre-tool binaries. | |
| for _, old := range oldtool { | |
| xremove(pathf("%s/bin/%s", goroot, old)) | |
| } | |
| // If $GOBIN is set and has a Go compiler, it must be cleaned. | |
| for _, char := range "56789" { | |
| if isfile(pathf("%s/%c%s", gobin, char, "g")) { | |
| for _, old := range oldtool { | |
| xremove(pathf("%s/%s", gobin, old)) | |
| } | |
| break | |
| } | |
| } | |
| // For release, make sure excluded things are excluded. | |
| goversion := findgoversion() | |
| if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) { | |
| for _, dir := range unreleased { | |
| if p := pathf("%s/%s", goroot, dir); isdir(p) { | |
| fatalf("%s should not exist in release build", p) | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| * Tool building | |
| */ | |
| // deptab lists changes to the default dependencies for a given prefix. | |
| // deps ending in /* read the whole directory; deps beginning with - | |
| // exclude files with that prefix. | |
| // Note that this table applies only to the build of cmd/go, | |
| // after the main compiler bootstrap. | |
| var deptab = []struct { | |
| prefix string // prefix of target | |
| dep []string // dependency tweaks for targets with that prefix | |
| }{ | |
| {"cmd/go/internal/cfg", []string{ | |
| "zdefaultcc.go", | |
| "zosarch.go", | |
| }}, | |
| {"runtime/internal/sys", []string{ | |
| "zversion.go", | |
| }}, | |
| {"go/build", []string{ | |
| "zcgo.go", | |
| }}, | |
| } | |
| // depsuffix records the allowed suffixes for source files. | |
| var depsuffix = []string{ | |
| ".s", | |
| ".go", | |
| } | |
| // gentab records how to generate some trivial files. | |
| var gentab = []struct { | |
| nameprefix string | |
| gen func(string, string) | |
| }{ | |
| {"zdefaultcc.go", mkzdefaultcc}, | |
| {"zosarch.go", mkzosarch}, | |
| {"zversion.go", mkzversion}, | |
| {"zcgo.go", mkzcgo}, | |
| // not generated anymore, but delete the file if we see it | |
| {"enam.c", nil}, | |
| {"anames5.c", nil}, | |
| {"anames6.c", nil}, | |
| {"anames8.c", nil}, | |
| {"anames9.c", nil}, | |
| } | |
| // installed maps from a dir name (as given to install) to a chan | |
| // closed when the dir's package is installed. | |
| var installed = make(map[string]chan struct{}) | |
| var installedMu sync.Mutex | |
| func install(dir string) { | |
| <-startInstall(dir) | |
| } | |
| func startInstall(dir string) chan struct{} { | |
| installedMu.Lock() | |
| ch := installed[dir] | |
| if ch == nil { | |
| ch = make(chan struct{}) | |
| installed[dir] = ch | |
| go runInstall(dir, ch) | |
| } | |
| installedMu.Unlock() | |
| return ch | |
| } | |
| // runInstall installs the library, package, or binary associated with dir, | |
| // which is relative to $GOROOT/src. | |
| func runInstall(pkg string, ch chan struct{}) { | |
| if pkg == "net" || pkg == "os/user" || pkg == "crypto/x509" { | |
| fatalf("go_bootstrap cannot depend on cgo package %s", pkg) | |
| } | |
| defer close(ch) | |
| if pkg == "unsafe" { | |
| return | |
| } | |
| if vflag > 0 { | |
| if goos != gohostos || goarch != gohostarch { | |
| errprintf("%s (%s/%s)\n", pkg, goos, goarch) | |
| } else { | |
| errprintf("%s\n", pkg) | |
| } | |
| } | |
| workdir := pathf("%s/%s", workdir, pkg) | |
| xmkdirall(workdir) | |
| var clean []string | |
| defer func() { | |
| for _, name := range clean { | |
| xremove(name) | |
| } | |
| }() | |
| // dir = full path to pkg. | |
| dir := pathf("%s/src/%s", goroot, pkg) | |
| name := filepath.Base(dir) | |
| // ispkg predicts whether the package should be linked as a binary, based | |
| // on the name. There should be no "main" packages in vendor, since | |
| // 'go mod vendor' will only copy imported packages there. | |
| ispkg := !strings.HasPrefix(pkg, "cmd/") || strings.Contains(pkg, "/internal/") || strings.Contains(pkg, "/vendor/") | |
| // Start final link command line. | |
| // Note: code below knows that link.p[targ] is the target. | |
| var ( | |
| link []string | |
| targ int | |
| ispackcmd bool | |
| ) | |
| if ispkg { | |
| // Go library (package). | |
| ispackcmd = true | |
| link = []string{"pack", packagefile(pkg)} | |
| targ = len(link) - 1 | |
| xmkdirall(filepath.Dir(link[targ])) | |
| } else { | |
| // Go command. | |
| elem := name | |
| if elem == "go" { | |
| elem = "go_bootstrap" | |
| } | |
| link = []string{pathf("%s/link", tooldir)} | |
| if goos == "android" { | |
| link = append(link, "-buildmode=pie") | |
| } | |
| if goldflags != "" { | |
| link = append(link, goldflags) | |
| } | |
| link = append(link, "-extld="+compilerEnvLookup(defaultcc, goos, goarch)) | |
| link = append(link, "-o", pathf("%s/%s%s", tooldir, elem, exe)) | |
| targ = len(link) - 1 | |
| } | |
| ttarg := mtime(link[targ]) | |
| // Gather files that are sources for this target. | |
| // Everything in that directory, and any target-specific | |
| // additions. | |
| files := xreaddir(dir) | |
| // Remove files beginning with . or _, | |
| // which are likely to be editor temporary files. | |
| // This is the same heuristic build.ScanDir uses. | |
| // There do exist real C files beginning with _, | |
| // so limit that check to just Go files. | |
| files = filter(files, func(p string) bool { | |
| return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go")) | |
| }) | |
| for _, dt := range deptab { | |
| if pkg == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(pkg, dt.prefix) { | |
| for _, p := range dt.dep { | |
| p = os.ExpandEnv(p) | |
| files = append(files, p) | |
| } | |
| } | |
| } | |
| files = uniq(files) | |
| // Convert to absolute paths. | |
| for i, p := range files { | |
| if !filepath.IsAbs(p) { | |
| files[i] = pathf("%s/%s", dir, p) | |
| } | |
| } | |
| // Is the target up-to-date? | |
| var gofiles, sfiles, missing []string | |
| stale := rebuildall | |
| files = filter(files, func(p string) bool { | |
| for _, suf := range depsuffix { | |
| if strings.HasSuffix(p, suf) { | |
| goto ok | |
| } | |
| } | |
| return false | |
| ok: | |
| t := mtime(p) | |
| if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, pkg) { | |
| return false | |
| } | |
| if strings.HasSuffix(p, ".go") { | |
| gofiles = append(gofiles, p) | |
| } else if strings.HasSuffix(p, ".s") { | |
| sfiles = append(sfiles, p) | |
| } | |
| if t.After(ttarg) { | |
| stale = true | |
| } | |
| if t.IsZero() { | |
| missing = append(missing, p) | |
| } | |
| return true | |
| }) | |
| // If there are no files to compile, we're done. | |
| if len(files) == 0 { | |
| return | |
| } | |
| if !stale { | |
| return | |
| } | |
| // For package runtime, copy some files into the work space. | |
| if pkg == "runtime" { | |
| xmkdirall(pathf("%s/pkg/include", goroot)) | |
| // For use by assembly and C files. | |
| copyfile(pathf("%s/pkg/include/textflag.h", goroot), | |
| pathf("%s/src/runtime/textflag.h", goroot), 0) | |
| copyfile(pathf("%s/pkg/include/funcdata.h", goroot), | |
| pathf("%s/src/runtime/funcdata.h", goroot), 0) | |
| copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot), | |
| pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0) | |
| } | |
| // Generate any missing files; regenerate existing ones. | |
| for _, p := range files { | |
| elem := filepath.Base(p) | |
| for _, gt := range gentab { | |
| if gt.gen == nil { | |
| continue | |
| } | |
| if strings.HasPrefix(elem, gt.nameprefix) { | |
| if vflag > 1 { | |
| errprintf("generate %s\n", p) | |
| } | |
| gt.gen(dir, p) | |
| // Do not add generated file to clean list. | |
| // In runtime, we want to be able to | |
| // build the package with the go tool, | |
| // and it assumes these generated files already | |
| // exist (it does not know how to build them). | |
| // The 'clean' command can remove | |
| // the generated files. | |
| goto built | |
| } | |
| } | |
| // Did not rebuild p. | |
| if find(p, missing) >= 0 { | |
| fatalf("missing file %s", p) | |
| } | |
| built: | |
| } | |
| // Resolve imported packages to actual package paths. | |
| // Make sure they're installed. | |
| importMap := make(map[string]string) | |
| for _, p := range gofiles { | |
| for _, imp := range readimports(p) { | |
| importMap[imp] = resolveVendor(imp, dir) | |
| } | |
| } | |
| sortedImports := make([]string, 0, len(importMap)) | |
| for imp := range importMap { | |
| sortedImports = append(sortedImports, imp) | |
| } | |
| sort.Strings(sortedImports) | |
| for _, dep := range importMap { | |
| startInstall(dep) | |
| } | |
| for _, dep := range importMap { | |
| install(dep) | |
| } | |
| if goos != gohostos || goarch != gohostarch { | |
| // We've generated the right files; the go command can do the build. | |
| if vflag > 1 { | |
| errprintf("skip build for cross-compile %s\n", pkg) | |
| } | |
| return | |
| } | |
| asmArgs := []string{ | |
| pathf("%s/asm", tooldir), | |
| "-I", workdir, | |
| "-I", pathf("%s/pkg/include", goroot), | |
| "-D", "GOOS_" + goos, | |
| "-D", "GOARCH_" + goarch, | |
| "-D", "GOOS_GOARCH_" + goos + "_" + goarch, | |
| "-p", pkg, | |
| } | |
| if goarch == "mips" || goarch == "mipsle" { | |
| // Define GOMIPS_value from gomips. | |
| asmArgs = append(asmArgs, "-D", "GOMIPS_"+gomips) | |
| } | |
| if goarch == "mips64" || goarch == "mips64le" { | |
| // Define GOMIPS64_value from gomips64. | |
| asmArgs = append(asmArgs, "-D", "GOMIPS64_"+gomips64) | |
| } | |
| goasmh := pathf("%s/go_asm.h", workdir) | |
| if IsRuntimePackagePath(pkg) { | |
| asmArgs = append(asmArgs, "-compiling-runtime") | |
| if os.Getenv("GOEXPERIMENT") == "regabi" { | |
| // In order to make it easier to port runtime assembly | |
| // to the register ABI, we introduce a macro | |
| // indicating the experiment is enabled. | |
| // | |
| // Note: a similar change also appears in | |
| // cmd/go/internal/work/gc.go. | |
| // | |
| // TODO(austin): Remove this once we commit to the | |
| // register ABI (#40724). | |
| asmArgs = append(asmArgs, "-D=GOEXPERIMENT_REGABI=1") | |
| } | |
| } | |
| // Collect symabis from assembly code. | |
| var symabis string | |
| if len(sfiles) > 0 { | |
| symabis = pathf("%s/symabis", workdir) | |
| var wg sync.WaitGroup | |
| asmabis := append(asmArgs[:len(asmArgs):len(asmArgs)], "-gensymabis", "-o", symabis) | |
| asmabis = append(asmabis, sfiles...) | |
| if err := ioutil.WriteFile(goasmh, nil, 0666); err != nil { | |
| fatalf("cannot write empty go_asm.h: %s", err) | |
| } | |
| bgrun(&wg, dir, asmabis...) | |
| bgwait(&wg) | |
| } | |
| // Build an importcfg file for the compiler. | |
| buf := &bytes.Buffer{} | |
| for _, imp := range sortedImports { | |
| if imp == "unsafe" { | |
| continue | |
| } | |
| dep := importMap[imp] | |
| if imp != dep { | |
| fmt.Fprintf(buf, "importmap %s=%s\n", imp, dep) | |
| } | |
| fmt.Fprintf(buf, "packagefile %s=%s\n", dep, packagefile(dep)) | |
| } | |
| importcfg := pathf("%s/importcfg", workdir) | |
| if err := ioutil.WriteFile(importcfg, buf.Bytes(), 0666); err != nil { | |
| fatalf("cannot write importcfg file: %v", err) | |
| } | |
| var archive string | |
| // The next loop will compile individual non-Go files. | |
| // Hand the Go files to the compiler en masse. | |
| // For packages containing assembly, this writes go_asm.h, which | |
| // the assembly files will need. | |
| pkgName := pkg | |
| if strings.HasPrefix(pkg, "cmd/") && strings.Count(pkg, "/") == 1 { | |
| pkgName = "main" | |
| } | |
| b := pathf("%s/_go_.a", workdir) | |
| clean = append(clean, b) | |
| if !ispackcmd { | |
| link = append(link, b) | |
| } else { | |
| archive = b | |
| } | |
| // Compile Go code. | |
| compile := []string{pathf("%s/compile", tooldir), "-std", "-pack", "-o", b, "-p", pkgName, "-importcfg", importcfg} | |
| if gogcflags != "" { | |
| compile = append(compile, strings.Fields(gogcflags)...) | |
| } | |
| if pkg == "runtime" { | |
| compile = append(compile, "-+") | |
| } | |
| if len(sfiles) > 0 { | |
| compile = append(compile, "-asmhdr", goasmh) | |
| } | |
| if symabis != "" { | |
| compile = append(compile, "-symabis", symabis) | |
| } | |
| if goos == "android" { | |
| compile = append(compile, "-shared") | |
| } | |
| compile = append(compile, gofiles...) | |
| var wg sync.WaitGroup | |
| // We use bgrun and immediately wait for it instead of calling run() synchronously. | |
| // This executes all jobs through the bgwork channel and allows the process | |
| // to exit cleanly in case an error occurs. | |
| bgrun(&wg, dir, compile...) | |
| bgwait(&wg) | |
| // Compile the files. | |
| for _, p := range sfiles { | |
| // Assembly file for a Go package. | |
| compile := asmArgs[:len(asmArgs):len(asmArgs)] | |
| doclean := true | |
| b := pathf("%s/%s", workdir, filepath.Base(p)) | |
| // Change the last character of the output file (which was c or s). | |
| b = b[:len(b)-1] + "o" | |
| compile = append(compile, "-o", b, p) | |
| bgrun(&wg, dir, compile...) | |
| link = append(link, b) | |
| if doclean { | |
| clean = append(clean, b) | |
| } | |
| } | |
| bgwait(&wg) | |
| if ispackcmd { | |
| xremove(link[targ]) | |
| dopack(link[targ], archive, link[targ+1:]) | |
| return | |
| } | |
| // Remove target before writing it. | |
| xremove(link[targ]) | |
| bgrun(&wg, "", link...) | |
| bgwait(&wg) | |
| } | |
| // packagefile returns the path to a compiled .a file for the given package | |
| // path. Paths may need to be resolved with resolveVendor first. | |
| func packagefile(pkg string) string { | |
| return pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, pkg) | |
| } | |
| // matchfield reports whether the field (x,y,z) matches this build. | |
| // all the elements in the field must be satisfied. | |
| func matchfield(f string) bool { | |
| for _, tag := range strings.Split(f, ",") { | |
| if !matchtag(tag) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| // matchtag reports whether the tag (x or !x) matches this build. | |
| func matchtag(tag string) bool { | |
| if tag == "" { | |
| return false | |
| } | |
| if tag[0] == '!' { | |
| if len(tag) == 1 || tag[1] == '!' { | |
| return false | |
| } | |
| return !matchtag(tag[1:]) | |
| } | |
| if os.Getenv("GOEXPERIMENT") == "regabi" && tag == "goexperiment.regabi" { | |
| // TODO: maybe we can handle GOEXPERIMENT more generally. | |
| // Or remove once we commit to regabi (#40724). | |
| return true | |
| } | |
| return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || | |
| (goos == "android" && tag == "linux") || | |
| (goos == "illumos" && tag == "solaris") || | |
| (goos == "ios" && tag == "darwin") | |
| } | |
| // shouldbuild reports whether we should build this file. | |
| // It applies the same rules that are used with context tags | |
| // in package go/build, except it's less picky about the order | |
| // of GOOS and GOARCH. | |
| // We also allow the special tag cmd_go_bootstrap. | |
| // See ../go/bootstrap.go and package go/build. | |
| func shouldbuild(file, pkg string) bool { | |
| // Check file name for GOOS or GOARCH. | |
| name := filepath.Base(file) | |
| excluded := func(list []string, ok string) bool { | |
| for _, x := range list { | |
| if x == ok || (ok == "android" && x == "linux") || (ok == "illumos" && x == "solaris") || (ok == "ios" && x == "darwin") { | |
| continue | |
| } | |
| i := strings.Index(name, x) | |
| if i <= 0 || name[i-1] != '_' { | |
| continue | |
| } | |
| i += len(x) | |
| if i == len(name) || name[i] == '.' || name[i] == '_' { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| if excluded(okgoos, goos) || excluded(okgoarch, goarch) { | |
| return false | |
| } | |
| // Omit test files. | |
| if strings.Contains(name, "_test") { | |
| return false | |
| } | |
| // Check file contents for // +build lines. | |
| for _, p := range strings.Split(readfile(file), "\n") { | |
| p = strings.TrimSpace(p) | |
| if p == "" { | |
| continue | |
| } | |
| code := p | |
| i := strings.Index(code, "//") | |
| if i > 0 { | |
| code = strings.TrimSpace(code[:i]) | |
| } | |
| if code == "package documentation" { | |
| return false | |
| } | |
| if code == "package main" && pkg != "cmd/go" && pkg != "cmd/cgo" { | |
| return false | |
| } | |
| if !strings.HasPrefix(p, "//") { | |
| break | |
| } | |
| if !strings.Contains(p, "+build") { | |
| continue | |
| } | |
| fields := strings.Fields(p[2:]) | |
| if len(fields) < 1 || fields[0] != "+build" { | |
| continue | |
| } | |
| for _, p := range fields[1:] { | |
| if matchfield(p) { | |
| goto fieldmatch | |
| } | |
| } | |
| return false | |
| fieldmatch: | |
| } | |
| return true | |
| } | |
| // copy copies the file src to dst, via memory (so only good for small files). | |
| func copyfile(dst, src string, flag int) { | |
| if vflag > 1 { | |
| errprintf("cp %s %s\n", src, dst) | |
| } | |
| writefile(readfile(src), dst, flag) | |
| } | |
| // dopack copies the package src to dst, | |
| // appending the files listed in extra. | |
| // The archive format is the traditional Unix ar format. | |
| func dopack(dst, src string, extra []string) { | |
| bdst := bytes.NewBufferString(readfile(src)) | |
| for _, file := range extra { | |
| b := readfile(file) | |
| // find last path element for archive member name | |
| i := strings.LastIndex(file, "/") + 1 | |
| j := strings.LastIndex(file, `\`) + 1 | |
| if i < j { | |
| i = j | |
| } | |
| fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b)) | |
| bdst.WriteString(b) | |
| if len(b)&1 != 0 { | |
| bdst.WriteByte(0) | |
| } | |
| } | |
| writefile(bdst.String(), dst, 0) | |
| } | |
| var runtimegen = []string{ | |
| "zaexperiment.h", | |
| "zversion.go", | |
| } | |
| // cleanlist is a list of packages with generated files and commands. | |
| var cleanlist = []string{ | |
| "runtime/internal/sys", | |
| "cmd/cgo", | |
| "cmd/go/internal/cfg", | |
| "go/build", | |
| } | |
| func clean() { | |
| for _, name := range cleanlist { | |
| path := pathf("%s/src/%s", goroot, name) | |
| // Remove generated files. | |
| for _, elem := range xreaddir(path) { | |
| for _, gt := range gentab { | |
| if strings.HasPrefix(elem, gt.nameprefix) { | |
| xremove(pathf("%s/%s", path, elem)) | |
| } | |
| } | |
| } | |
| // Remove generated binary named for directory. | |
| if strings.HasPrefix(name, "cmd/") { | |
| xremove(pathf("%s/%s", path, name[4:])) | |
| } | |
| } | |
| // remove runtimegen files. | |
| path := pathf("%s/src/runtime", goroot) | |
| for _, elem := range runtimegen { | |
| xremove(pathf("%s/%s", path, elem)) | |
| } | |
| if rebuildall { | |
| // Remove object tree. | |
| xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)) | |
| // Remove installed packages and tools. | |
| xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)) | |
| xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch)) | |
| xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch)) | |
| xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch)) | |
| xremoveall(tooldir) | |
| // Remove cached version info. | |
| xremove(pathf("%s/VERSION.cache", goroot)) | |
| } | |
| } | |
| /* | |
| * command implementations | |
| */ | |
| // The env command prints the default environment. | |
| func cmdenv() { | |
| path := flag.Bool("p", false, "emit updated PATH") | |
| plan9 := flag.Bool("9", false, "emit plan 9 syntax") | |
| windows := flag.Bool("w", false, "emit windows syntax") | |
| xflagparse(0) | |
| format := "%s=\"%s\"\n" | |
| switch { | |
| case *plan9: | |
| format = "%s='%s'\n" | |
| case *windows: | |
| format = "set %s=%s\r\n" | |
| } | |
| xprintf(format, "GOARCH", goarch) | |
| xprintf(format, "GOBIN", gobin) | |
| xprintf(format, "GOCACHE", os.Getenv("GOCACHE")) | |
| xprintf(format, "GODEBUG", os.Getenv("GODEBUG")) | |
| xprintf(format, "GOHOSTARCH", gohostarch) | |
| xprintf(format, "GOHOSTOS", gohostos) | |
| xprintf(format, "GOOS", goos) | |
| xprintf(format, "GOPROXY", os.Getenv("GOPROXY")) | |
| xprintf(format, "GOROOT", goroot) | |
| xprintf(format, "GOTMPDIR", os.Getenv("GOTMPDIR")) | |
| xprintf(format, "GOTOOLDIR", tooldir) | |
| if goarch == "arm" { | |
| xprintf(format, "GOARM", goarm) | |
| } | |
| if goarch == "386" { | |
| xprintf(format, "GO386", go386) | |
| } | |
| if goarch == "mips" || goarch == "mipsle" { | |
| xprintf(format, "GOMIPS", gomips) | |
| } | |
| if goarch == "mips64" || goarch == "mips64le" { | |
| xprintf(format, "GOMIPS64", gomips64) | |
| } | |
| if goarch == "ppc64" || goarch == "ppc64le" { | |
| xprintf(format, "GOPPC64", goppc64) | |
| } | |
| if *path { | |
| sep := ":" | |
| if gohostos == "windows" { | |
| sep = ";" | |
| } | |
| xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH"))) | |
| } | |
| } | |
| var ( | |
| timeLogEnabled = os.Getenv("GOBUILDTIMELOGFILE") != "" | |
| timeLogMu sync.Mutex | |
| timeLogFile *os.File | |
| timeLogStart time.Time | |
| ) | |
| func timelog(op, name string) { | |
| if !timeLogEnabled { | |
| return | |
| } | |
| timeLogMu.Lock() | |
| defer timeLogMu.Unlock() | |
| if timeLogFile == nil { | |
| f, err := os.OpenFile(os.Getenv("GOBUILDTIMELOGFILE"), os.O_RDWR|os.O_APPEND, 0666) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| buf := make([]byte, 100) | |
| n, _ := f.Read(buf) | |
| s := string(buf[:n]) | |
| if i := strings.Index(s, "\n"); i >= 0 { | |
| s = s[:i] | |
| } | |
| i := strings.Index(s, " start") | |
| if i < 0 { | |
| log.Fatalf("time log %s does not begin with start line", os.Getenv("GOBUILDTIMELOGFILE")) | |
| } | |
| t, err := time.Parse(time.UnixDate, s[:i]) | |
| if err != nil { | |
| log.Fatalf("cannot parse time log line %q: %v", s, err) | |
| } | |
| timeLogStart = t | |
| timeLogFile = f | |
| } | |
| t := time.Now() | |
| fmt.Fprintf(timeLogFile, "%s %+.1fs %s %s\n", t.Format(time.UnixDate), t.Sub(timeLogStart).Seconds(), op, name) | |
| } | |
| var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link"} | |
| // The bootstrap command runs a build from scratch, | |
| // stopping at having installed the go_bootstrap command. | |
| // | |
| // WARNING: This command runs after cmd/dist is built with Go 1.4. | |
| // It rebuilds and installs cmd/dist with the new toolchain, so other | |
| // commands (like "go tool dist test" in run.bash) can rely on bug fixes | |
| // made since Go 1.4, but this function cannot. In particular, the uses | |
| // of os/exec in this function cannot assume that | |
| // cmd.Env = append(os.Environ(), "X=Y") | |
| // sets $X to Y in the command's environment. That guarantee was | |
| // added after Go 1.4, and in fact in Go 1.4 it was typically the opposite: | |
| // if $X was already present in os.Environ(), most systems preferred | |
| // that setting, not the new one. | |
| func cmdbootstrap() { | |
| timelog("start", "dist bootstrap") | |
| defer timelog("end", "dist bootstrap") | |
| var noBanner bool | |
| var debug bool | |
| flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all") | |
| flag.BoolVar(&debug, "d", debug, "enable debugging of bootstrap process") | |
| flag.BoolVar(&noBanner, "no-banner", noBanner, "do not print banner") | |
| xflagparse(0) | |
| // Set GOPATH to an internal directory. We shouldn't actually | |
| // need to store files here, since the toolchain won't | |
| // depend on modules outside of vendor directories, but if | |
| // GOPATH points somewhere else (e.g., to GOROOT), the | |
| // go tool may complain. | |
| os.Setenv("GOPATH", pathf("%s/pkg/obj/gopath", goroot)) | |
| if debug { | |
| // cmd/buildid is used in debug mode. | |
| toolchain = append(toolchain, "cmd/buildid") | |
| } | |
| if isdir(pathf("%s/src/pkg", goroot)) { | |
| fatalf("\n\n"+ | |
| "The Go package sources have moved to $GOROOT/src.\n"+ | |
| "*** %s still exists. ***\n"+ | |
| "It probably contains stale files that may confuse the build.\n"+ | |
| "Please (check what's there and) remove it and try again.\n"+ | |
| "See https://golang.org/s/go14nopkg\n", | |
| pathf("%s/src/pkg", goroot)) | |
| } | |
| if rebuildall { | |
| clean() | |
| } | |
| setup() | |
| timelog("build", "toolchain1") | |
| checkCC() | |
| bootstrapBuildTools() | |
| // Remember old content of $GOROOT/bin for comparison below. | |
| oldBinFiles, _ := filepath.Glob(pathf("%s/bin/*", goroot)) | |
| // For the main bootstrap, building for host os/arch. | |
| oldgoos = goos | |
| oldgoarch = goarch | |
| goos = gohostos | |
| goarch = gohostarch | |
| os.Setenv("GOHOSTARCH", gohostarch) | |
| os.Setenv("GOHOSTOS", gohostos) | |
| os.Setenv("GOARCH", goarch) | |
| os.Setenv("GOOS", goos) | |
| timelog("build", "go_bootstrap") | |
| xprintf("Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.\n") | |
| install("runtime") // dependency not visible in sources; also sets up textflag.h | |
| install("cmd/go") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now | |
| goldflags = os.Getenv("GO_LDFLAGS") // we were using $BOOT_GO_LDFLAGS until now | |
| goBootstrap := pathf("%s/go_bootstrap", tooldir) | |
| cmdGo := pathf("%s/go", gobin) | |
| if debug { | |
| run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") | |
| copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec) | |
| } | |
| // To recap, so far we have built the new toolchain | |
| // (cmd/asm, cmd/cgo, cmd/compile, cmd/link) | |
| // using Go 1.4's toolchain and go command. | |
| // Then we built the new go command (as go_bootstrap) | |
| // using the new toolchain and our own build logic (above). | |
| // | |
| // toolchain1 = mk(new toolchain, go1.4 toolchain, go1.4 cmd/go) | |
| // go_bootstrap = mk(new cmd/go, toolchain1, cmd/dist) | |
| // | |
| // The toolchain1 we built earlier is built from the new sources, | |
| // but because it was built using cmd/go it has no build IDs. | |
| // The eventually installed toolchain needs build IDs, so we need | |
| // to do another round: | |
| // | |
| // toolchain2 = mk(new toolchain, toolchain1, go_bootstrap) | |
| // | |
| timelog("build", "toolchain2") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n") | |
| os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch)) | |
| goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...) | |
| if debug { | |
| run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") | |
| run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch)) | |
| copyfile(pathf("%s/compile2", tooldir), pathf("%s/compile", tooldir), writeExec) | |
| } | |
| // Toolchain2 should be semantically equivalent to toolchain1, | |
| // but it was built using the new compilers instead of the Go 1.4 compilers, | |
| // so it should at the least run faster. Also, toolchain1 had no build IDs | |
| // in the binaries, while toolchain2 does. In non-release builds, the | |
| // toolchain's build IDs feed into constructing the build IDs of built targets, | |
| // so in non-release builds, everything now looks out-of-date due to | |
| // toolchain2 having build IDs - that is, due to the go command seeing | |
| // that there are new compilers. In release builds, the toolchain's reported | |
| // version is used in place of the build ID, and the go command does not | |
| // see that change from toolchain1 to toolchain2, so in release builds, | |
| // nothing looks out of date. | |
| // To keep the behavior the same in both non-release and release builds, | |
| // we force-install everything here. | |
| // | |
| // toolchain3 = mk(new toolchain, toolchain2, go_bootstrap) | |
| // | |
| timelog("build", "toolchain3") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| xprintf("Building Go toolchain3 using go_bootstrap and Go toolchain2.\n") | |
| goInstall(goBootstrap, append([]string{"-a", "-i"}, toolchain...)...) | |
| if debug { | |
| run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") | |
| run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch)) | |
| copyfile(pathf("%s/compile3", tooldir), pathf("%s/compile", tooldir), writeExec) | |
| } | |
| checkNotStale(goBootstrap, append(toolchain, "runtime/internal/sys")...) | |
| if goos == oldgoos && goarch == oldgoarch { | |
| // Common case - not setting up for cross-compilation. | |
| timelog("build", "toolchain") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| xprintf("Building packages and commands for %s/%s.\n", goos, goarch) | |
| } else { | |
| // GOOS/GOARCH does not match GOHOSTOS/GOHOSTARCH. | |
| // Finish GOHOSTOS/GOHOSTARCH installation and then | |
| // run GOOS/GOARCH installation. | |
| timelog("build", "host toolchain") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| xprintf("Building packages and commands for host, %s/%s.\n", goos, goarch) | |
| goInstall(goBootstrap, "std", "cmd") | |
| checkNotStale(goBootstrap, "std", "cmd") | |
| checkNotStale(cmdGo, "std", "cmd") | |
| timelog("build", "target toolchain") | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| goos = oldgoos | |
| goarch = oldgoarch | |
| os.Setenv("GOOS", goos) | |
| os.Setenv("GOARCH", goarch) | |
| os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch)) | |
| xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch) | |
| } | |
| targets := []string{"std", "cmd"} | |
| if goos == "js" && goarch == "wasm" { | |
| // Skip the cmd tools for js/wasm. They're not usable. | |
| targets = targets[:1] | |
| } | |
| goInstall(goBootstrap, targets...) | |
| checkNotStale(goBootstrap, targets...) | |
| checkNotStale(cmdGo, targets...) | |
| if debug { | |
| run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full") | |
| run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch)) | |
| checkNotStale(goBootstrap, append(toolchain, "runtime/internal/sys")...) | |
| copyfile(pathf("%s/compile4", tooldir), pathf("%s/compile", tooldir), writeExec) | |
| } | |
| // Check that there are no new files in $GOROOT/bin other than | |
| // go and gofmt and $GOOS_$GOARCH (target bin when cross-compiling). | |
| binFiles, _ := filepath.Glob(pathf("%s/bin/*", goroot)) | |
| ok := map[string]bool{} | |
| for _, f := range oldBinFiles { | |
| ok[f] = true | |
| } | |
| for _, f := range binFiles { | |
| elem := strings.TrimSuffix(filepath.Base(f), ".exe") | |
| if !ok[f] && elem != "go" && elem != "gofmt" && elem != goos+"_"+goarch { | |
| fatalf("unexpected new file in $GOROOT/bin: %s", elem) | |
| } | |
| } | |
| // Remove go_bootstrap now that we're done. | |
| xremove(pathf("%s/go_bootstrap", tooldir)) | |
| if goos == "android" { | |
| // Make sure the exec wrapper will sync a fresh $GOROOT to the device. | |
| xremove(pathf("%s/go_android_exec-adb-sync-status", os.TempDir())) | |
| } | |
| if wrapperPath := wrapperPathFor(goos, goarch); wrapperPath != "" { | |
| oldcc := os.Getenv("CC") | |
| os.Setenv("GOOS", gohostos) | |
| os.Setenv("GOARCH", gohostarch) | |
| os.Setenv("CC", compilerEnvLookup(defaultcc, gohostos, gohostarch)) | |
| goCmd(cmdGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gobin, goos, goarch, exe), wrapperPath) | |
| // Restore environment. | |
| // TODO(elias.naur): support environment variables in goCmd? | |
| os.Setenv("GOOS", goos) | |
| os.Setenv("GOARCH", goarch) | |
| os.Setenv("CC", oldcc) | |
| } | |
| // Print trailing banner unless instructed otherwise. | |
| if !noBanner { | |
| banner() | |
| } | |
| } | |
| func wrapperPathFor(goos, goarch string) string { | |
| switch { | |
| case goos == "android": | |
| if gohostos != "android" { | |
| return pathf("%s/misc/android/go_android_exec.go", goroot) | |
| } | |
| case goos == "ios": | |
| if gohostos != "ios" { | |
| return pathf("%s/misc/ios/go_ios_exec.go", goroot) | |
| } | |
| } | |
| return "" | |
| } | |
| func goInstall(goBinary string, args ...string) { | |
| goCmd(goBinary, "install", args...) | |
| } | |
| func goCmd(goBinary string, cmd string, args ...string) { | |
| goCmd := []string{goBinary, cmd, "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags} | |
| if vflag > 0 { | |
| goCmd = append(goCmd, "-v") | |
| } | |
| // Force only one process at a time on vx32 emulation. | |
| if gohostos == "plan9" && os.Getenv("sysname") == "vx32" { | |
| goCmd = append(goCmd, "-p=1") | |
| } | |
| run(workdir, ShowOutput|CheckExit, append(goCmd, args...)...) | |
| } | |
| func checkNotStale(goBinary string, targets ...string) { | |
| out := run(workdir, CheckExit, | |
| append([]string{ | |
| goBinary, | |
| "list", "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags, | |
| "-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}", | |
| }, targets...)...) | |
| if strings.Contains(out, "\tSTALE ") { | |
| os.Setenv("GODEBUG", "gocachehash=1") | |
| for _, target := range []string{"runtime/internal/sys", "cmd/dist", "cmd/link"} { | |
| if strings.Contains(out, "STALE "+target) { | |
| run(workdir, ShowOutput|CheckExit, goBinary, "list", "-f={{.ImportPath}} {{.Stale}}", target) | |
| break | |
| } | |
| } | |
| fatalf("unexpected stale targets reported by %s list -gcflags=\"%s\" -ldflags=\"%s\" for %v:\n%s", goBinary, gogcflags, goldflags, targets, out) | |
| } | |
| } | |
| // Cannot use go/build directly because cmd/dist for a new release | |
| // builds against an old release's go/build, which may be out of sync. | |
| // To reduce duplication, we generate the list for go/build from this. | |
| // | |
| // We list all supported platforms in this list, so that this is the | |
| // single point of truth for supported platforms. This list is used | |
| // by 'go tool dist list'. | |
| var cgoEnabled = map[string]bool{ | |
| "aix/ppc64": true, | |
| "darwin/amd64": true, | |
| "darwin/arm64": true, | |
| "dragonfly/amd64": true, | |
| "freebsd/386": true, | |
| "freebsd/amd64": true, | |
| "freebsd/arm": true, | |
| "freebsd/arm64": true, | |
| "illumos/amd64": true, | |
| "linux/386": true, | |
| "linux/amd64": true, | |
| "linux/arm": true, | |
| "linux/arm64": true, | |
| "linux/ppc64": false, | |
| "linux/ppc64le": true, | |
| "linux/mips": true, | |
| "linux/mipsle": true, | |
| "linux/mips64": true, | |
| "linux/mips64le": true, | |
| "linux/riscv64": true, | |
| "linux/s390x": true, | |
| "linux/sparc64": true, | |
| "android/386": true, | |
| "android/amd64": true, | |
| "android/arm": true, | |
| "android/arm64": true, | |
| "ios/arm64": true, | |
| "ios/amd64": true, | |
| "js/wasm": false, | |
| "netbsd/386": true, | |
| "netbsd/amd64": true, | |
| "netbsd/arm": true, | |
| "netbsd/arm64": true, | |
| "openbsd/386": true, | |
| "openbsd/amd64": true, | |
| "openbsd/arm": true, | |
| "openbsd/arm64": true, | |
| "openbsd/mips64": false, | |
| "plan9/386": false, | |
| "plan9/amd64": false, | |
| "plan9/arm": false, | |
| "solaris/amd64": true, | |
| "windows/386": true, | |
| "windows/amd64": true, | |
| "windows/arm": false, | |
| "windows/arm64": false, | |
| } | |
| // List of platforms which are supported but not complete yet. These get | |
| // filtered out of cgoEnabled for 'dist list'. See golang.org/issue/28944 | |
| var incomplete = map[string]bool{ | |
| "linux/sparc64": true, | |
| } | |
| func needCC() bool { | |
| switch os.Getenv("CGO_ENABLED") { | |
| case "1": | |
| return true | |
| case "0": | |
| return false | |
| } | |
| return cgoEnabled[gohostos+"/"+gohostarch] | |
| } | |
| func checkCC() { | |
| if !needCC() { | |
| return | |
| } | |
| if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil { | |
| outputHdr := "" | |
| if len(output) > 0 { | |
| outputHdr = "\nCommand output:\n\n" | |
| } | |
| fatalf("cannot invoke C compiler %q: %v\n\n"+ | |
| "Go needs a system C compiler for use with cgo.\n"+ | |
| "To set a C compiler, set CC=the-compiler.\n"+ | |
| "To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc[""], err, outputHdr, output) | |
| } | |
| } | |
| func defaulttarg() string { | |
| // xgetwd might return a path with symlinks fully resolved, and if | |
| // there happens to be symlinks in goroot, then the hasprefix test | |
| // will never succeed. Instead, we use xrealwd to get a canonical | |
| // goroot/src before the comparison to avoid this problem. | |
| pwd := xgetwd() | |
| src := pathf("%s/src/", goroot) | |
| real_src := xrealwd(src) | |
| if !strings.HasPrefix(pwd, real_src) { | |
| fatalf("current directory %s is not under %s", pwd, real_src) | |
| } | |
| pwd = pwd[len(real_src):] | |
| // guard against xrealwd returning the directory without the trailing / | |
| pwd = strings.TrimPrefix(pwd, "/") | |
| return pwd | |
| } | |
| // Install installs the list of packages named on the command line. | |
| func cmdinstall() { | |
| xflagparse(-1) | |
| if flag.NArg() == 0 { | |
| install(defaulttarg()) | |
| } | |
| for _, arg := range flag.Args() { | |
| install(arg) | |
| } | |
| } | |
| // Clean deletes temporary objects. | |
| func cmdclean() { | |
| xflagparse(0) | |
| clean() | |
| } | |
| // Banner prints the 'now you've installed Go' banner. | |
| func cmdbanner() { | |
| xflagparse(0) | |
| banner() | |
| } | |
| func banner() { | |
| if vflag > 0 { | |
| xprintf("\n") | |
| } | |
| xprintf("---\n") | |
| xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot) | |
| xprintf("Installed commands in %s\n", gobin) | |
| if !xsamefile(goroot_final, goroot) { | |
| // If the files are to be moved, don't check that gobin | |
| // is on PATH; assume they know what they are doing. | |
| } else if gohostos == "plan9" { | |
| // Check that gobin is bound before /bin. | |
| pid := strings.Replace(readfile("#c/pid"), " ", "", -1) | |
| ns := fmt.Sprintf("/proc/%s/ns", pid) | |
| if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) { | |
| xprintf("*** You need to bind %s before /bin.\n", gobin) | |
| } | |
| } else { | |
| // Check that gobin appears in $PATH. | |
| pathsep := ":" | |
| if gohostos == "windows" { | |
| pathsep = ";" | |
| } | |
| if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) { | |
| xprintf("*** You need to add %s to your PATH.\n", gobin) | |
| } | |
| } | |
| if !xsamefile(goroot_final, goroot) { | |
| xprintf("\n"+ | |
| "The binaries expect %s to be copied or moved to %s\n", | |
| goroot, goroot_final) | |
| } | |
| } | |
| // Version prints the Go version. | |
| func cmdversion() { | |
| xflagparse(0) | |
| xprintf("%s\n", findgoversion()) | |
| } | |
| // cmdlist lists all supported platforms. | |
| func cmdlist() { | |
| jsonFlag := flag.Bool("json", false, "produce JSON output") | |
| xflagparse(0) | |
| var plats []string | |
| for p := range cgoEnabled { | |
| if incomplete[p] { | |
| continue | |
| } | |
| plats = append(plats, p) | |
| } | |
| sort.Strings(plats) | |
| if !*jsonFlag { | |
| for _, p := range plats { | |
| xprintf("%s\n", p) | |
| } | |
| return | |
| } | |
| type jsonResult struct { | |
| GOOS string | |
| GOARCH string | |
| CgoSupported bool | |
| } | |
| var results []jsonResult | |
| for _, p := range plats { | |
| fields := strings.Split(p, "/") | |
| results = append(results, jsonResult{ | |
| GOOS: fields[0], | |
| GOARCH: fields[1], | |
| CgoSupported: cgoEnabled[p]}) | |
| } | |
| out, err := json.MarshalIndent(results, "", "\t") | |
| if err != nil { | |
| fatalf("json marshal error: %v", err) | |
| } | |
| if _, err := os.Stdout.Write(out); err != nil { | |
| fatalf("write failed: %v", err) | |
| } | |
| } | |
| // IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it | |
| // belongs to the collection of "runtime-related" packages, including | |
| // "runtime" itself, "reflect", "syscall", and the | |
| // "runtime/internal/*" packages. See also the function of the same | |
| // name in cmd/internal/objabi/path.go. | |
| func IsRuntimePackagePath(pkgpath string) bool { | |
| rval := false | |
| switch pkgpath { | |
| case "runtime": | |
| rval = true | |
| case "reflect": | |
| rval = true | |
| case "syscall": | |
| rval = true | |
| case "crypto/x509/internal/macos": // libc function wrappers need to be ABIInternal | |
| rval = true | |
| default: | |
| rval = strings.HasPrefix(pkgpath, "runtime/internal") | |
| } | |
| return rval | |
| } |