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 toolchain2 and toolchain3.
   459  // (toolchain1 and go_bootstrap 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 gitDir == "" {
   491  		return false
   492  	}
   493  	if !filepath.IsAbs(gitDir) {
   494  		gitDir = filepath.Join(goroot, gitDir)
   495  	}
   496  	return isdir(gitDir)
   497  }
   498  
   499  // isJJRepo reports whether the working directory is inside a jj repository.
   500  func isJJRepo() bool {
   501  	// Don't check the error from jj, similarly to what we do in isGitRepo.
   502  	jjDir := chomp(run(goroot, 0, "jj", "--no-pager", "--color=never", "root"))
   503  	if jjDir == "" {
   504  		return false
   505  	}
   506  	if !filepath.IsAbs(jjDir) {
   507  		jjDir = filepath.Join(goroot, jjDir)
   508  	}
   509  	return isdir(jjDir)
   510  }
   511  
   512  /*
   513   * Initial tree setup.
   514   */
   515  
   516  // The old tools that no longer live in $GOBIN or $GOROOT/bin.
   517  var oldtool = []string{
   518  	"5a", "5c", "5g", "5l",
   519  	"6a", "6c", "6g", "6l",
   520  	"8a", "8c", "8g", "8l",
   521  	"9a", "9c", "9g", "9l",
   522  	"6cov",
   523  	"6nm",
   524  	"6prof",
   525  	"cgo",
   526  	"ebnflint",
   527  	"goapi",
   528  	"gofix",
   529  	"goinstall",
   530  	"gomake",
   531  	"gopack",
   532  	"gopprof",
   533  	"gotest",
   534  	"gotype",
   535  	"govet",
   536  	"goyacc",
   537  	"quietgcc",
   538  }
   539  
   540  // Unreleased directories (relative to $GOROOT) that should
   541  // not be in release branches.
   542  var unreleased = []string{
   543  	"src/cmd/newlink",
   544  	"src/cmd/objwriter",
   545  	"src/debug/goobj",
   546  	"src/old",
   547  }
   548  
   549  // setup sets up the tree for the initial build.
   550  func setup() {
   551  	// Create bin directory.
   552  	if p := pathf("%s/bin", goroot); !isdir(p) {
   553  		xmkdir(p)
   554  	}
   555  
   556  	// Create package directory.
   557  	if p := pathf("%s/pkg", goroot); !isdir(p) {
   558  		xmkdir(p)
   559  	}
   560  
   561  	goosGoarch := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
   562  	if rebuildall {
   563  		xremoveall(goosGoarch)
   564  	}
   565  	xmkdirall(goosGoarch)
   566  	xatexit(func() {
   567  		if files := xreaddir(goosGoarch); len(files) == 0 {
   568  			xremove(goosGoarch)
   569  		}
   570  	})
   571  
   572  	if goos != gohostos || goarch != gohostarch {
   573  		p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
   574  		if rebuildall {
   575  			xremoveall(p)
   576  		}
   577  		xmkdirall(p)
   578  	}
   579  
   580  	// Create object directory.
   581  	// We used to use it for C objects.
   582  	// Now we use it for the build cache, to separate dist's cache
   583  	// from any other cache the user might have, and for the location
   584  	// to build the bootstrap versions of the standard library.
   585  	obj := pathf("%s/pkg/obj", goroot)
   586  	if !isdir(obj) {
   587  		xmkdir(obj)
   588  	}
   589  	xatexit(func() { xremove(obj) })
   590  
   591  	// Create build cache directory.
   592  	objGobuild := pathf("%s/pkg/obj/go-build", goroot)
   593  	if rebuildall {
   594  		xremoveall(objGobuild)
   595  	}
   596  	xmkdirall(objGobuild)
   597  	xatexit(func() { xremoveall(objGobuild) })
   598  
   599  	// Create tool directory.
   600  	// We keep it in pkg/, just like the object directory above.
   601  	if rebuildall {
   602  		xremoveall(tooldir)
   603  	}
   604  	xmkdirall(tooldir)
   605  
   606  	// Remove tool binaries from before the tool/gohostos_gohostarch
   607  	xremoveall(pathf("%s/bin/tool", goroot))
   608  
   609  	// Remove old pre-tool binaries.
   610  	for _, old := range oldtool {
   611  		xremove(pathf("%s/bin/%s", goroot, old))
   612  	}
   613  
   614  	// Special release-specific setup.
   615  	if isRelease {
   616  		// Make sure release-excluded things are excluded.
   617  		for _, dir := range unreleased {
   618  			if p := pathf("%s/%s", goroot, dir); isdir(p) {
   619  				fatalf("%s should not exist in release build", p)
   620  			}
   621  		}
   622  	}
   623  }
   624  
   625  /*
   626   * Tool building
   627   */
   628  
   629  // mustLinkExternal is a copy of internal/platform.MustLinkExternal,
   630  // duplicated here to avoid version skew in the MustLinkExternal function
   631  // during bootstrapping.
   632  func mustLinkExternal(goos, goarch string, cgoEnabled bool) bool {
   633  	if cgoEnabled {
   634  		switch goarch {
   635  		case "mips", "mipsle", "mips64", "mips64le":
   636  			// Internally linking cgo is incomplete on some architectures.
   637  			// https://golang.org/issue/14449
   638  			return true
   639  		case "ppc64":
   640  			// Big Endian PPC64 cgo internal linking is not implemented for aix.
   641  			if goos == "aix" {
   642  				return true
   643  			}
   644  		}
   645  
   646  		switch goos {
   647  		case "android":
   648  			return true
   649  		case "dragonfly":
   650  			// It seems that on Dragonfly thread local storage is
   651  			// set up by the dynamic linker, so internal cgo linking
   652  			// doesn't work. Test case is "go test runtime/cgo".
   653  			return true
   654  		}
   655  	}
   656  
   657  	switch goos {
   658  	case "android":
   659  		if goarch != "arm64" {
   660  			return true
   661  		}
   662  	case "ios":
   663  		if goarch == "arm64" {
   664  			return true
   665  		}
   666  	}
   667  	return false
   668  }
   669  
   670  // gentab records how to generate some trivial files.
   671  // Files listed here should also be listed in ../distpack/pack.go's srcArch.Remove list.
   672  var gentab = []struct {
   673  	pkg  string // Relative to $GOROOT/src
   674  	file string
   675  	gen  func(dir, file string)
   676  }{
   677  	{"cmd/go/internal/cfg", "zdefaultcc.go", mkzdefaultcc},
   678  	{"internal/runtime/sys", "zversion.go", mkzversion},
   679  	{"time/tzdata", "zzipdata.go", mktzdata},
   680  }
   681  
   682  func writeGeneratedFiles() {
   683  	xmkdirall(pathf("%s/pkg/include", goroot))
   684  	copyfile(pathf("%s/pkg/include/textflag.h", goroot),
   685  		pathf("%s/src/runtime/textflag.h", goroot), 0)
   686  	copyfile(pathf("%s/pkg/include/funcdata.h", goroot),
   687  		pathf("%s/src/runtime/funcdata.h", goroot), 0)
   688  	copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot),
   689  		pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0)
   690  	copyfile(pathf("%s/pkg/include/asm_amd64.h", goroot),
   691  		pathf("%s/src/runtime/asm_amd64.h", goroot), 0)
   692  	copyfile(pathf("%s/pkg/include/asm_riscv64.h", goroot),
   693  		pathf("%s/src/runtime/asm_riscv64.h", goroot), 0)
   694  	for _, gt := range gentab {
   695  		dir := pathf("%s/src/%s", goroot, gt.pkg)
   696  		gt.gen(dir, pathf("%s/%s", dir, gt.file))
   697  	}
   698  }
   699  
   700  // copyfile copies the file src to dst, via memory (so only good for small files).
   701  func copyfile(dst, src string, flag int) {
   702  	if vflag > 1 {
   703  		errprintf("cp %s %s\n", src, dst)
   704  	}
   705  	writefile(readfile(src), dst, flag)
   706  }
   707  
   708  func clean() {
   709  	generated := []byte(generatedHeader)
   710  
   711  	// Remove generated source files.
   712  	filepath.WalkDir(pathf("%s/src", goroot), func(path string, d fs.DirEntry, err error) error {
   713  		switch {
   714  		case err != nil:
   715  			// ignore
   716  		case d.IsDir() && (d.Name() == "vendor" || d.Name() == "testdata"):
   717  			return filepath.SkipDir
   718  		case d.IsDir() && d.Name() != "dist":
   719  			// Remove generated binary named for directory, but not dist out from under us.
   720  			exe := filepath.Join(path, d.Name())
   721  			if info, err := os.Stat(exe); err == nil && !info.IsDir() {
   722  				xremove(exe)
   723  			}
   724  			xremove(exe + ".exe")
   725  		case !d.IsDir() && strings.HasPrefix(d.Name(), "z"):
   726  			// Remove generated file, identified by marker string.
   727  			head := make([]byte, 512)
   728  			if f, err := os.Open(path); err == nil {
   729  				io.ReadFull(f, head)
   730  				f.Close()
   731  			}
   732  			if bytes.HasPrefix(head, generated) {
   733  				xremove(path)
   734  			}
   735  		}
   736  		return nil
   737  	})
   738  
   739  	if rebuildall {
   740  		// Remove object tree.
   741  		xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
   742  
   743  		// Remove installed packages and tools.
   744  		xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
   745  		xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
   746  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch))
   747  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch))
   748  		xremoveall(tooldir)
   749  
   750  		// Remove cached version info.
   751  		xremove(pathf("%s/VERSION.cache", goroot))
   752  
   753  		// Remove distribution packages.
   754  		xremoveall(pathf("%s/pkg/distpack", goroot))
   755  	}
   756  }
   757  
   758  /*
   759   * command implementations
   760   */
   761  
   762  // The env command prints the default environment.
   763  func cmdenv() {
   764  	path := flag.Bool("p", false, "emit updated PATH")
   765  	plan9 := flag.Bool("9", gohostos == "plan9", "emit plan 9 syntax")
   766  	windows := flag.Bool("w", gohostos == "windows", "emit windows syntax")
   767  	xflagparse(0)
   768  
   769  	format := "%s=\"%s\";\n" // Include ; to separate variables when 'dist env' output is used with eval.
   770  	switch {
   771  	case *plan9:
   772  		format = "%s='%s'\n"
   773  	case *windows:
   774  		format = "set %s=%s\r\n"
   775  	}
   776  
   777  	xprintf(format, "GO111MODULE", "")
   778  	xprintf(format, "GOARCH", goarch)
   779  	xprintf(format, "GOBIN", gorootBin)
   780  	xprintf(format, "GODEBUG", os.Getenv("GODEBUG"))
   781  	xprintf(format, "GOENV", "off")
   782  	xprintf(format, "GOFLAGS", "")
   783  	xprintf(format, "GOHOSTARCH", gohostarch)
   784  	xprintf(format, "GOHOSTOS", gohostos)
   785  	xprintf(format, "GOOS", goos)
   786  	xprintf(format, "GOPROXY", os.Getenv("GOPROXY"))
   787  	xprintf(format, "GOROOT", goroot)
   788  	xprintf(format, "GOTMPDIR", os.Getenv("GOTMPDIR"))
   789  	xprintf(format, "GOTOOLDIR", tooldir)
   790  	if goarch == "arm" {
   791  		xprintf(format, "GOARM", goarm)
   792  	}
   793  	if goarch == "arm64" {
   794  		xprintf(format, "GOARM64", goarm64)
   795  	}
   796  	if goarch == "386" {
   797  		xprintf(format, "GO386", go386)
   798  	}
   799  	if goarch == "amd64" {
   800  		xprintf(format, "GOAMD64", goamd64)
   801  	}
   802  	if goarch == "mips" || goarch == "mipsle" {
   803  		xprintf(format, "GOMIPS", gomips)
   804  	}
   805  	if goarch == "mips64" || goarch == "mips64le" {
   806  		xprintf(format, "GOMIPS64", gomips64)
   807  	}
   808  	if goarch == "ppc64" || goarch == "ppc64le" {
   809  		xprintf(format, "GOPPC64", goppc64)
   810  	}
   811  	if goarch == "riscv64" {
   812  		xprintf(format, "GORISCV64", goriscv64)
   813  	}
   814  	xprintf(format, "GOWORK", "off")
   815  
   816  	if *path {
   817  		sep := ":"
   818  		if gohostos == "windows" {
   819  			sep = ";"
   820  		}
   821  		xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gorootBin, sep, os.Getenv("PATH")))
   822  
   823  		// Also include $DIST_UNMODIFIED_PATH with the original $PATH
   824  		// for the internal needs of "dist banner", along with export
   825  		// so that it reaches the dist process. See its comment below.
   826  		var exportFormat string
   827  		if !*windows && !*plan9 {
   828  			exportFormat = "export " + format
   829  		} else {
   830  			exportFormat = format
   831  		}
   832  		xprintf(exportFormat, "DIST_UNMODIFIED_PATH", os.Getenv("PATH"))
   833  	}
   834  }
   835  
   836  var (
   837  	timeLogEnabled = os.Getenv("GOBUILDTIMELOGFILE") != ""
   838  	timeLogMu      sync.Mutex
   839  	timeLogFile    *os.File
   840  	timeLogStart   time.Time
   841  )
   842  
   843  func timelog(op, name string) {
   844  	if !timeLogEnabled {
   845  		return
   846  	}
   847  	timeLogMu.Lock()
   848  	defer timeLogMu.Unlock()
   849  	if timeLogFile == nil {
   850  		f, err := os.OpenFile(os.Getenv("GOBUILDTIMELOGFILE"), os.O_RDWR|os.O_APPEND, 0666)
   851  		if err != nil {
   852  			log.Fatal(err)
   853  		}
   854  		buf := make([]byte, 100)
   855  		n, _ := f.Read(buf)
   856  		s := string(buf[:n])
   857  		if i := strings.Index(s, "\n"); i >= 0 {
   858  			s = s[:i]
   859  		}
   860  		i := strings.Index(s, " start")
   861  		if i < 0 {
   862  			log.Fatalf("time log %s does not begin with start line", os.Getenv("GOBUILDTIMELOGFILE"))
   863  		}
   864  		t, err := time.Parse(time.UnixDate, s[:i])
   865  		if err != nil {
   866  			log.Fatalf("cannot parse time log line %q: %v", s, err)
   867  		}
   868  		timeLogStart = t
   869  		timeLogFile = f
   870  	}
   871  	t := time.Now()
   872  	fmt.Fprintf(timeLogFile, "%s %+.1fs %s %s\n", t.Format(time.UnixDate), t.Sub(timeLogStart).Seconds(), op, name)
   873  }
   874  
   875  // toolenv returns the environment to use when building commands in cmd.
   876  //
   877  // This is a function instead of a variable because the exact toolenv depends
   878  // on the GOOS and GOARCH, and (at least for now) those are modified in place
   879  // to switch between the host and target configurations when cross-compiling.
   880  func toolenv() []string {
   881  	var env []string
   882  	if !mustLinkExternal(goos, goarch, false) {
   883  		// Unless the platform requires external linking,
   884  		// we disable cgo to get static binaries for cmd/go and cmd/pprof,
   885  		// so that they work on systems without the same dynamic libraries
   886  		// as the original build system.
   887  		env = append(env, "CGO_ENABLED=0")
   888  	}
   889  	if isRelease || os.Getenv("GO_BUILDER_NAME") != "" {
   890  		// Add -trimpath for reproducible builds of releases.
   891  		// Include builders so that -trimpath is well-tested ahead of releases.
   892  		// Do not include local development, so that people working in the
   893  		// main branch for day-to-day work on the Go toolchain itself can
   894  		// still have full paths for stack traces for compiler crashes and the like.
   895  		env = append(env, "GOFLAGS=-trimpath -ldflags=-w -gcflags=cmd/...=-dwarf=false")
   896  	}
   897  	return env
   898  }
   899  
   900  var (
   901  	toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link", "cmd/preprofile"}
   902  
   903  	// Keep in sync with binExes in cmd/distpack/pack.go.
   904  	binExesIncludedInDistpack = []string{"cmd/go", "cmd/gofmt"}
   905  
   906  	// Keep in sync with the filter in cmd/distpack/pack.go.
   907  	toolsIncludedInDistpack = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/cover", "cmd/export", "cmd/fix", "cmd/link", "cmd/preprofile", "cmd/vet"}
   908  
   909  	// We could install all tools in "cmd", but is unnecessary because we will
   910  	// remove them in distpack, so instead install the tools that will actually
   911  	// be included in distpack, which is a superset of toolchain. Not installing
   912  	// the tools will help us test what happens when the tools aren't present.
   913  	toolsToInstall = slices.Concat(binExesIncludedInDistpack, toolsIncludedInDistpack)
   914  )
   915  
   916  // The bootstrap command runs a build from scratch,
   917  // stopping at having installed the go_bootstrap command.
   918  //
   919  // WARNING: This command runs after cmd/dist is built with the Go bootstrap toolchain.
   920  // It rebuilds and installs cmd/dist with the new toolchain, so other
   921  // commands (like "go tool dist test" in run.bash) can rely on bug fixes
   922  // made since the Go bootstrap version, but this function cannot.
   923  func cmdbootstrap() {
   924  	timelog("start", "dist bootstrap")
   925  	defer timelog("end", "dist bootstrap")
   926  
   927  	var debug, distpack, force, noBanner, noClean bool
   928  	flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
   929  	flag.BoolVar(&debug, "d", debug, "enable debugging of bootstrap process")
   930  	flag.StringVar(&debugTrace, "debug-trace", debugTrace, "write a merged trace of the toolchain builds to `file`")
   931  	flag.BoolVar(&distpack, "distpack", distpack, "write distribution files to pkg/distpack")
   932  	flag.BoolVar(&force, "force", force, "build even if the port is marked as broken")
   933  	flag.BoolVar(&noBanner, "no-banner", noBanner, "do not print banner")
   934  	flag.BoolVar(&noClean, "no-clean", noClean, "print deprecation warning")
   935  
   936  	xflagparse(0)
   937  
   938  	distSpan := startSpan("dist bootstrap")
   939  
   940  	if noClean {
   941  		xprintf("warning: --no-clean is deprecated and has no effect; use 'go install std cmd' instead\n")
   942  	}
   943  
   944  	// Don't build broken ports by default.
   945  	if broken[goos+"/"+goarch] && !force {
   946  		fatalf("build stopped because the port %s/%s is marked as broken\n\n"+
   947  			"Use the -force flag to build anyway.\n", goos, goarch)
   948  	}
   949  
   950  	// Set GOPATH to an internal directory. We shouldn't actually
   951  	// need to store files here, since the toolchain won't
   952  	// depend on modules outside of vendor directories, but if
   953  	// GOPATH points somewhere else (e.g., to GOROOT), the
   954  	// go tool may complain.
   955  	os.Setenv("GOPATH", pathf("%s/pkg/obj/gopath", goroot))
   956  
   957  	// Set GOPROXY=off to avoid downloading modules to the modcache in
   958  	// the GOPATH set above to be inside GOROOT. The modcache is read
   959  	// only so if we downloaded to the modcache, we'd create readonly
   960  	// files in GOROOT, which is undesirable. See #67463)
   961  	os.Setenv("GOPROXY", "off")
   962  
   963  	// Use a build cache separate from the default user one.
   964  	// Also one that will be wiped out during startup, so that
   965  	// make.bash really does start from a clean slate.
   966  	oldgocache = os.Getenv("GOCACHE")
   967  	os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot))
   968  
   969  	// Disable GOEXPERIMENT when building toolchain1 and
   970  	// go_bootstrap. We don't need any experiments for the
   971  	// bootstrap toolchain, and this lets us avoid duplicating the
   972  	// GOEXPERIMENT-related build logic from cmd/go here. If the
   973  	// bootstrap toolchain is < Go 1.17, it will ignore this
   974  	// anyway since GOEXPERIMENT is baked in; otherwise it will
   975  	// pick it up from the environment we set here. Once we're
   976  	// using toolchain1 with dist as the build system, we need to
   977  	// override this to keep the experiments assumed by the
   978  	// toolchain and by dist consistent. Once go_bootstrap takes
   979  	// over the build process, we'll set this back to the original
   980  	// GOEXPERIMENT.
   981  	os.Setenv("GOEXPERIMENT", "none")
   982  
   983  	if isdir(pathf("%s/src/pkg", goroot)) {
   984  		fatalf("\n\n"+
   985  			"The Go package sources have moved to $GOROOT/src.\n"+
   986  			"*** %s still exists. ***\n"+
   987  			"It probably contains stale files that may confuse the build.\n"+
   988  			"Please (check what's there and) remove it and try again.\n"+
   989  			"See https://golang.org/s/go14nopkg\n",
   990  			pathf("%s/src/pkg", goroot))
   991  	}
   992  
   993  	setupSpan := startSpan("setup")
   994  	if rebuildall {
   995  		clean()
   996  	}
   997  
   998  	setup()
   999  	writeGeneratedFiles()
  1000  
  1001  	timelog("build", "toolchain1 and go_bootstrap")
  1002  	checkCC()
  1003  	setupSpan.done()
  1004  	bootstrapSpan := startSpan("bootstrapBuildTools")
  1005  	bootstrapBuildTools()
  1006  	bootstrapSpan.done()
  1007  
  1008  	// Remember old content of $GOROOT/bin for comparison below.
  1009  	oldBinFiles, err := filepath.Glob(pathf("%s/bin/*", goroot))
  1010  	if err != nil {
  1011  		fatalf("glob: %v", err)
  1012  	}
  1013  
  1014  	// For the main bootstrap, building for host os/arch.
  1015  	oldgoos = goos
  1016  	oldgoarch = goarch
  1017  	goos = gohostos
  1018  	goarch = gohostarch
  1019  	os.Setenv("GOHOSTARCH", gohostarch)
  1020  	os.Setenv("GOHOSTOS", gohostos)
  1021  	os.Setenv("GOARCH", goarch)
  1022  	os.Setenv("GOOS", goos)
  1023  
  1024  	gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now
  1025  	setNoOpt()
  1026  	goldflags = os.Getenv("GO_LDFLAGS") // we were using $BOOT_GO_LDFLAGS until now
  1027  	goBootstrap := pathf("%s/go_bootstrap", tooldir)
  1028  	if debug {
  1029  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1030  		copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec)
  1031  	}
  1032  
  1033  	// To recap, so far we have built the new toolchain
  1034  	// (cmd/asm, cmd/cgo, cmd/compile, cmd/link, cmd/preprofile)
  1035  	// and the new go command (as go_bootstrap)
  1036  	// using the Go bootstrap toolchain and its go command.
  1037  	//
  1038  	//	toolchain1 = mk(new toolchain, bootstrap toolchain, bootstrap cmd/go)  # go_bootstrap is cmd/go copied from toolchain1
  1039  	//
  1040  	// The toolchain1 we built earlier is built from the new sources,
  1041  	// but because it was built using cmd/go it has no build IDs.
  1042  	// The eventually installed toolchain needs build IDs, so we need
  1043  	// to do another round:
  1044  	//
  1045  	//	toolchain2 = mk(new toolchain, toolchain1, go_bootstrap)
  1046  	//
  1047  	timelog("build", "toolchain2")
  1048  	if vflag > 0 {
  1049  		xprintf("\n")
  1050  	}
  1051  	xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
  1052  	os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch))
  1053  	// Now that cmd/go is in charge of the build process, enable GOEXPERIMENT.
  1054  	os.Setenv("GOEXPERIMENT", goexperiment)
  1055  	toolchain2Span := startSpan("toolchain2")
  1056  	goInstall(toolenv(), goBootstrap, append(maybeTraceFlag("toolchain2"), toolchain...)...)
  1057  	toolchain2Span.done()
  1058  	if debug {
  1059  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1060  		copyfile(pathf("%s/compile2", tooldir), pathf("%s/compile", tooldir), writeExec)
  1061  	}
  1062  
  1063  	// Toolchain2 should be semantically equivalent to toolchain1,
  1064  	// but it was built using the newly built compiler instead of the Go bootstrap compiler,
  1065  	// so it should at the least run faster. Also, toolchain1 had no build IDs
  1066  	// in the binaries, while toolchain2 does. In non-release builds, the
  1067  	// toolchain's build IDs feed into constructing the build IDs of built targets,
  1068  	// so in non-release builds, everything now looks out-of-date due to
  1069  	// toolchain2 having build IDs - that is, due to the go command seeing
  1070  	// that there are new compilers. In release builds, the toolchain's reported
  1071  	// version is used in place of the build ID, and the go command does not
  1072  	// see that change from toolchain1 to toolchain2, so in release builds,
  1073  	// nothing looks out of date.
  1074  	// To keep the behavior the same in both non-release and release builds,
  1075  	// we force-install everything here.
  1076  	//
  1077  	//	toolchain3 = mk(new toolchain, toolchain2, go_bootstrap)
  1078  	//
  1079  	timelog("build", "toolchain3")
  1080  	if vflag > 0 {
  1081  		xprintf("\n")
  1082  	}
  1083  	xprintf("Building Go toolchain3 and commands using go_bootstrap and Go toolchain2.\n")
  1084  	toolchain3Span := startSpan("toolchain3")
  1085  	goInstall(toolenv(), goBootstrap, append(append(maybeTraceFlag("toolchain3"), "-a"), toolsToInstall...)...)
  1086  	toolchain3Span.done()
  1087  	if debug {
  1088  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1089  		copyfile(pathf("%s/compile3", tooldir), pathf("%s/compile", tooldir), writeExec)
  1090  	}
  1091  
  1092  	// If goexperiment == "", so that the first compiler was semantically
  1093  	// identical to the second compiler, toolchain3 has converged and can
  1094  	// be used as the final toolchain (or the final host toolchain in the
  1095  	// case of a cross compile). Otherwise we need to do one more build.
  1096  	if goexperiment != "" {
  1097  		xprintf("Building commands for GOEXPERIMENT=%s convergence for %s/%s.\n", goexperiment, goos, goarch)
  1098  		convergenceSpan := startSpan("GOEXPERIMENT convergence")
  1099  		goInstall(toolenv(), goBootstrap, append(append(maybeTraceFlag("toolchainGOEXPRIMENT"), "-a"), toolsToInstall...)...)
  1100  		convergenceSpan.done()
  1101  		if debug {
  1102  			run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1103  			copyfile(pathf("%s/compile3goexp", tooldir), pathf("%s/compile", tooldir), writeExec)
  1104  		}
  1105  	}
  1106  
  1107  	if goos == oldgoos && goarch == oldgoarch {
  1108  		// Common case - not setting up for cross-compilation.
  1109  		timelog("build", "toolchain")
  1110  		if vflag > 0 {
  1111  			xprintf("\n")
  1112  		}
  1113  		xprintf("Checking command staleness for %s/%s.\n", goos, goarch)
  1114  	} else {
  1115  		// GOOS/GOARCH does not match GOHOSTOS/GOHOSTARCH.
  1116  		// Check the GOHOSTOS/GOHOSTARCH build and then
  1117  		// run GOOS/GOARCH installation.
  1118  		timelog("build", "host toolchain")
  1119  		if vflag > 0 {
  1120  			xprintf("\n")
  1121  		}
  1122  		xprintf("Checking command staleness for host, %s/%s.\n", goos, goarch)
  1123  		hostStaleSpan := startSpan("host staleness checks")
  1124  		checkNotStale(toolenv(), goBootstrap, toolsToInstall...)
  1125  		checkNotStale(toolenv(), gorootBinGo, toolsToInstall...)
  1126  		hostStaleSpan.done()
  1127  
  1128  		timelog("build", "target toolchain")
  1129  		if vflag > 0 {
  1130  			xprintf("\n")
  1131  		}
  1132  		goos = oldgoos
  1133  		goarch = oldgoarch
  1134  		os.Setenv("GOOS", goos)
  1135  		os.Setenv("GOARCH", goarch)
  1136  		os.Setenv("CC", compilerEnvLookup("CC", defaultcc, goos, goarch))
  1137  		xprintf("Building commands for target, %s/%s.\n", goos, goarch)
  1138  		targetSpan := startSpan("target toolchain")
  1139  		goInstall(toolenv(), goBootstrap, append(append(maybeTraceFlag("toolchainTarget"), "-a"), toolsToInstall...)...)
  1140  		targetSpan.done()
  1141  	}
  1142  
  1143  	staleSpan := startSpan("staleness checks")
  1144  	checkNotStale(toolenv(), goBootstrap, toolsToInstall...)
  1145  	checkNotStale(toolenv(), gorootBinGo, toolsToInstall...)
  1146  	staleSpan.done()
  1147  	if debug {
  1148  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  1149  		checkNotStale(toolenv(), goBootstrap, toolchain...)
  1150  		copyfile(pathf("%s/compile4", tooldir), pathf("%s/compile", tooldir), writeExec)
  1151  	}
  1152  
  1153  	// Check that there are no new files in $GOROOT/bin other than
  1154  	// go and gofmt and $GOOS_$GOARCH (target bin when cross-compiling).
  1155  	binFiles, err := filepath.Glob(pathf("%s/bin/*", goroot))
  1156  	if err != nil {
  1157  		fatalf("glob: %v", err)
  1158  	}
  1159  
  1160  	ok := map[string]bool{}
  1161  	for _, f := range oldBinFiles {
  1162  		ok[f] = true
  1163  	}
  1164  	for _, f := range binFiles {
  1165  		if gohostos == "darwin" && filepath.Base(f) == ".DS_Store" {
  1166  			continue // unfortunate but not unexpected
  1167  		}
  1168  		elem := strings.TrimSuffix(filepath.Base(f), ".exe")
  1169  		if !ok[f] && elem != "go" && elem != "gofmt" && elem != goos+"_"+goarch {
  1170  			fatalf("unexpected new file in $GOROOT/bin: %s", elem)
  1171  		}
  1172  	}
  1173  
  1174  	// Remove go_bootstrap now that we're done.
  1175  	xremove(pathf("%s/go_bootstrap"+exe, tooldir))
  1176  
  1177  	if goos == "android" {
  1178  		// Make sure the exec wrapper will sync a fresh $GOROOT to the device.
  1179  		xremove(pathf("%s/go_android_exec-adb-sync-status", os.TempDir()))
  1180  	}
  1181  
  1182  	if wrapperPath := wrapperPathFor(goos, goarch); wrapperPath != "" {
  1183  		oldcc := os.Getenv("CC")
  1184  		os.Setenv("GOOS", gohostos)
  1185  		os.Setenv("GOARCH", gohostarch)
  1186  		os.Setenv("CC", compilerEnvLookup("CC", defaultcc, gohostos, gohostarch))
  1187  		goCmd(nil, gorootBinGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gorootBin, goos, goarch, exe), wrapperPath)
  1188  		// Restore environment.
  1189  		// TODO(elias.naur): support environment variables in goCmd?
  1190  		os.Setenv("GOOS", goos)
  1191  		os.Setenv("GOARCH", goarch)
  1192  		os.Setenv("CC", oldcc)
  1193  	}
  1194  
  1195  	if distpack {
  1196  		xprintf("Packaging archives for %s/%s.\n", goos, goarch)
  1197  		distpackSpan := startSpan("distpack")
  1198  		run("", ShowOutput|CheckExit, gorootBinGo, "tool", "distpack")
  1199  		distpackSpan.done()
  1200  	}
  1201  
  1202  	// Print trailing banner unless instructed otherwise.
  1203  	if !noBanner {
  1204  		banner()
  1205  	}
  1206  
  1207  	distSpan.done()
  1208  	if debugTrace != "" {
  1209  		writeTrace()
  1210  	}
  1211  }
  1212  
  1213  func wrapperPathFor(goos, goarch string) string {
  1214  	switch {
  1215  	case goos == "android":
  1216  		if gohostos != "android" {
  1217  			return pathf("%s/misc/go_android_exec/main.go", goroot)
  1218  		}
  1219  	case goos == "ios":
  1220  		if gohostos != "ios" {
  1221  			return pathf("%s/misc/ios/go_ios_exec.go", goroot)
  1222  		}
  1223  	}
  1224  	return ""
  1225  }
  1226  
  1227  func goInstall(env []string, goBinary string, args ...string) {
  1228  	goCmd(env, goBinary, "install", args...)
  1229  }
  1230  
  1231  func appendCompilerFlags(args []string) []string {
  1232  	if gogcflags != "" {
  1233  		args = append(args, "-gcflags=all="+gogcflags)
  1234  	}
  1235  	if goldflags != "" {
  1236  		args = append(args, "-ldflags=all="+goldflags)
  1237  	}
  1238  	return args
  1239  }
  1240  
  1241  func goCmd(env []string, goBinary string, cmd string, args ...string) {
  1242  	goCmd := []string{goBinary, cmd}
  1243  	if noOpt {
  1244  		goCmd = append(goCmd, "-tags=noopt")
  1245  	}
  1246  	goCmd = appendCompilerFlags(goCmd)
  1247  	if vflag > 0 {
  1248  		goCmd = append(goCmd, "-v")
  1249  	}
  1250  
  1251  	// Force only one process at a time on vx32 emulation.
  1252  	if gohostos == "plan9" && os.Getenv("sysname") == "vx32" {
  1253  		goCmd = append(goCmd, "-p=1")
  1254  	}
  1255  
  1256  	runEnv(workdir, ShowOutput|CheckExit, env, append(goCmd, args...)...)
  1257  }
  1258  
  1259  func checkNotStale(env []string, goBinary string, targets ...string) {
  1260  	goCmd := []string{goBinary, "list"}
  1261  	if noOpt {
  1262  		goCmd = append(goCmd, "-tags=noopt")
  1263  	}
  1264  	goCmd = appendCompilerFlags(goCmd)
  1265  	goCmd = append(goCmd, "-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}")
  1266  
  1267  	out := runEnv(workdir, CheckExit, env, append(goCmd, targets...)...)
  1268  	if strings.Contains(out, "\tSTALE ") {
  1269  		os.Setenv("GODEBUG", "gocachehash=1")
  1270  		for _, target := range []string{"internal/runtime/sys", "cmd/dist", "cmd/link"} {
  1271  			if strings.Contains(out, "STALE "+target) {
  1272  				run(workdir, ShowOutput|CheckExit, goBinary, "list", "-f={{.ImportPath}} {{.Stale}}", target)
  1273  				break
  1274  			}
  1275  		}
  1276  		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)
  1277  	}
  1278  }
  1279  
  1280  // Cannot use go/build directly because cmd/dist for a new release
  1281  // builds against an old release's go/build, which may be out of sync.
  1282  // To reduce duplication, we generate the list for go/build from this.
  1283  //
  1284  // We list all supported platforms in this list, so that this is the
  1285  // single point of truth for supported platforms. This list is used
  1286  // by 'go tool dist list'.
  1287  var cgoEnabled = map[string]bool{
  1288  	"aix/ppc64":       true,
  1289  	"darwin/amd64":    true,
  1290  	"darwin/arm64":    true,
  1291  	"dragonfly/amd64": true,
  1292  	"freebsd/386":     true,
  1293  	"freebsd/amd64":   true,
  1294  	"freebsd/arm":     true,
  1295  	"freebsd/arm64":   true,
  1296  	"freebsd/riscv64": true,
  1297  	"illumos/amd64":   true,
  1298  	"linux/386":       true,
  1299  	"linux/amd64":     true,
  1300  	"linux/arm":       true,
  1301  	"linux/arm64":     true,
  1302  	"linux/loong64":   true,
  1303  	"linux/ppc64":     true,
  1304  	"linux/ppc64le":   true,
  1305  	"linux/mips":      true,
  1306  	"linux/mipsle":    true,
  1307  	"linux/mips64":    true,
  1308  	"linux/mips64le":  true,
  1309  	"linux/riscv64":   true,
  1310  	"linux/s390x":     true,
  1311  	"linux/sparc64":   true,
  1312  	"android/386":     true,
  1313  	"android/amd64":   true,
  1314  	"android/arm":     true,
  1315  	"android/arm64":   true,
  1316  	"ios/arm64":       true,
  1317  	"ios/amd64":       true,
  1318  	"js/wasm":         false,
  1319  	"wasip1/wasm":     false,
  1320  	"netbsd/386":      true,
  1321  	"netbsd/amd64":    true,
  1322  	"netbsd/arm":      true,
  1323  	"netbsd/arm64":    true,
  1324  	"openbsd/386":     true,
  1325  	"openbsd/amd64":   true,
  1326  	"openbsd/arm":     true,
  1327  	"openbsd/arm64":   true,
  1328  	"openbsd/ppc64":   false,
  1329  	"openbsd/riscv64": true,
  1330  	"plan9/386":       false,
  1331  	"plan9/amd64":     false,
  1332  	"plan9/arm":       false,
  1333  	"solaris/amd64":   true,
  1334  	"windows/386":     true,
  1335  	"windows/amd64":   true,
  1336  	"windows/arm64":   true,
  1337  }
  1338  
  1339  // List of platforms that are marked as broken ports.
  1340  // These require -force flag to build, and also
  1341  // get filtered out of cgoEnabled for 'dist list'.
  1342  // See go.dev/issue/56679.
  1343  var broken = map[string]bool{
  1344  	"freebsd/riscv64": true, // Broken: go.dev/issue/76475.
  1345  	"linux/sparc64":   true, // An incomplete port. See CL 132155.
  1346  }
  1347  
  1348  // List of platforms which are first class ports. See go.dev/issue/38874.
  1349  var firstClass = map[string]bool{
  1350  	"darwin/amd64":  true,
  1351  	"darwin/arm64":  true,
  1352  	"linux/386":     true,
  1353  	"linux/amd64":   true,
  1354  	"linux/arm":     true,
  1355  	"linux/arm64":   true,
  1356  	"windows/386":   true,
  1357  	"windows/amd64": true,
  1358  }
  1359  
  1360  // We only need CC if cgo is forced on, or if the platform requires external linking.
  1361  // Otherwise the go command will automatically disable it.
  1362  func needCC() bool {
  1363  	return os.Getenv("CGO_ENABLED") == "1" || mustLinkExternal(gohostos, gohostarch, false)
  1364  }
  1365  
  1366  func checkCC() {
  1367  	if !needCC() {
  1368  		return
  1369  	}
  1370  	cc1 := defaultcc[""]
  1371  	if cc1 == "" {
  1372  		cc1 = "gcc"
  1373  		for _, os := range clangos {
  1374  			if gohostos == os {
  1375  				cc1 = "clang"
  1376  				break
  1377  			}
  1378  		}
  1379  	}
  1380  	cc, err := quotedSplit(cc1)
  1381  	if err != nil {
  1382  		fatalf("split CC: %v", err)
  1383  	}
  1384  	var ccHelp = append(cc, "--help")
  1385  
  1386  	if output, err := exec.Command(ccHelp[0], ccHelp[1:]...).CombinedOutput(); err != nil {
  1387  		outputHdr := ""
  1388  		if len(output) > 0 {
  1389  			outputHdr = "\nCommand output:\n\n"
  1390  		}
  1391  		fatalf("cannot invoke C compiler %q: %v\n\n"+
  1392  			"Go needs a system C compiler for use with cgo.\n"+
  1393  			"To set a C compiler, set CC=the-compiler.\n"+
  1394  			"To disable cgo, set CGO_ENABLED=0.\n%s%s", cc, err, outputHdr, output)
  1395  	}
  1396  }
  1397  
  1398  // Clean deletes temporary objects.
  1399  func cmdclean() {
  1400  	xflagparse(0)
  1401  	clean()
  1402  }
  1403  
  1404  // Banner prints the 'now you've installed Go' banner.
  1405  func cmdbanner() {
  1406  	xflagparse(0)
  1407  	banner()
  1408  }
  1409  
  1410  func banner() {
  1411  	if vflag > 0 {
  1412  		xprintf("\n")
  1413  	}
  1414  	xprintf("---\n")
  1415  	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
  1416  	xprintf("Installed commands in %s\n", gorootBin)
  1417  
  1418  	if gohostos == "plan9" {
  1419  		// Check that GOROOT/bin is bound before /bin.
  1420  		pid := strings.ReplaceAll(readfile("#c/pid"), " ", "")
  1421  		ns := fmt.Sprintf("/proc/%s/ns", pid)
  1422  		if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gorootBin)) {
  1423  			xprintf("*** You need to bind %s before /bin.\n", gorootBin)
  1424  		}
  1425  	} else {
  1426  		// Check that GOROOT/bin appears in $PATH.
  1427  		pathsep := ":"
  1428  		if gohostos == "windows" {
  1429  			pathsep = ";"
  1430  		}
  1431  		path := os.Getenv("PATH")
  1432  		if p, ok := os.LookupEnv("DIST_UNMODIFIED_PATH"); ok {
  1433  			// Scripts that modify $PATH and then run dist should also provide
  1434  			// dist with an unmodified copy of $PATH via $DIST_UNMODIFIED_PATH.
  1435  			// Use it here when determining if the user still needs to update
  1436  			// their $PATH. See go.dev/issue/42563.
  1437  			path = p
  1438  		}
  1439  		if !strings.Contains(pathsep+path+pathsep, pathsep+gorootBin+pathsep) {
  1440  			xprintf("*** You need to add %s to your PATH.\n", gorootBin)
  1441  		}
  1442  	}
  1443  }
  1444  
  1445  // Version prints the Go version.
  1446  func cmdversion() {
  1447  	xflagparse(0)
  1448  	xprintf("%s\n", findgoversion())
  1449  }
  1450  
  1451  // cmdlist lists all supported platforms.
  1452  func cmdlist() {
  1453  	jsonFlag := flag.Bool("json", false, "produce JSON output")
  1454  	brokenFlag := flag.Bool("broken", false, "include broken ports")
  1455  	xflagparse(0)
  1456  
  1457  	var plats []string
  1458  	for p := range cgoEnabled {
  1459  		if broken[p] && !*brokenFlag {
  1460  			continue
  1461  		}
  1462  		plats = append(plats, p)
  1463  	}
  1464  	sort.Strings(plats)
  1465  
  1466  	if !*jsonFlag {
  1467  		for _, p := range plats {
  1468  			xprintf("%s\n", p)
  1469  		}
  1470  		return
  1471  	}
  1472  
  1473  	type jsonResult struct {
  1474  		GOOS         string
  1475  		GOARCH       string
  1476  		CgoSupported bool
  1477  		FirstClass   bool
  1478  		Broken       bool `json:",omitempty"`
  1479  	}
  1480  	var results []jsonResult
  1481  	for _, p := range plats {
  1482  		fields := strings.Split(p, "/")
  1483  		results = append(results, jsonResult{
  1484  			GOOS:         fields[0],
  1485  			GOARCH:       fields[1],
  1486  			CgoSupported: cgoEnabled[p],
  1487  			FirstClass:   firstClass[p],
  1488  			Broken:       broken[p],
  1489  		})
  1490  	}
  1491  	out, err := json.MarshalIndent(results, "", "\t")
  1492  	if err != nil {
  1493  		fatalf("json marshal error: %v", err)
  1494  	}
  1495  	if _, err := os.Stdout.Write(out); err != nil {
  1496  		fatalf("write failed: %v", err)
  1497  	}
  1498  }
  1499  
  1500  func setNoOpt() {
  1501  	for gcflag := range strings.SplitSeq(gogcflags, " ") {
  1502  		if gcflag == "-N" || gcflag == "-l" {
  1503  			noOpt = true
  1504  			break
  1505  		}
  1506  	}
  1507  }
  1508  

View as plain text