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