...

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		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" || goos == "android" {
   391			// Assume all darwin/arm and 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 gohostarch != "arm" || goos != gohostos {
   397			// Conservative default for cross-compilation.
   398			return "5"
   399		}
   400	
   401		// Try to exec ourselves in a mode to detect VFP support.
   402		// Seeing how far it gets determines which instructions failed.
   403		// The test is OS-agnostic.
   404		out := run("", 0, os.Args[0], "-check-goarm")
   405		v1ok := strings.Contains(out, "VFPv1 OK.")
   406		v3ok := strings.Contains(out, "VFPv3 OK.")
   407	
   408		if v1ok && v3ok {
   409			return "7"
   410		}
   411		if v1ok {
   412			return "6"
   413		}
   414		return "5"
   415	}
   416	
   417	func min(a, b int) int {
   418		if a < b {
   419			return a
   420		}
   421		return b
   422	}
   423	
   424	// elfIsLittleEndian detects if the ELF file is little endian.
   425	func elfIsLittleEndian(fn string) bool {
   426		// read the ELF file header to determine the endianness without using the
   427		// debug/elf package.
   428		file, err := os.Open(fn)
   429		if err != nil {
   430			fatalf("failed to open file to determine endianness: %v", err)
   431		}
   432		defer file.Close()
   433		var hdr [16]byte
   434		if _, err := io.ReadFull(file, hdr[:]); err != nil {
   435			fatalf("failed to read ELF header to determine endianness: %v", err)
   436		}
   437		// hdr[5] is EI_DATA byte, 1 is ELFDATA2LSB and 2 is ELFDATA2MSB
   438		switch hdr[5] {
   439		default:
   440			fatalf("unknown ELF endianness of %s: EI_DATA = %d", fn, hdr[5])
   441		case 1:
   442			return true
   443		case 2:
   444			return false
   445		}
   446		panic("unreachable")
   447	}
   448	
   449	// count is a flag.Value that is like a flag.Bool and a flag.Int.
   450	// If used as -name, it increments the count, but -name=x sets the count.
   451	// Used for verbose flag -v.
   452	type count int
   453	
   454	func (c *count) String() string {
   455		return fmt.Sprint(int(*c))
   456	}
   457	
   458	func (c *count) Set(s string) error {
   459		switch s {
   460		case "true":
   461			*c++
   462		case "false":
   463			*c = 0
   464		default:
   465			n, err := strconv.Atoi(s)
   466			if err != nil {
   467				return fmt.Errorf("invalid count %q", s)
   468			}
   469			*c = count(n)
   470		}
   471		return nil
   472	}
   473	
   474	func (c *count) IsBoolFlag() bool {
   475		return true
   476	}
   477	
   478	func xflagparse(maxargs int) {
   479		flag.Var((*count)(&vflag), "v", "verbosity")
   480		flag.Parse()
   481		if maxargs >= 0 && flag.NArg() > maxargs {
   482			flag.Usage()
   483		}
   484	}
   485	

View as plain text