...
Run Format

Source file src/cmd/dist/build.go

Documentation: cmd/dist

  // Copyright 2012 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package main
  
  import (
  	"bytes"
  	"encoding/json"
  	"flag"
  	"fmt"
  	"log"
  	"os"
  	"os/exec"
  	"path/filepath"
  	"sort"
  	"strings"
  	"sync"
  	"time"
  )
  
  // Initialization for any invocation.
  
  // The usual variables.
  var (
  	goarch           string
  	gobin            string
  	gohostarch       string
  	gohostos         string
  	goos             string
  	goarm            string
  	go386            string
  	gomips           string
  	goroot           string
  	goroot_final     string
  	goextlinkenabled string
  	gogcflags        string // For running built compiler
  	goldflags        string
  	workdir          string
  	tooldir          string
  	oldgoos          string
  	oldgoarch        string
  	exe              string
  	defaultcc        map[string]string
  	defaultcxx       map[string]string
  	defaultcflags    string
  	defaultldflags   string
  	defaultpkgconfig string
  
  	rebuildall   bool
  	defaultclang bool
  
  	vflag int // verbosity
  )
  
  // The known architectures.
  var okgoarch = []string{
  	"386",
  	"amd64",
  	"amd64p32",
  	"arm",
  	"arm64",
  	"mips",
  	"mipsle",
  	"mips64",
  	"mips64le",
  	"ppc64",
  	"ppc64le",
  	"s390x",
  }
  
  // The known operating systems.
  var okgoos = []string{
  	"darwin",
  	"dragonfly",
  	"linux",
  	"android",
  	"solaris",
  	"freebsd",
  	"nacl",
  	"netbsd",
  	"openbsd",
  	"plan9",
  	"windows",
  }
  
  // find reports the first index of p in l[0:n], or else -1.
  func find(p string, l []string) int {
  	for i, s := range l {
  		if p == s {
  			return i
  		}
  	}
  	return -1
  }
  
  // xinit handles initialization of the various global state, like goroot and goarch.
  func xinit() {
  	b := os.Getenv("GOROOT")
  	if b == "" {
  		fatalf("$GOROOT must be set")
  	}
  	goroot = filepath.Clean(b)
  
  	b = os.Getenv("GOROOT_FINAL")
  	if b == "" {
  		b = goroot
  	}
  	goroot_final = b
  
  	b = os.Getenv("GOBIN")
  	if b == "" {
  		b = pathf("%s/bin", goroot)
  	}
  	gobin = b
  
  	b = os.Getenv("GOOS")
  	if b == "" {
  		b = gohostos
  	}
  	goos = b
  	if find(goos, okgoos) < 0 {
  		fatalf("unknown $GOOS %s", goos)
  	}
  
  	b = os.Getenv("GOARM")
  	if b == "" {
  		b = xgetgoarm()
  	}
  	goarm = b
  
  	b = os.Getenv("GO386")
  	if b == "" {
  		if cansse2() {
  			b = "sse2"
  		} else {
  			b = "387"
  		}
  	}
  	go386 = b
  
  	b = os.Getenv("GOMIPS")
  	if b == "" {
  		b = "hardfloat"
  	}
  	gomips = b
  
  	if p := pathf("%s/src/all.bash", goroot); !isfile(p) {
  		fatalf("$GOROOT is not set correctly or not exported\n"+
  			"\tGOROOT=%s\n"+
  			"\t%s does not exist", goroot, p)
  	}
  
  	b = os.Getenv("GOHOSTARCH")
  	if b != "" {
  		gohostarch = b
  	}
  	if find(gohostarch, okgoarch) < 0 {
  		fatalf("unknown $GOHOSTARCH %s", gohostarch)
  	}
  
  	b = os.Getenv("GOARCH")
  	if b == "" {
  		b = gohostarch
  	}
  	goarch = b
  	if find(goarch, okgoarch) < 0 {
  		fatalf("unknown $GOARCH %s", goarch)
  	}
  
  	b = os.Getenv("GO_EXTLINK_ENABLED")
  	if b != "" {
  		if b != "0" && b != "1" {
  			fatalf("unknown $GO_EXTLINK_ENABLED %s", b)
  		}
  		goextlinkenabled = b
  	}
  
  	gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
  
  	cc, cxx := "gcc", "g++"
  	if defaultclang {
  		cc, cxx = "clang", "clang++"
  	}
  	defaultcc = compilerEnv("CC", cc)
  	defaultcxx = compilerEnv("CXX", cxx)
  
  	defaultcflags = os.Getenv("CFLAGS")
  	defaultldflags = os.Getenv("LDFLAGS")
  
  	b = os.Getenv("PKG_CONFIG")
  	if b == "" {
  		b = "pkg-config"
  	}
  	defaultpkgconfig = b
  
  	// For tools being invoked but also for os.ExpandEnv.
  	os.Setenv("GO386", go386)
  	os.Setenv("GOARCH", goarch)
  	os.Setenv("GOARM", goarm)
  	os.Setenv("GOHOSTARCH", gohostarch)
  	os.Setenv("GOHOSTOS", gohostos)
  	os.Setenv("GOOS", goos)
  	os.Setenv("GOMIPS", gomips)
  	os.Setenv("GOROOT", goroot)
  	os.Setenv("GOROOT_FINAL", goroot_final)
  
  	// Use a build cache separate from the default user one.
  	// Also one that will be wiped out during startup, so that
  	// make.bash really does start from a clean slate.
  	// But if the user has specified no caching, don't cache.
  	if os.Getenv("GOCACHE") != "off" {
  		os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot))
  	}
  
  	// Make the environment more predictable.
  	os.Setenv("LANG", "C")
  	os.Setenv("LANGUAGE", "en_US.UTF8")
  
  	workdir = xworkdir()
  	xatexit(rmworkdir)
  
  	tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
  }
  
  // compilerEnv returns a map from "goos/goarch" to the
  // compiler setting to use for that platform.
  // The entry for key "" covers any goos/goarch not explicitly set in the map.
  // For example, compilerEnv("CC", "gcc") returns the C compiler settings
  // read from $CC, defaulting to gcc.
  //
  // The result is a map because additional environment variables
  // can be set to change the compiler based on goos/goarch settings.
  // The following applies to all envNames but CC is assumed to simplify
  // the presentation.
  //
  // If no environment variables are set, we use def for all goos/goarch.
  // $CC, if set, applies to all goos/goarch but is overridden by the following.
  // $CC_FOR_TARGET, if set, applies to all goos/goarch except gohostos/gohostarch,
  // but is overridden by the following.
  // If gohostos=goos and gohostarch=goarch, then $CC_FOR_TARGET applies even for gohostos/gohostarch.
  // $CC_FOR_goos_goarch, if set, applies only to goos/goarch.
  func compilerEnv(envName, def string) map[string]string {
  	m := map[string]string{"": def}
  
  	if env := os.Getenv(envName); env != "" {
  		m[""] = env
  	}
  	if env := os.Getenv(envName + "_FOR_TARGET"); env != "" {
  		if gohostos != goos || gohostarch != goarch {
  			m[gohostos+"/"+gohostarch] = m[""]
  		}
  		m[""] = env
  	}
  
  	for _, goos := range okgoos {
  		for _, goarch := range okgoarch {
  			if env := os.Getenv(envName + "_FOR_" + goos + "_" + goarch); env != "" {
  				m[goos+"/"+goarch] = env
  			}
  		}
  	}
  
  	return m
  }
  
  // compilerEnvLookup returns the compiler settings for goos/goarch in map m.
  func compilerEnvLookup(m map[string]string, goos, goarch string) string {
  	if cc := m[goos+"/"+goarch]; cc != "" {
  		return cc
  	}
  	return m[""]
  }
  
  // rmworkdir deletes the work directory.
  func rmworkdir() {
  	if vflag > 1 {
  		errprintf("rm -rf %s\n", workdir)
  	}
  	xremoveall(workdir)
  }
  
  // Remove trailing spaces.
  func chomp(s string) string {
  	return strings.TrimRight(s, " \t\r\n")
  }
  
  func branchtag(branch string) (tag string, precise bool) {
  	log := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch)
  	tag = branch
  	for row, line := range strings.Split(log, "\n") {
  		// Each line is either blank, or looks like
  		//	  (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
  		// We need to find an element starting with refs/tags/.
  		const s = " refs/tags/"
  		i := strings.Index(line, s)
  		if i < 0 {
  			continue
  		}
  		// Trim off known prefix.
  		line = line[i+len(s):]
  		// The tag name ends at a comma or paren.
  		j := strings.IndexAny(line, ",)")
  		if j < 0 {
  			continue // malformed line; ignore it
  		}
  		tag = line[:j]
  		if row == 0 {
  			precise = true // tag denotes HEAD
  		}
  		break
  	}
  	return
  }
  
  // findgoversion determines the Go version to use in the version string.
  func findgoversion() string {
  	// The $GOROOT/VERSION file takes priority, for distributions
  	// without the source repo.
  	path := pathf("%s/VERSION", goroot)
  	if isfile(path) {
  		b := chomp(readfile(path))
  		// Commands such as "dist version > VERSION" will cause
  		// the shell to create an empty VERSION file and set dist's
  		// stdout to its fd. dist in turn looks at VERSION and uses
  		// its content if available, which is empty at this point.
  		// Only use the VERSION file if it is non-empty.
  		if b != "" {
  			// Some builders cross-compile the toolchain on linux-amd64
  			// and then copy the toolchain to the target builder (say, linux-arm)
  			// for use there. But on non-release (devel) branches, the compiler
  			// used on linux-amd64 will be an amd64 binary, and the compiler
  			// shipped to linux-arm will be an arm binary, so they will have different
  			// content IDs (they are binaries for different architectures) and so the
  			// packages compiled by the running-on-amd64 compiler will appear
  			// stale relative to the running-on-arm compiler. Avoid this by setting
  			// the version string to something that doesn't begin with devel.
  			// Then the version string will be used in place of the content ID,
  			// and the packages will look up-to-date.
  			// TODO(rsc): Really the builders could be writing out a better VERSION file instead,
  			// but it is easier to change cmd/dist than to try to make changes to
  			// the builder while Brad is away.
  			if strings.HasPrefix(b, "devel") {
  				if hostType := os.Getenv("META_BUILDLET_HOST_TYPE"); strings.Contains(hostType, "-cross") {
  					fmt.Fprintf(os.Stderr, "warning: changing VERSION from %q to %q\n", b, "builder "+hostType)
  					b = "builder " + hostType
  				}
  			}
  			return b
  		}
  	}
  
  	// The $GOROOT/VERSION.cache file is a cache to avoid invoking
  	// git every time we run this command. Unlike VERSION, it gets
  	// deleted by the clean command.
  	path = pathf("%s/VERSION.cache", goroot)
  	if isfile(path) {
  		return chomp(readfile(path))
  	}
  
  	// Show a nicer error message if this isn't a Git repo.
  	if !isGitRepo() {
  		fatalf("FAILED: not a Git repo; must put a VERSION file in $GOROOT")
  	}
  
  	// Otherwise, use Git.
  	// What is the current branch?
  	branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD"))
  
  	// What are the tags along the current branch?
  	tag := "devel"
  	precise := false
  
  	// If we're on a release branch, use the closest matching tag
  	// that is on the release branch (and not on the master branch).
  	if strings.HasPrefix(branch, "release-branch.") {
  		tag, precise = branchtag(branch)
  	}
  
  	if !precise {
  		// Tag does not point at HEAD; add hash and date to version.
  		tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD"))
  	}
  
  	// Cache version.
  	writefile(tag, path, 0)
  
  	return tag
  }
  
  // isGitRepo reports whether the working directory is inside a Git repository.
  func isGitRepo() bool {
  	// NB: simply checking the exit code of `git rev-parse --git-dir` would
  	// suffice here, but that requires deviating from the infrastructure
  	// provided by `run`.
  	gitDir := chomp(run(goroot, 0, "git", "rev-parse", "--git-dir"))
  	if !filepath.IsAbs(gitDir) {
  		gitDir = filepath.Join(goroot, gitDir)
  	}
  	return isdir(gitDir)
  }
  
  /*
   * Initial tree setup.
   */
  
  // The old tools that no longer live in $GOBIN or $GOROOT/bin.
  var oldtool = []string{
  	"5a", "5c", "5g", "5l",
  	"6a", "6c", "6g", "6l",
  	"8a", "8c", "8g", "8l",
  	"9a", "9c", "9g", "9l",
  	"6cov",
  	"6nm",
  	"6prof",
  	"cgo",
  	"ebnflint",
  	"goapi",
  	"gofix",
  	"goinstall",
  	"gomake",
  	"gopack",
  	"gopprof",
  	"gotest",
  	"gotype",
  	"govet",
  	"goyacc",
  	"quietgcc",
  }
  
  // Unreleased directories (relative to $GOROOT) that should
  // not be in release branches.
  var unreleased = []string{
  	"src/cmd/newlink",
  	"src/cmd/objwriter",
  	"src/debug/goobj",
  	"src/old",
  }
  
  // setup sets up the tree for the initial build.
  func setup() {
  	// Create bin directory.
  	if p := pathf("%s/bin", goroot); !isdir(p) {
  		xmkdir(p)
  	}
  
  	// Create package directory.
  	if p := pathf("%s/pkg", goroot); !isdir(p) {
  		xmkdir(p)
  	}
  
  	p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
  	if rebuildall {
  		xremoveall(p)
  	}
  	xmkdirall(p)
  
  	if goos != gohostos || goarch != gohostarch {
  		p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
  		if rebuildall {
  			xremoveall(p)
  		}
  		xmkdirall(p)
  	}
  
  	// Create object directory.
  	// We used to use it for C objects.
  	// Now we use it for the build cache, to separate dist's cache
  	// from any other cache the user might have.
  	p = pathf("%s/pkg/obj/go-build", goroot)
  	if rebuildall {
  		xremoveall(p)
  	}
  	xmkdirall(p)
  
  	// Create tool directory.
  	// We keep it in pkg/, just like the object directory above.
  	if rebuildall {
  		xremoveall(tooldir)
  	}
  	xmkdirall(tooldir)
  
  	// Remove tool binaries from before the tool/gohostos_gohostarch
  	xremoveall(pathf("%s/bin/tool", goroot))
  
  	// Remove old pre-tool binaries.
  	for _, old := range oldtool {
  		xremove(pathf("%s/bin/%s", goroot, old))
  	}
  
  	// If $GOBIN is set and has a Go compiler, it must be cleaned.
  	for _, char := range "56789" {
  		if isfile(pathf("%s/%c%s", gobin, char, "g")) {
  			for _, old := range oldtool {
  				xremove(pathf("%s/%s", gobin, old))
  			}
  			break
  		}
  	}
  
  	// For release, make sure excluded things are excluded.
  	goversion := findgoversion()
  	if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
  		for _, dir := range unreleased {
  			if p := pathf("%s/%s", goroot, dir); isdir(p) {
  				fatalf("%s should not exist in release build", p)
  			}
  		}
  	}
  }
  
  /*
   * Tool building
   */
  
  // deptab lists changes to the default dependencies for a given prefix.
  // deps ending in /* read the whole directory; deps beginning with -
  // exclude files with that prefix.
  // Note that this table applies only to the build of cmd/go,
  // after the main compiler bootstrap.
  var deptab = []struct {
  	prefix string   // prefix of target
  	dep    []string // dependency tweaks for targets with that prefix
  }{
  	{"cmd/go/internal/cfg", []string{
  		"zdefaultcc.go",
  		"zosarch.go",
  	}},
  	{"runtime/internal/sys", []string{
  		"zversion.go",
  	}},
  	{"go/build", []string{
  		"zcgo.go",
  	}},
  }
  
  // depsuffix records the allowed suffixes for source files.
  var depsuffix = []string{
  	".s",
  	".go",
  }
  
  // gentab records how to generate some trivial files.
  var gentab = []struct {
  	nameprefix string
  	gen        func(string, string)
  }{
  	{"zdefaultcc.go", mkzdefaultcc},
  	{"zosarch.go", mkzosarch},
  	{"zversion.go", mkzversion},
  	{"zcgo.go", mkzcgo},
  
  	// not generated anymore, but delete the file if we see it
  	{"enam.c", nil},
  	{"anames5.c", nil},
  	{"anames6.c", nil},
  	{"anames8.c", nil},
  	{"anames9.c", nil},
  }
  
  // installed maps from a dir name (as given to install) to a chan
  // closed when the dir's package is installed.
  var installed = make(map[string]chan struct{})
  var installedMu sync.Mutex
  
  func install(dir string) {
  	<-startInstall(dir)
  }
  
  func startInstall(dir string) chan struct{} {
  	installedMu.Lock()
  	ch := installed[dir]
  	if ch == nil {
  		ch = make(chan struct{})
  		installed[dir] = ch
  		go runInstall(dir, ch)
  	}
  	installedMu.Unlock()
  	return ch
  }
  
  // runInstall installs the library, package, or binary associated with dir,
  // which is relative to $GOROOT/src.
  func runInstall(dir string, ch chan struct{}) {
  	if dir == "net" || dir == "os/user" || dir == "crypto/x509" {
  		fatalf("go_bootstrap cannot depend on cgo package %s", dir)
  	}
  
  	defer close(ch)
  
  	if dir == "unsafe" {
  		return
  	}
  
  	if vflag > 0 {
  		if goos != gohostos || goarch != gohostarch {
  			errprintf("%s (%s/%s)\n", dir, goos, goarch)
  		} else {
  			errprintf("%s\n", dir)
  		}
  	}
  
  	workdir := pathf("%s/%s", workdir, dir)
  	xmkdirall(workdir)
  
  	var clean []string
  	defer func() {
  		for _, name := range clean {
  			xremove(name)
  		}
  	}()
  
  	// path = full path to dir.
  	path := pathf("%s/src/%s", goroot, dir)
  	name := filepath.Base(dir)
  
  	ispkg := !strings.HasPrefix(dir, "cmd/") || strings.Contains(dir, "/internal/")
  
  	// Start final link command line.
  	// Note: code below knows that link.p[targ] is the target.
  	var (
  		link      []string
  		targ      int
  		ispackcmd bool
  	)
  	if ispkg {
  		// Go library (package).
  		ispackcmd = true
  		link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
  		targ = len(link) - 1
  		xmkdirall(filepath.Dir(link[targ]))
  	} else {
  		// Go command.
  		elem := name
  		if elem == "go" {
  			elem = "go_bootstrap"
  		}
  		link = []string{pathf("%s/link", tooldir), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
  		targ = len(link) - 1
  	}
  	ttarg := mtime(link[targ])
  
  	// Gather files that are sources for this target.
  	// Everything in that directory, and any target-specific
  	// additions.
  	files := xreaddir(path)
  
  	// Remove files beginning with . or _,
  	// which are likely to be editor temporary files.
  	// This is the same heuristic build.ScanDir uses.
  	// There do exist real C files beginning with _,
  	// so limit that check to just Go files.
  	files = filter(files, func(p string) bool {
  		return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
  	})
  
  	for _, dt := range deptab {
  		if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
  			for _, p := range dt.dep {
  				p = os.ExpandEnv(p)
  				files = append(files, p)
  			}
  		}
  	}
  	files = uniq(files)
  
  	// Convert to absolute paths.
  	for i, p := range files {
  		if !filepath.IsAbs(p) {
  			files[i] = pathf("%s/%s", path, p)
  		}
  	}
  
  	// Is the target up-to-date?
  	var gofiles, missing []string
  	stale := rebuildall
  	files = filter(files, func(p string) bool {
  		for _, suf := range depsuffix {
  			if strings.HasSuffix(p, suf) {
  				goto ok
  			}
  		}
  		return false
  	ok:
  		t := mtime(p)
  		if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
  			return false
  		}
  		if strings.HasSuffix(p, ".go") {
  			gofiles = append(gofiles, p)
  		}
  		if t.After(ttarg) {
  			stale = true
  		}
  		if t.IsZero() {
  			missing = append(missing, p)
  		}
  		return true
  	})
  
  	// If there are no files to compile, we're done.
  	if len(files) == 0 {
  		return
  	}
  
  	if !stale {
  		return
  	}
  
  	// For package runtime, copy some files into the work space.
  	if dir == "runtime" {
  		xmkdirall(pathf("%s/pkg/include", goroot))
  		// For use by assembly and C files.
  		copyfile(pathf("%s/pkg/include/textflag.h", goroot),
  			pathf("%s/src/runtime/textflag.h", goroot), 0)
  		copyfile(pathf("%s/pkg/include/funcdata.h", goroot),
  			pathf("%s/src/runtime/funcdata.h", goroot), 0)
  		copyfile(pathf("%s/pkg/include/asm_ppc64x.h", goroot),
  			pathf("%s/src/runtime/asm_ppc64x.h", goroot), 0)
  	}
  
  	// Generate any missing files; regenerate existing ones.
  	for _, p := range files {
  		elem := filepath.Base(p)
  		for _, gt := range gentab {
  			if gt.gen == nil {
  				continue
  			}
  			if strings.HasPrefix(elem, gt.nameprefix) {
  				if vflag > 1 {
  					errprintf("generate %s\n", p)
  				}
  				gt.gen(path, p)
  				// Do not add generated file to clean list.
  				// In runtime, we want to be able to
  				// build the package with the go tool,
  				// and it assumes these generated files already
  				// exist (it does not know how to build them).
  				// The 'clean' command can remove
  				// the generated files.
  				goto built
  			}
  		}
  		// Did not rebuild p.
  		if find(p, missing) >= 0 {
  			fatalf("missing file %s", p)
  		}
  	built:
  	}
  
  	// Make sure dependencies are installed.
  	var deps []string
  	for _, p := range gofiles {
  		deps = append(deps, readimports(p)...)
  	}
  	for _, dir1 := range deps {
  		startInstall(dir1)
  	}
  	for _, dir1 := range deps {
  		install(dir1)
  	}
  
  	if goos != gohostos || goarch != gohostarch {
  		// We've generated the right files; the go command can do the build.
  		if vflag > 1 {
  			errprintf("skip build for cross-compile %s\n", dir)
  		}
  		return
  	}
  
  	var archive string
  	// The next loop will compile individual non-Go files.
  	// Hand the Go files to the compiler en masse.
  	// For package runtime, this writes go_asm.h, which
  	// the assembly files will need.
  	pkg := dir
  	if strings.HasPrefix(dir, "cmd/") && strings.Count(dir, "/") == 1 {
  		pkg = "main"
  	}
  	b := pathf("%s/_go_.a", workdir)
  	clean = append(clean, b)
  	if !ispackcmd {
  		link = append(link, b)
  	} else {
  		archive = b
  	}
  	compile := []string{pathf("%s/compile", tooldir), "-std", "-pack", "-o", b, "-p", pkg}
  	if gogcflags != "" {
  		compile = append(compile, strings.Fields(gogcflags)...)
  	}
  	if dir == "runtime" {
  		compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
  	}
  	compile = append(compile, gofiles...)
  	run(path, CheckExit|ShowOutput, compile...)
  
  	// Compile the files.
  	var wg sync.WaitGroup
  	for _, p := range files {
  		if !strings.HasSuffix(p, ".s") {
  			continue
  		}
  
  		var compile []string
  		// Assembly file for a Go package.
  		compile = []string{
  			pathf("%s/asm", tooldir),
  			"-I", workdir,
  			"-I", pathf("%s/pkg/include", goroot),
  			"-D", "GOOS_" + goos,
  			"-D", "GOARCH_" + goarch,
  			"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
  		}
  
  		if goarch == "mips" || goarch == "mipsle" {
  			// Define GOMIPS_value from gomips.
  			compile = append(compile, "-D", "GOMIPS_"+gomips)
  		}
  
  		doclean := true
  		b := pathf("%s/%s", workdir, filepath.Base(p))
  
  		// Change the last character of the output file (which was c or s).
  		b = b[:len(b)-1] + "o"
  		compile = append(compile, "-o", b, p)
  		bgrun(&wg, path, compile...)
  
  		link = append(link, b)
  		if doclean {
  			clean = append(clean, b)
  		}
  	}
  	bgwait(&wg)
  
  	if ispackcmd {
  		xremove(link[targ])
  		dopack(link[targ], archive, link[targ+1:])
  		return
  	}
  
  	// Remove target before writing it.
  	xremove(link[targ])
  	run("", CheckExit|ShowOutput, link...)
  }
  
  // matchfield reports whether the field (x,y,z) matches this build.
  // all the elements in the field must be satisfied.
  func matchfield(f string) bool {
  	for _, tag := range strings.Split(f, ",") {
  		if !matchtag(tag) {
  			return false
  		}
  	}
  	return true
  }
  
  // matchtag reports whether the tag (x or !x) matches this build.
  func matchtag(tag string) bool {
  	if tag == "" {
  		return false
  	}
  	if tag[0] == '!' {
  		if len(tag) == 1 || tag[1] == '!' {
  			return false
  		}
  		return !matchtag(tag[1:])
  	}
  	return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux")
  }
  
  // shouldbuild reports whether we should build this file.
  // It applies the same rules that are used with context tags
  // in package go/build, except it's less picky about the order
  // of GOOS and GOARCH.
  // We also allow the special tag cmd_go_bootstrap.
  // See ../go/bootstrap.go and package go/build.
  func shouldbuild(file, dir string) bool {
  	// Check file name for GOOS or GOARCH.
  	name := filepath.Base(file)
  	excluded := func(list []string, ok string) bool {
  		for _, x := range list {
  			if x == ok || ok == "android" && x == "linux" {
  				continue
  			}
  			i := strings.Index(name, x)
  			if i <= 0 || name[i-1] != '_' {
  				continue
  			}
  			i += len(x)
  			if i == len(name) || name[i] == '.' || name[i] == '_' {
  				return true
  			}
  		}
  		return false
  	}
  	if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
  		return false
  	}
  
  	// Omit test files.
  	if strings.Contains(name, "_test") {
  		return false
  	}
  
  	// Check file contents for // +build lines.
  	for _, p := range strings.Split(readfile(file), "\n") {
  		p = strings.TrimSpace(p)
  		if p == "" {
  			continue
  		}
  		code := p
  		i := strings.Index(code, "//")
  		if i > 0 {
  			code = strings.TrimSpace(code[:i])
  		}
  		if code == "package documentation" {
  			return false
  		}
  		if code == "package main" && dir != "cmd/go" && dir != "cmd/cgo" {
  			return false
  		}
  		if !strings.HasPrefix(p, "//") {
  			break
  		}
  		if !strings.Contains(p, "+build") {
  			continue
  		}
  		fields := strings.Fields(p[2:])
  		if len(fields) < 1 || fields[0] != "+build" {
  			continue
  		}
  		for _, p := range fields[1:] {
  			if matchfield(p) {
  				goto fieldmatch
  			}
  		}
  		return false
  	fieldmatch:
  	}
  
  	return true
  }
  
  // copy copies the file src to dst, via memory (so only good for small files).
  func copyfile(dst, src string, flag int) {
  	if vflag > 1 {
  		errprintf("cp %s %s\n", src, dst)
  	}
  	writefile(readfile(src), dst, flag)
  }
  
  // dopack copies the package src to dst,
  // appending the files listed in extra.
  // The archive format is the traditional Unix ar format.
  func dopack(dst, src string, extra []string) {
  	bdst := bytes.NewBufferString(readfile(src))
  	for _, file := range extra {
  		b := readfile(file)
  		// find last path element for archive member name
  		i := strings.LastIndex(file, "/") + 1
  		j := strings.LastIndex(file, `\`) + 1
  		if i < j {
  			i = j
  		}
  		fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
  		bdst.WriteString(b)
  		if len(b)&1 != 0 {
  			bdst.WriteByte(0)
  		}
  	}
  	writefile(bdst.String(), dst, 0)
  }
  
  var runtimegen = []string{
  	"zaexperiment.h",
  	"zversion.go",
  }
  
  // cleanlist is a list of packages with generated files and commands.
  var cleanlist = []string{
  	"runtime/internal/sys",
  	"cmd/cgo",
  	"cmd/go/internal/cfg",
  	"go/build",
  }
  
  func clean() {
  	for _, name := range cleanlist {
  		path := pathf("%s/src/%s", goroot, name)
  		// Remove generated files.
  		for _, elem := range xreaddir(path) {
  			for _, gt := range gentab {
  				if strings.HasPrefix(elem, gt.nameprefix) {
  					xremove(pathf("%s/%s", path, elem))
  				}
  			}
  		}
  		// Remove generated binary named for directory.
  		if strings.HasPrefix(name, "cmd/") {
  			xremove(pathf("%s/%s", path, name[4:]))
  		}
  	}
  
  	// remove runtimegen files.
  	path := pathf("%s/src/runtime", goroot)
  	for _, elem := range runtimegen {
  		xremove(pathf("%s/%s", path, elem))
  	}
  
  	if rebuildall {
  		// Remove object tree.
  		xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
  
  		// Remove installed packages and tools.
  		xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
  		xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, gohostos, gohostarch))
  		xremoveall(pathf("%s/pkg/%s_%s_race", goroot, goos, goarch))
  		xremoveall(tooldir)
  
  		// Remove cached version info.
  		xremove(pathf("%s/VERSION.cache", goroot))
  	}
  }
  
  /*
   * command implementations
   */
  
  // The env command prints the default environment.
  func cmdenv() {
  	path := flag.Bool("p", false, "emit updated PATH")
  	plan9 := flag.Bool("9", false, "emit plan 9 syntax")
  	windows := flag.Bool("w", false, "emit windows syntax")
  	xflagparse(0)
  
  	format := "%s=\"%s\"\n"
  	switch {
  	case *plan9:
  		format = "%s='%s'\n"
  	case *windows:
  		format = "set %s=%s\r\n"
  	}
  
  	xprintf(format, "GOROOT", goroot)
  	xprintf(format, "GOBIN", gobin)
  	xprintf(format, "GOARCH", goarch)
  	xprintf(format, "GOOS", goos)
  	xprintf(format, "GOHOSTARCH", gohostarch)
  	xprintf(format, "GOHOSTOS", gohostos)
  	xprintf(format, "GOTOOLDIR", tooldir)
  	if goarch == "arm" {
  		xprintf(format, "GOARM", goarm)
  	}
  	if goarch == "386" {
  		xprintf(format, "GO386", go386)
  	}
  	if goarch == "mips" || goarch == "mipsle" {
  		xprintf(format, "GOMIPS", gomips)
  	}
  
  	if *path {
  		sep := ":"
  		if gohostos == "windows" {
  			sep = ";"
  		}
  		xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
  	}
  }
  
  var (
  	timeLogEnabled = os.Getenv("GOBUILDTIMELOGFILE") != ""
  	timeLogMu      sync.Mutex
  	timeLogFile    *os.File
  	timeLogStart   time.Time
  )
  
  func timelog(op, name string) {
  	if !timeLogEnabled {
  		return
  	}
  	timeLogMu.Lock()
  	defer timeLogMu.Unlock()
  	if timeLogFile == nil {
  		f, err := os.OpenFile(os.Getenv("GOBUILDTIMELOGFILE"), os.O_RDWR|os.O_APPEND, 0666)
  		if err != nil {
  			log.Fatal(err)
  		}
  		buf := make([]byte, 100)
  		n, _ := f.Read(buf)
  		s := string(buf[:n])
  		if i := strings.Index(s, "\n"); i >= 0 {
  			s = s[:i]
  		}
  		i := strings.Index(s, " start")
  		if i < 0 {
  			log.Fatalf("time log %s does not begin with start line", os.Getenv("GOBULDTIMELOGFILE"))
  		}
  		t, err := time.Parse(time.UnixDate, s[:i])
  		if err != nil {
  			log.Fatalf("cannot parse time log line %q: %v", s, err)
  		}
  		timeLogStart = t
  		timeLogFile = f
  	}
  	t := time.Now()
  	fmt.Fprintf(timeLogFile, "%s %+.1fs %s %s\n", t.Format(time.UnixDate), t.Sub(timeLogStart).Seconds(), op, name)
  }
  
  var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link"}
  
  // The bootstrap command runs a build from scratch,
  // stopping at having installed the go_bootstrap command.
  //
  // WARNING: This command runs after cmd/dist is built with Go 1.4.
  // It rebuilds and installs cmd/dist with the new toolchain, so other
  // commands (like "go tool dist test" in run.bash) can rely on bug fixes
  // made since Go 1.4, but this function cannot. In particular, the uses
  // of os/exec in this function cannot assume that
  //	cmd.Env = append(os.Environ(), "X=Y")
  // sets $X to Y in the command's environment. That guarantee was
  // added after Go 1.4, and in fact in Go 1.4 it was typically the opposite:
  // if $X was already present in os.Environ(), most systems preferred
  // that setting, not the new one.
  func cmdbootstrap() {
  	timelog("start", "dist bootstrap")
  	defer timelog("end", "dist bootstrap")
  
  	var noBanner bool
  	var debug bool
  	flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
  	flag.BoolVar(&debug, "d", debug, "enable debugging of bootstrap process")
  	flag.BoolVar(&noBanner, "no-banner", noBanner, "do not print banner")
  
  	xflagparse(0)
  
  	if debug {
  		// cmd/buildid is used in debug mode.
  		toolchain = append(toolchain, "cmd/buildid")
  	}
  
  	if isdir(pathf("%s/src/pkg", goroot)) {
  		fatalf("\n\n"+
  			"The Go package sources have moved to $GOROOT/src.\n"+
  			"*** %s still exists. ***\n"+
  			"It probably contains stale files that may confuse the build.\n"+
  			"Please (check what's there and) remove it and try again.\n"+
  			"See https://golang.org/s/go14nopkg\n",
  			pathf("%s/src/pkg", goroot))
  	}
  
  	if rebuildall {
  		clean()
  	}
  
  	setup()
  
  	timelog("build", "toolchain1")
  	checkCC()
  	bootstrapBuildTools()
  
  	// Remember old content of $GOROOT/bin for comparison below.
  	oldBinFiles, _ := filepath.Glob(pathf("%s/bin/*", goroot))
  
  	// For the main bootstrap, building for host os/arch.
  	oldgoos = goos
  	oldgoarch = goarch
  	goos = gohostos
  	goarch = gohostarch
  	os.Setenv("GOHOSTARCH", gohostarch)
  	os.Setenv("GOHOSTOS", gohostos)
  	os.Setenv("GOARCH", goarch)
  	os.Setenv("GOOS", goos)
  
  	timelog("build", "go_bootstrap")
  	xprintf("Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.\n")
  	install("runtime") // dependency not visible in sources; also sets up textflag.h
  	install("cmd/go")
  	if vflag > 0 {
  		xprintf("\n")
  	}
  
  	gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now
  	goldflags = os.Getenv("GO_LDFLAGS")
  	goBootstrap := pathf("%s/go_bootstrap", tooldir)
  	cmdGo := pathf("%s/go", gobin)
  	if debug {
  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  		copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec)
  	}
  
  	// To recap, so far we have built the new toolchain
  	// (cmd/asm, cmd/cgo, cmd/compile, cmd/link)
  	// using Go 1.4's toolchain and go command.
  	// Then we built the new go command (as go_bootstrap)
  	// using the new toolchain and our own build logic (above).
  	//
  	//	toolchain1 = mk(new toolchain, go1.4 toolchain, go1.4 cmd/go)
  	//	go_bootstrap = mk(new cmd/go, toolchain1, cmd/dist)
  	//
  	// The toolchain1 we built earlier is built from the new sources,
  	// but because it was built using cmd/go it has no build IDs.
  	// The eventually installed toolchain needs build IDs, so we need
  	// to do another round:
  	//
  	//	toolchain2 = mk(new toolchain, toolchain1, go_bootstrap)
  	//
  	timelog("build", "toolchain2")
  	if vflag > 0 {
  		xprintf("\n")
  	}
  	xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
  	os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
  	goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...)
  	if debug {
  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  		run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch))
  		copyfile(pathf("%s/compile2", tooldir), pathf("%s/compile", tooldir), writeExec)
  	}
  
  	// Toolchain2 should be semantically equivalent to toolchain1,
  	// but it was built using the new compilers instead of the Go 1.4 compilers,
  	// so it should at the least run faster. Also, toolchain1 had no build IDs
  	// in the binaries, while toolchain2 does. In non-release builds, the
  	// toolchain's build IDs feed into constructing the build IDs of built targets,
  	// so in non-release builds, everything now looks out-of-date due to
  	// toolchain2 having build IDs - that is, due to the go command seeing
  	// that there are new compilers. In release builds, the toolchain's reported
  	// version is used in place of the build ID, and the go command does not
  	// see that change from toolchain1 to toolchain2, so in release builds,
  	// nothing looks out of date.
  	// To keep the behavior the same in both non-release and release builds,
  	// we force-install everything here.
  	//
  	//	toolchain3 = mk(new toolchain, toolchain2, go_bootstrap)
  	//
  	timelog("build", "toolchain3")
  	if vflag > 0 {
  		xprintf("\n")
  	}
  	xprintf("Building Go toolchain3 using go_bootstrap and Go toolchain2.\n")
  	goInstall(goBootstrap, append([]string{"-a", "-i"}, toolchain...)...)
  	if debug {
  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  		run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch))
  		copyfile(pathf("%s/compile3", tooldir), pathf("%s/compile", tooldir), writeExec)
  	}
  	checkNotStale(goBootstrap, append(toolchain, "runtime/internal/sys")...)
  
  	if goos == oldgoos && goarch == oldgoarch {
  		// Common case - not setting up for cross-compilation.
  		timelog("build", "toolchain")
  		if vflag > 0 {
  			xprintf("\n")
  		}
  		xprintf("Building packages and commands for %s/%s.\n", goos, goarch)
  	} else {
  		// GOOS/GOARCH does not match GOHOSTOS/GOHOSTARCH.
  		// Finish GOHOSTOS/GOHOSTARCH installation and then
  		// run GOOS/GOARCH installation.
  		timelog("build", "host toolchain")
  		if vflag > 0 {
  			xprintf("\n")
  		}
  		xprintf("Building packages and commands for host, %s/%s.\n", goos, goarch)
  		goInstall(goBootstrap, "std", "cmd")
  		checkNotStale(goBootstrap, "std", "cmd")
  		checkNotStale(cmdGo, "std", "cmd")
  
  		timelog("build", "target toolchain")
  		if vflag > 0 {
  			xprintf("\n")
  		}
  		goos = oldgoos
  		goarch = oldgoarch
  		os.Setenv("GOOS", goos)
  		os.Setenv("GOARCH", goarch)
  		os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
  		xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch)
  	}
  	goInstall(goBootstrap, "std", "cmd")
  	checkNotStale(goBootstrap, "std", "cmd")
  	checkNotStale(cmdGo, "std", "cmd")
  	if debug {
  		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
  		run("", ShowOutput|CheckExit, pathf("%s/buildid", tooldir), pathf("%s/pkg/%s_%s/runtime/internal/sys.a", goroot, goos, goarch))
  		checkNotStale(goBootstrap, append(toolchain, "runtime/internal/sys")...)
  		copyfile(pathf("%s/compile4", tooldir), pathf("%s/compile", tooldir), writeExec)
  	}
  
  	// Check that there are no new files in $GOROOT/bin other than
  	// go and gofmt and $GOOS_$GOARCH (target bin when cross-compiling).
  	binFiles, _ := filepath.Glob(pathf("%s/bin/*", goroot))
  	ok := map[string]bool{}
  	for _, f := range oldBinFiles {
  		ok[f] = true
  	}
  	for _, f := range binFiles {
  		elem := strings.TrimSuffix(filepath.Base(f), ".exe")
  		if !ok[f] && elem != "go" && elem != "gofmt" && elem != goos+"_"+goarch {
  			fatalf("unexpected new file in $GOROOT/bin: %s", elem)
  		}
  	}
  
  	// Remove go_bootstrap now that we're done.
  	xremove(pathf("%s/go_bootstrap", tooldir))
  
  	// Print trailing banner unless instructed otherwise.
  	if !noBanner {
  		banner()
  	}
  }
  
  func goInstall(goBinary string, args ...string) {
  	installCmd := []string{goBinary, "install", "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags}
  	if vflag > 0 {
  		installCmd = append(installCmd, "-v")
  	}
  
  	// Force only one process at a time on vx32 emulation.
  	if gohostos == "plan9" && os.Getenv("sysname") == "vx32" {
  		installCmd = append(installCmd, "-p=1")
  	}
  
  	run(goroot, ShowOutput|CheckExit, append(installCmd, args...)...)
  }
  
  func checkNotStale(goBinary string, targets ...string) {
  	out := run(goroot, CheckExit,
  		append([]string{
  			goBinary,
  			"list", "-gcflags=all=" + gogcflags, "-ldflags=all=" + goldflags,
  			"-f={{if .Stale}}\tSTALE {{.ImportPath}}: {{.StaleReason}}{{end}}",
  		}, targets...)...)
  	if strings.Contains(out, "\tSTALE ") {
  		os.Setenv("GODEBUG", "gocachehash=1")
  		for _, target := range []string{"runtime/internal/sys", "cmd/dist", "cmd/link"} {
  			if strings.Contains(out, "STALE "+target) {
  				run(goroot, ShowOutput|CheckExit, goBinary, "list", "-f={{.ImportPath}} {{.Stale}}", target)
  				break
  			}
  		}
  		fatalf("unexpected stale targets reported by %s list -gcflags=\"%s\" -ldflags=\"%s\" for %v:\n%s", goBinary, gogcflags, goldflags, targets, out)
  	}
  }
  
  // Cannot use go/build directly because cmd/dist for a new release
  // builds against an old release's go/build, which may be out of sync.
  // To reduce duplication, we generate the list for go/build from this.
  //
  // We list all supported platforms in this list, so that this is the
  // single point of truth for supported platforms. This list is used
  // by 'go tool dist list'.
  var cgoEnabled = map[string]bool{
  	"darwin/386":      true,
  	"darwin/amd64":    true,
  	"darwin/arm":      true,
  	"darwin/arm64":    true,
  	"dragonfly/amd64": true,
  	"freebsd/386":     true,
  	"freebsd/amd64":   true,
  	"freebsd/arm":     false,
  	"linux/386":       true,
  	"linux/amd64":     true,
  	"linux/arm":       true,
  	"linux/arm64":     true,
  	"linux/ppc64":     false,
  	"linux/ppc64le":   true,
  	"linux/mips":      true,
  	"linux/mipsle":    true,
  	"linux/mips64":    true,
  	"linux/mips64le":  true,
  	"linux/s390x":     true,
  	"android/386":     true,
  	"android/amd64":   true,
  	"android/arm":     true,
  	"android/arm64":   true,
  	"nacl/386":        false,
  	"nacl/amd64p32":   false,
  	"nacl/arm":        false,
  	"netbsd/386":      true,
  	"netbsd/amd64":    true,
  	"netbsd/arm":      true,
  	"openbsd/386":     true,
  	"openbsd/amd64":   true,
  	"openbsd/arm":     false,
  	"plan9/386":       false,
  	"plan9/amd64":     false,
  	"plan9/arm":       false,
  	"solaris/amd64":   true,
  	"windows/386":     true,
  	"windows/amd64":   true,
  }
  
  func needCC() bool {
  	switch os.Getenv("CGO_ENABLED") {
  	case "1":
  		return true
  	case "0":
  		return false
  	}
  	return cgoEnabled[gohostos+"/"+gohostarch]
  }
  
  func checkCC() {
  	if !needCC() {
  		return
  	}
  	if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil {
  		outputHdr := ""
  		if len(output) > 0 {
  			outputHdr = "\nCommand output:\n\n"
  		}
  		fatalf("cannot invoke C compiler %q: %v\n\n"+
  			"Go needs a system C compiler for use with cgo.\n"+
  			"To set a C compiler, set CC=the-compiler.\n"+
  			"To disable cgo, set CGO_ENABLED=0.\n%s%s", defaultcc, err, outputHdr, output)
  	}
  }
  
  func defaulttarg() string {
  	// xgetwd might return a path with symlinks fully resolved, and if
  	// there happens to be symlinks in goroot, then the hasprefix test
  	// will never succeed. Instead, we use xrealwd to get a canonical
  	// goroot/src before the comparison to avoid this problem.
  	pwd := xgetwd()
  	src := pathf("%s/src/", goroot)
  	real_src := xrealwd(src)
  	if !strings.HasPrefix(pwd, real_src) {
  		fatalf("current directory %s is not under %s", pwd, real_src)
  	}
  	pwd = pwd[len(real_src):]
  	// guard against xrealwd returning the directory without the trailing /
  	pwd = strings.TrimPrefix(pwd, "/")
  
  	return pwd
  }
  
  // Install installs the list of packages named on the command line.
  func cmdinstall() {
  	xflagparse(-1)
  
  	if flag.NArg() == 0 {
  		install(defaulttarg())
  	}
  
  	for _, arg := range flag.Args() {
  		install(arg)
  	}
  }
  
  // Clean deletes temporary objects.
  func cmdclean() {
  	xflagparse(0)
  	clean()
  }
  
  // Banner prints the 'now you've installed Go' banner.
  func cmdbanner() {
  	xflagparse(0)
  	banner()
  }
  
  func banner() {
  	if vflag > 0 {
  		xprintf("\n")
  	}
  	xprintf("---\n")
  	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
  	xprintf("Installed commands in %s\n", gobin)
  
  	if !xsamefile(goroot_final, goroot) {
  		// If the files are to be moved, don't check that gobin
  		// is on PATH; assume they know what they are doing.
  	} else if gohostos == "plan9" {
  		// Check that gobin is bound before /bin.
  		pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
  		ns := fmt.Sprintf("/proc/%s/ns", pid)
  		if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
  			xprintf("*** You need to bind %s before /bin.\n", gobin)
  		}
  	} else {
  		// Check that gobin appears in $PATH.
  		pathsep := ":"
  		if gohostos == "windows" {
  			pathsep = ";"
  		}
  		if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
  			xprintf("*** You need to add %s to your PATH.\n", gobin)
  		}
  	}
  
  	if !xsamefile(goroot_final, goroot) {
  		xprintf("\n"+
  			"The binaries expect %s to be copied or moved to %s\n",
  			goroot, goroot_final)
  	}
  }
  
  // Version prints the Go version.
  func cmdversion() {
  	xflagparse(0)
  	xprintf("%s\n", findgoversion())
  }
  
  // cmdlist lists all supported platforms.
  func cmdlist() {
  	jsonFlag := flag.Bool("json", false, "produce JSON output")
  	xflagparse(0)
  
  	var plats []string
  	for p := range cgoEnabled {
  		plats = append(plats, p)
  	}
  	sort.Strings(plats)
  
  	if !*jsonFlag {
  		for _, p := range plats {
  			xprintf("%s\n", p)
  		}
  		return
  	}
  
  	type jsonResult struct {
  		GOOS         string
  		GOARCH       string
  		CgoSupported bool
  	}
  	var results []jsonResult
  	for _, p := range plats {
  		fields := strings.Split(p, "/")
  		results = append(results, jsonResult{
  			GOOS:         fields[0],
  			GOARCH:       fields[1],
  			CgoSupported: cgoEnabled[p]})
  	}
  	out, err := json.MarshalIndent(results, "", "\t")
  	if err != nil {
  		fatalf("json marshal error: %v", err)
  	}
  	if _, err := os.Stdout.Write(out); err != nil {
  		fatalf("write failed: %v", err)
  	}
  }
  

View as plain text