Source file src/cmd/dist/build.go

     1  // Copyright 2012 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  	"bytes"
     9  	"encoding/json"
    10  	"flag"
    11  	"fmt"
    12  	"io"
    13  	"io/fs"
    14  	"log"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"regexp"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  )
    25  
    26  // Initialization for any invocation.
    27  
    28  // The usual variables.
    29  var (
    30  	goarch           string
    31  	gorootBin        string
    32  	gorootBinGo      string
    33  	gohostarch       string
    34  	gohostos         string
    35  	goos             string
    36  	goarm            string
    37  	goarm64          string
    38  	go386            string
    39  	goamd64          string
    40  	gomips           string
    41  	gomips64         string
    42  	goppc64          string
    43  	goriscv64        string
    44  	goroot           string
    45  	goextlinkenabled string
    46  	gogcflags        string // For running built compiler
    47  	goldflags        string
    48  	goexperiment     string
    49  	gofips140        string
    50  	workdir          string
    51  	tooldir          string
    52  	oldgoos          string
    53  	oldgoarch        string
    54  	oldgocache       string
    55  	exe              string
    56  	defaultcc        map[string]string
    57  	defaultcxx       map[string]string
    58  	defaultpkgconfig string
    59  	defaultldso      string
    60  
    61  	rebuildall bool
    62  	noOpt      bool
    63  	isRelease  bool
    64  
    65  	vflag int // verbosity
    66  )
    67  
    68  // The known architectures.
    69  var okgoarch = []string{
    70  	"386",
    71  	"amd64",
    72  	"arm",
    73  	"arm64",
    74  	"loong64",
    75  	"mips",
    76  	"mipsle",
    77  	"mips64",
    78  	"mips64le",
    79  	"ppc64",
    80  	"ppc64le",
    81  	"riscv64",
    82  	"s390x",
    83  	"sparc64",
    84  	"wasm",
    85  }
    86  
    87  // The known operating systems.
    88  var okgoos = []string{
    89  	"darwin",
    90  	"dragonfly",
    91  	"illumos",
    92  	"ios",
    93  	"js",
    94  	"wasip1",
    95  	"linux",
    96  	"android",
    97  	"solaris",
    98  	"freebsd",
    99  	"nacl", // keep;
   100  	"netbsd",
   101  	"openbsd",
   102  	"plan9",
   103  	"windows",
   104  	"aix",
   105  }
   106  
   107  // find reports the first index of p in l[0:n], or else -1.
   108  func find(p string, l []string) int {
   109  	for i, s := range l {
   110  		if p == s {
   111  			return i
   112  		}
   113  	}
   114  	return -1
   115  }
   116  
   117  // xinit handles initialization of the various global state, like goroot and goarch.
   118  func xinit() {
   119  	b := os.Getenv("GOROOT")
   120  	if b == "" {
   121  		fatalf("$GOROOT must be set")
   122  	}
   123  	goroot = filepath.Clean(b)
   124  	gorootBin = pathf("%s/bin", goroot)
   125  
   126  	// Don't run just 'go' because the build infrastructure
   127  	// runs cmd/dist inside go/bin often, and on Windows
   128  	// it will be found in the current directory and refuse to exec.
   129  	// All exec calls rewrite "go" into gorootBinGo.
   130  	gorootBinGo = pathf("%s/bin/go", goroot)
   131  
   132  	b = os.Getenv("GOOS")
   133  	if b == "" {
   134  		b = gohostos
   135  	}
   136  	goos = b
   137  	if find(goos, okgoos) < 0 {
   138  		fatalf("unknown $GOOS %s", goos)
   139  	}
   140  
   141  	b = os.Getenv("GOARM")
   142  	if b == "" {
   143  		b = xgetgoarm()
   144  	}
   145  	goarm = b
   146  
   147  	b = os.Getenv("GOARM64")
   148  	if b == "" {
   149  		b = "v8.0"
   150  	}
   151  	goarm64 = b
   152  
   153  	b = os.Getenv("GO386")
   154  	if b == "" {
   155  		b = "sse2"
   156  	}
   157  	go386 = b
   158  
   159  	b = os.Getenv("GOAMD64")
   160  	if b == "" {
   161  		b = "v1"
   162  	}
   163  	goamd64 = b
   164  
   165  	b = os.Getenv("GOMIPS")
   166  	if b == "" {
   167  		b = "hardfloat"
   168  	}
   169  	gomips = b
   170  
   171  	b = os.Getenv("GOMIPS64")
   172  	if b == "" {
   173  		b = "hardfloat"
   174  	}
   175  	gomips64 = b
   176  
   177  	b = os.Getenv("GOPPC64")
   178  	if b == "" {
   179  		b = "power8"
   180  	}
   181  	goppc64 = b
   182  
   183  	b = os.Getenv("GORISCV64")
   184  	if b == "" {
   185  		b = "rva20u64"
   186  	}
   187  	goriscv64 = b
   188  
   189  	b = os.Getenv("GOFIPS140")
   190  	if b == "" {
   191  		b = "off"
   192  	}
   193  	gofips140 = b
   194  
   195  	if p := pathf("%s/src/all.bash", goroot); !isfile(p) {
   196  		fatalf("$GOROOT is not set correctly or not exported\n"+
   197  			"\tGOROOT=%s\n"+
   198  			"\t%s does not exist", goroot, p)
   199  	}
   200  
   201  	b = os.Getenv("GOHOSTARCH")
   202  	if b != "" {
   203  		gohostarch = b
   204  	}
   205  	if find(gohostarch, okgoarch) < 0 {
   206  		fatalf("unknown $GOHOSTARCH %s", gohostarch)
   207  	}
   208  
   209  	b = os.Getenv("GOARCH")
   210  	if b == "" {
   211  		b = gohostarch
   212  	}
   213  	goarch = b
   214  	if find(goarch, okgoarch) < 0 {
   215  		fatalf("unknown $GOARCH %s", goarch)
   216  	}
   217  
   218  	b = os.Getenv("GO_EXTLINK_ENABLED")
   219  	if b != "" {
   220  		if b != "0" && b != "1" {
   221  			fatalf("unknown $GO_EXTLINK_ENABLED %s", b)
   222  		}
   223  		goextlinkenabled = b
   224  	}
   225  
   226  	goexperiment = os.Getenv("GOEXPERIMENT")
   227  	// TODO(mdempsky): Validate known experiments?
   228  
   229  	gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
   230  	goldflags = os.Getenv("BOOT_GO_LDFLAGS")
   231  
   232  	defaultcc = compilerEnv("CC", "")
   233  	defaultcxx = compilerEnv("CXX", "")
   234  
   235  	b = os.Getenv("PKG_CONFIG")
   236  	if b == "" {
   237  		b = "pkg-config"
   238  	}
   239  	defaultpkgconfig = b
   240  
   241  	defaultldso = os.Getenv("GO_LDSO")
   242  
   243  	// For tools being invoked but also for os.ExpandEnv.
   244  	os.Setenv("GO386", go386)
   245  	os.Setenv("GOAMD64", goamd64)
   246  	os.Setenv("GOARCH", goarch)
   247  	os.Setenv("GOARM", goarm)
   248  	os.Setenv("GOARM64", goarm64)
   249  	os.Setenv("GOHOSTARCH", gohostarch)
   250  	os.Setenv("GOHOSTOS", gohostos)
   251  	os.Setenv("GOOS", goos)
   252  	os.Setenv("GOMIPS", gomips)
   253  	os.Setenv("GOMIPS64", gomips64)
   254  	os.Setenv("GOPPC64", goppc64)
   255  	os.Setenv("GORISCV64", goriscv64)
   256  	os.Setenv("GOROOT", goroot)
   257  	os.Setenv("GOFIPS140", gofips140)
   258  
   259  	// Set GOBIN to GOROOT/bin. The meaning of GOBIN has drifted over time
   260  	// (see https://go.dev/issue/3269, https://go.dev/cl/183058,
   261  	// https://go.dev/issue/31576). Since we want binaries installed by 'dist' to
   262  	// always go to GOROOT/bin anyway.
   263  	os.Setenv("GOBIN", gorootBin)
   264  
   265  	// Make the environment more predictable.
   266  	os.Setenv("LANG", "C")
   267  	os.Setenv("LANGUAGE", "en_US.UTF8")
   268  	os.Unsetenv("GO111MODULE")
   269  	os.Setenv("GOENV", "off")
   270  	os.Unsetenv("GOFLAGS")
   271  	os.Setenv("GOWORK", "off")
   272  
   273  	// Create the go.mod for building toolchain2 and toolchain3. Toolchain1 and go_bootstrap are built with
   274  	// a separate go.mod (with a lower required go version to allow all allowed bootstrap toolchain versions)
   275  	// in bootstrapBuildTools.
   276  	modVer := goModVersion()
   277  	workdir = xworkdir()
   278  	if err := os.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap\n\ngo "+modVer+"\n"), 0666); err != nil {
   279  		fatalf("cannot write stub go.mod: %s", err)
   280  	}
   281  	xatexit(rmworkdir)
   282  
   283  	tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
   284  
   285  	goversion := findgoversion()
   286  	isRelease = strings.HasPrefix(goversion, "release.") || strings.HasPrefix(goversion, "go")
   287  }
   288  
   289  // compilerEnv returns a map from "goos/goarch" to the
   290  // compiler setting to use for that platform.
   291  // The entry for key "" covers any goos/goarch not explicitly set in the map.
   292  // For example, compilerEnv("CC", "gcc") returns the C compiler settings
   293  // read from $CC, defaulting to gcc.
   294  //
   295  // The result is a map because additional environment variables
   296  // can be set to change the compiler based on goos/goarch settings.
   297  // The following applies to all envNames but CC is assumed to simplify
   298  // the presentation.
   299  //
   300  // If no environment variables are set, we use def for all goos/goarch.
   301  // $CC, if set, applies to all goos/goarch but is overridden by the following.
   302  // $CC_FOR_TARGET, if set, applies to all goos/goarch except gohostos/gohostarch,
   303  // but is overridden by the following.
   304  // If gohostos=goos and gohostarch=goarch, then $CC_FOR_TARGET applies even for gohostos/gohostarch.
   305  // $CC_FOR_goos_goarch, if set, applies only to goos/goarch.
   306  func compilerEnv(envName, def string) map[string]string {
   307  	m := map[string]string{"": def}
   308  
   309  	if env := os.Getenv(envName); env != "" {
   310  		m[""] = env
   311  	}
   312  	if env := os.Getenv(envName + "_FOR_TARGET"); env != "" {
   313  		if gohostos != goos || gohostarch != goarch {
   314  			m[gohostos+"/"+gohostarch] = m[""]
   315  		}
   316  		m[""] = env
   317  	}
   318  
   319  	for _, goos := range okgoos {
   320  		for _, goarch := range okgoarch {
   321  			if env := os.Getenv(envName + "_FOR_" + goos + "_" + goarch); env != "" {
   322  				m[goos+"/"+goarch] = env
   323  			}
   324  		}
   325  	}
   326  
   327  	return m
   328  }
   329  
   330  // clangos lists the operating systems where we prefer clang to gcc.
   331  var clangos = []string{
   332  	"darwin", "ios", // macOS 10.9 and later require clang
   333  	"freebsd", // FreeBSD 10 and later do not ship gcc
   334  	"openbsd", // OpenBSD ships with GCC 4.2, which is now quite old.
   335  }
   336  
   337  // compilerEnvLookup returns the compiler settings for goos/goarch in map m.
   338  // kind is "CC" or "CXX".
   339  func compilerEnvLookup(kind string, m map[string]string, goos, goarch string) string {
   340  	if !needCC() {
   341  		return ""
   342  	}
   343  	if cc := m[goos+"/"+goarch]; cc != "" {
   344  		return cc
   345  	}
   346  	if cc := m[""]; cc != "" {
   347  		return cc
   348  	}
   349  	for _, os := range clangos {
   350  		if goos == os {
   351  			if kind == "CXX" {
   352  				return "clang++"
   353  			}
   354  			return "clang"
   355  		}
   356  	}
   357  	if kind == "CXX" {
   358  		return "g++"
   359  	}
   360  	return "gcc"
   361  }
   362  
   363  // rmworkdir deletes the work directory.
   364  func rmworkdir() {
   365  	if vflag > 1 {
   366  		errprintf("rm -rf %s\n", workdir)
   367  	}
   368  	xremoveall(workdir)
   369  }
   370  
   371  // Remove trailing spaces.
   372  func chomp(s string) string {
   373  	return strings.TrimRight(s, " \t\r\n")
   374  }
   375  
   376  // findgoversion determines the Go version to use in the version string.
   377  // It also parses any other metadata found in the version file.
   378  func findgoversion() string {
   379  	// The $GOROOT/VERSION file takes priority, for distributions
   380  	// without the source repo.
   381  	path := pathf("%s/VERSION", goroot)
   382  	if isfile(path) {
   383  		b := chomp(readfile(path))
   384  
   385  		// Starting in Go 1.21 the VERSION file starts with the
   386  		// version on a line by itself but then can contain other
   387  		// metadata about the release, one item per line.
   388  		if i := strings.Index(b, "\n"); i >= 0 {
   389  			rest := b[i+1:]
   390  			b = chomp(b[:i])
   391  			for _, line := range strings.Split(rest, "\n") {
   392  				f := strings.Fields(line)
   393  				if len(f) == 0 {
   394  					continue
   395  				}
   396  				switch f[0] {
   397  				default:
   398  					fatalf("VERSION: unexpected line: %s", line)
   399  				case "time":
   400  					if len(f) != 2 {
   401  						fatalf("VERSION: unexpected time line: %s", line)
   402  					}
   403  					_, err := time.Parse(time.RFC3339, f[1])
   404  					if err != nil {
   405  						fatalf("VERSION: bad time: %s", err)
   406  					}
   407  				}
   408  			}
   409  		}
   410  
   411  		// Commands such as "dist version > VERSION" will cause
   412  		// the shell to create an empty VERSION file and set dist's
   413  		// stdout to its fd. dist in turn looks at VERSION and uses
   414  		// its content if available, which is empty at this point.
   415  		// Only use the VERSION file if it is non-empty.
   416  		if b != "" {
   417  			return b
   418  		}
   419  	}
   420  
   421  	// The $GOROOT/VERSION.cache file is a cache to avoid invoking
   422  	// git every time we run this command. Unlike VERSION, it gets
   423  	// deleted by the clean command.
   424  	path = pathf("%s/VERSION.cache", goroot)
   425  	if isfile(path) {
   426  		return chomp(readfile(path))
   427  	}
   428  
   429  	// Show a nicer error message if this isn't a Git repo.
   430  	if !isGitRepo() {
   431  		fatalf("FAILED: not a Git repo; must put a VERSION file in $GOROOT")
   432  	}
   433  
   434  	// Otherwise, use Git.
   435  	//
   436  	// Include 1.x base version, hash, and date in the version.
   437  	//
   438  	// Note that we lightly parse internal/goversion/goversion.go to
   439  	// obtain the base version. We can't just import the package,
   440  	// because cmd/dist is built with a bootstrap GOROOT which could
   441  	// be an entirely different version of Go. We assume
   442  	// that the file contains "const Version = <Integer>".
   443  	goversionSource := readfile(pathf("%s/src/internal/goversion/goversion.go", goroot))
   444  	m := regexp.MustCompile(`(?m)^const Version = (\d+)`).FindStringSubmatch(goversionSource)
   445  	if m == nil {
   446  		fatalf("internal/goversion/goversion.go does not contain 'const Version = ...'")
   447  	}
   448  	version := fmt.Sprintf("devel go1.%s-", m[1])
   449  	version += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format:%h %cd", "HEAD"))
   450  
   451  	// Cache version.
   452  	writefile(version, path, 0)
   453  
   454  	return version
   455  }
   456  
   457  // goModVersion returns the go version declared in src/go.mod. This is the
   458  // go version to use in the go.mod building go_bootstrap, toolchain2, and toolchain3.
   459  // (toolchain1 must be built with requiredBootstrapVersion(goModVersion))
   460  func goModVersion() string {
   461  	goMod := readfile(pathf("%s/src/go.mod", goroot))
   462  	m := regexp.MustCompile(`(?m)^go (1.\d+)$`).FindStringSubmatch(goMod)
   463  	if m == nil {
   464  		fatalf("std go.mod does not contain go 1.X")
   465  	}
   466  	return m[1]
   467  }
   468  
   469  func requiredBootstrapVersion(v string) string {
   470  	minorstr, ok := strings.CutPrefix(v, "1.")
   471  	if !ok {
   472  		fatalf("go version %q in go.mod does not start with %q", v, "1.")
   473  	}
   474  	minor, err := strconv.Atoi(minorstr)
   475  	if err != nil {
   476  		fatalf("invalid go version minor component %q: %v", minorstr, err)
   477  	}
   478  	// Per go.dev/doc/install/source, for N >= 22, Go version 1.N will require a Go 1.M compiler,
   479  	// where M is N-2 rounded down to an even number. Example: Go 1.24 and 1.25 require Go 1.22.
   480  	requiredMinor := minor - 2 - minor%2
   481  	return "1." + strconv.Itoa(requiredMinor)
   482  }
   483  
   484  // isGitRepo reports whether the working directory is inside a Git repository.
   485  func isGitRepo() bool {
   486  	// NB: simply checking the exit code of `git rev-parse --git-dir` would
   487  	// suffice here, but that requires deviating from the infrastructure
   488  	// provided by `run`.
   489  	gitDir := chomp(run(goroot, 0, "git", "rev-parse", "--git-dir"))
   490  	if !filepath.IsAbs(gitDir) {
   491  		gitDir = filepath.Join(goroot, gitDir)
   492  	}
   493  	return isdir(gitDir)
   494  }
   495  
   496  /*
   497   * Initial tree setup.
   498   */
   499  
   500  // The old tools that no longer live in $GOBIN or $GOROOT/bin.
   501  var oldtool = []string{
   502  	"5a", "5c", "5g", "5l",
   503  	"6a", "6c", "6g", "6l",
   504  	"8a", "8c", "8g", "8l",
   505  	"9a", "9c", "9g", "9l",
   506  	"6cov",
   507  	"6nm",
   508  	"6prof",
   509  	"cgo",
   510  	"ebnflint",
   511  	"goapi",
   512  	"gofix",
   513  	"goinstall",
   514  	"gomake",
   515  	"gopack",
   516  	"gopprof",
   517  	"gotest",
   518  	"gotype",
   519  	"govet",
   520  	"goyacc",
   521  	"quietgcc",
   522  }
   523  
   524  // Unreleased directories (relative to $GOROOT) that should
   525  // not be in release branches.
   526  var unreleased = []string{
   527  	"src/cmd/newlink",
   528  	"src/cmd/objwriter",
   529  	"src/debug/goobj",
   530  	"src/old",
   531  }
   532  
   533  // setup sets up the tree for the initial build.
   534  func setup() {
   535  	// Create bin directory.
   536  	if p := pathf("%s/bin", goroot); !isdir(p) {
   537  		xmkdir(p)
   538  	}
   539  
   540  	// Create package directory.
   541  	if p := pathf("%s/pkg", goroot); !isdir(p) {
   542  		xmkdir(p)
   543  	}
   544  
   545  	goosGoarch := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
   546  	if rebuildall {
   547  		xremoveall(goosGoarch)
   548  	}
   549  	xmkdirall(goosGoarch)
   550  	xatexit(func() {
   551  		if files := xreaddir(goosGoarch); len(files) == 0 {
   552  			xremove(goosGoarch)
   553  		}
   554  	})
   555  
   556  	if goos != gohostos || goarch != gohostarch {
   557  		p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
   558  		if rebuildall {
   559  			xremoveall(p)
   560  		}
   561  		xmkdirall(p)
   562  	}
   563  
   564  	// Create object directory.
   565  	// We used to use it for C objects.
   566  	// Now we use it for the build cache, to separate dist's cache
   567  	// from any other cache the user might have, and for the location
   568  	// to build the bootstrap versions of the standard library.
   569  	obj := pathf("%s/pkg/obj", goroot)
   570  	if !isdir(obj) {
   571  		xmkdir(obj)
   572  	}
   573  	xatexit(func() { xremove(obj) })
   574  
   575  	// Create build cache directory.
   576  	objGobuild := pathf("%s/pkg/obj/go-build", goroot)
   577  	if rebuildall {
   578  		xremoveall(objGobuild)
   579  	}
   580  	xmkdirall(objGobuild)
   581  	xatexit(func() { xremoveall(objGobuild) })
   582  
   583  	// Create directory for bootstrap versions of standard library .a files.
   584  	objGoBootstrap := pathf("%s/pkg/obj/go-bootstrap", goroot)
   585  	if rebuildall {
   586  		xremoveall(objGoBootstrap)
   587  	}
   588  	xmkdirall(objGoBootstrap)
   589  	xatexit(func() { xremoveall(objGoBootstrap) })
   590  
   591  	// Create tool directory.
   592  	// We keep it in pkg/, just like the object directory above.
   593  	if rebuildall {
   594  		xremoveall(tooldir)
   595  	}
   596  	xmkdirall(tooldir)
   597  
   598  	// Remove tool binaries from before the tool/gohostos_gohostarch
   599  	xremoveall(pathf("%s/bin/tool", goroot))
   600  
   601  	// Remove old pre-tool binaries.
   602  	for _, old := range oldtool {
   603  		xremove(pathf("%s/bin/%s", goroot, old))
   604  	}
   605  
   606  	// Special release-specific setup.
   607  	if isRelease {
   608  		// Make sure release-excluded things are excluded.
   609  		for _, dir := range unreleased {
   610  			if p := pathf("%s/%s", goroot, dir); isdir(p) {
   611  				fatalf("%s should not exist in release build", p)
   612  			}
   613  		}
   614  	}
   615  }
   616  
   617  /*
   618   * Tool building
   619   */
   620  
   621  // mustLinkExternal is a copy of internal/platform.MustLinkExternal,
   622  // duplicated here to avoid version skew in the MustLinkExternal function
   623  // during bootstrapping.
   624  func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool {
   625  	if cgoEnabled {
   626  		switch goarch {
   627  		case "loong64", "mips", "mipsle", "mips64", "mips64le":
   628  			// Internally linking cgo is incomplete on some architectures.
   629  			// https://golang.org/issue/14449
   630  			return true
   631  		case "arm64":
   632  			if goos == "windows" {
   633  				// windows/arm64 internal linking is not implemented.
   634  				return true
   635  			}
   636  		case "ppc64":
   637  			// Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
   638  			if goos == "aix" || goos == "linux" {
   639  				return true
   640  			}
   641  		}
   642  
   643  		switch goos {
   644  		case "android":
   645  			return true
   646  		case "dragonfly":
   647  			// It seems that on Dragonfly thread local storage is
   648  			// set up by the dynamic linker, so internal cgo linking
   649  			// doesn't work. Test case is "go test runtime/cgo".
   650  			return true
   651  		}
   652  	}
   653  
   654  	switch goos {
   655  	case "android":
   656  		if goarch != "arm64" {
   657  			return true
   658  		}
   659  	case "ios":
   660  		if goarch == "arm64" {
   661  			return true
   662  		}
   663  	}
   664  	return false
   665  }
   666  
   667  // depsuffix records the allowed suffixes for source files.
   668  var depsuffix = []string{
   669  	".s",
   670  	".go",
   671  }
   672  
   673  // gentab records how to generate some trivial files.
   674  // Files listed here should also be listed in ../distpack/pack.go's srcArch.Remove list.
   675  var gentab = []struct {
   676  	pkg  string // Relative to $GOROOT/src
   677  	file string
   678  	gen  func(dir, file string)
   679  }{
   680  	{"go/build", "zcgo.go", mkzcgo},
   681  	{"cmd/go/internal/cfg", "zdefaultcc.go", mkzdefaultcc},
   682  	{"internal/runtime/sys", "zversion.go", mkzversion},
   683  	{"time/tzdata", "zzipdata.go", mktzdata},
   684  }
   685  
   686  // installed maps from a dir name (as given to install) to a chan
   687  // closed when the dir's package is installed.
   688  var installed = make(map[string]chan struct{})
   689  var installedMu sync.Mutex
   690  
   691  func install(dir string) {
   692  	<-startInstall(dir)
   693  }
   694  
   695  func startInstall(dir string) chan struct{} {
   696  	installedMu.Lock()
   697  	ch := installed[dir]
   698  	if ch == nil {
   699  		ch = make(chan struct{})
   700  		installed[dir] = ch
   701  		go runInstall(dir, ch)
   702  	}
   703  	installedMu.Unlock()
   704  	return ch
   705  }
   706  
   707  // runInstall installs the library, package, or binary associated with pkg,
   708  // which is relative to $GOROOT/src.
   709  func runInstall(pkg string, ch chan struct{}) {
   710  	if pkg == "net" || pkg == "os/user" || pkg == "crypto/x509" {
   711  		fatalf("go_bootstrap cannot depend on cgo package %s", pkg)
   712  	}
   713  
   714  	defer close(ch)
   715  
   716  	if pkg == "unsafe" {
   717  		return
   718  	}
   719  
   720  	if vflag > 0 {
   721  		if goos != gohostos || goarch != gohostarch {
   722  			errprintf("%s (%s/%s)\n", pkg, goos, goarch)
   723  		} else {
   724  			errprintf("%s\n", pkg)
   725  		}
   726  	}
   727  
   728  	workdir := pathf("%s/%s", workdir, pkg)
   729  	xmkdirall(workdir)
   730  
   731  	var clean []string
   732  	defer func() {
   733  		for _, name := range clean {
   734  			xremove(name)
   735  		}
   736  	}()
   737  
   738  	// dir = full path to pkg.
   739  	dir := pathf("%s/src/%s", goroot, pkg)
   740  	name := filepath.Base(dir)
   741  
   742  	// ispkg predicts whether the package should be linked as a binary, based
   743  	// on the name. There should be no "main" packages in vendor, since
   744  	// 'go mod vendor' will only copy imported packages there.
   745  	ispkg := !strings.HasPrefix(pkg, "cmd/") || strings.Contains(pkg, "/internal/") || strings.Contains(pkg, "/vendor/")
   746  
   747  	// Start final link command line.
   748  	// Note: code below knows that link.p[targ] is the target.
   749  	var (
   750  		link      []string
   751  		targ      int
   752  		ispackcmd bool
   753  	)
   754  	if ispkg {
   755  		// Go library (package).
   756  		ispackcmd = true
   757  		link = []string{"pack", packagefile(pkg)}
   758  		targ = len(link) - 1
   759  		xmkdirall(filepath.Dir(link[targ]))
   760  	} else {
   761  		// Go command.
   762  		elem := name
   763  		if elem == "go" {
   764  			elem = "go_bootstrap"
   765  		}
   766  		link = []string{pathf("%s/link", tooldir)}
   767  		if goos == "android" {
   768  			link = append(link, "-buildmode=pie")
   769  		}
   770  		if goldflags != "" {
   771  			link = append(link, goldflags)
   772  		}
   773  		link = append(link, "-extld="+compilerEnvLookup("CC", defaultcc, goos, goarch))
   774  		link = append(link, "-L="+pathf("%s/pkg/obj/go-bootstrap/%s_%s", goroot, goos, goarch))
   775  		link = append(link, "-o", pathf("%s/%s%s", tooldir, elem, exe))
   776  		targ = len(link) - 1
   777  	}
   778  	ttarg := mtime(link[targ])
   779  
   780  	// Gather files that are sources for this target.
   781  	// Everything in that directory, and any target-specific
   782  	// additions.
   783  	files := xreaddir(dir)
   784  
   785  	// Remove files beginning with . or _,
   786  	// which are likely to be editor temporary files.
   787  	// This is the same heuristic build.ScanDir uses.
   788  	// There do exist real C files beginning with _,
   789  	// so limit that check to just Go files.
   790  	files = filter(files, func(p string) bool {
   791  		return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
   792  	})
   793  
   794  	// Add generated files for this package.
   795  	for _, gt := range gentab {
   796  		if gt.pkg == pkg {
   797  			files = append(files, gt.file)
   798  		}
   799  	}
   800  	files = uniq(files)
   801  
   802  	// Convert to absolute paths.
   803  	for i, p := range files {
   804  		if !filepath.IsAbs(p) {
   805  			files[i] = pathf("%s/%s", dir, p)
   806  		}
   807  	}
   808  
   809  	// Is the target up-to-date?
   810  	var gofiles, sfiles []string
   811  	stale := rebuildall
   812  	files = filter(files, func(p string) bool {
   813  		for _, suf := range depsuffix {
   814  			if strings.HasSuffix(p, suf) {
   815  				goto ok
   816  			}
   817  		}
   818  		return false
   819  	ok:
   820  		t := mtime(p)
   821  		if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, pkg) {
   822  			return false
   823  		}
   824  		if strings.HasSuffix(p, ".go") {
   825  			gofiles = append(gofiles, p)
   826  		} else if strings.HasSuffix(p, ".s") {
   827  			sfiles = append(sfiles, p)
   828  		}
   829  		if t.After(ttarg) {
   830  			stale = true
   831  		}
   832  		return true
   833  	})
   834  
   835  	// If there are no files to compile, we're done.
   836  	if len(files) == 0 {
   837  		return
   838  	}
   839  
   840  	if !stale {
   841  		return
   842  	}
   843  
   844  	// For package runtime, copy some files into the work space.
   845  	if pkg == "runtime" {
   846  		xmkdirall(pathf("%s/pkg/include", goroot))
   847  		// For use by assembly and C files.
   848  		copyfile(pathf("%s/pkg/include/textflag.h", goroot),
   849  			pathf("%s/src/runtime/textflag.h", goroot), 0)
   850  		copyfile(pathf("%s/pkg/include/funcdata.h", goroot),
   851  			pathf("%s/src/runtime/funcdata.h", goroot), 0)
   852  		copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot),
   853  			pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0)
   854  		copyfile(pathf("%s/pkg/include/asm_amd64.h", goroot),
   855  			pathf("%s/src/runtime/asm_amd64.h", goroot), 0)
   856  		copyfile(pathf("%s/pkg/include/asm_riscv64.h", goroot),
   857  			pathf("%s/src/runtime/asm_riscv64.h", goroot), 0)
   858  	}
   859  
   860  	// Generate any missing files; regenerate existing ones.
   861  	for _, gt := range gentab {
   862  		if gt.pkg != pkg {
   863  			continue
   864  		}
   865  		p := pathf("%s/%s", dir, gt.file)
   866  		if vflag > 1 {
   867  			errprintf("generate %s\n", p)
   868  		}
   869  		gt.gen(dir, p)
   870  		// Do not add generated file to clean list.
   871  		// In runtime, we want to be able to
   872  		// build the package with the go tool,
   873  		// and it assumes these generated files already
   874  		// exist (it does not know how to build them).
   875  		// The 'clean' command can remove
   876  		// the generated files.
   877  	}
   878  
   879  	// Resolve imported packages to actual package paths.
   880  	// Make sure they're installed.
   881  	importMap := make(map[string]string)
   882  	for _, p := range gofiles {
   883  		for _, imp := range readimports(p) {
   884  			if imp == "C" {
   885  				fatalf("%s imports C", p)
   886  			}
   887  			importMap[imp] = resolveVendor(imp, dir)
   888  		}
   889  	}
   890  	sortedImports := make([]string, 0, len(importMap))
   891  	for imp := range importMap {
   892  		sortedImports = append(sortedImports, imp)
   893  	}
   894  	sort.Strings(sortedImports)
   895  
   896  	for _, dep := range importMap {
   897  		if dep == "C" {
   898  			fatalf("%s imports C", pkg)
   899  		}
   900  		startInstall(dep)
   901  	}
   902  	for _, dep := range importMap {
   903  		install(dep)
   904  	}
   905  
   906  	if goos != gohostos || goarch != gohostarch {
   907  		// We've generated the right files; the go command can do the build.
   908  		if vflag > 1 {
   909  			errprintf("skip build for cross-compile %s\n", pkg)
   910  		}
   911  		return
   912  	}
   913  
   914  	asmArgs := []string{
   915  		pathf("%s/asm", tooldir),
   916  		"-I", workdir,
   917  		"-I", pathf("%s/pkg/include", goroot),
   918  		"-D", "GOOS_" + goos,
   919  		"-D", "GOARCH_" + goarch,
   920  		"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
   921  		"-p", pkg,
   922  	}
   923  	if goarch == "mips" || goarch == "mipsle" {
   924  		// Define GOMIPS_value from gomips.
   925  		asmArgs = append(asmArgs, "-D", "GOMIPS_"+gomips)
   926  	}
   927  	if goarch == "mips64" || goarch == "mips64le" {
   928  		// Define GOMIPS64_value from gomips64.
   929  		asmArgs = append(asmArgs, "-D", "GOMIPS64_"+gomips64)
   930  	}
   931  	if goarch == "ppc64" || goarch == "ppc64le" {
   932  		// We treat each powerpc version as a superset of functionality.
   933  		switch goppc64 {
   934  		case "power10":
   935  			asmArgs = append(asmArgs, "-D", "GOPPC64_power10")
   936  			fallthrough
   937  		case "power9":
   938  			asmArgs = append(asmArgs, "-D", "GOPPC64_power9")
   939  			fallthrough
   940  		default: // This should always be power8.
   941  			asmArgs = append(asmArgs, "-D", "GOPPC64_power8")
   942  		}
   943  	}
   944  	if goarch == "riscv64" {
   945  		// Define GORISCV64_value from goriscv64
   946  		asmArgs = append(asmArgs, "-D", "GORISCV64_"+goriscv64)
   947  	}
   948  	if goarch == "arm" {
   949  		// Define GOARM_value from goarm, which can be either a version
   950  		// like "6", or a version and a FP mode, like "7,hardfloat".
   951  		switch {
   952  		case strings.Contains(goarm, "7"):
   953  			asmArgs = append(asmArgs, "-D", "GOARM_7")
   954  			fallthrough
   955  		case strings.Contains(goarm, "6"):
   956  			asmArgs = append(asmArgs, "-D", "GOARM_6")
   957  			fallthrough
   958  		default:
   959  			asmArgs = append(asmArgs, "-D", "GOARM_5")
   960  		}
   961  	}
   962  	goasmh := pathf("%s/go_asm.h", workdir)
   963  
   964  	// Collect symabis from assembly code.
   965  	var symabis string
   966  	if len(sfiles) > 0 {
   967  		symabis = pathf("%s/symabis", workdir)
   968  		var wg sync.WaitGroup
   969  		asmabis := append(asmArgs[:len(asmArgs):len(asmArgs)], "-gensymabis", "-o", symabis)
   970  		asmabis = append(asmabis, sfiles...)
   971  		if err := os.WriteFile(goasmh, nil, 0666); err != nil {
   972  			fatalf("cannot write empty go_asm.h: %s", err)
   973  		}
   974  		bgrun(&wg, dir, asmabis...)
   975  		bgwait(&wg)
   976  	}
   977  
   978  	// Build an importcfg file for the compiler.
   979  	buf := &bytes.Buffer{}
   980  	for _, imp := range sortedImports {
   981  		if imp == "unsafe" {
   982  			continue
   983  		}
   984  		dep := importMap[imp]
   985  		if imp != dep {
   986  			fmt.Fprintf(buf, "importmap %s=%s\n", imp, dep)
   987  		}
   988  		fmt.Fprintf(buf, "packagefile %s=%s\n", dep, packagefile(dep))
   989  	}
   990  	importcfg := pathf("%s/importcfg", workdir)
   991  	if err := os.WriteFile(importcfg, buf.Bytes(), 0666); err != nil {
   992  		fatalf("cannot write importcfg file: %v", err)
   993  	}
   994  
   995  	var archive string
   996  	// The next loop will compile individual non-Go files.
   997  	// Hand the Go files to the compiler en masse.
   998  	// For packages containing assembly, this writes go_asm.h, which
   999  	// the assembly files will need.
  1000  	pkgName := pkg
  1001  	if strings.HasPrefix(pkg, "cmd/") && strings.Count(pkg, "/") == 1 {
  1002  		pkgName = "main"
  1003  	}
  1004  	b := pathf("%s/_go_.a", workdir)
  1005  	clean = append(clean, b)
  1006  	if !ispackcmd {
  1007  		link = append(link, b)
  1008  	} else {
  1009  		archive = b
  1010  	}
  1011  
  1012  	// Compile Go code.
  1013  	compile := []string{pathf("%s/compile", tooldir), "-std", "-pack", "-o", b, "-p", pkgName, "-importcfg", importcfg}
  1014  	if gogcflags != "" {
  1015  		compile = append(compile, strings.Fields(gogcflags)...)
  1016  	}
  1017  	if len(sfiles) > 0 {
  1018  		compile = append(compile, "-asmhdr", goasmh)
  1019  	}
  1020  	if symabis != "" {
  1021  		compile = append(compile, "-symabis", symabis)
  1022  	}
  1023  	if goos == "android" {
  1024  		compile = append(compile, "-shared")
  1025  	}
  1026  
  1027  	compile = append(compile, gofiles...)
  1028  	var wg sync.WaitGroup
  1029  	// We use bgrun and immediately wait for it instead of calling run() synchronously.
  1030  	// This executes all jobs through the bgwork channel and allows the process
  1031  	// to exit cleanly in case an error occurs.
  1032  	bgrun(&wg, dir, compile...)
  1033  	bgwait(&wg)
  1034  
  1035  	// Compile the files.
  1036  	for _, p := range sfiles {
  1037  		// Assembly file for a Go package.
  1038  		compile := asmArgs[:len(asmArgs):len(asmArgs)]
  1039  
  1040  		doclean := true
  1041  		b := pathf("%s/%s", workdir, filepath.Base(p))
  1042  
  1043  		// Change the last character of the output file (which was c or s).
  1044  		b = b[:len(b)-1] + "o"
  1045  		compile = append(compile, "-o", b, p)
  1046  		bgrun(&wg, dir, compile...)
  1047  
  1048  		link = append(link, b)
  1049  		if doclean {
  1050  			clean = append(clean, b)
  1051  		}
  1052  	}
  1053  	bgwait(&wg)
  1054  
  1055  	if ispackcmd {
  1056  		xremove(link[targ])
  1057  		dopack(link[targ], archive, link[targ+1:])
  1058  		return
  1059  	}
  1060  
  1061  	// Remove target before writing it.
  1062  	xremove(link[targ])
  1063  	bgrun(&wg, "", link...)
  1064  	bgwait(&wg)
  1065  }
  1066  
  1067  // packagefile returns the path to a compiled .a file for the given package
  1068  // path. Paths may need to be resolved with resolveVendor first.
  1069  func packagefile(pkg string) string {
  1070  	return pathf("%s/pkg/obj/go-bootstrap/%s_%s/%s.a", goroot, goos, goarch, pkg)
  1071  }
  1072  
  1073  // unixOS is the set of GOOS values matched by the "unix" build tag.
  1074  // This is the same list as in internal/syslist/syslist.go.
  1075  var unixOS = map[string]bool{
  1076  	"aix":       true,
  1077  	"android":   true,
  1078  	"darwin":    true,
  1079  	"dragonfly": true,
  1080  	"freebsd":   true,
  1081  	"hurd":      true,
  1082  	"illumos":   true,
  1083  	"ios":       true,
  1084  	"linux":     true,
  1085  	"netbsd":    true,
  1086  	"openbsd":   true,
  1087  	"solaris":   true,
  1088  }
  1089  
  1090  // matchtag reports whether the tag matches this build.
  1091  func matchtag(tag string) bool {
  1092  	switch tag {
  1093  	case "gc", "cmd_go_bootstrap", "go1.1":
  1094  		return true
  1095  	case "linux":
  1096  		return goos == "linux" || goos == "android"
  1097  	case "solaris":
  1098  		return goos == "solaris" || goos == "illumos"
  1099  	case "darwin":
  1100  		return goos == "darwin" || goos == "ios"
  1101  	case goos, goarch:
  1102  		return true
  1103  	case "unix":
  1104  		return unixOS[goos]
  1105  	default:
  1106  		return false
  1107  	}
  1108  }
  1109  
  1110  // shouldbuild reports whether we should build this file.
  1111  // It applies the same rules that are used with context tags
  1112  // in package go/build, except it's less picky about the order
  1113  // of GOOS and GOARCH.
  1114  // We also allow the special tag cmd_go_bootstrap.
  1115  // See ../go/bootstrap.go and package go/build.
  1116  func shouldbuild(file, pkg string) bool {
  1117  	// Check file name for GOOS or GOARCH.
  1118  	name := filepath.Base(file)
  1119  	excluded := func(list []string, ok string) bool {
  1120  		for _, x := range list {
  1121  			if x == ok || (ok == "android" && x == "linux") || (ok == "illumos" && x == "solaris") || (ok == "ios" && x == "darwin") {
  1122  				continue
  1123  			}
  1124  			i := strings.Index(name, x)
  1125  			if i <= 0 || name[i-1] != '_' {
  1126  				continue
  1127  			}
  1128  			i += len(x)
  1129  			if i == len(name) || name[i] == '.' || name[i] == '_' {
  1130  				return true
  1131  			}
  1132  		}
  1133  		return false
  1134  	}
  1135  	if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
  1136  		return false
  1137  	}
  1138  
  1139  	// Omit test files.
  1140  	if strings.Contains(name, "_test") {
  1141  		return false
  1142  	}
  1143  
  1144  	// Check file contents for //go:build lines.
  1145  	for _, p := range strings.Split(readfile(file), "\n") {
  1146  		p = strings.TrimSpace(p)
  1147  		if p == "" {
  1148  			continue
  1149  		}
  1150  		code := p
  1151  		i := strings.Index(code, "//")
  1152  		if i > 0 {
  1153  			code = strings.TrimSpace(code[:i])
  1154  		}
  1155  		if code == "package documentation" {
  1156  			return false
  1157  		}
  1158  		if code == "package main" && pkg != "cmd/go" && pkg != "cmd/cgo" {
  1159  			return false
  1160  		}
  1161  		if !strings.HasPrefix(p, "//") {
  1162  			break
  1163  		}
  1164  		if strings.HasPrefix(p, "//go:build ") {
  1165  			matched, err := matchexpr(p[len("//go:build "):])
  1166  			if err != nil {
  1167  				errprintf("%s: %v", file, err)
  1168  			}
  1169  			return matched
  1170  		}
  1171  	}
  1172  
  1173  	return true
  1174  }
  1175  
  1176  // copyfile copies the file src to dst, via memory (so only good for small files).
  1177  func copyfile(dst, src string, flag int) {
  1178  	if vflag > 1 {
  1179  		errprintf("cp %s %s\n", src, dst)
  1180  	}
  1181  	writefile(readfile(src), dst, flag)
  1182  }
  1183  
  1184  // dopack copies the package src to dst,
  1185  // appending the files listed in extra.
  1186  // The archive format is the traditional Unix ar format.
  1187  func dopack(dst, src string, extra []string) {
  1188  	bdst := bytes.NewBufferString(readfile(src))
  1189  	for _, file := range extra {
  1190  		b := readfile(file)
  1191  		// find last path element for archive member name
  1192  		i := strings.LastIndex(file, "/") + 1
  1193  		j := strings.LastIndex(file, `\`) + 1
  1194  		if i < j {
  1195  			i = j
  1196  		}
  1197  		fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
  1198  		bdst.WriteString(b)
  1199  		if len(b)&1 != 0 {
  1200  			bdst.WriteByte(0)
  1201  		}
  1202  	}
  1203  	writefile(bdst.String(), dst, 0)
  1204  }
  1205  
  1206  func clean() {
  1207  	generated := []byte(generatedHeader)
  1208  
  1209  	// Remove generated source files.
  1210  	filepath.WalkDir(pathf("%s/src", goroot), func(path string, d fs.DirEntry, err error) error {
  1211  		switch {
  1212  		case err != nil:
  1213  			// ignore
  1214  		case d.IsDir() && (d.Name() == "vendor" || d.Name() == "testdata"):
  1215  			return filepath.SkipDir
  1216  		case d.IsDir() && d.Name() != "dist":
  1217  			// Remove generated binary named for directory, but not dist out from under us.
  1218  			exe := filepath.Join(path, d.Name())
  1219  			if info, err := os.Stat(exe); err == nil && !info.IsDir() {
  1220  				xremove(exe)
  1221  			}
  1222  			xremove(exe + ".exe")
  1223  		case !d.IsDir() && strings.HasPrefix(d.Name(), "z"):
  1224  			// Remove generated file, identified by marker string.
  1225  			head := make([]byte, 512)
  1226  			if f, err := os.Open(path); err == nil {
  1227  				io.ReadFull(f, head)
  1228  				f.Close()
  1229  			}
  1230  			if bytes.HasPrefix(head, generated) {
  1231  				xremove(path)
  1232  			}
  1233  		}
  1234  		return nil
  1235  	})
  1236  
  1237  	if rebuildall {
  1238  		// Remove object tree.
  1239  		xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
  1240  
  1241  		// Remove installed packages and tools.
  1242  		xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
  1243  		xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
  1244  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch))
  1245  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch))
  1246  		xremoveall(tooldir)
  1247  
  1248  		// Remove cached version info.
  1249  		xremove(pathf("%s/VERSION.cache", goroot))
  1250  
  1251  		// Remove distribution packages.
  1252  		xremoveall(pathf("%s/pkg/distpack", goroot))
  1253  	}
  1254  }
  1255  
  1256  /*
  1257   * command implementations
  1258   */
  1259  
  1260  // The env command prints the default environment.
  1261  func cmdenv() {
  1262  	path := flag.Bool("p", false, "emit updated PATH")
  1263  	plan9 := flag.Bool("9", gohostos == "plan9", "emit plan 9 syntax")
  1264  	windows := flag.Bool("w", gohostos == "windows", "emit windows syntax")
  1265  	xflagparse(0)
  1266  
  1267  	format := "%s=\"%s\";\n" // Include ; to separate variables when 'dist env' output is used with eval.
  1268  	switch {
  1269  	case *plan9:
  1270  		format = "%s='%s'\n"
  1271  	case *windows:
  1272  		format = "set %s=%s\r\n"
  1273  	}
  1274  
  1275  	xprintf(format, "GO111MODULE", "")
  1276  	xprintf(format, "GOARCH", goarch)
  1277  	xprintf(format, "GOBIN", gorootBin)
  1278  	xprintf(format, "GODEBUG", os.Getenv("GODEBUG"))
  1279  	xprintf(format, "GOENV", "off")
  1280  	xprintf(format, "GOFLAGS", "")
  1281  	xprintf(format, "GOHOSTARCH", gohostarch)
  1282  	xprintf(format, "GOHOSTOS", gohostos)
  1283  	xprintf(format, "GOOS", goos)
  1284  	xprintf(format, "GOPROXY", os.Getenv("GOPROXY"))
  1285  	xprintf(format, "GOROOT", goroot)
  1286  	xprintf(format, "GOTMPDIR", os.Getenv("GOTMPDIR"))
  1287  	xprintf(format, "GOTOOLDIR", tooldir)
  1288  	if goarch == "arm" {
  1289  		xprintf(format, "GOARM", goarm)
  1290  	}
  1291  	if goarch == "arm64" {
  1292  		xprintf(format, "GOARM64", goarm64)
  1293  	}
  1294  	if goarch == "386" {
  1295  		xprintf(format, "GO386", go386)
  1296  	}
  1297  	if goarch == "amd64" {
  1298  		xprintf(format, "GOAMD64", goamd64)
  1299  	}
  1300  	if goarch == "mips" || goarch == "mipsle" {
  1301  		xprintf(format, "GOMIPS", gomips)
  1302  	}
  1303  	if goarch == "mips64" || goarch == "mips64le" {
  1304  		xprintf(format, "GOMIPS64", gomips64)
  1305  	}
  1306  	if goarch == "ppc64" || goarch == "ppc64le" {
  1307  		xprintf(format, "GOPPC64", goppc64)
  1308  	}
  1309  	if goarch == "riscv64" {
  1310  		xprintf(format, "GORISCV64", goriscv64)
  1311  	}
  1312  	xprintf(format, "GOWORK", "off")
  1313  
  1314  	if *path {
  1315  		sep := ":"
  1316  		if gohostos == "windows" {
  1317  			sep = ";"
  1318  		}
  1319  		xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gorootBin, sep, os.Getenv("PATH")))
  1320  
  1321  		// Also include $DIST_UNMODIFIED_PATH with the original $PATH
  1322  		// for the internal needs of "dist banner", along with export
  1323  		// so that it reaches the dist process. See its comment below.
  1324  		var exportFormat string
  1325  		if !*windows && !*plan9 {
  1326  			exportFormat = "export " + format
  1327  		} else {
  1328  			exportFormat = format
  1329  		}
  1330  		xprintf(exportFormat, "DIST_UNMODIFIED_PATH", os.Getenv("PATH"))
  1331  	}
  1332  }
  1333  
  1334  var (
  1335  	timeLogEnabled = os.Getenv("GOBUILDTIMELOGFILE") != ""
  1336  	timeLogMu      sync.Mutex
  1337  	timeLogFile    *os.File
  1338  	timeLogStart   time.Time
  1339  )
  1340  
  1341  func timelog(op, name string) {
  1342  	if !timeLogEnabled {
  1343  		return
  1344  	}
  1345  	timeLogMu.Lock()
  1346  	defer timeLogMu.Unlock()
  1347  	if timeLogFile == nil {
  1348  		f, err := os.OpenFile(os.Getenv("GOBUILDTIMELOGFILE"), os.O_RDWR|os.O_APPEND, 0666)
  1349  		if err != nil {
  1350  			log.Fatal(err)
  1351  		}
  1352  		buf := make([]byte, 100)
  1353  		n, _ := f.Read(buf)
  1354  		s := string(buf[:n])
  1355  		if i := strings.Index(s, "\n"); i >= 0 {
  1356  			s = s[:i]
  1357  		}
  1358  		i := strings.Index(s, " start")
  1359  		if i < 0 {
  1360  			log.Fatalf("time log %s does not begin with start line", os.Getenv("GOBUILDTIMELOGFILE"))
  1361  		}
  1362  		t, err := time.Parse(time.UnixDate, s[:i])
  1363  		if err != nil {
  1364  			log.Fatalf("cannot parse time log line %q: %v", s, err)
  1365  		}
  1366  		timeLogStart = t
  1367  		timeLogFile = f
  1368  	}
  1369  	t := time.Now()
  1370  	fmt.Fprintf(timeLogFile, "%s %+.1fs %s %s\n", t.Format(time.UnixDate), t.Sub(timeLogStart).Seconds(), op, name)
  1371  }
  1372  
  1373  // toolenv returns the environment to use when building commands in cmd.
  1374  //
  1375  // This is a function instead of a variable because the exact toolenv depends
  1376  // on the GOOS and GOARCH, and (at least for now) those are modified in place
  1377  // to switch between the host and target configurations when cross-compiling.
  1378  func toolenv() []string {
  1379  	var env []string
  1380  	if !mustLinkExternal(goos, goarch, false) {
  1381  		// Unless the platform requires external linking,
  1382  		// we disable cgo to get static binaries for cmd/go and cmd/pprof,
  1383  		// so that they work on systems without the same dynamic libraries
  1384  		// as the original build system.
  1385  		env = append(env, "CGO_ENABLED=0")
  1386  	}
  1387  	if isRelease || os.Getenv("GO_BUILDER_NAME") != "" {
  1388  		// Add -trimpath for reproducible builds of releases.
  1389  		// Include builders so that -trimpath is well-tested ahead of releases.
  1390  		// Do not include local development, so that people working in the
  1391  		// main branch for day-to-day work on the Go toolchain itself can
  1392  		// still have full paths for stack traces for compiler crashes and the like.
  1393  		env = append(env, "GOFLAGS=-trimpath -ldflags=-w -gcflags=cmd/...=-dwarf=false")
  1394  	}
  1395  	return env
  1396  }
  1397  
  1398  var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link", "cmd/preprofile"}
  1399  
  1400  // The bootstrap command runs a build from scratch,
  1401  // stopping at having installed the go_bootstrap command.
  1402  //
  1403  // WARNING: This command runs after cmd/dist is built with the Go bootstrap toolchain.
  1404  // It rebuilds and installs cmd/dist with the new toolchain, so other
  1405  // commands (like "go tool dist test" in run.bash) can rely on bug fixes
  1406  // made since the Go bootstrap version, but this function cannot.
  1407  func cmdbootstrap() {
  1408  	timelog("start", "dist bootstrap")
  1409  	defer timelog("end", "dist bootstrap")
  1410  
  1411  	var debug, distpack, force, noBanner, noClean bool
  1412  	flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
  1413  	flag.BoolVar(&debug, "d", debug, "enable debugging of bootstrap process")
  1414  	flag.BoolVar(&distpack, "distpack", distpack, "write distribution files to pkg/distpack")
  1415  	flag.BoolVar(&force, "force", force, "build even if the port is marked as broken")
  1416  	flag.BoolVar(&noBanner, "no-banner", noBanner, "do not print banner")
  1417  	flag.BoolVar(&noClean, "no-clean", noClean, "print deprecation warning")
  1418  
  1419  	xflagparse(0)
  1420  
  1421  	if noClean {
  1422  		xprintf("warning: --no-clean is deprecated and has no effect; use 'go install std cmd' instead\n")
  1423  	}
  1424  
  1425  	// Don't build broken ports by default.
  1426  	if broken[goos+"/"+goarch] && !force {
  1427  		fatalf("build stopped because the port %s/%s is marked as broken\n\n"+
  1428  			"Use the -force flag to build anyway.\n", goos, goarch)
  1429  	}
  1430  
  1431  	// Set GOPATH to an internal directory. We shouldn't actually
  1432  	// need to store files here, since the toolchain won't
  1433  	// depend on modules outside of vendor directories, but if
  1434  	// GOPATH points somewhere else (e.g., to GOROOT), the
  1435  	// go tool may complain.
  1436  	os.Setenv("GOPATH", pathf("%s/pkg/obj/gopath", goroot))
  1437  
  1438  	// Set GOPROXY=off to avoid downloading modules to the modcache in
  1439  	// the GOPATH set above to be inside GOROOT. The modcache is read
  1440  	// only so if we downloaded to the modcache, we'd create readonly
  1441  	// files in GOROOT, which is undesirable. See #67463)
  1442  	os.Setenv("GOPROXY", "off")
  1443  
  1444  	// Use a build cache separate from the default user one.
  1445  	// Also one that will be wiped out during startup, so that
  1446  	// make.bash really does start from a clean slate.
  1447  	oldgocache = os.Getenv("GOCACHE")
  1448  	os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot))
  1449  
  1450  	// Disable GOEXPERIMENT when building toolchain1 and
  1451  	// go_bootstrap. We don't need any experiments for the
  1452  	// bootstrap toolchain, and this lets us avoid duplicating the
  1453  	// GOEXPERIMENT-related build logic from cmd/go here. If the
  1454  	// bootstrap toolchain is < Go 1.17, it will ignore this
  1455  	// anyway since GOEXPERIMENT is baked in; otherwise it will
  1456  	// pick it up from the environment we set here. Once we're
  1457  	// using toolchain1 with dist as the build system, we need to
  1458  	// override this to keep the experiments assumed by the
  1459  	// toolchain and by dist consistent. Once go_bootstrap takes
  1460  	// over the build process, we'll set this back to the original
  1461  	// GOEXPERIMENT.
  1462  	os.Setenv("GOEXPERIMENT", "none")
  1463  
  1464  	if debug {
  1465  		// cmd/buildid is used in debug mode.
  1466  		toolchain = append(toolchain, "cmd/buildid")
  1467  	}
  1468  
  1469  	if isdir(pathf("%s/src/pkg", goroot)) {
  1470  		fatalf("\n\n"+
  1471  			"The Go package sources have moved to $GOROOT/src.\n"+
  1472  			"*** %s still exists. ***\n"+
  1473  			"It probably contains stale files that may confuse the build.\n"+
  1474  			"Please (check what's there and) remove it and try again.\n"+
  1475  			"See https://golang.org/s/go14nopkg\n",
  1476  			pathf("%s/src/pkg", goroot))
  1477  	}
  1478  
  1479  	if rebuildall {
  1480  		clean()
  1481  	}
  1482  
  1483  	setup()
  1484  
  1485  	timelog("build", "toolchain1")
  1486  	checkCC()
  1487  	bootstrapBuildTools()
  1488  
  1489  	// Remember old content of $GOROOT/bin for comparison below.
  1490  	oldBinFiles, err := filepath.Glob(pathf("%s/bin/*", goroot))
  1491  	if err != nil {
  1492  		fatalf("glob: %v", err)
  1493  	}
  1494  
  1495  	// For the main bootstrap, building for host os/arch.
  1496  	oldgoos = goos
  1497  	oldgoarch = goarch
  1498  	goos = gohostos
  1499  	goarch = gohostarch
  1500  	os.Setenv("GOHOSTARCH", gohostarch)
  1501  	os.Setenv("GOHOSTOS", gohostos)
  1502  	os.Setenv("GOARCH", goarch)
  1503  	os.Setenv("GOOS", goos)
  1504  
  1505  	timelog("build", "go_bootstrap")
  1506  	xprintf("Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.\n")
  1507  	install("runtime")     // dependency not visible in sources; also sets up textflag.h
  1508  	install("time/tzdata") // no dependency in sources; creates generated file
  1509  	install("cmd/go")
  1510  	if vflag > 0 {
  1511  		xprintf("\n")
  1512  	}
  1513  
  1514  	gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now
  1515  	setNoOpt()
  1516  	goldflags = os.Getenv("GO_LDFLAGS") // we were using $BOOT_GO_LDFLAGS until now
  1517  	goBootstrap := pathf("%s/go_bootstrap", tooldir)
  1518  	if debug {
  1519  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1520  		copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec)
  1521  	}
  1522  
  1523  	// To recap, so far we have built the new toolchain
  1524  	// (cmd/asm, cmd/cgo, cmd/compile, cmd/link)
  1525  	// using the Go bootstrap toolchain and go command.
  1526  	// Then we built the new go command (as go_bootstrap)
  1527  	// using the new toolchain and our own build logic (above).
  1528  	//
  1529  	//	toolchain1 = mk(new toolchain, go1.17 toolchain, go1.17 cmd/go)
  1530  	//	go_bootstrap = mk(new cmd/go, toolchain1, cmd/dist)
  1531  	//
  1532  	// The toolchain1 we built earlier is built from the new sources,
  1533  	// but because it was built using cmd/go it has no build IDs.
  1534  	// The eventually installed toolchain needs build IDs, so we need
  1535  	// to do another round:
  1536  	//
  1537  	//	toolchain2 = mk(new toolchain, toolchain1, go_bootstrap)
  1538  	//
  1539  	timelog("build", "toolchain2")
  1540  	if vflag > 0 {
  1541  		xprintf("\n")
  1542  	}
  1543  	xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
  1544  	os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch))
  1545  	// Now that cmd/go is in charge of the build process, enable GOEXPERIMENT.
  1546  	os.Setenv("GOEXPERIMENT", goexperiment)
  1547  	// No need to enable PGO for toolchain2.
  1548  	goInstall(toolenv(), goBootstrap, append([]string{"-pgo=off"}, toolchain...)...)
  1549  	if debug {
  1550  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1551  		copyfile(pathf("%s/compile2", tooldir), pathf("%s/compile", tooldir), writeExec)
  1552  	}
  1553  
  1554  	// Toolchain2 should be semantically equivalent to toolchain1,
  1555  	// but it was built using the newly built compiler instead of the Go bootstrap compiler,
  1556  	// so it should at the least run faster. Also, toolchain1 had no build IDs
  1557  	// in the binaries, while toolchain2 does. In non-release builds, the
  1558  	// toolchain's build IDs feed into constructing the build IDs of built targets,
  1559  	// so in non-release builds, everything now looks out-of-date due to
  1560  	// toolchain2 having build IDs - that is, due to the go command seeing
  1561  	// that there are new compilers. In release builds, the toolchain's reported
  1562  	// version is used in place of the build ID, and the go command does not
  1563  	// see that change from toolchain1 to toolchain2, so in release builds,
  1564  	// nothing looks out of date.
  1565  	// To keep the behavior the same in both non-release and release builds,
  1566  	// we force-install everything here.
  1567  	//
  1568  	//	toolchain3 = mk(new toolchain, toolchain2, go_bootstrap)
  1569  	//
  1570  	timelog("build", "toolchain3")
  1571  	if vflag > 0 {
  1572  		xprintf("\n")
  1573  	}
  1574  	xprintf("Building Go toolchain3 using go_bootstrap and Go toolchain2.\n")
  1575  	goInstall(toolenv(), goBootstrap, append([]string{"-a"}, toolchain...)...)
  1576  	if debug {
  1577  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1578  		copyfile(pathf("%s/compile3", tooldir), pathf("%s/compile", tooldir), writeExec)
  1579  	}
  1580  
  1581  	// Now that toolchain3 has been built from scratch, its compiler and linker
  1582  	// should have accurate build IDs suitable for caching.
  1583  	// Now prime the build cache with the rest of the standard library for
  1584  	// testing, and so that the user can run 'go install std cmd' to quickly
  1585  	// iterate on local changes without waiting for a full rebuild.
  1586  	if _, err := os.Stat(pathf("%s/VERSION", goroot)); err == nil {
  1587  		// If we have a VERSION file, then we use the Go version
  1588  		// instead of build IDs as a cache key, and there is no guarantee
  1589  		// that code hasn't changed since the last time we ran a build
  1590  		// with this exact VERSION file (especially if someone is working
  1591  		// on a release branch). We must not fall back to the shared build cache
  1592  		// in this case. Leave $GOCACHE alone.
  1593  	} else {
  1594  		os.Setenv("GOCACHE", oldgocache)
  1595  	}
  1596  
  1597  	if goos == oldgoos && goarch == oldgoarch {
  1598  		// Common case - not setting up for cross-compilation.
  1599  		timelog("build", "toolchain")
  1600  		if vflag > 0 {
  1601  			xprintf("\n")
  1602  		}
  1603  		xprintf("Building packages and commands for %s/%s.\n", goos, goarch)
  1604  	} else {
  1605  		// GOOS/GOARCH does not match GOHOSTOS/GOHOSTARCH.
  1606  		// Finish GOHOSTOS/GOHOSTARCH installation and then
  1607  		// run GOOS/GOARCH installation.
  1608  		timelog("build", "host toolchain")
  1609  		if vflag > 0 {
  1610  			xprintf("\n")
  1611  		}
  1612  		xprintf("Building commands for host, %s/%s.\n", goos, goarch)
  1613  		goInstall(toolenv(), goBootstrap, "cmd")
  1614  		checkNotStale(toolenv(), goBootstrap, "cmd")
  1615  		checkNotStale(toolenv(), gorootBinGo, "cmd")
  1616  
  1617  		timelog("build", "target toolchain")
  1618  		if vflag > 0 {
  1619  			xprintf("\n")
  1620  		}
  1621  		goos = oldgoos
  1622  		goarch = oldgoarch
  1623  		os.Setenv("GOOS", goos)
  1624  		os.Setenv("GOARCH", goarch)
  1625  		os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch))
  1626  		xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch)
  1627  	}
  1628  	goInstall(nil, goBootstrap, "std")
  1629  	goInstall(toolenv(), goBootstrap, "cmd")
  1630  	checkNotStale(toolenv(), goBootstrap, toolchain...)
  1631  	checkNotStale(nil, goBootstrap, "std")
  1632  	checkNotStale(toolenv(), goBootstrap, "cmd")
  1633  	checkNotStale(nil, gorootBinGo, "std")
  1634  	checkNotStale(toolenv(), gorootBinGo, "cmd")
  1635  	if debug {
  1636  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1637  		checkNotStale(toolenv(), goBootstrap, toolchain...)
  1638  		copyfile(pathf("%s/compile4", tooldir), pathf("%s/compile", tooldir), writeExec)
  1639  	}
  1640  
  1641  	// Check that there are no new files in $GOROOT/bin other than
  1642  	// go and gofmt and $GOOS_$GOARCH (target bin when cross-compiling).
  1643  	binFiles, err := filepath.Glob(pathf("%s/bin/*", goroot))
  1644  	if err != nil {
  1645  		fatalf("glob: %v", err)
  1646  	}
  1647  
  1648  	ok := map[string]bool{}
  1649  	for _, f := range oldBinFiles {
  1650  		ok[f] = true
  1651  	}
  1652  	for _, f := range binFiles {
  1653  		if gohostos == "darwin" && filepath.Base(f) == ".DS_Store" {
  1654  			continue // unfortunate but not unexpected
  1655  		}
  1656  		elem := strings.TrimSuffix(filepath.Base(f), ".exe")
  1657  		if !ok[f] && elem != "go" && elem != "gofmt" && elem != goos+"_"+goarch {
  1658  			fatalf("unexpected new file in $GOROOT/bin: %s", elem)
  1659  		}
  1660  	}
  1661  
  1662  	// Remove go_bootstrap now that we're done.
  1663  	xremove(pathf("%s/go_bootstrap"+exe, tooldir))
  1664  
  1665  	if goos == "android" {
  1666  		// Make sure the exec wrapper will sync a fresh $GOROOT to the device.
  1667  		xremove(pathf("%s/go_android_exec-adb-sync-status", os.TempDir()))
  1668  	}
  1669  
  1670  	if wrapperPath := wrapperPathFor(goos, goarch); wrapperPath != "" {
  1671  		oldcc := os.Getenv("CC")
  1672  		os.Setenv("GOOS", gohostos)
  1673  		os.Setenv("GOARCH", gohostarch)
  1674  		os.Setenv("CC", compilerEnvLookup("CC", defaultcc, gohostos, gohostarch))
  1675  		goCmd(nil, gorootBinGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gorootBin, goos, goarch, exe), wrapperPath)
  1676  		// Restore environment.
  1677  		// TODO(elias.naur): support environment variables in goCmd?
  1678  		os.Setenv("GOOS", goos)
  1679  		os.Setenv("GOARCH", goarch)
  1680  		os.Setenv("CC", oldcc)
  1681  	}
  1682  
  1683  	if distpack {
  1684  		xprintf("Packaging archives for %s/%s.\n", goos, goarch)
  1685  		run("", ShowOutput|CheckExit, pathf("%s/distpack", tooldir))
  1686  	}
  1687  
  1688  	// Print trailing banner unless instructed otherwise.
  1689  	if !noBanner {
  1690  		banner()
  1691  	}
  1692  }
  1693  
  1694  func wrapperPathFor(goos, goarch string) string {
  1695  	switch {
  1696  	case goos == "android":
  1697  		if gohostos != "android" {
  1698  			return pathf("%s/misc/go_android_exec/main.go", goroot)
  1699  		}
  1700  	case goos == "ios":
  1701  		if gohostos != "ios" {
  1702  			return pathf("%s/misc/ios/go_ios_exec.go", goroot)
  1703  		}
  1704  	}
  1705  	return ""
  1706  }
  1707  
  1708  func goInstall(env []string, goBinary string, args ...string) {
  1709  	goCmd(env, goBinary, "install", args...)
  1710  }
  1711  
  1712  func appendCompilerFlags(args []string) []string {
  1713  	if gogcflags != "" {
  1714  		args = append(args, "-gcflags=all="+gogcflags)
  1715  	}
  1716  	if goldflags != "" {
  1717  		args = append(args, "-ldflags=all="+goldflags)
  1718  	}
  1719  	return args
  1720  }
  1721  
  1722  func goCmd(env []string, goBinary string, cmd string, args ...string) {
  1723  	goCmd := []string{goBinary, cmd}
  1724  	if noOpt {
  1725  		goCmd = append(goCmd, "-tags=noopt")
  1726  	}
  1727  	goCmd = appendCompilerFlags(goCmd)
  1728  	if vflag > 0 {
  1729  		goCmd = append(goCmd, "-v")
  1730  	}
  1731  
  1732  	// Force only one process at a time on vx32 emulation.
  1733  	if gohostos == "plan9" && os.Getenv("sysname") == "vx32" {
  1734  		goCmd = append(goCmd, "-p=1")
  1735  	}
  1736  
  1737  	runEnv(workdir, ShowOutput|CheckExit, env, append(goCmd, args...)...)
  1738  }
  1739  
  1740  func checkNotStale(env []string, goBinary string, targets ...string) {
  1741  	goCmd := []string{goBinary, "list"}
  1742  	if noOpt {
  1743  		goCmd = append(goCmd, "-tags=noopt")
  1744  	}
  1745  	goCmd = appendCompilerFlags(goCmd)
  1746  	goCmd = append(goCmd, "-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}")
  1747  
  1748  	out := runEnv(workdir, CheckExit, env, append(goCmd, targets...)...)
  1749  	if strings.Contains(out, "\tSTALE ") {
  1750  		os.Setenv("GODEBUG", "gocachehash=1")
  1751  		for _, target := range []string{"internal/runtime/sys", "cmd/dist", "cmd/link"} {
  1752  			if strings.Contains(out, "STALE "+target) {
  1753  				run(workdir, ShowOutput|CheckExit, goBinary, "list", "-f={{.ImportPath}} {{.Stale}}", target)
  1754  				break
  1755  			}
  1756  		}
  1757  		fatalf("unexpected stale targets reported by %s list -gcflags=\"%s\" -ldflags=\"%s\" for %v (consider rerunning with GOMAXPROCS=1 GODEBUG=gocachehash=1):\n%s", goBinary, gogcflags, goldflags, targets, out)
  1758  	}
  1759  }
  1760  
  1761  // Cannot use go/build directly because cmd/dist for a new release
  1762  // builds against an old release's go/build, which may be out of sync.
  1763  // To reduce duplication, we generate the list for go/build from this.
  1764  //
  1765  // We list all supported platforms in this list, so that this is the
  1766  // single point of truth for supported platforms. This list is used
  1767  // by 'go tool dist list'.
  1768  var cgoEnabled = map[string]bool{
  1769  	"aix/ppc64":       true,
  1770  	"darwin/amd64":    true,
  1771  	"darwin/arm64":    true,
  1772  	"dragonfly/amd64": true,
  1773  	"freebsd/386":     true,
  1774  	"freebsd/amd64":   true,
  1775  	"freebsd/arm":     true,
  1776  	"freebsd/arm64":   true,
  1777  	"freebsd/riscv64": true,
  1778  	"illumos/amd64":   true,
  1779  	"linux/386":       true,
  1780  	"linux/amd64":     true,
  1781  	"linux/arm":       true,
  1782  	"linux/arm64":     true,
  1783  	"linux/loong64":   true,
  1784  	"linux/ppc64":     false,
  1785  	"linux/ppc64le":   true,
  1786  	"linux/mips":      true,
  1787  	"linux/mipsle":    true,
  1788  	"linux/mips64":    true,
  1789  	"linux/mips64le":  true,
  1790  	"linux/riscv64":   true,
  1791  	"linux/s390x":     true,
  1792  	"linux/sparc64":   true,
  1793  	"android/386":     true,
  1794  	"android/amd64":   true,
  1795  	"android/arm":     true,
  1796  	"android/arm64":   true,
  1797  	"ios/arm64":       true,
  1798  	"ios/amd64":       true,
  1799  	"js/wasm":         false,
  1800  	"wasip1/wasm":     false,
  1801  	"netbsd/386":      true,
  1802  	"netbsd/amd64":    true,
  1803  	"netbsd/arm":      true,
  1804  	"netbsd/arm64":    true,
  1805  	"openbsd/386":     true,
  1806  	"openbsd/amd64":   true,
  1807  	"openbsd/arm":     true,
  1808  	"openbsd/arm64":   true,
  1809  	"openbsd/mips64":  true,
  1810  	"openbsd/ppc64":   false,
  1811  	"openbsd/riscv64": true,
  1812  	"plan9/386":       false,
  1813  	"plan9/amd64":     false,
  1814  	"plan9/arm":       false,
  1815  	"solaris/amd64":   true,
  1816  	"windows/386":     true,
  1817  	"windows/amd64":   true,
  1818  	"windows/arm":     false,
  1819  	"windows/arm64":   true,
  1820  }
  1821  
  1822  // List of platforms that are marked as broken ports.
  1823  // These require -force flag to build, and also
  1824  // get filtered out of cgoEnabled for 'dist list'.
  1825  // See go.dev/issue/56679.
  1826  var broken = map[string]bool{
  1827  	"linux/sparc64":  true, // An incomplete port. See CL 132155.
  1828  	"openbsd/mips64": true, // Broken: go.dev/issue/58110.
  1829  	"windows/arm":    true, // Broken: go.dev/issue/68552.
  1830  }
  1831  
  1832  // List of platforms which are first class ports. See go.dev/issue/38874.
  1833  var firstClass = map[string]bool{
  1834  	"darwin/amd64":  true,
  1835  	"darwin/arm64":  true,
  1836  	"linux/386":     true,
  1837  	"linux/amd64":   true,
  1838  	"linux/arm":     true,
  1839  	"linux/arm64":   true,
  1840  	"windows/386":   true,
  1841  	"windows/amd64": true,
  1842  }
  1843  
  1844  // We only need CC if cgo is forced on, or if the platform requires external linking.
  1845  // Otherwise the go command will automatically disable it.
  1846  func needCC() bool {
  1847  	return os.Getenv("CGO_ENABLED") == "1" || mustLinkExternal(gohostos, gohostarch, false)
  1848  }
  1849  
  1850  func checkCC() {
  1851  	if !needCC() {
  1852  		return
  1853  	}
  1854  	cc1 := defaultcc[""]
  1855  	if cc1 == "" {
  1856  		cc1 = "gcc"
  1857  		for _, os := range clangos {
  1858  			if gohostos == os {
  1859  				cc1 = "clang"
  1860  				break
  1861  			}
  1862  		}
  1863  	}
  1864  	cc, err := quotedSplit(cc1)
  1865  	if err != nil {
  1866  		fatalf("split CC: %v", err)
  1867  	}
  1868  	var ccHelp = append(cc, "--help")
  1869  
  1870  	if output, err := exec.Command(ccHelp[0], ccHelp[1:]...).CombinedOutput(); err != nil {
  1871  		outputHdr := ""
  1872  		if len(output) > 0 {
  1873  			outputHdr = "\nCommand output:\n\n"
  1874  		}
  1875  		fatalf("cannot invoke C compiler %q: %v\n\n"+
  1876  			"Go needs a system C compiler for use with cgo.\n"+
  1877  			"To set a C compiler, set CC=the-compiler.\n"+
  1878  			"To disable cgo, set CGO_ENABLED=0.\n%s%s", cc, err, outputHdr, output)
  1879  	}
  1880  }
  1881  
  1882  func defaulttarg() string {
  1883  	// xgetwd might return a path with symlinks fully resolved, and if
  1884  	// there happens to be symlinks in goroot, then the hasprefix test
  1885  	// will never succeed. Instead, we use xrealwd to get a canonical
  1886  	// goroot/src before the comparison to avoid this problem.
  1887  	pwd := xgetwd()
  1888  	src := pathf("%s/src/", goroot)
  1889  	real_src := xrealwd(src)
  1890  	if !strings.HasPrefix(pwd, real_src) {
  1891  		fatalf("current directory %s is not under %s", pwd, real_src)
  1892  	}
  1893  	pwd = pwd[len(real_src):]
  1894  	// guard against xrealwd returning the directory without the trailing /
  1895  	pwd = strings.TrimPrefix(pwd, "/")
  1896  
  1897  	return pwd
  1898  }
  1899  
  1900  // Install installs the list of packages named on the command line.
  1901  func cmdinstall() {
  1902  	xflagparse(-1)
  1903  
  1904  	if flag.NArg() == 0 {
  1905  		install(defaulttarg())
  1906  	}
  1907  
  1908  	for _, arg := range flag.Args() {
  1909  		install(arg)
  1910  	}
  1911  }
  1912  
  1913  // Clean deletes temporary objects.
  1914  func cmdclean() {
  1915  	xflagparse(0)
  1916  	clean()
  1917  }
  1918  
  1919  // Banner prints the 'now you've installed Go' banner.
  1920  func cmdbanner() {
  1921  	xflagparse(0)
  1922  	banner()
  1923  }
  1924  
  1925  func banner() {
  1926  	if vflag > 0 {
  1927  		xprintf("\n")
  1928  	}
  1929  	xprintf("---\n")
  1930  	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
  1931  	xprintf("Installed commands in %s\n", gorootBin)
  1932  
  1933  	if gohostos == "plan9" {
  1934  		// Check that GOROOT/bin is bound before /bin.
  1935  		pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
  1936  		ns := fmt.Sprintf("/proc/%s/ns", pid)
  1937  		if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gorootBin)) {
  1938  			xprintf("*** You need to bind %s before /bin.\n", gorootBin)
  1939  		}
  1940  	} else {
  1941  		// Check that GOROOT/bin appears in $PATH.
  1942  		pathsep := ":"
  1943  		if gohostos == "windows" {
  1944  			pathsep = ";"
  1945  		}
  1946  		path := os.Getenv("PATH")
  1947  		if p, ok := os.LookupEnv("DIST_UNMODIFIED_PATH"); ok {
  1948  			// Scripts that modify $PATH and then run dist should also provide
  1949  			// dist with an unmodified copy of $PATH via $DIST_UNMODIFIED_PATH.
  1950  			// Use it here when determining if the user still needs to update
  1951  			// their $PATH. See go.dev/issue/42563.
  1952  			path = p
  1953  		}
  1954  		if !strings.Contains(pathsep+path+pathsep, pathsep+gorootBin+pathsep) {
  1955  			xprintf("*** You need to add %s to your PATH.\n", gorootBin)
  1956  		}
  1957  	}
  1958  }
  1959  
  1960  // Version prints the Go version.
  1961  func cmdversion() {
  1962  	xflagparse(0)
  1963  	xprintf("%s\n", findgoversion())
  1964  }
  1965  
  1966  // cmdlist lists all supported platforms.
  1967  func cmdlist() {
  1968  	jsonFlag := flag.Bool("json", false, "produce JSON output")
  1969  	brokenFlag := flag.Bool("broken", false, "include broken ports")
  1970  	xflagparse(0)
  1971  
  1972  	var plats []string
  1973  	for p := range cgoEnabled {
  1974  		if broken[p] && !*brokenFlag {
  1975  			continue
  1976  		}
  1977  		plats = append(plats, p)
  1978  	}
  1979  	sort.Strings(plats)
  1980  
  1981  	if !*jsonFlag {
  1982  		for _, p := range plats {
  1983  			xprintf("%s\n", p)
  1984  		}
  1985  		return
  1986  	}
  1987  
  1988  	type jsonResult struct {
  1989  		GOOS         string
  1990  		GOARCH       string
  1991  		CgoSupported bool
  1992  		FirstClass   bool
  1993  		Broken       bool `json:",omitempty"`
  1994  	}
  1995  	var results []jsonResult
  1996  	for _, p := range plats {
  1997  		fields := strings.Split(p, "/")
  1998  		results = append(results, jsonResult{
  1999  			GOOS:         fields[0],
  2000  			GOARCH:       fields[1],
  2001  			CgoSupported: cgoEnabled[p],
  2002  			FirstClass:   firstClass[p],
  2003  			Broken:       broken[p],
  2004  		})
  2005  	}
  2006  	out, err := json.MarshalIndent(results, "", "\t")
  2007  	if err != nil {
  2008  		fatalf("json marshal error: %v", err)
  2009  	}
  2010  	if _, err := os.Stdout.Write(out); err != nil {
  2011  		fatalf("write failed: %v", err)
  2012  	}
  2013  }
  2014  
  2015  func setNoOpt() {
  2016  	for _, gcflag := range strings.Split(gogcflags, " ") {
  2017  		if gcflag == "-N" || gcflag == "-l" {
  2018  			noOpt = true
  2019  			break
  2020  		}
  2021  	}
  2022  }
  2023  

View as plain text