Home | History | Annotate | Download | only in 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 	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 }
    490