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

View as plain text