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"
     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 }
    591