github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/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  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    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  const (
    55  	CheckExit = 1 << iota
    56  	ShowOutput
    57  	Background
    58  )
    59  
    60  var outputLock sync.Mutex
    61  
    62  // run runs the command line cmd in dir.
    63  // If mode has ShowOutput set and Background unset, run passes cmd's output to
    64  // stdout/stderr directly. Otherwise, run returns cmd's output as a string.
    65  // If mode has CheckExit set and the command fails, run calls fatalf.
    66  // If mode has Background set, this command is being run as a
    67  // Background job. Only bgrun should use the Background mode,
    68  // not other callers.
    69  func run(dir string, mode int, cmd ...string) string {
    70  	if vflag > 1 {
    71  		errprintf("run: %s\n", strings.Join(cmd, " "))
    72  	}
    73  
    74  	xcmd := exec.Command(cmd[0], cmd[1:]...)
    75  	xcmd.Dir = dir
    76  	var data []byte
    77  	var err error
    78  
    79  	// If we want to show command output and this is not
    80  	// a background command, assume it's the only thing
    81  	// running, so we can just let it write directly stdout/stderr
    82  	// as it runs without fear of mixing the output with some
    83  	// other command's output. Not buffering lets the output
    84  	// appear as it is printed instead of once the command exits.
    85  	// This is most important for the invocation of 'go1.4 build -v bootstrap/...'.
    86  	if mode&(Background|ShowOutput) == ShowOutput {
    87  		xcmd.Stdout = os.Stdout
    88  		xcmd.Stderr = os.Stderr
    89  		err = xcmd.Run()
    90  	} else {
    91  		data, err = xcmd.CombinedOutput()
    92  	}
    93  	if err != nil && mode&CheckExit != 0 {
    94  		outputLock.Lock()
    95  		if len(data) > 0 {
    96  			xprintf("%s\n", data)
    97  		}
    98  		outputLock.Unlock()
    99  		if mode&Background != 0 {
   100  			// Prevent fatalf from waiting on our own goroutine's
   101  			// bghelper to exit:
   102  			bghelpers.Done()
   103  		}
   104  		fatalf("FAILED: %v: %v", strings.Join(cmd, " "), err)
   105  	}
   106  	if mode&ShowOutput != 0 {
   107  		outputLock.Lock()
   108  		os.Stdout.Write(data)
   109  		outputLock.Unlock()
   110  	}
   111  	if vflag > 2 {
   112  		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
   113  	}
   114  	return string(data)
   115  }
   116  
   117  var maxbg = 4 /* maximum number of jobs to run at once */
   118  
   119  var (
   120  	bgwork = make(chan func(), 1e5)
   121  
   122  	bghelpers sync.WaitGroup
   123  
   124  	dieOnce sync.Once // guards close of dying
   125  	dying   = make(chan struct{})
   126  )
   127  
   128  func bginit() {
   129  	bghelpers.Add(maxbg)
   130  	for i := 0; i < maxbg; i++ {
   131  		go bghelper()
   132  	}
   133  }
   134  
   135  func bghelper() {
   136  	defer bghelpers.Done()
   137  	for {
   138  		select {
   139  		case <-dying:
   140  			return
   141  		case w := <-bgwork:
   142  			// Dying takes precedence over doing more work.
   143  			select {
   144  			case <-dying:
   145  				return
   146  			default:
   147  				w()
   148  			}
   149  		}
   150  	}
   151  }
   152  
   153  // bgrun is like run but runs the command in the background.
   154  // CheckExit|ShowOutput mode is implied (since output cannot be returned).
   155  // bgrun adds 1 to wg immediately, and calls Done when the work completes.
   156  func bgrun(wg *sync.WaitGroup, dir string, cmd ...string) {
   157  	wg.Add(1)
   158  	bgwork <- func() {
   159  		defer wg.Done()
   160  		run(dir, CheckExit|ShowOutput|Background, cmd...)
   161  	}
   162  }
   163  
   164  // bgwait waits for pending bgruns to finish.
   165  // bgwait must be called from only a single goroutine at a time.
   166  func bgwait(wg *sync.WaitGroup) {
   167  	done := make(chan struct{})
   168  	go func() {
   169  		wg.Wait()
   170  		close(done)
   171  	}()
   172  	select {
   173  	case <-done:
   174  	case <-dying:
   175  	}
   176  }
   177  
   178  // xgetwd returns the current directory.
   179  func xgetwd() string {
   180  	wd, err := os.Getwd()
   181  	if err != nil {
   182  		fatalf("%s", err)
   183  	}
   184  	return wd
   185  }
   186  
   187  // xrealwd returns the 'real' name for the given path.
   188  // real is defined as what xgetwd returns in that directory.
   189  func xrealwd(path string) string {
   190  	old := xgetwd()
   191  	if err := os.Chdir(path); err != nil {
   192  		fatalf("chdir %s: %v", path, err)
   193  	}
   194  	real := xgetwd()
   195  	if err := os.Chdir(old); err != nil {
   196  		fatalf("chdir %s: %v", old, err)
   197  	}
   198  	return real
   199  }
   200  
   201  // isdir reports whether p names an existing directory.
   202  func isdir(p string) bool {
   203  	fi, err := os.Stat(p)
   204  	return err == nil && fi.IsDir()
   205  }
   206  
   207  // isfile reports whether p names an existing file.
   208  func isfile(p string) bool {
   209  	fi, err := os.Stat(p)
   210  	return err == nil && fi.Mode().IsRegular()
   211  }
   212  
   213  // mtime returns the modification time of the file p.
   214  func mtime(p string) time.Time {
   215  	fi, err := os.Stat(p)
   216  	if err != nil {
   217  		return time.Time{}
   218  	}
   219  	return fi.ModTime()
   220  }
   221  
   222  // readfile returns the content of the named file.
   223  func readfile(file string) string {
   224  	data, err := ioutil.ReadFile(file)
   225  	if err != nil {
   226  		fatalf("%v", err)
   227  	}
   228  	return string(data)
   229  }
   230  
   231  const (
   232  	writeExec = 1 << iota
   233  	writeSkipSame
   234  )
   235  
   236  // writefile writes text to the named file, creating it if needed.
   237  // if exec is non-zero, marks the file as executable.
   238  // If the file already exists and has the expected content,
   239  // it is not rewritten, to avoid changing the time stamp.
   240  func writefile(text, file string, flag int) {
   241  	new := []byte(text)
   242  	if flag&writeSkipSame != 0 {
   243  		old, err := ioutil.ReadFile(file)
   244  		if err == nil && bytes.Equal(old, new) {
   245  			return
   246  		}
   247  	}
   248  	mode := os.FileMode(0666)
   249  	if flag&writeExec != 0 {
   250  		mode = 0777
   251  	}
   252  	err := ioutil.WriteFile(file, new, mode)
   253  	if err != nil {
   254  		fatalf("%v", err)
   255  	}
   256  }
   257  
   258  // xmkdir creates the directory p.
   259  func xmkdir(p string) {
   260  	err := os.Mkdir(p, 0777)
   261  	if err != nil {
   262  		fatalf("%v", err)
   263  	}
   264  }
   265  
   266  // xmkdirall creates the directory p and its parents, as needed.
   267  func xmkdirall(p string) {
   268  	err := os.MkdirAll(p, 0777)
   269  	if err != nil {
   270  		fatalf("%v", err)
   271  	}
   272  }
   273  
   274  // xremove removes the file p.
   275  func xremove(p string) {
   276  	if vflag > 2 {
   277  		errprintf("rm %s\n", p)
   278  	}
   279  	os.Remove(p)
   280  }
   281  
   282  // xremoveall removes the file or directory tree rooted at p.
   283  func xremoveall(p string) {
   284  	if vflag > 2 {
   285  		errprintf("rm -r %s\n", p)
   286  	}
   287  	os.RemoveAll(p)
   288  }
   289  
   290  // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
   291  // The names are relative to dir; they are not full paths.
   292  func xreaddir(dir string) []string {
   293  	f, err := os.Open(dir)
   294  	if err != nil {
   295  		fatalf("%v", err)
   296  	}
   297  	defer f.Close()
   298  	names, err := f.Readdirnames(-1)
   299  	if err != nil {
   300  		fatalf("reading %s: %v", dir, err)
   301  	}
   302  	return names
   303  }
   304  
   305  // xreaddir replaces dst with a list of the names of the files in dir.
   306  // The names are relative to dir; they are not full paths.
   307  func xreaddirfiles(dir string) []string {
   308  	f, err := os.Open(dir)
   309  	if err != nil {
   310  		fatalf("%v", err)
   311  	}
   312  	defer f.Close()
   313  	infos, err := f.Readdir(-1)
   314  	if err != nil {
   315  		fatalf("reading %s: %v", dir, err)
   316  	}
   317  	var names []string
   318  	for _, fi := range infos {
   319  		if !fi.IsDir() {
   320  			names = append(names, fi.Name())
   321  		}
   322  	}
   323  	return names
   324  }
   325  
   326  // xworkdir creates a new temporary directory to hold object files
   327  // and returns the name of that directory.
   328  func xworkdir() string {
   329  	name, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-tool-dist-")
   330  	if err != nil {
   331  		fatalf("%v", err)
   332  	}
   333  	return name
   334  }
   335  
   336  // fatalf prints an error message to standard error and exits.
   337  func fatalf(format string, args ...interface{}) {
   338  	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
   339  
   340  	dieOnce.Do(func() { close(dying) })
   341  
   342  	// Wait for background goroutines to finish,
   343  	// so that exit handler that removes the work directory
   344  	// is not fighting with active writes or open files.
   345  	bghelpers.Wait()
   346  
   347  	xexit(2)
   348  }
   349  
   350  var atexits []func()
   351  
   352  // xexit exits the process with return code n.
   353  func xexit(n int) {
   354  	for i := len(atexits) - 1; i >= 0; i-- {
   355  		atexits[i]()
   356  	}
   357  	os.Exit(n)
   358  }
   359  
   360  // xatexit schedules the exit-handler f to be run when the program exits.
   361  func xatexit(f func()) {
   362  	atexits = append(atexits, f)
   363  }
   364  
   365  // xprintf prints a message to standard output.
   366  func xprintf(format string, args ...interface{}) {
   367  	fmt.Printf(format, args...)
   368  }
   369  
   370  // errprintf prints a message to standard output.
   371  func errprintf(format string, args ...interface{}) {
   372  	fmt.Fprintf(os.Stderr, format, args...)
   373  }
   374  
   375  // xsamefile reports whether f1 and f2 are the same file (or dir)
   376  func xsamefile(f1, f2 string) bool {
   377  	fi1, err1 := os.Stat(f1)
   378  	fi2, err2 := os.Stat(f2)
   379  	if err1 != nil || err2 != nil {
   380  		return f1 == f2
   381  	}
   382  	return os.SameFile(fi1, fi2)
   383  }
   384  
   385  func xgetgoarm() string {
   386  	if goos == "nacl" {
   387  		// NaCl guarantees VFPv3 and is always cross-compiled.
   388  		return "7"
   389  	}
   390  	if goos == "darwin" {
   391  		// Assume all darwin/arm devices are have VFPv3. This
   392  		// port is also mostly cross-compiled, so it makes little
   393  		// sense to auto-detect the setting.
   394  		return "7"
   395  	}
   396  	if gohostarch != "arm" || goos != gohostos {
   397  		// Conservative default for cross-compilation.
   398  		return "5"
   399  	}
   400  	if goos == "freebsd" || goos == "openbsd" {
   401  		// FreeBSD has broken VFP support.
   402  		// OpenBSD currently only supports softfloat.
   403  		return "5"
   404  	}
   405  
   406  	// Try to exec ourselves in a mode to detect VFP support.
   407  	// Seeing how far it gets determines which instructions failed.
   408  	// The test is OS-agnostic.
   409  	out := run("", 0, os.Args[0], "-check-goarm")
   410  	v1ok := strings.Contains(out, "VFPv1 OK.")
   411  	v3ok := strings.Contains(out, "VFPv3 OK.")
   412  
   413  	if v1ok && v3ok {
   414  		return "7"
   415  	}
   416  	if v1ok {
   417  		return "6"
   418  	}
   419  	return "5"
   420  }
   421  
   422  func min(a, b int) int {
   423  	if a < b {
   424  		return a
   425  	}
   426  	return b
   427  }
   428  
   429  // elfIsLittleEndian detects if the ELF file is little endian.
   430  func elfIsLittleEndian(fn string) bool {
   431  	// read the ELF file header to determine the endianness without using the
   432  	// debug/elf package.
   433  	file, err := os.Open(fn)
   434  	if err != nil {
   435  		fatalf("failed to open file to determine endianness: %v", err)
   436  	}
   437  	defer file.Close()
   438  	var hdr [16]byte
   439  	if _, err := io.ReadFull(file, hdr[:]); err != nil {
   440  		fatalf("failed to read ELF header to determine endianness: %v", err)
   441  	}
   442  	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
   443  	switch hdr[5] {
   444  	default:
   445  		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
   446  	case 1:
   447  		return true
   448  	case 2:
   449  		return false
   450  	}
   451  	panic("unreachable")
   452  }
   453  
   454  // count is a flag.Value that is like a flag.Bool and a flag.Int.
   455  // If used as -name, it increments the count, but -name=x sets the count.
   456  // Used for verbose flag -v.
   457  type count int
   458  
   459  func (c *count) String() string {
   460  	return fmt.Sprint(int(*c))
   461  }
   462  
   463  func (c *count) Set(s string) error {
   464  	switch s {
   465  	case "true":
   466  		*c++
   467  	case "false":
   468  		*c = 0
   469  	default:
   470  		n, err := strconv.Atoi(s)
   471  		if err != nil {
   472  			return fmt.Errorf("invalid count %q", s)
   473  		}
   474  		*c = count(n)
   475  	}
   476  	return nil
   477  }
   478  
   479  func (c *count) IsBoolFlag() bool {
   480  	return true
   481  }
   482  
   483  func xflagparse(maxargs int) {
   484  	flag.Var((*count)(&vflag), "v", "verbosity")
   485  	flag.Parse()
   486  	if maxargs >= 0 && flag.NArg() > maxargs {
   487  		flag.Usage()
   488  	}
   489  }