github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/cmd/dist/build.go (about)

     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  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  // Initialization for any invocation.
    21  
    22  // The usual variables.
    23  var (
    24  	goarch           string
    25  	gobin            string
    26  	gohostarch       string
    27  	gohostos         string
    28  	goos             string
    29  	goarm            string
    30  	go386            string
    31  	goroot           string
    32  	goroot_final     string
    33  	goextlinkenabled string
    34  	gogcflags        string // For running built compiler
    35  	workdir          string
    36  	tooldir          string
    37  	oldgoos          string
    38  	oldgoarch        string
    39  	slash            string
    40  	exe              string
    41  	defaultcc        string
    42  	defaultcflags    string
    43  	defaultldflags   string
    44  	defaultcxxtarget string
    45  	defaultcctarget  string
    46  	rebuildall       bool
    47  	defaultclang     bool
    48  
    49  	vflag int // verbosity
    50  )
    51  
    52  // The known architectures.
    53  var okgoarch = []string{
    54  	"386",
    55  	"amd64",
    56  	"amd64p32",
    57  	"arm",
    58  	"arm64",
    59  	"mips64",
    60  	"mips64le",
    61  	"ppc64",
    62  	"ppc64le",
    63  	"s390x",
    64  	"sparc64",
    65  }
    66  
    67  // The known operating systems.
    68  var okgoos = []string{
    69  	"darwin",
    70  	"dragonfly",
    71  	"linux",
    72  	"android",
    73  	"solaris",
    74  	"freebsd",
    75  	"nacl",
    76  	"netbsd",
    77  	"openbsd",
    78  	"plan9",
    79  	"windows",
    80  }
    81  
    82  // find reports the first index of p in l[0:n], or else -1.
    83  func find(p string, l []string) int {
    84  	for i, s := range l {
    85  		if p == s {
    86  			return i
    87  		}
    88  	}
    89  	return -1
    90  }
    91  
    92  // xinit handles initialization of the various global state, like goroot and goarch.
    93  func xinit() {
    94  	goroot = os.Getenv("GOROOT")
    95  	if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 {
    96  		// if not "/" or "c:\", then strip trailing path separator
    97  		goroot = strings.TrimSuffix(goroot, slash)
    98  	}
    99  	if goroot == "" {
   100  		fatal("$GOROOT must be set")
   101  	}
   102  
   103  	goroot_final = os.Getenv("GOROOT_FINAL")
   104  	if goroot_final == "" {
   105  		goroot_final = goroot
   106  	}
   107  
   108  	b := os.Getenv("GOBIN")
   109  	if b == "" {
   110  		b = goroot + slash + "bin"
   111  	}
   112  	gobin = b
   113  
   114  	b = os.Getenv("GOOS")
   115  	if b == "" {
   116  		b = gohostos
   117  	}
   118  	goos = b
   119  	if find(goos, okgoos) < 0 {
   120  		fatal("unknown $GOOS %s", goos)
   121  	}
   122  
   123  	b = os.Getenv("GOARM")
   124  	if b == "" {
   125  		b = xgetgoarm()
   126  	}
   127  	goarm = b
   128  
   129  	b = os.Getenv("GO386")
   130  	if b == "" {
   131  		if cansse2() {
   132  			b = "sse2"
   133  		} else {
   134  			b = "387"
   135  		}
   136  	}
   137  	go386 = b
   138  
   139  	p := pathf("%s/src/all.bash", goroot)
   140  	if !isfile(p) {
   141  		fatal("$GOROOT is not set correctly or not exported\n"+
   142  			"\tGOROOT=%s\n"+
   143  			"\t%s does not exist", goroot, p)
   144  	}
   145  
   146  	b = os.Getenv("GOHOSTARCH")
   147  	if b != "" {
   148  		gohostarch = b
   149  	}
   150  
   151  	if find(gohostarch, okgoarch) < 0 {
   152  		fatal("unknown $GOHOSTARCH %s", gohostarch)
   153  	}
   154  
   155  	b = os.Getenv("GOARCH")
   156  	if b == "" {
   157  		b = gohostarch
   158  	}
   159  	goarch = b
   160  	if find(goarch, okgoarch) < 0 {
   161  		fatal("unknown $GOARCH %s", goarch)
   162  	}
   163  
   164  	b = os.Getenv("GO_EXTLINK_ENABLED")
   165  	if b != "" {
   166  		if b != "0" && b != "1" {
   167  			fatal("unknown $GO_EXTLINK_ENABLED %s", b)
   168  		}
   169  		goextlinkenabled = b
   170  	}
   171  
   172  	gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
   173  
   174  	b = os.Getenv("CC")
   175  	if b == "" {
   176  		// Use clang on OS X, because gcc is deprecated there.
   177  		// Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
   178  		// actually runs clang. We prepare different command
   179  		// lines for the two binaries, so it matters what we call it.
   180  		// See golang.org/issue/5822.
   181  		if defaultclang {
   182  			b = "clang"
   183  		} else {
   184  			b = "gcc"
   185  		}
   186  	}
   187  	defaultcc = b
   188  
   189  	defaultcflags = os.Getenv("CFLAGS")
   190  
   191  	defaultldflags = os.Getenv("LDFLAGS")
   192  
   193  	b = os.Getenv("CC_FOR_TARGET")
   194  	if b == "" {
   195  		b = defaultcc
   196  	}
   197  	defaultcctarget = b
   198  
   199  	b = os.Getenv("CXX_FOR_TARGET")
   200  	if b == "" {
   201  		b = os.Getenv("CXX")
   202  		if b == "" {
   203  			if defaultclang {
   204  				b = "clang++"
   205  			} else {
   206  				b = "g++"
   207  			}
   208  		}
   209  	}
   210  	defaultcxxtarget = b
   211  
   212  	// For tools being invoked but also for os.ExpandEnv.
   213  	os.Setenv("GO386", go386)
   214  	os.Setenv("GOARCH", goarch)
   215  	os.Setenv("GOARM", goarm)
   216  	os.Setenv("GOHOSTARCH", gohostarch)
   217  	os.Setenv("GOHOSTOS", gohostos)
   218  	os.Setenv("GOOS", goos)
   219  	os.Setenv("GOROOT", goroot)
   220  	os.Setenv("GOROOT_FINAL", goroot_final)
   221  
   222  	// Make the environment more predictable.
   223  	os.Setenv("LANG", "C")
   224  	os.Setenv("LANGUAGE", "en_US.UTF8")
   225  
   226  	workdir = xworkdir()
   227  	xatexit(rmworkdir)
   228  
   229  	tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
   230  }
   231  
   232  // rmworkdir deletes the work directory.
   233  func rmworkdir() {
   234  	if vflag > 1 {
   235  		errprintf("rm -rf %s\n", workdir)
   236  	}
   237  	xremoveall(workdir)
   238  }
   239  
   240  // Remove trailing spaces.
   241  func chomp(s string) string {
   242  	return strings.TrimRight(s, " \t\r\n")
   243  }
   244  
   245  func branchtag(branch string) (tag string, precise bool) {
   246  	b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch)
   247  	tag = branch
   248  	for _, line := range splitlines(b) {
   249  		// Each line is either blank, or looks like
   250  		//	  (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
   251  		// We need to find an element starting with refs/tags/.
   252  		i := strings.Index(line, " refs/tags/")
   253  		if i < 0 {
   254  			continue
   255  		}
   256  		i += len(" refs/tags/")
   257  		// The tag name ends at a comma or paren (prefer the first).
   258  		j := strings.Index(line[i:], ",")
   259  		if j < 0 {
   260  			j = strings.Index(line[i:], ")")
   261  		}
   262  		if j < 0 {
   263  			continue // malformed line; ignore it
   264  		}
   265  		tag = line[i : i+j]
   266  		if i == 0 {
   267  			precise = true // tag denotes HEAD
   268  		}
   269  		break
   270  	}
   271  	return
   272  }
   273  
   274  // findgoversion determines the Go version to use in the version string.
   275  func findgoversion() string {
   276  	// The $GOROOT/VERSION file takes priority, for distributions
   277  	// without the source repo.
   278  	path := pathf("%s/VERSION", goroot)
   279  	if isfile(path) {
   280  		b := chomp(readfile(path))
   281  		// Commands such as "dist version > VERSION" will cause
   282  		// the shell to create an empty VERSION file and set dist's
   283  		// stdout to its fd. dist in turn looks at VERSION and uses
   284  		// its content if available, which is empty at this point.
   285  		// Only use the VERSION file if it is non-empty.
   286  		if b != "" {
   287  			return b
   288  		}
   289  	}
   290  
   291  	// The $GOROOT/VERSION.cache file is a cache to avoid invoking
   292  	// git every time we run this command. Unlike VERSION, it gets
   293  	// deleted by the clean command.
   294  	path = pathf("%s/VERSION.cache", goroot)
   295  	if isfile(path) {
   296  		return chomp(readfile(path))
   297  	}
   298  
   299  	// Show a nicer error message if this isn't a Git repo.
   300  	if !isGitRepo() {
   301  		fatal("FAILED: not a Git repo; must put a VERSION file in $GOROOT")
   302  	}
   303  
   304  	// Otherwise, use Git.
   305  	// What is the current branch?
   306  	branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD"))
   307  
   308  	// What are the tags along the current branch?
   309  	tag := "devel"
   310  	precise := false
   311  
   312  	// If we're on a release branch, use the closest matching tag
   313  	// that is on the release branch (and not on the master branch).
   314  	if strings.HasPrefix(branch, "release-branch.") {
   315  		tag, precise = branchtag(branch)
   316  	}
   317  
   318  	if !precise {
   319  		// Tag does not point at HEAD; add hash and date to version.
   320  		tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD"))
   321  	}
   322  
   323  	// Cache version.
   324  	writefile(tag, path, 0)
   325  
   326  	return tag
   327  }
   328  
   329  // isGitRepo reports whether the working directory is inside a Git repository.
   330  func isGitRepo() bool {
   331  	// NB: simply checking the exit code of `git rev-parse --git-dir` would
   332  	// suffice here, but that requires deviating from the infrastructure
   333  	// provided by `run`.
   334  	gitDir := chomp(run(goroot, 0, "git", "rev-parse", "--git-dir"))
   335  	if !filepath.IsAbs(gitDir) {
   336  		gitDir = filepath.Join(goroot, gitDir)
   337  	}
   338  	fi, err := os.Stat(gitDir)
   339  	return err == nil && fi.IsDir()
   340  }
   341  
   342  /*
   343   * Initial tree setup.
   344   */
   345  
   346  // The old tools that no longer live in $GOBIN or $GOROOT/bin.
   347  var oldtool = []string{
   348  	"5a", "5c", "5g", "5l",
   349  	"6a", "6c", "6g", "6l",
   350  	"8a", "8c", "8g", "8l",
   351  	"9a", "9c", "9g", "9l",
   352  	"6cov",
   353  	"6nm",
   354  	"6prof",
   355  	"cgo",
   356  	"ebnflint",
   357  	"goapi",
   358  	"gofix",
   359  	"goinstall",
   360  	"gomake",
   361  	"gopack",
   362  	"gopprof",
   363  	"gotest",
   364  	"gotype",
   365  	"govet",
   366  	"goyacc",
   367  	"quietgcc",
   368  }
   369  
   370  // Unreleased directories (relative to $GOROOT) that should
   371  // not be in release branches.
   372  var unreleased = []string{
   373  	"src/cmd/newlink",
   374  	"src/cmd/objwriter",
   375  	"src/debug/goobj",
   376  	"src/old",
   377  }
   378  
   379  // setup sets up the tree for the initial build.
   380  func setup() {
   381  	// Create bin directory.
   382  	if p := pathf("%s/bin", goroot); !isdir(p) {
   383  		xmkdir(p)
   384  	}
   385  
   386  	// Create package directory.
   387  	if p := pathf("%s/pkg", goroot); !isdir(p) {
   388  		xmkdir(p)
   389  	}
   390  
   391  	p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
   392  	if rebuildall {
   393  		xremoveall(p)
   394  	}
   395  	xmkdirall(p)
   396  
   397  	if goos != gohostos || goarch != gohostarch {
   398  		p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
   399  		if rebuildall {
   400  			xremoveall(p)
   401  		}
   402  		xmkdirall(p)
   403  	}
   404  
   405  	// Create object directory.
   406  	// We keep it in pkg/ so that all the generated binaries
   407  	// are in one tree. If pkg/obj/libgc.a exists, it is a dreg from
   408  	// before we used subdirectories of obj. Delete all of obj
   409  	// to clean up.
   410  	if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) {
   411  		xremoveall(pathf("%s/pkg/obj", goroot))
   412  	}
   413  	p = pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)
   414  	if rebuildall {
   415  		xremoveall(p)
   416  	}
   417  	xmkdirall(p)
   418  
   419  	// Create tool directory.
   420  	// We keep it in pkg/, just like the object directory above.
   421  	if rebuildall {
   422  		xremoveall(tooldir)
   423  	}
   424  	xmkdirall(tooldir)
   425  
   426  	// Remove tool binaries from before the tool/gohostos_gohostarch
   427  	xremoveall(pathf("%s/bin/tool", goroot))
   428  
   429  	// Remove old pre-tool binaries.
   430  	for _, old := range oldtool {
   431  		xremove(pathf("%s/bin/%s", goroot, old))
   432  	}
   433  
   434  	// If $GOBIN is set and has a Go compiler, it must be cleaned.
   435  	for _, char := range "56789" {
   436  		if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
   437  			for _, old := range oldtool {
   438  				xremove(pathf("%s/%s", gobin, old))
   439  			}
   440  			break
   441  		}
   442  	}
   443  
   444  	// For release, make sure excluded things are excluded.
   445  	goversion := findgoversion()
   446  	if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
   447  		for _, dir := range unreleased {
   448  			if p := pathf("%s/%s", goroot, dir); isdir(p) {
   449  				fatal("%s should not exist in release build", p)
   450  			}
   451  		}
   452  	}
   453  }
   454  
   455  /*
   456   * Tool building
   457   */
   458  
   459  // deptab lists changes to the default dependencies for a given prefix.
   460  // deps ending in /* read the whole directory; deps beginning with -
   461  // exclude files with that prefix.
   462  var deptab = []struct {
   463  	prefix string   // prefix of target
   464  	dep    []string // dependency tweaks for targets with that prefix
   465  }{
   466  	{"cmd/go", []string{
   467  		"zdefaultcc.go",
   468  		"zosarch.go",
   469  	}},
   470  	{"runtime/internal/sys", []string{
   471  		"zversion.go",
   472  	}},
   473  	{"go/build", []string{
   474  		"zcgo.go",
   475  	}},
   476  }
   477  
   478  // depsuffix records the allowed suffixes for source files.
   479  var depsuffix = []string{
   480  	".s",
   481  	".go",
   482  }
   483  
   484  // gentab records how to generate some trivial files.
   485  var gentab = []struct {
   486  	nameprefix string
   487  	gen        func(string, string)
   488  }{
   489  	{"zdefaultcc.go", mkzdefaultcc},
   490  	{"zosarch.go", mkzosarch},
   491  	{"zversion.go", mkzversion},
   492  	{"zcgo.go", mkzcgo},
   493  
   494  	// not generated anymore, but delete the file if we see it
   495  	{"enam.c", nil},
   496  	{"anames5.c", nil},
   497  	{"anames6.c", nil},
   498  	{"anames8.c", nil},
   499  	{"anames9.c", nil},
   500  }
   501  
   502  // installed maps from a dir name (as given to install) to a chan
   503  // closed when the dir's package is installed.
   504  var installed = make(map[string]chan struct{})
   505  
   506  // install installs the library, package, or binary associated with dir,
   507  // which is relative to $GOROOT/src.
   508  func install(dir string) {
   509  	if ch, ok := installed[dir]; ok {
   510  		defer close(ch)
   511  	}
   512  	for _, dep := range builddeps[dir] {
   513  		<-installed[dep]
   514  	}
   515  
   516  	if vflag > 0 {
   517  		if goos != gohostos || goarch != gohostarch {
   518  			errprintf("%s (%s/%s)\n", dir, goos, goarch)
   519  		} else {
   520  			errprintf("%s\n", dir)
   521  		}
   522  	}
   523  
   524  	workdir := pathf("%s/%s", workdir, dir)
   525  	xmkdirall(workdir)
   526  
   527  	var clean []string
   528  	defer func() {
   529  		for _, name := range clean {
   530  			xremove(name)
   531  		}
   532  	}()
   533  
   534  	// path = full path to dir.
   535  	path := pathf("%s/src/%s", goroot, dir)
   536  	name := filepath.Base(dir)
   537  
   538  	ispkg := !strings.HasPrefix(dir, "cmd/") || strings.HasPrefix(dir, "cmd/internal/") || strings.HasPrefix(dir, "cmd/asm/internal/")
   539  
   540  	// Start final link command line.
   541  	// Note: code below knows that link.p[targ] is the target.
   542  	var (
   543  		link      []string
   544  		targ      int
   545  		ispackcmd bool
   546  	)
   547  	if ispkg {
   548  		// Go library (package).
   549  		ispackcmd = true
   550  		link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
   551  		targ = len(link) - 1
   552  		xmkdirall(filepath.Dir(link[targ]))
   553  	} else {
   554  		// Go command.
   555  		elem := name
   556  		if elem == "go" {
   557  			elem = "go_bootstrap"
   558  		}
   559  		link = []string{pathf("%s/link", tooldir), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
   560  		targ = len(link) - 1
   561  	}
   562  	ttarg := mtime(link[targ])
   563  
   564  	// Gather files that are sources for this target.
   565  	// Everything in that directory, and any target-specific
   566  	// additions.
   567  	files := xreaddir(path)
   568  
   569  	// Remove files beginning with . or _,
   570  	// which are likely to be editor temporary files.
   571  	// This is the same heuristic build.ScanDir uses.
   572  	// There do exist real C files beginning with _,
   573  	// so limit that check to just Go files.
   574  	files = filter(files, func(p string) bool {
   575  		return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
   576  	})
   577  
   578  	for _, dt := range deptab {
   579  		if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
   580  			for _, p := range dt.dep {
   581  				p = os.ExpandEnv(p)
   582  				files = append(files, p)
   583  			}
   584  		}
   585  	}
   586  	files = uniq(files)
   587  
   588  	// Convert to absolute paths.
   589  	for i, p := range files {
   590  		if !isabs(p) {
   591  			files[i] = pathf("%s/%s", path, p)
   592  		}
   593  	}
   594  
   595  	// Is the target up-to-date?
   596  	var gofiles, missing []string
   597  	stale := rebuildall
   598  	files = filter(files, func(p string) bool {
   599  		for _, suf := range depsuffix {
   600  			if strings.HasSuffix(p, suf) {
   601  				goto ok
   602  			}
   603  		}
   604  		return false
   605  	ok:
   606  		t := mtime(p)
   607  		if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
   608  			return false
   609  		}
   610  		if strings.HasSuffix(p, ".go") {
   611  			gofiles = append(gofiles, p)
   612  		}
   613  		if t.After(ttarg) {
   614  			stale = true
   615  		}
   616  		if t.IsZero() {
   617  			missing = append(missing, p)
   618  		}
   619  		return true
   620  	})
   621  
   622  	// If there are no files to compile, we're done.
   623  	if len(files) == 0 {
   624  		return
   625  	}
   626  
   627  	if !stale {
   628  		return
   629  	}
   630  
   631  	// For package runtime, copy some files into the work space.
   632  	if dir == "runtime" || strings.HasPrefix(dir, "runtime/internal/") {
   633  		xmkdirall(pathf("%s/pkg/include", goroot))
   634  		// For use by assembly and C files.
   635  		copyfile(pathf("%s/pkg/include/textflag.h", goroot),
   636  			pathf("%s/src/runtime/textflag.h", goroot), 0)
   637  		copyfile(pathf("%s/pkg/include/funcdata.h", goroot),
   638  			pathf("%s/src/runtime/funcdata.h", goroot), 0)
   639  		copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot),
   640  			pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0)
   641  		copyfile(pathf("%s/pkg/include/asm_sparc64.h", goroot),
   642  			pathf("%s/src/runtime/asm_sparc64.h", goroot), 0)
   643  	}
   644  
   645  	// Generate any missing files; regenerate existing ones.
   646  	for _, p := range files {
   647  		elem := filepath.Base(p)
   648  		for _, gt := range gentab {
   649  			if gt.gen == nil {
   650  				continue
   651  			}
   652  			if strings.HasPrefix(elem, gt.nameprefix) {
   653  				if vflag > 1 {
   654  					errprintf("generate %s\n", p)
   655  				}
   656  				gt.gen(path, p)
   657  				// Do not add generated file to clean list.
   658  				// In runtime, we want to be able to
   659  				// build the package with the go tool,
   660  				// and it assumes these generated files already
   661  				// exist (it does not know how to build them).
   662  				// The 'clean' command can remove
   663  				// the generated files.
   664  				goto built
   665  			}
   666  		}
   667  		// Did not rebuild p.
   668  		if find(p, missing) >= 0 {
   669  			fatal("missing file %s", p)
   670  		}
   671  	built:
   672  	}
   673  
   674  	if goos != gohostos || goarch != gohostarch {
   675  		// We've generated the right files; the go command can do the build.
   676  		if vflag > 1 {
   677  			errprintf("skip build for cross-compile %s\n", dir)
   678  		}
   679  		return
   680  	}
   681  
   682  	var archive string
   683  	// The next loop will compile individual non-Go files.
   684  	// Hand the Go files to the compiler en masse.
   685  	// For package runtime, this writes go_asm.h, which
   686  	// the assembly files will need.
   687  	pkg := dir
   688  	if strings.HasPrefix(dir, "cmd/") {
   689  		pkg = "main"
   690  	}
   691  	b := pathf("%s/_go_.a", workdir)
   692  	clean = append(clean, b)
   693  	if !ispackcmd {
   694  		link = append(link, b)
   695  	} else {
   696  		archive = b
   697  	}
   698  	compile := []string{pathf("%s/compile", tooldir), "-pack", "-o", b, "-p", pkg}
   699  	if gogcflags != "" {
   700  		compile = append(compile, strings.Fields(gogcflags)...)
   701  	}
   702  	if dir == "runtime" {
   703  		compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
   704  	}
   705  	compile = append(compile, gofiles...)
   706  	run(path, CheckExit|ShowOutput, compile...)
   707  
   708  	// Compile the files.
   709  	var wg sync.WaitGroup
   710  	for _, p := range files {
   711  		if !strings.HasSuffix(p, ".s") {
   712  			continue
   713  		}
   714  
   715  		var compile []string
   716  		// Assembly file for a Go package.
   717  		compile = []string{
   718  			pathf("%s/asm", tooldir),
   719  			"-I", workdir,
   720  			"-I", pathf("%s/pkg/include", goroot),
   721  			"-D", "GOOS_" + goos,
   722  			"-D", "GOARCH_" + goarch,
   723  			"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
   724  		}
   725  
   726  		doclean := true
   727  		b := pathf("%s/%s", workdir, filepath.Base(p))
   728  
   729  		// Change the last character of the output file (which was c or s).
   730  		b = b[:len(b)-1] + "o"
   731  		compile = append(compile, "-o", b, p)
   732  		bgrun(&wg, path, compile...)
   733  
   734  		link = append(link, b)
   735  		if doclean {
   736  			clean = append(clean, b)
   737  		}
   738  	}
   739  	bgwait(&wg)
   740  
   741  	if ispackcmd {
   742  		xremove(link[targ])
   743  		dopack(link[targ], archive, link[targ+1:])
   744  		return
   745  	}
   746  
   747  	// Remove target before writing it.
   748  	xremove(link[targ])
   749  	run("", CheckExit|ShowOutput, link...)
   750  }
   751  
   752  // matchfield reports whether the field (x,y,z) matches this build.
   753  // all the elements in the field must be satisfied.
   754  func matchfield(f string) bool {
   755  	for _, tag := range strings.Split(f, ",") {
   756  		if !matchtag(tag) {
   757  			return false
   758  		}
   759  	}
   760  	return true
   761  }
   762  
   763  // matchtag reports whether the tag (x or !x) matches this build.
   764  func matchtag(tag string) bool {
   765  	if tag == "" {
   766  		return false
   767  	}
   768  	if tag[0] == '!' {
   769  		if len(tag) == 1 || tag[1] == '!' {
   770  			return false
   771  		}
   772  		return !matchtag(tag[1:])
   773  	}
   774  	return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux")
   775  }
   776  
   777  // shouldbuild reports whether we should build this file.
   778  // It applies the same rules that are used with context tags
   779  // in package go/build, except that the GOOS and GOARCH
   780  // can appear anywhere in the file name, not just after _.
   781  // In particular, they can be the entire file name (like windows.c).
   782  // We also allow the special tag cmd_go_bootstrap.
   783  // See ../go/bootstrap.go and package go/build.
   784  func shouldbuild(file, dir string) bool {
   785  	// Check file name for GOOS or GOARCH.
   786  	name := filepath.Base(file)
   787  	excluded := func(list []string, ok string) bool {
   788  		for _, x := range list {
   789  			if x == ok {
   790  				continue
   791  			}
   792  			i := strings.Index(name, x)
   793  			if i < 0 {
   794  				continue
   795  			}
   796  			i += len(x)
   797  			if i == len(name) || name[i] == '.' || name[i] == '_' {
   798  				return true
   799  			}
   800  		}
   801  		return false
   802  	}
   803  	if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
   804  		return false
   805  	}
   806  
   807  	// Omit test files.
   808  	if strings.Contains(name, "_test") {
   809  		return false
   810  	}
   811  
   812  	// Check file contents for // +build lines.
   813  	for _, p := range splitlines(readfile(file)) {
   814  		p = strings.TrimSpace(p)
   815  		if p == "" {
   816  			continue
   817  		}
   818  		code := p
   819  		i := strings.Index(code, "//")
   820  		if i > 0 {
   821  			code = strings.TrimSpace(code[:i])
   822  		}
   823  		if code == "package documentation" {
   824  			return false
   825  		}
   826  		if code == "package main" && dir != "cmd/go" && dir != "cmd/cgo" {
   827  			return false
   828  		}
   829  		if !strings.HasPrefix(p, "//") {
   830  			break
   831  		}
   832  		if !strings.Contains(p, "+build") {
   833  			continue
   834  		}
   835  		fields := splitfields(p[2:])
   836  		if len(fields) < 1 || fields[0] != "+build" {
   837  			continue
   838  		}
   839  		for _, p := range fields[1:] {
   840  			if matchfield(p) {
   841  				goto fieldmatch
   842  			}
   843  		}
   844  		return false
   845  	fieldmatch:
   846  	}
   847  
   848  	return true
   849  }
   850  
   851  // copy copies the file src to dst, via memory (so only good for small files).
   852  func copyfile(dst, src string, flag int) {
   853  	if vflag > 1 {
   854  		errprintf("cp %s %s\n", src, dst)
   855  	}
   856  	writefile(readfile(src), dst, flag)
   857  }
   858  
   859  // dopack copies the package src to dst,
   860  // appending the files listed in extra.
   861  // The archive format is the traditional Unix ar format.
   862  func dopack(dst, src string, extra []string) {
   863  	bdst := bytes.NewBufferString(readfile(src))
   864  	for _, file := range extra {
   865  		b := readfile(file)
   866  		// find last path element for archive member name
   867  		i := strings.LastIndex(file, "/") + 1
   868  		j := strings.LastIndex(file, `\`) + 1
   869  		if i < j {
   870  			i = j
   871  		}
   872  		fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
   873  		bdst.WriteString(b)
   874  		if len(b)&1 != 0 {
   875  			bdst.WriteByte(0)
   876  		}
   877  	}
   878  	writefile(bdst.String(), dst, 0)
   879  }
   880  
   881  // builddeps records the build dependencies for the 'go bootstrap' command.
   882  // It is a map[string][]string and generated by mkdeps.bash into deps.go.
   883  
   884  // buildlist is the list of directories being built, sorted by name.
   885  var buildlist = makeBuildlist()
   886  
   887  func makeBuildlist() []string {
   888  	var all []string
   889  	for dir := range builddeps {
   890  		all = append(all, dir)
   891  	}
   892  	sort.Strings(all)
   893  	return all
   894  }
   895  
   896  var runtimegen = []string{
   897  	"zaexperiment.h",
   898  	"zversion.go",
   899  }
   900  
   901  func clean() {
   902  	for _, name := range buildlist {
   903  		path := pathf("%s/src/%s", goroot, name)
   904  		// Remove generated files.
   905  		for _, elem := range xreaddir(path) {
   906  			for _, gt := range gentab {
   907  				if strings.HasPrefix(elem, gt.nameprefix) {
   908  					xremove(pathf("%s/%s", path, elem))
   909  				}
   910  			}
   911  		}
   912  		// Remove generated binary named for directory.
   913  		if strings.HasPrefix(name, "cmd/") {
   914  			xremove(pathf("%s/%s", path, name[4:]))
   915  		}
   916  	}
   917  
   918  	// remove runtimegen files.
   919  	path := pathf("%s/src/runtime", goroot)
   920  	for _, elem := range runtimegen {
   921  		xremove(pathf("%s/%s", path, elem))
   922  	}
   923  
   924  	if rebuildall {
   925  		// Remove object tree.
   926  		xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
   927  
   928  		// Remove installed packages and tools.
   929  		xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
   930  		xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
   931  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch))
   932  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch))
   933  		xremoveall(tooldir)
   934  
   935  		// Remove cached version info.
   936  		xremove(pathf("%s/VERSION.cache", goroot))
   937  	}
   938  }
   939  
   940  /*
   941   * command implementations
   942   */
   943  
   944  func usage() {
   945  	xprintf("usage: go tool dist [command]\n" +
   946  		"Commands are:\n" +
   947  		"\n" +
   948  		"banner         print installation banner\n" +
   949  		"bootstrap      rebuild everything\n" +
   950  		"clean          deletes all built files\n" +
   951  		"env [-p]       print environment (-p: include $PATH)\n" +
   952  		"install [dir]  install individual directory\n" +
   953  		"list [-json]   list all supported platforms\n" +
   954  		"test [-h]      run Go test(s)\n" +
   955  		"version        print Go version\n" +
   956  		"\n" +
   957  		"All commands take -v flags to emit extra information.\n",
   958  	)
   959  	xexit(2)
   960  }
   961  
   962  // The env command prints the default environment.
   963  func cmdenv() {
   964  	path := flag.Bool("p", false, "emit updated PATH")
   965  	plan9 := flag.Bool("9", false, "emit plan 9 syntax")
   966  	windows := flag.Bool("w", false, "emit windows syntax")
   967  	xflagparse(0)
   968  
   969  	format := "%s=\"%s\"\n"
   970  	switch {
   971  	case *plan9:
   972  		format = "%s='%s'\n"
   973  	case *windows:
   974  		format = "set %s=%s\r\n"
   975  	}
   976  
   977  	xprintf(format, "CC", defaultcc)
   978  	xprintf(format, "CC_FOR_TARGET", defaultcctarget)
   979  	xprintf(format, "GOROOT", goroot)
   980  	xprintf(format, "GOBIN", gobin)
   981  	xprintf(format, "GOARCH", goarch)
   982  	xprintf(format, "GOOS", goos)
   983  	xprintf(format, "GOHOSTARCH", gohostarch)
   984  	xprintf(format, "GOHOSTOS", gohostos)
   985  	xprintf(format, "GOTOOLDIR", tooldir)
   986  	if goarch == "arm" {
   987  		xprintf(format, "GOARM", goarm)
   988  	}
   989  	if goarch == "386" {
   990  		xprintf(format, "GO386", go386)
   991  	}
   992  
   993  	if *path {
   994  		sep := ":"
   995  		if gohostos == "windows" {
   996  			sep = ";"
   997  		}
   998  		xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
   999  	}
  1000  }
  1001  
  1002  // The bootstrap command runs a build from scratch,
  1003  // stopping at having installed the go_bootstrap command.
  1004  func cmdbootstrap() {
  1005  	flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
  1006  	xflagparse(0)
  1007  
  1008  	if isdir(pathf("%s/src/pkg", goroot)) {
  1009  		fatal("\n\n"+
  1010  			"The Go package sources have moved to $GOROOT/src.\n"+
  1011  			"*** %s still exists. ***\n"+
  1012  			"It probably contains stale files that may confuse the build.\n"+
  1013  			"Please (check what's there and) remove it and try again.\n"+
  1014  			"See https://golang.org/s/go14nopkg\n",
  1015  			pathf("%s/src/pkg", goroot))
  1016  	}
  1017  
  1018  	if rebuildall {
  1019  		clean()
  1020  	}
  1021  
  1022  	setup()
  1023  
  1024  	checkCC()
  1025  	bootstrapBuildTools()
  1026  
  1027  	// For the main bootstrap, building for host os/arch.
  1028  	oldgoos = goos
  1029  	oldgoarch = goarch
  1030  	goos = gohostos
  1031  	goarch = gohostarch
  1032  	os.Setenv("GOHOSTARCH", gohostarch)
  1033  	os.Setenv("GOHOSTOS", gohostos)
  1034  	os.Setenv("GOARCH", goarch)
  1035  	os.Setenv("GOOS", goos)
  1036  
  1037  	// TODO(rsc): Enable when appropriate.
  1038  	// This step is only needed if we believe that the Go compiler built from Go 1.4
  1039  	// will produce different object files than the Go compiler built from itself.
  1040  	// In the absence of bugs, that should not happen.
  1041  	// And if there are bugs, they're more likely in the current development tree
  1042  	// than in a standard release like Go 1.4, so don't do this rebuild by default.
  1043  	if false {
  1044  		xprintf("##### Building Go toolchain using itself.\n")
  1045  		for _, dir := range buildlist {
  1046  			installed[dir] = make(chan struct{})
  1047  		}
  1048  		var wg sync.WaitGroup
  1049  		for _, dir := range builddeps["cmd/go"] {
  1050  			wg.Add(1)
  1051  			dir := dir
  1052  			go func() {
  1053  				defer wg.Done()
  1054  				install(dir)
  1055  			}()
  1056  		}
  1057  		wg.Wait()
  1058  		xprintf("\n")
  1059  	}
  1060  
  1061  	xprintf("##### Building go_bootstrap for host, %s/%s.\n", gohostos, gohostarch)
  1062  	for _, dir := range buildlist {
  1063  		installed[dir] = make(chan struct{})
  1064  	}
  1065  	for _, dir := range buildlist {
  1066  		go install(dir)
  1067  	}
  1068  	<-installed["cmd/go"]
  1069  
  1070  	goos = oldgoos
  1071  	goarch = oldgoarch
  1072  	os.Setenv("GOARCH", goarch)
  1073  	os.Setenv("GOOS", goos)
  1074  
  1075  	// Build runtime for actual goos/goarch too.
  1076  	if goos != gohostos || goarch != gohostarch {
  1077  		installed["runtime"] = make(chan struct{})
  1078  		install("runtime")
  1079  	}
  1080  }
  1081  
  1082  // Cannot use go/build directly because cmd/dist for a new release
  1083  // builds against an old release's go/build, which may be out of sync.
  1084  // To reduce duplication, we generate the list for go/build from this.
  1085  //
  1086  // We list all supported platforms in this list, so that this is the
  1087  // single point of truth for supported platforms. This list is used
  1088  // by 'go tool dist list'.
  1089  var cgoEnabled = map[string]bool{
  1090  	"darwin/386":      true,
  1091  	"darwin/amd64":    true,
  1092  	"darwin/arm":      true,
  1093  	"darwin/arm64":    true,
  1094  	"dragonfly/amd64": true,
  1095  	"freebsd/386":     true,
  1096  	"freebsd/amd64":   true,
  1097  	"freebsd/arm":     false,
  1098  	"linux/386":       true,
  1099  	"linux/amd64":     true,
  1100  	"linux/arm":       true,
  1101  	"linux/arm64":     true,
  1102  	"linux/ppc64":     false,
  1103  	"linux/ppc64le":   true,
  1104  	"linux/mips64":    true,
  1105  	"linux/mips64le":  true,
  1106  	"linux/s390x":     true,
  1107  	"android/386":     true,
  1108  	"android/amd64":   true,
  1109  	"android/arm":     true,
  1110  	"android/arm64":   true,
  1111  	"nacl/386":        false,
  1112  	"nacl/amd64p32":   false,
  1113  	"nacl/arm":        false,
  1114  	"netbsd/386":      true,
  1115  	"netbsd/amd64":    true,
  1116  	"netbsd/arm":      true,
  1117  	"openbsd/386":     true,
  1118  	"openbsd/amd64":   true,
  1119  	"openbsd/arm":     false,
  1120  	"plan9/386":       false,
  1121  	"plan9/amd64":     false,
  1122  	"plan9/arm":       false,
  1123  	"solaris/amd64":   true,
  1124  	"solaris/sparc64": true,
  1125  	"windows/386":     true,
  1126  	"windows/amd64":   true,
  1127  }
  1128  
  1129  func needCC() bool {
  1130  	switch os.Getenv("CGO_ENABLED") {
  1131  	case "1":
  1132  		return true
  1133  	case "0":
  1134  		return false
  1135  	}
  1136  	return cgoEnabled[gohostos+"/"+gohostarch]
  1137  }
  1138  
  1139  func checkCC() {
  1140  	if !needCC() {
  1141  		return
  1142  	}
  1143  	if output, err := exec.Command(defaultcc, "--help").CombinedOutput(); err != nil {
  1144  		outputHdr := ""
  1145  		if len(output) > 0 {
  1146  			outputHdr = "\nCommand output:\n\n"
  1147  		}
  1148  		fatal("cannot invoke C compiler %q: %v\n\n"+
  1149  			"Go needs a system C compiler for use with cgo.\n"+
  1150  			"To set a C compiler, set CC=the-compiler.\n"+
  1151  			"To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc, err, outputHdr, output)
  1152  	}
  1153  }
  1154  
  1155  func defaulttarg() string {
  1156  	// xgetwd might return a path with symlinks fully resolved, and if
  1157  	// there happens to be symlinks in goroot, then the hasprefix test
  1158  	// will never succeed. Instead, we use xrealwd to get a canonical
  1159  	// goroot/src before the comparison to avoid this problem.
  1160  	pwd := xgetwd()
  1161  	src := pathf("%s/src/", goroot)
  1162  	real_src := xrealwd(src)
  1163  	if !strings.HasPrefix(pwd, real_src) {
  1164  		fatal("current directory %s is not under %s", pwd, real_src)
  1165  	}
  1166  	pwd = pwd[len(real_src):]
  1167  	// guard against xrealwd returning the directory without the trailing /
  1168  	pwd = strings.TrimPrefix(pwd, "/")
  1169  
  1170  	return pwd
  1171  }
  1172  
  1173  // Install installs the list of packages named on the command line.
  1174  func cmdinstall() {
  1175  	xflagparse(-1)
  1176  
  1177  	if flag.NArg() == 0 {
  1178  		install(defaulttarg())
  1179  	}
  1180  
  1181  	for _, arg := range flag.Args() {
  1182  		install(arg)
  1183  	}
  1184  }
  1185  
  1186  // Clean deletes temporary objects.
  1187  func cmdclean() {
  1188  	xflagparse(0)
  1189  	clean()
  1190  }
  1191  
  1192  // Banner prints the 'now you've installed Go' banner.
  1193  func cmdbanner() {
  1194  	xflagparse(0)
  1195  
  1196  	xprintf("\n")
  1197  	xprintf("---\n")
  1198  	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
  1199  	xprintf("Installed commands in %s\n", gobin)
  1200  
  1201  	if !xsamefile(goroot_final, goroot) {
  1202  		// If the files are to be moved, don't check that gobin
  1203  		// is on PATH; assume they know what they are doing.
  1204  	} else if gohostos == "plan9" {
  1205  		// Check that gobin is bound before /bin.
  1206  		pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
  1207  		ns := fmt.Sprintf("/proc/%s/ns", pid)
  1208  		if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
  1209  			xprintf("*** You need to bind %s before /bin.\n", gobin)
  1210  		}
  1211  	} else {
  1212  		// Check that gobin appears in $PATH.
  1213  		pathsep := ":"
  1214  		if gohostos == "windows" {
  1215  			pathsep = ";"
  1216  		}
  1217  		if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
  1218  			xprintf("*** You need to add %s to your PATH.\n", gobin)
  1219  		}
  1220  	}
  1221  
  1222  	if !xsamefile(goroot_final, goroot) {
  1223  		xprintf("\n"+
  1224  			"The binaries expect %s to be copied or moved to %s\n",
  1225  			goroot, goroot_final)
  1226  	}
  1227  }
  1228  
  1229  // Version prints the Go version.
  1230  func cmdversion() {
  1231  	xflagparse(0)
  1232  	xprintf("%s\n", findgoversion())
  1233  }
  1234  
  1235  // cmdlist lists all supported platforms.
  1236  func cmdlist() {
  1237  	jsonFlag := flag.Bool("json", false, "produce JSON output")
  1238  	xflagparse(0)
  1239  
  1240  	var plats []string
  1241  	for p := range cgoEnabled {
  1242  		plats = append(plats, p)
  1243  	}
  1244  	sort.Strings(plats)
  1245  
  1246  	if !*jsonFlag {
  1247  		for _, p := range plats {
  1248  			xprintf("%s\n", p)
  1249  		}
  1250  		return
  1251  	}
  1252  
  1253  	type jsonResult struct {
  1254  		GOOS         string
  1255  		GOARCH       string
  1256  		CgoSupported bool
  1257  	}
  1258  	var results []jsonResult
  1259  	for _, p := range plats {
  1260  		fields := strings.Split(p, "/")
  1261  		results = append(results, jsonResult{
  1262  			GOOS:         fields[0],
  1263  			GOARCH:       fields[1],
  1264  			CgoSupported: cgoEnabled[p]})
  1265  	}
  1266  	out, err := json.MarshalIndent(results, "", "\t")
  1267  	if err != nil {
  1268  		fatal("json marshal error: %v", err)
  1269  	}
  1270  	if _, err := os.Stdout.Write(out); err != nil {
  1271  		fatal("write failed: %v", err)
  1272  	}
  1273  }