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

View as plain text