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 	"fmt"
     10 	"io/ioutil"
     11 	"os"
     12 	"os/exec"
     13 	"path/filepath"
     14 	"runtime"
     15 	"sort"
     16 	"strconv"
     17 	"strings"
     18 	"sync"
     19 	"sync/atomic"
     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, run collects cmd's output and returns it as a string;
     76 // otherwise, run prints cmd's output to standard output after the command finishes.
     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 			bgdied.Done()
    113 		}
    114 		fatal("FAILED: %v: %v", strings.Join(cmd, " "), err)
    115 	}
    116 	if mode&ShowOutput != 0 {
    117 		outputLock.Lock()
    118 		os.Stdout.Write(data)
    119 		outputLock.Unlock()
    120 	}
    121 	if vflag > 2 {
    122 		errprintf("run: %s DONE\n", strings.Join(cmd, " "))
    123 	}
    124 	return string(data)
    125 }
    126 
    127 var maxbg = 4 /* maximum number of jobs to run at once */
    128 
    129 var (
    130 	bgwork = make(chan func(), 1e5)
    131 	bgdone = make(chan struct{}, 1e5)
    132 
    133 	bgdied sync.WaitGroup
    134 	nwork  int32
    135 	ndone  int32
    136 
    137 	dying  = make(chan bool)
    138 	nfatal int32
    139 )
    140 
    141 func bginit() {
    142 	bgdied.Add(maxbg)
    143 	for i := 0; i < maxbg; i++ {
    144 		go bghelper()
    145 	}
    146 }
    147 
    148 func bghelper() {
    149 	for {
    150 		w := <-bgwork
    151 		w()
    152 
    153 		// Stop if we're dying.
    154 		if atomic.LoadInt32(&nfatal) > 0 {
    155 			bgdied.Done()
    156 			return
    157 		}
    158 	}
    159 }
    160 
    161 // bgrun is like run but runs the command in the background.
    162 // CheckExit|ShowOutput mode is implied (since output cannot be returned).
    163 func bgrun(dir string, cmd ...string) {
    164 	bgwork <- func() {
    165 		run(dir, CheckExit|ShowOutput|Background, cmd...)
    166 	}
    167 }
    168 
    169 // bgwait waits for pending bgruns to finish.
    170 // bgwait must be called from only a single goroutine at a time.
    171 func bgwait() {
    172 	var wg sync.WaitGroup
    173 	wg.Add(maxbg)
    174 	done := make(chan bool)
    175 	for i := 0; i < maxbg; i++ {
    176 		bgwork <- func() {
    177 			wg.Done()
    178 
    179 			// Hold up bg goroutine until either the wait finishes
    180 			// or the program starts dying due to a call to fatal.
    181 			select {
    182 			case <-dying:
    183 			case <-done:
    184 			}
    185 		}
    186 	}
    187 	wg.Wait()
    188 	close(done)
    189 }
    190 
    191 // xgetwd returns the current directory.
    192 func xgetwd() string {
    193 	wd, err := os.Getwd()
    194 	if err != nil {
    195 		fatal("%s", err)
    196 	}
    197 	return wd
    198 }
    199 
    200 // xrealwd returns the 'real' name for the given path.
    201 // real is defined as what xgetwd returns in that directory.
    202 func xrealwd(path string) string {
    203 	old := xgetwd()
    204 	if err := os.Chdir(path); err != nil {
    205 		fatal("chdir %s: %v", path, err)
    206 	}
    207 	real := xgetwd()
    208 	if err := os.Chdir(old); err != nil {
    209 		fatal("chdir %s: %v", old, err)
    210 	}
    211 	return real
    212 }
    213 
    214 // isdir reports whether p names an existing directory.
    215 func isdir(p string) bool {
    216 	fi, err := os.Stat(p)
    217 	return err == nil && fi.IsDir()
    218 }
    219 
    220 // isfile reports whether p names an existing file.
    221 func isfile(p string) bool {
    222 	fi, err := os.Stat(p)
    223 	return err == nil && fi.Mode().IsRegular()
    224 }
    225 
    226 // mtime returns the modification time of the file p.
    227 func mtime(p string) time.Time {
    228 	fi, err := os.Stat(p)
    229 	if err != nil {
    230 		return time.Time{}
    231 	}
    232 	return fi.ModTime()
    233 }
    234 
    235 // isabs reports whether p is an absolute path.
    236 func isabs(p string) bool {
    237 	return filepath.IsAbs(p)
    238 }
    239 
    240 // readfile returns the content of the named file.
    241 func readfile(file string) string {
    242 	data, err := ioutil.ReadFile(file)
    243 	if err != nil {
    244 		fatal("%v", err)
    245 	}
    246 	return string(data)
    247 }
    248 
    249 const (
    250 	writeExec = 1 << iota
    251 	writeSkipSame
    252 )
    253 
    254 // writefile writes b to the named file, creating it if needed.
    255 // if exec is non-zero, marks the file as executable.
    256 // If the file already exists and has the expected content,
    257 // it is not rewritten, to avoid changing the time stamp.
    258 func writefile(b, file string, flag int) {
    259 	new := []byte(b)
    260 	if flag&writeSkipSame != 0 {
    261 		old, err := ioutil.ReadFile(file)
    262 		if err == nil && bytes.Equal(old, new) {
    263 			return
    264 		}
    265 	}
    266 	mode := os.FileMode(0666)
    267 	if flag&writeExec != 0 {
    268 		mode = 0777
    269 	}
    270 	err := ioutil.WriteFile(file, new, mode)
    271 	if err != nil {
    272 		fatal("%v", err)
    273 	}
    274 }
    275 
    276 // xmkdir creates the directory p.
    277 func xmkdir(p string) {
    278 	err := os.Mkdir(p, 0777)
    279 	if err != nil {
    280 		fatal("%v", err)
    281 	}
    282 }
    283 
    284 // xmkdirall creates the directory p and its parents, as needed.
    285 func xmkdirall(p string) {
    286 	err := os.MkdirAll(p, 0777)
    287 	if err != nil {
    288 		fatal("%v", err)
    289 	}
    290 }
    291 
    292 // xremove removes the file p.
    293 func xremove(p string) {
    294 	if vflag > 2 {
    295 		errprintf("rm %s\n", p)
    296 	}
    297 	os.Remove(p)
    298 }
    299 
    300 // xremoveall removes the file or directory tree rooted at p.
    301 func xremoveall(p string) {
    302 	if vflag > 2 {
    303 		errprintf("rm -r %s\n", p)
    304 	}
    305 	os.RemoveAll(p)
    306 }
    307 
    308 // xreaddir replaces dst with a list of the names of the files and subdirectories in dir.
    309 // The names are relative to dir; they are not full paths.
    310 func xreaddir(dir string) []string {
    311 	f, err := os.Open(dir)
    312 	if err != nil {
    313 		fatal("%v", err)
    314 	}
    315 	defer f.Close()
    316 	names, err := f.Readdirnames(-1)
    317 	if err != nil {
    318 		fatal("reading %s: %v", dir, err)
    319 	}
    320 	return names
    321 }
    322 
    323 // xreaddir replaces dst with a list of the names of the files in dir.
    324 // The names are relative to dir; they are not full paths.
    325 func xreaddirfiles(dir string) []string {
    326 	f, err := os.Open(dir)
    327 	if err != nil {
    328 		fatal("%v", err)
    329 	}
    330 	defer f.Close()
    331 	infos, err := f.Readdir(-1)
    332 	if err != nil {
    333 		fatal("reading %s: %v", dir, err)
    334 	}
    335 	var names []string
    336 	for _, fi := range infos {
    337 		if !fi.IsDir() {
    338 			names = append(names, fi.Name())
    339 		}
    340 	}
    341 	return names
    342 }
    343 
    344 // xworkdir creates a new temporary directory to hold object files
    345 // and returns the name of that directory.
    346 func xworkdir() string {
    347 	name, err := ioutil.TempDir("", "go-tool-dist-")
    348 	if err != nil {
    349 		fatal("%v", err)
    350 	}
    351 	return name
    352 }
    353 
    354 // fatal prints an error message to standard error and exits.
    355 func fatal(format string, args ...interface{}) {
    356 	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
    357 
    358 	// Wait for background goroutines to finish,
    359 	// so that exit handler that removes the work directory
    360 	// is not fighting with active writes or open files.
    361 	if atomic.AddInt32(&nfatal, 1) == 1 {
    362 		close(dying)
    363 	}
    364 	for i := 0; i < maxbg; i++ {
    365 		bgwork <- func() {} // wake up workers so they notice nfatal > 0
    366 	}
    367 	bgdied.Wait()
    368 
    369 	xexit(2)
    370 }
    371 
    372 var atexits []func()
    373 
    374 // xexit exits the process with return code n.
    375 func xexit(n int) {
    376 	for i := len(atexits) - 1; i >= 0; i-- {
    377 		atexits[i]()
    378 	}
    379 	os.Exit(n)
    380 }
    381 
    382 // xatexit schedules the exit-handler f to be run when the program exits.
    383 func xatexit(f func()) {
    384 	atexits = append(atexits, f)
    385 }
    386 
    387 // xprintf prints a message to standard output.
    388 func xprintf(format string, args ...interface{}) {
    389 	fmt.Printf(format, args...)
    390 }
    391 
    392 // errprintf prints a message to standard output.
    393 func errprintf(format string, args ...interface{}) {
    394 	fmt.Fprintf(os.Stderr, format, args...)
    395 }
    396 
    397 // main takes care of OS-specific startup and dispatches to xmain.
    398 func main() {
    399 	os.Setenv("TERM", "dumb") // disable escape codes in clang errors
    400 
    401 	slash = string(filepath.Separator)
    402 
    403 	gohostos = runtime.GOOS
    404 	switch gohostos {
    405 	case "darwin":
    406 		// Even on 64-bit platform, darwin uname -m prints i386.
    407 		if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
    408 			gohostarch = "amd64"
    409 		}
    410 	case "solaris":
    411 		// Even on 64-bit platform, solaris uname -m prints i86pc.
    412 		out := run("", CheckExit, "isainfo", "-n")
    413 		if strings.Contains(out, "amd64") {
    414 			gohostarch = "amd64"
    415 		}
    416 		if strings.Contains(out, "i386") {
    417 			gohostarch = "386"
    418 		}
    419 	case "plan9":
    420 		gohostarch = os.Getenv("objtype")
    421 		if gohostarch == "" {
    422 			fatal("$objtype is unset")
    423 		}
    424 	case "windows":
    425 		exe = ".exe"
    426 	}
    427 
    428 	sysinit()
    429 
    430 	if gohostarch == "" {
    431 		// Default Unix system.
    432 		out := run("", CheckExit, "uname", "-m")
    433 		switch {
    434 		case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
    435 			gohostarch = "amd64"
    436 		case strings.Contains(out, "86"):
    437 			gohostarch = "386"
    438 		case strings.Contains(out, "arm"):
    439 			gohostarch = "arm"
    440 		case strings.Contains(out, "aarch64"):
    441 			gohostarch = "arm64"
    442 		case strings.Contains(out, "ppc64le"):
    443 			gohostarch = "ppc64le"
    444 		case strings.Contains(out, "ppc64"):
    445 			gohostarch = "ppc64"
    446 		case gohostos == "darwin":
    447 			if strings.Contains(run("", CheckExit, "uname", "-v"), "RELEASE_ARM_") {
    448 				gohostarch = "arm"
    449 			}
    450 		default:
    451 			fatal("unknown architecture: %s", out)
    452 		}
    453 	}
    454 
    455 	if gohostarch == "arm" {
    456 		maxbg = min(maxbg, runtime.NumCPU())
    457 	}
    458 	bginit()
    459 
    460 	// The OS X 10.6 linker does not support external linking mode.
    461 	// See golang.org/issue/5130.
    462 	//
    463 	// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
    464 	// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
    465 	// See golang.org/issue/5822.
    466 	//
    467 	// Roughly, OS X 10.N shows up as uname release (N+4),
    468 	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
    469 	if gohostos == "darwin" {
    470 		rel := run("", CheckExit, "uname", "-r")
    471 		if i := strings.Index(rel, "."); i >= 0 {
    472 			rel = rel[:i]
    473 		}
    474 		osx, _ := strconv.Atoi(rel)
    475 		if osx <= 6+4 {
    476 			goextlinkenabled = "0"
    477 		}
    478 		if osx >= 8+4 {
    479 			defaultclang = true
    480 		}
    481 	}
    482 
    483 	xinit()
    484 	xmain()
    485 	xexit(0)
    486 }
    487 
    488 // xsamefile reports whether f1 and f2 are the same file (or dir)
    489 func xsamefile(f1, f2 string) bool {
    490 	fi1, err1 := os.Stat(f1)
    491 	fi2, err2 := os.Stat(f2)
    492 	if err1 != nil || err2 != nil {
    493 		return f1 == f2
    494 	}
    495 	return os.SameFile(fi1, fi2)
    496 }
    497 
    498 func xgetgoarm() string {
    499 	if goos == "nacl" {
    500 		// NaCl guarantees VFPv3 and is always cross-compiled.
    501 		return "7"
    502 	}
    503 	if goos == "darwin" {
    504 		// Assume all darwin/arm devices are have VFPv3. This
    505 		// port is also mostly cross-compiled, so it makes little
    506 		// sense to auto-detect the setting.
    507 		return "7"
    508 	}
    509 	if gohostarch != "arm" || goos != gohostos {
    510 		// Conservative default for cross-compilation.
    511 		return "5"
    512 	}
    513 	if goos == "freebsd" || goos == "openbsd" {
    514 		// FreeBSD has broken VFP support.
    515 		// OpenBSD currently only supports softfloat.
    516 		return "5"
    517 	}
    518 	if goos != "linux" {
    519 		// All other arm platforms that we support
    520 		// require ARMv7.
    521 		return "7"
    522 	}
    523 	cpuinfo := readfile("/proc/cpuinfo")
    524 	goarm := "5"
    525 	for _, line := range splitlines(cpuinfo) {
    526 		line := strings.SplitN(line, ":", 2)
    527 		if len(line) < 2 {
    528 			continue
    529 		}
    530 		if strings.TrimSpace(line[0]) != "Features" {
    531 			continue
    532 		}
    533 		features := splitfields(line[1])
    534 		sort.Strings(features) // so vfpv3 sorts after vfp
    535 
    536 		// Infer GOARM value from the vfp features available
    537 		// on this host. Values of GOARM detected are:
    538 		// 5: no vfp support was found
    539 		// 6: vfp (v1) support was detected, but no higher
    540 		// 7: vfpv3 support was detected.
    541 		// This matches the assertions in runtime.checkarm.
    542 		for _, f := range features {
    543 			switch f {
    544 			case "vfp":
    545 				goarm = "6"
    546 			case "vfpv3":
    547 				goarm = "7"
    548 			}
    549 		}
    550 	}
    551 	return goarm
    552 }
    553 
    554 func min(a, b int) int {
    555 	if a < b {
    556 		return a
    557 	}
    558 	return b
    559 }
    560