Source file src/cmd/dist/build.go

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

View as plain text