github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/src/cmd/dist/util.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  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	"sync/atomic"
    20  	"time"
    21  )
    22  
    23  // pathf is fmt.Sprintf for generating paths
    24  // (on windows it turns / into \ after the printf).
    25  func pathf(format string, args ...interface{}) string {
    26  	return filepath.Clean(fmt.Sprintf(format, args...))
    27  }
    28  
    29  // filter returns a slice containing the elements x from list for which f(x) == true.
    30  func filter(list []string, f func(string) bool) []string {
    31  	var out []string
    32  	for _, x := range list {
    33  		if f(x) {
    34  			out = append(out, x)
    35  		}
    36  	}
    37  	return out
    38  }
    39  
    40  // uniq returns a sorted slice containing the unique elements of list.
    41  func uniq(list []string) []string {
    42  	out := make([]string, len(list))
    43  	copy(out, list)
    44  	sort.Strings(out)
    45  	keep := out[:0]
    46  	for _, x := range out {
    47  		if len(keep) == 0 || keep[len(keep)-1] != x {
    48  			keep = append(keep, x)
    49  		}
    50  	}
    51  	return keep
    52  }
    53  
    54  // splitlines returns a slice with the result of splitting
    55  // the input p after each \n.
    56  func splitlines(p string) []string {
    57  	return strings.SplitAfter(p, "\n")
    58  }
    59  
    60  // splitfields replaces the vector v with the result of splitting
    61  // the input p into non-empty fields containing no spaces.
    62  func splitfields(p string) []string {
    63  	return strings.Fields(p)
    64  }
    65  
    66  const (
    67  	CheckExit = 1 << iota
    68  	ShowOutput
    69  	Background
    70  )
    71  
    72  var outputLock sync.Mutex
    73  
    74  // run runs the command line cmd in dir.
    75  // If mode has ShowOutput set, run collects cmd's output and returns it as a string;
    76  // otherwise, run prints cmd's output to standard output after the command finishes.
    77  // If mode has CheckExit set and the command fails, run calls fatal.
    78  // If mode has Background set, this command is being run as a
    79  // Background job. Only bgrun should use the Background mode,
    80  // not other callers.
    81  func run(dir string, mode int, cmd ...string) string {
    82  	if vflag > 1 {
    83  		errprintf("run: %s\n", strings.Join(cmd, " "))
    84  	}
    85  
    86  	xcmd := exec.Command(cmd[0], cmd[1:]...)
    87  	xcmd.Dir = dir
    88  	var data []byte
    89  	var err error
    90  
    91  	// If we want to show command output and this is not
    92  	// a background command, assume it's the only thing
    93  	// running, so we can just let it write directly stdout/stderr
    94  	// as it runs without fear of mixing the output with some
    95  	// other command's output. Not buffering lets the output
    96  	// appear as it is printed instead of once the command exits.
    97  	// This is most important for the invocation of 'go1.4 build -v bootstrap/...'.
    98  	if mode&(Background|ShowOutput) == ShowOutput {
    99  		xcmd.Stdout = os.Stdout
   100  		xcmd.Stderr = os.Stderr
   101  		err = xcmd.Run()
   102  	} else {
   103  		data, err = xcmd.CombinedOutput()
   104  	}
   105  	if err != nil && mode&CheckExit != 0 {
   106  		outputLock.Lock()
   107  		if len(data) > 0 {
   108  			xprintf("%s\n", data)
   109  		}
   110  		outputLock.Unlock()
   111  		if mode&Background != 0 {
   112  			bgdied.Done()
   113  		}
   114  		fatal("FAILED: %v: %v", strings.Join(cmd, " "), err)
   115  	}
   116  	if mode&ShowOutput != 0 {
   117  		outputLock.Lock()
   118  		os.Stdout.Write(data)
   119  		outputLock.Unlock()
   120  	}
   121  	if vflag > 2 {
   122  		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
   123  	}
   124  	return string(data)
   125  }
   126  
   127  var maxbg = 4 /* maximum number of jobs to run at once */
   128  
   129  var (
   130  	bgwork = make(chan func(), 1e5)
   131  	bgdone = make(chan struct{}, 1e5)
   132  
   133  	bgdied sync.WaitGroup
   134  	nwork  int32
   135  	ndone  int32
   136  
   137  	dying  = make(chan bool)
   138  	nfatal int32
   139  )
   140  
   141  func bginit() {
   142  	bgdied.Add(maxbg)
   143  	for i := 0; i < maxbg; i++ {
   144  		go bghelper()
   145  	}
   146  }
   147  
   148  func bghelper() {
   149  	for {
   150  		w := <-bgwork
   151  		w()
   152  
   153  		// Stop if we're dying.
   154  		if atomic.LoadInt32(&nfatal) > 0 {
   155  			bgdied.Done()
   156  			return
   157  		}
   158  	}
   159  }
   160  
   161  // bgrun is like run but runs the command in the background.
   162  // CheckExit|ShowOutput mode is implied (since output cannot be returned).
   163  func bgrun(dir string, cmd ...string) {
   164  	bgwork <- func() {
   165  		run(dir, CheckExit|ShowOutput|Background, cmd...)
   166  	}
   167  }
   168  
   169  // bgwait waits for pending bgruns to finish.
   170  // bgwait must be called from only a single goroutine at a time.
   171  func bgwait() {
   172  	var wg sync.WaitGroup
   173  	wg.Add(maxbg)
   174  	done := make(chan bool)
   175  	for i := 0; i < maxbg; i++ {
   176  		bgwork <- func() {
   177  			wg.Done()
   178  
   179  			// Hold up bg goroutine until either the wait finishes
   180  			// or the program starts dying due to a call to fatal.
   181  			select {
   182  			case <-dying:
   183  			case <-done:
   184  			}
   185  		}
   186  	}
   187  	wg.Wait()
   188  	close(done)
   189  }
   190  
   191  // xgetwd returns the current directory.
   192  func xgetwd() string {
   193  	wd, err := os.Getwd()
   194  	if err != nil {
   195  		fatal("%s", err)
   196  	}
   197  	return wd
   198  }
   199  
   200  // xrealwd returns the 'real' name for the given path.
   201  // real is defined as what xgetwd returns in that directory.
   202  func xrealwd(path string) string {
   203  	old := xgetwd()
   204  	if err := os.Chdir(path); err != nil {
   205  		fatal("chdir %s: %v", path, err)
   206  	}
   207  	real := xgetwd()
   208  	if err := os.Chdir(old); err != nil {
   209  		fatal("chdir %s: %v", old, err)
   210  	}
   211  	return real
   212  }
   213  
   214  // isdir reports whether p names an existing directory.
   215  func isdir(p string) bool {
   216  	fi, err := os.Stat(p)
   217  	return err == nil && fi.IsDir()
   218  }
   219  
   220  // isfile reports whether p names an existing file.
   221  func isfile(p string) bool {
   222  	fi, err := os.Stat(p)
   223  	return err == nil && fi.Mode().IsRegular()
   224  }
   225  
   226  // mtime returns the modification time of the file p.
   227  func mtime(p string) time.Time {
   228  	fi, err := os.Stat(p)
   229  	if err != nil {
   230  		return time.Time{}
   231  	}
   232  	return fi.ModTime()
   233  }
   234  
   235  // isabs reports whether p is an absolute path.
   236  func isabs(p string) bool {
   237  	return filepath.IsAbs(p)
   238  }
   239  
   240  // readfile returns the content of the named file.
   241  func readfile(file string) string {
   242  	data, err := ioutil.ReadFile(file)
   243  	if err != nil {
   244  		fatal("%v", err)
   245  	}
   246  	return string(data)
   247  }
   248  
   249  const (
   250  	writeExec = 1 << iota
   251  	writeSkipSame
   252  )
   253  
   254  // writefile writes b to the named file, creating it if needed.
   255  // if exec is non-zero, marks the file as executable.
   256  // If the file already exists and has the expected content,
   257  // it is not rewritten, to avoid changing the time stamp.
   258  func writefile(b, file string, flag int) {
   259  	new := []byte(b)
   260  	if flag&writeSkipSame != 0 {
   261  		old, err := ioutil.ReadFile(file)
   262  		if err == nil && bytes.Equal(old, new) {
   263  			return
   264  		}
   265  	}
   266  	mode := os.FileMode(0666)
   267  	if flag&writeExec != 0 {
   268  		mode = 0777
   269  	}
   270  	err := ioutil.WriteFile(file, new, mode)
   271  	if err != nil {
   272  		fatal("%v", err)
   273  	}
   274  }
   275  
   276  // xmkdir creates the directory p.
   277  func xmkdir(p string) {
   278  	err := os.Mkdir(p, 0777)
   279  	if err != nil {
   280  		fatal("%v", err)
   281  	}
   282  }
   283  
   284  // xmkdirall creates the directory p and its parents, as needed.
   285  func xmkdirall(p string) {
   286  	err := os.MkdirAll(p, 0777)
   287  	if err != nil {
   288  		fatal("%v", err)
   289  	}
   290  }
   291  
   292  // xremove removes the file p.
   293  func xremove(p string) {
   294  	if vflag > 2 {
   295  		errprintf("rm %s\n", p)
   296  	}
   297  	os.Remove(p)
   298  }
   299  
   300  // xremoveall removes the file or directory tree rooted at p.
   301  func xremoveall(p string) {
   302  	if vflag > 2 {
   303  		errprintf("rm -r %s\n", p)
   304  	}
   305  	os.RemoveAll(p)
   306  }
   307  
   308  // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
   309  // The names are relative to dir; they are not full paths.
   310  func xreaddir(dir string) []string {
   311  	f, err := os.Open(dir)
   312  	if err != nil {
   313  		fatal("%v", err)
   314  	}
   315  	defer f.Close()
   316  	names, err := f.Readdirnames(-1)
   317  	if err != nil {
   318  		fatal("reading %s: %v", dir, err)
   319  	}
   320  	return names
   321  }
   322  
   323  // xreaddir replaces dst with a list of the names of the files in dir.
   324  // The names are relative to dir; they are not full paths.
   325  func xreaddirfiles(dir string) []string {
   326  	f, err := os.Open(dir)
   327  	if err != nil {
   328  		fatal("%v", err)
   329  	}
   330  	defer f.Close()
   331  	infos, err := f.Readdir(-1)
   332  	if err != nil {
   333  		fatal("reading %s: %v", dir, err)
   334  	}
   335  	var names []string
   336  	for _, fi := range infos {
   337  		if !fi.IsDir() {
   338  			names = append(names, fi.Name())
   339  		}
   340  	}
   341  	return names
   342  }
   343  
   344  // xworkdir creates a new temporary directory to hold object files
   345  // and returns the name of that directory.
   346  func xworkdir() string {
   347  	name, err := ioutil.TempDir("", "go-tool-dist-")
   348  	if err != nil {
   349  		fatal("%v", err)
   350  	}
   351  	return name
   352  }
   353  
   354  // fatal prints an error message to standard error and exits.
   355  func fatal(format string, args ...interface{}) {
   356  	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
   357  
   358  	// Wait for background goroutines to finish,
   359  	// so that exit handler that removes the work directory
   360  	// is not fighting with active writes or open files.
   361  	if atomic.AddInt32(&nfatal, 1) == 1 {
   362  		close(dying)
   363  	}
   364  	for i := 0; i < maxbg; i++ {
   365  		bgwork <- func() {} // wake up workers so they notice nfatal > 0
   366  	}
   367  	bgdied.Wait()
   368  
   369  	xexit(2)
   370  }
   371  
   372  var atexits []func()
   373  
   374  // xexit exits the process with return code n.
   375  func xexit(n int) {
   376  	for i := len(atexits) - 1; i >= 0; i-- {
   377  		atexits[i]()
   378  	}
   379  	os.Exit(n)
   380  }
   381  
   382  // xatexit schedules the exit-handler f to be run when the program exits.
   383  func xatexit(f func()) {
   384  	atexits = append(atexits, f)
   385  }
   386  
   387  // xprintf prints a message to standard output.
   388  func xprintf(format string, args ...interface{}) {
   389  	fmt.Printf(format, args...)
   390  }
   391  
   392  // errprintf prints a message to standard output.
   393  func errprintf(format string, args ...interface{}) {
   394  	fmt.Fprintf(os.Stderr, format, args...)
   395  }
   396  
   397  // main takes care of OS-specific startup and dispatches to xmain.
   398  func main() {
   399  	os.Setenv("TERM", "dumb") // disable escape codes in clang errors
   400  
   401  	slash = string(filepath.Separator)
   402  
   403  	gohostos = runtime.GOOS
   404  	switch gohostos {
   405  	case "darwin":
   406  		// Even on 64-bit platform, darwin uname -m prints i386.
   407  		if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
   408  			gohostarch = "amd64"
   409  		}
   410  	case "solaris":
   411  		// Even on 64-bit platform, solaris uname -m prints i86pc.
   412  		out := run("", CheckExit, "isainfo", "-n")
   413  		if strings.Contains(out, "amd64") {
   414  			gohostarch = "amd64"
   415  		}
   416  		if strings.Contains(out, "i386") {
   417  			gohostarch = "386"
   418  		}
   419  	case "plan9":
   420  		gohostarch = os.Getenv("objtype")
   421  		if gohostarch == "" {
   422  			fatal("$objtype is unset")
   423  		}
   424  	case "windows":
   425  		exe = ".exe"
   426  	}
   427  
   428  	sysinit()
   429  
   430  	if gohostarch == "" {
   431  		// Default Unix system.
   432  		out := run("", CheckExit, "uname", "-m")
   433  		switch {
   434  		case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
   435  			gohostarch = "amd64"
   436  		case strings.Contains(out, "86"):
   437  			gohostarch = "386"
   438  		case strings.Contains(out, "arm"):
   439  			gohostarch = "arm"
   440  		case strings.Contains(out, "aarch64"):
   441  			gohostarch = "arm64"
   442  		case strings.Contains(out, "ppc64le"):
   443  			gohostarch = "ppc64le"
   444  		case strings.Contains(out, "ppc64"):
   445  			gohostarch = "ppc64"
   446  		case gohostos == "darwin":
   447  			if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") {
   448  				gohostarch = "arm"
   449  			}
   450  		default:
   451  			fatal("unknown architecture: %s", out)
   452  		}
   453  	}
   454  
   455  	if gohostarch == "arm" {
   456  		maxbg = min(maxbg, runtime.NumCPU())
   457  	}
   458  	bginit()
   459  
   460  	// The OS X 10.6 linker does not support external linking mode.
   461  	// See golang.org/issue/5130.
   462  	//
   463  	// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
   464  	// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
   465  	// See golang.org/issue/5822.
   466  	//
   467  	// Roughly, OS X 10.N shows up as uname release (N+4),
   468  	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
   469  	if gohostos == "darwin" {
   470  		rel := run("", CheckExit, "uname", "-r")
   471  		if i := strings.Index(rel, "."); i >= 0 {
   472  			rel = rel[:i]
   473  		}
   474  		osx, _ := strconv.Atoi(rel)
   475  		if osx <= 6+4 {
   476  			goextlinkenabled = "0"
   477  		}
   478  		if osx >= 8+4 {
   479  			defaultclang = true
   480  		}
   481  	}
   482  
   483  	xinit()
   484  	xmain()
   485  	xexit(0)
   486  }
   487  
   488  // xsamefile reports whether f1 and f2 are the same file (or dir)
   489  func xsamefile(f1, f2 string) bool {
   490  	fi1, err1 := os.Stat(f1)
   491  	fi2, err2 := os.Stat(f2)
   492  	if err1 != nil || err2 != nil {
   493  		return f1 == f2
   494  	}
   495  	return os.SameFile(fi1, fi2)
   496  }
   497  
   498  func xgetgoarm() string {
   499  	if goos == "nacl" {
   500  		// NaCl guarantees VFPv3 and is always cross-compiled.
   501  		return "7"
   502  	}
   503  	if goos == "darwin" {
   504  		// Assume all darwin/arm devices are have VFPv3. This
   505  		// port is also mostly cross-compiled, so it makes little
   506  		// sense to auto-detect the setting.
   507  		return "7"
   508  	}
   509  	if gohostarch != "arm" || goos != gohostos {
   510  		// Conservative default for cross-compilation.
   511  		return "5"
   512  	}
   513  	if goos == "freebsd" || goos == "openbsd" {
   514  		// FreeBSD has broken VFP support.
   515  		// OpenBSD currently only supports softfloat.
   516  		return "5"
   517  	}
   518  	if goos != "linux" {
   519  		// All other arm platforms that we support
   520  		// require ARMv7.
   521  		return "7"
   522  	}
   523  	cpuinfo := readfile("/proc/cpuinfo")
   524  	goarm := "5"
   525  	for _, line := range splitlines(cpuinfo) {
   526  		line := strings.SplitN(line, ":", 2)
   527  		if len(line) < 2 {
   528  			continue
   529  		}
   530  		if strings.TrimSpace(line[0]) != "Features" {
   531  			continue
   532  		}
   533  		features := splitfields(line[1])
   534  		sort.Strings(features) // so vfpv3 sorts after vfp
   535  
   536  		// Infer GOARM value from the vfp features available
   537  		// on this host. Values of GOARM detected are:
   538  		// 5: no vfp support was found
   539  		// 6: vfp (v1) support was detected, but no higher
   540  		// 7: vfpv3 support was detected.
   541  		// This matches the assertions in runtime.checkarm.
   542  		for _, f := range features {
   543  			switch f {
   544  			case "vfp":
   545  				goarm = "6"
   546  			case "vfpv3":
   547  				goarm = "7"
   548  			}
   549  		}
   550  	}
   551  	return goarm
   552  }
   553  
   554  func min(a, b int) int {
   555  	if a < b {
   556  		return a
   557  	}
   558  	return b
   559  }