Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/dist/util.go

Documentation: cmd/dist

     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  	xremove(file) // in case of symlink tricks by misc/reboot test
   253  	err := ioutil.WriteFile(file, new, mode)
   254  	if err != nil {
   255  		fatalf("%v", err)
   256  	}
   257  }
   258  
   259  // xmkdir creates the directory p.
   260  func xmkdir(p string) {
   261  	err := os.Mkdir(p, 0777)
   262  	if err != nil {
   263  		fatalf("%v", err)
   264  	}
   265  }
   266  
   267  // xmkdirall creates the directory p and its parents, as needed.
   268  func xmkdirall(p string) {
   269  	err := os.MkdirAll(p, 0777)
   270  	if err != nil {
   271  		fatalf("%v", err)
   272  	}
   273  }
   274  
   275  // xremove removes the file p.
   276  func xremove(p string) {
   277  	if vflag > 2 {
   278  		errprintf("rm %s\n", p)
   279  	}
   280  	os.Remove(p)
   281  }
   282  
   283  // xremoveall removes the file or directory tree rooted at p.
   284  func xremoveall(p string) {
   285  	if vflag > 2 {
   286  		errprintf("rm -r %s\n", p)
   287  	}
   288  	os.RemoveAll(p)
   289  }
   290  
   291  // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
   292  // The names are relative to dir; they are not full paths.
   293  func xreaddir(dir string) []string {
   294  	f, err := os.Open(dir)
   295  	if err != nil {
   296  		fatalf("%v", err)
   297  	}
   298  	defer f.Close()
   299  	names, err := f.Readdirnames(-1)
   300  	if err != nil {
   301  		fatalf("reading %s: %v", dir, err)
   302  	}
   303  	return names
   304  }
   305  
   306  // xreaddir replaces dst with a list of the names of the files in dir.
   307  // The names are relative to dir; they are not full paths.
   308  func xreaddirfiles(dir string) []string {
   309  	f, err := os.Open(dir)
   310  	if err != nil {
   311  		fatalf("%v", err)
   312  	}
   313  	defer f.Close()
   314  	infos, err := f.Readdir(-1)
   315  	if err != nil {
   316  		fatalf("reading %s: %v", dir, err)
   317  	}
   318  	var names []string
   319  	for _, fi := range infos {
   320  		if !fi.IsDir() {
   321  			names = append(names, fi.Name())
   322  		}
   323  	}
   324  	return names
   325  }
   326  
   327  // xworkdir creates a new temporary directory to hold object files
   328  // and returns the name of that directory.
   329  func xworkdir() string {
   330  	name, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-tool-dist-")
   331  	if err != nil {
   332  		fatalf("%v", err)
   333  	}
   334  	return name
   335  }
   336  
   337  // fatalf prints an error message to standard error and exits.
   338  func fatalf(format string, args ...interface{}) {
   339  	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
   340  
   341  	dieOnce.Do(func() { close(dying) })
   342  
   343  	// Wait for background goroutines to finish,
   344  	// so that exit handler that removes the work directory
   345  	// is not fighting with active writes or open files.
   346  	bghelpers.Wait()
   347  
   348  	xexit(2)
   349  }
   350  
   351  var atexits []func()
   352  
   353  // xexit exits the process with return code n.
   354  func xexit(n int) {
   355  	for i := len(atexits) - 1; i >= 0; i-- {
   356  		atexits[i]()
   357  	}
   358  	os.Exit(n)
   359  }
   360  
   361  // xatexit schedules the exit-handler f to be run when the program exits.
   362  func xatexit(f func()) {
   363  	atexits = append(atexits, f)
   364  }
   365  
   366  // xprintf prints a message to standard output.
   367  func xprintf(format string, args ...interface{}) {
   368  	fmt.Printf(format, args...)
   369  }
   370  
   371  // errprintf prints a message to standard output.
   372  func errprintf(format string, args ...interface{}) {
   373  	fmt.Fprintf(os.Stderr, format, args...)
   374  }
   375  
   376  // xsamefile reports whether f1 and f2 are the same file (or dir)
   377  func xsamefile(f1, f2 string) bool {
   378  	fi1, err1 := os.Stat(f1)
   379  	fi2, err2 := os.Stat(f2)
   380  	if err1 != nil || err2 != nil {
   381  		return f1 == f2
   382  	}
   383  	return os.SameFile(fi1, fi2)
   384  }
   385  
   386  func xgetgoarm() string {
   387  	if goos == "android" {
   388  		// Assume all android devices have VFPv3.
   389  		// These ports are also mostly cross-compiled, so it makes little
   390  		// sense to auto-detect the setting.
   391  		return "7"
   392  	}
   393  	if goos == "windows" {
   394  		// windows/arm only works with ARMv7 executables.
   395  		return "7"
   396  	}
   397  	if gohostarch != "arm" || goos != gohostos {
   398  		// Conservative default for cross-compilation.
   399  		return "5"
   400  	}
   401  
   402  	// Try to exec ourselves in a mode to detect VFP support.
   403  	// Seeing how far it gets determines which instructions failed.
   404  	// The test is OS-agnostic.
   405  	out := run("", 0, os.Args[0], "-check-goarm")
   406  	v1ok := strings.Contains(out, "VFPv1 OK.")
   407  	v3ok := strings.Contains(out, "VFPv3 OK.")
   408  
   409  	if v1ok && v3ok {
   410  		return "7"
   411  	}
   412  	if v1ok {
   413  		return "6"
   414  	}
   415  	return "5"
   416  }
   417  
   418  func min(a, b int) int {
   419  	if a < b {
   420  		return a
   421  	}
   422  	return b
   423  }
   424  
   425  // elfIsLittleEndian detects if the ELF file is little endian.
   426  func elfIsLittleEndian(fn string) bool {
   427  	// read the ELF file header to determine the endianness without using the
   428  	// debug/elf package.
   429  	file, err := os.Open(fn)
   430  	if err != nil {
   431  		fatalf("failed to open file to determine endianness: %v", err)
   432  	}
   433  	defer file.Close()
   434  	var hdr [16]byte
   435  	if _, err := io.ReadFull(file, hdr[:]); err != nil {
   436  		fatalf("failed to read ELF header to determine endianness: %v", err)
   437  	}
   438  	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
   439  	switch hdr[5] {
   440  	default:
   441  		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
   442  	case 1:
   443  		return true
   444  	case 2:
   445  		return false
   446  	}
   447  	panic("unreachable")
   448  }
   449  
   450  // count is a flag.Value that is like a flag.Bool and a flag.Int.
   451  // If used as -name, it increments the count, but -name=x sets the count.
   452  // Used for verbose flag -v.
   453  type count int
   454  
   455  func (c *count) String() string {
   456  	return fmt.Sprint(int(*c))
   457  }
   458  
   459  func (c *count) Set(s string) error {
   460  	switch s {
   461  	case "true":
   462  		*c++
   463  	case "false":
   464  		*c = 0
   465  	default:
   466  		n, err := strconv.Atoi(s)
   467  		if err != nil {
   468  			return fmt.Errorf("invalid count %q", s)
   469  		}
   470  		*c = count(n)
   471  	}
   472  	return nil
   473  }
   474  
   475  func (c *count) IsBoolFlag() bool {
   476  	return true
   477  }
   478  
   479  func xflagparse(maxargs int) {
   480  	flag.Var((*count)(&vflag), "v", "verbosity")
   481  	flag.Parse()
   482  	if maxargs >= 0 && flag.NArg() > maxargs {
   483  		flag.Usage()
   484  	}
   485  }
   486  

View as plain text