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