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

View as plain text