Source file src/cmd/dist/util.go

     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  	setDir(xcmd, 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  		// Don't return to the caller, to avoid reporting additional errors
   176  		// to the user.
   177  		select {}
   178  	}
   179  }
   180  
   181  // xgetwd returns the current directory.
   182  func xgetwd() string {
   183  	wd, err := os.Getwd()
   184  	if err != nil {
   185  		fatalf("%s", err)
   186  	}
   187  	return wd
   188  }
   189  
   190  // xrealwd returns the 'real' name for the given path.
   191  // real is defined as what xgetwd returns in that directory.
   192  func xrealwd(path string) string {
   193  	old := xgetwd()
   194  	if err := os.Chdir(path); err != nil {
   195  		fatalf("chdir %s: %v", path, err)
   196  	}
   197  	real := xgetwd()
   198  	if err := os.Chdir(old); err != nil {
   199  		fatalf("chdir %s: %v", old, err)
   200  	}
   201  	return real
   202  }
   203  
   204  // isdir reports whether p names an existing directory.
   205  func isdir(p string) bool {
   206  	fi, err := os.Stat(p)
   207  	return err == nil && fi.IsDir()
   208  }
   209  
   210  // isfile reports whether p names an existing file.
   211  func isfile(p string) bool {
   212  	fi, err := os.Stat(p)
   213  	return err == nil && fi.Mode().IsRegular()
   214  }
   215  
   216  // mtime returns the modification time of the file p.
   217  func mtime(p string) time.Time {
   218  	fi, err := os.Stat(p)
   219  	if err != nil {
   220  		return time.Time{}
   221  	}
   222  	return fi.ModTime()
   223  }
   224  
   225  // readfile returns the content of the named file.
   226  func readfile(file string) string {
   227  	data, err := ioutil.ReadFile(file)
   228  	if err != nil {
   229  		fatalf("%v", err)
   230  	}
   231  	return string(data)
   232  }
   233  
   234  const (
   235  	writeExec = 1 << iota
   236  	writeSkipSame
   237  )
   238  
   239  // writefile writes text to the named file, creating it if needed.
   240  // if exec is non-zero, marks the file as executable.
   241  // If the file already exists and has the expected content,
   242  // it is not rewritten, to avoid changing the time stamp.
   243  func writefile(text, file string, flag int) {
   244  	new := []byte(text)
   245  	if flag&writeSkipSame != 0 {
   246  		old, err := ioutil.ReadFile(file)
   247  		if err == nil && bytes.Equal(old, new) {
   248  			return
   249  		}
   250  	}
   251  	mode := os.FileMode(0666)
   252  	if flag&writeExec != 0 {
   253  		mode = 0777
   254  	}
   255  	xremove(file) // in case of symlink tricks by misc/reboot test
   256  	err := ioutil.WriteFile(file, new, mode)
   257  	if err != nil {
   258  		fatalf("%v", err)
   259  	}
   260  }
   261  
   262  // xmkdir creates the directory p.
   263  func xmkdir(p string) {
   264  	err := os.Mkdir(p, 0777)
   265  	if err != nil {
   266  		fatalf("%v", err)
   267  	}
   268  }
   269  
   270  // xmkdirall creates the directory p and its parents, as needed.
   271  func xmkdirall(p string) {
   272  	err := os.MkdirAll(p, 0777)
   273  	if err != nil {
   274  		fatalf("%v", err)
   275  	}
   276  }
   277  
   278  // xremove removes the file p.
   279  func xremove(p string) {
   280  	if vflag > 2 {
   281  		errprintf("rm %s\n", p)
   282  	}
   283  	os.Remove(p)
   284  }
   285  
   286  // xremoveall removes the file or directory tree rooted at p.
   287  func xremoveall(p string) {
   288  	if vflag > 2 {
   289  		errprintf("rm -r %s\n", p)
   290  	}
   291  	os.RemoveAll(p)
   292  }
   293  
   294  // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
   295  // The names are relative to dir; they are not full paths.
   296  func xreaddir(dir string) []string {
   297  	f, err := os.Open(dir)
   298  	if err != nil {
   299  		fatalf("%v", err)
   300  	}
   301  	defer f.Close()
   302  	names, err := f.Readdirnames(-1)
   303  	if err != nil {
   304  		fatalf("reading %s: %v", dir, err)
   305  	}
   306  	return names
   307  }
   308  
   309  // xreaddir replaces dst with a list of the names of the files in dir.
   310  // The names are relative to dir; they are not full paths.
   311  func xreaddirfiles(dir string) []string {
   312  	f, err := os.Open(dir)
   313  	if err != nil {
   314  		fatalf("%v", err)
   315  	}
   316  	defer f.Close()
   317  	infos, err := f.Readdir(-1)
   318  	if err != nil {
   319  		fatalf("reading %s: %v", dir, err)
   320  	}
   321  	var names []string
   322  	for _, fi := range infos {
   323  		if !fi.IsDir() {
   324  			names = append(names, fi.Name())
   325  		}
   326  	}
   327  	return names
   328  }
   329  
   330  // xworkdir creates a new temporary directory to hold object files
   331  // and returns the name of that directory.
   332  func xworkdir() string {
   333  	name, err := ioutil.TempDir(os.Getenv("GOTMPDIR"), "go-tool-dist-")
   334  	if err != nil {
   335  		fatalf("%v", err)
   336  	}
   337  	return name
   338  }
   339  
   340  // fatalf prints an error message to standard error and exits.
   341  func fatalf(format string, args ...interface{}) {
   342  	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
   343  
   344  	dieOnce.Do(func() { close(dying) })
   345  
   346  	// Wait for background goroutines to finish,
   347  	// so that exit handler that removes the work directory
   348  	// is not fighting with active writes or open files.
   349  	bghelpers.Wait()
   350  
   351  	xexit(2)
   352  }
   353  
   354  var atexits []func()
   355  
   356  // xexit exits the process with return code n.
   357  func xexit(n int) {
   358  	for i := len(atexits) - 1; i >= 0; i-- {
   359  		atexits[i]()
   360  	}
   361  	os.Exit(n)
   362  }
   363  
   364  // xatexit schedules the exit-handler f to be run when the program exits.
   365  func xatexit(f func()) {
   366  	atexits = append(atexits, f)
   367  }
   368  
   369  // xprintf prints a message to standard output.
   370  func xprintf(format string, args ...interface{}) {
   371  	fmt.Printf(format, args...)
   372  }
   373  
   374  // errprintf prints a message to standard output.
   375  func errprintf(format string, args ...interface{}) {
   376  	fmt.Fprintf(os.Stderr, format, args...)
   377  }
   378  
   379  // xsamefile reports whether f1 and f2 are the same file (or dir)
   380  func xsamefile(f1, f2 string) bool {
   381  	fi1, err1 := os.Stat(f1)
   382  	fi2, err2 := os.Stat(f2)
   383  	if err1 != nil || err2 != nil {
   384  		return f1 == f2
   385  	}
   386  	return os.SameFile(fi1, fi2)
   387  }
   388  
   389  func xgetgoarm() string {
   390  	if goos == "android" {
   391  		// Assume all android devices have VFPv3.
   392  		// These ports are also mostly cross-compiled, so it makes little
   393  		// sense to auto-detect the setting.
   394  		return "7"
   395  	}
   396  	if goos == "windows" {
   397  		// windows/arm only works with ARMv7 executables.
   398  		return "7"
   399  	}
   400  	if gohostarch != "arm" || goos != gohostos {
   401  		// Conservative default for cross-compilation.
   402  		return "5"
   403  	}
   404  
   405  	// Try to exec ourselves in a mode to detect VFP support.
   406  	// Seeing how far it gets determines which instructions failed.
   407  	// The test is OS-agnostic.
   408  	out := run("", 0, os.Args[0], "-check-goarm")
   409  	v1ok := strings.Contains(out, "VFPv1 OK.")
   410  	v3ok := strings.Contains(out, "VFPv3 OK.")
   411  
   412  	if v1ok && v3ok {
   413  		return "7"
   414  	}
   415  	if v1ok {
   416  		return "6"
   417  	}
   418  	return "5"
   419  }
   420  
   421  func min(a, b int) int {
   422  	if a < b {
   423  		return a
   424  	}
   425  	return b
   426  }
   427  
   428  // elfIsLittleEndian detects if the ELF file is little endian.
   429  func elfIsLittleEndian(fn string) bool {
   430  	// read the ELF file header to determine the endianness without using the
   431  	// debug/elf package.
   432  	file, err := os.Open(fn)
   433  	if err != nil {
   434  		fatalf("failed to open file to determine endianness: %v", err)
   435  	}
   436  	defer file.Close()
   437  	var hdr [16]byte
   438  	if _, err := io.ReadFull(file, hdr[:]); err != nil {
   439  		fatalf("failed to read ELF header to determine endianness: %v", err)
   440  	}
   441  	// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
   442  	switch hdr[5] {
   443  	default:
   444  		fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
   445  	case 1:
   446  		return true
   447  	case 2:
   448  		return false
   449  	}
   450  	panic("unreachable")
   451  }
   452  
   453  // count is a flag.Value that is like a flag.Bool and a flag.Int.
   454  // If used as -name, it increments the count, but -name=x sets the count.
   455  // Used for verbose flag -v.
   456  type count int
   457  
   458  func (c *count) String() string {
   459  	return fmt.Sprint(int(*c))
   460  }
   461  
   462  func (c *count) Set(s string) error {
   463  	switch s {
   464  	case "true":
   465  		*c++
   466  	case "false":
   467  		*c = 0
   468  	default:
   469  		n, err := strconv.Atoi(s)
   470  		if err != nil {
   471  			return fmt.Errorf("invalid count %q", s)
   472  		}
   473  		*c = count(n)
   474  	}
   475  	return nil
   476  }
   477  
   478  func (c *count) IsBoolFlag() bool {
   479  	return true
   480  }
   481  
   482  func xflagparse(maxargs int) {
   483  	flag.Var((*count)(&vflag), "v", "verbosity")
   484  	flag.Parse()
   485  	if maxargs >= 0 && flag.NArg() > maxargs {
   486  		flag.Usage()
   487  	}
   488  }
   489  

View as plain text