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

View as plain text