Source file src/cmd/dist/build.go

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

View as plain text