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