Home | History | Annotate | Download | only in dist
      1 // Copyright 2015 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 // Build toolchain using Go 1.4.
      6 //
      7 // The general strategy is to copy the source files we need into
      8 // a new GOPATH workspace, adjust import paths appropriately,
      9 // invoke the Go 1.4 go command to build those sources,
     10 // and then copy the binaries back.
     11 
     12 package main
     13 
     14 import (
     15 	"fmt"
     16 	"os"
     17 	"path/filepath"
     18 	"runtime"
     19 	"strings"
     20 )
     21 
     22 // bootstrapDirs is a list of directories holding code that must be
     23 // compiled with a Go 1.4 toolchain to produce the bootstrapTargets.
     24 // All directories in this list are relative to and must be below $GOROOT/src.
     25 //
     26 // The list has have two kinds of entries: names beginning with cmd/ with
     27 // no other slashes, which are commands, and other paths, which are packages
     28 // supporting the commands. Packages in the standard library can be listed
     29 // if a newer copy needs to be substituted for the Go 1.4 copy when used
     30 // by the command packages.
     31 // These will be imported during bootstrap as bootstrap/name, like bootstrap/math/big.
     32 var bootstrapDirs = []string{
     33 	"cmd/asm",
     34 	"cmd/asm/internal/arch",
     35 	"cmd/asm/internal/asm",
     36 	"cmd/asm/internal/flags",
     37 	"cmd/asm/internal/lex",
     38 	"cmd/cgo",
     39 	"cmd/compile",
     40 	"cmd/compile/internal/amd64",
     41 	"cmd/compile/internal/arm",
     42 	"cmd/compile/internal/arm64",
     43 	"cmd/compile/internal/gc",
     44 	"cmd/compile/internal/mips",
     45 	"cmd/compile/internal/mips64",
     46 	"cmd/compile/internal/ppc64",
     47 	"cmd/compile/internal/types",
     48 	"cmd/compile/internal/s390x",
     49 	"cmd/compile/internal/ssa",
     50 	"cmd/compile/internal/syntax",
     51 	"cmd/compile/internal/x86",
     52 	"cmd/internal/bio",
     53 	"cmd/internal/gcprog",
     54 	"cmd/internal/dwarf",
     55 	"cmd/internal/edit",
     56 	"cmd/internal/objabi",
     57 	"cmd/internal/obj",
     58 	"cmd/internal/obj/arm",
     59 	"cmd/internal/obj/arm64",
     60 	"cmd/internal/obj/mips",
     61 	"cmd/internal/obj/ppc64",
     62 	"cmd/internal/obj/s390x",
     63 	"cmd/internal/obj/x86",
     64 	"cmd/internal/src",
     65 	"cmd/internal/sys",
     66 	"cmd/link",
     67 	"cmd/link/internal/amd64",
     68 	"cmd/link/internal/arm",
     69 	"cmd/link/internal/arm64",
     70 	"cmd/link/internal/ld",
     71 	"cmd/link/internal/loadelf",
     72 	"cmd/link/internal/loadmacho",
     73 	"cmd/link/internal/loadpe",
     74 	"cmd/link/internal/mips",
     75 	"cmd/link/internal/mips64",
     76 	"cmd/link/internal/objfile",
     77 	"cmd/link/internal/ppc64",
     78 	"cmd/link/internal/s390x",
     79 	"cmd/link/internal/sym",
     80 	"cmd/link/internal/x86",
     81 	"container/heap",
     82 	"debug/dwarf",
     83 	"debug/elf",
     84 	"debug/macho",
     85 	"debug/pe",
     86 	"math/big",
     87 	"math/bits",
     88 	"sort",
     89 }
     90 
     91 // File prefixes that are ignored by go/build anyway, and cause
     92 // problems with editor generated temporary files (#18931).
     93 var ignorePrefixes = []string{
     94 	".",
     95 	"_",
     96 }
     97 
     98 // File suffixes that use build tags introduced since Go 1.4.
     99 // These must not be copied into the bootstrap build directory.
    100 var ignoreSuffixes = []string{
    101 	"_arm64.s",
    102 	"_arm64.go",
    103 }
    104 
    105 func bootstrapBuildTools() {
    106 	goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
    107 	if goroot_bootstrap == "" {
    108 		goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME"))
    109 	}
    110 	xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap)
    111 
    112 	mkzbootstrap(pathf("%s/src/cmd/internal/objabi/zbootstrap.go", goroot))
    113 
    114 	// Use $GOROOT/pkg/bootstrap as the bootstrap workspace root.
    115 	// We use a subdirectory of $GOROOT/pkg because that's the
    116 	// space within $GOROOT where we store all generated objects.
    117 	// We could use a temporary directory outside $GOROOT instead,
    118 	// but it is easier to debug on failure if the files are in a known location.
    119 	workspace := pathf("%s/pkg/bootstrap", goroot)
    120 	xremoveall(workspace)
    121 	base := pathf("%s/src/bootstrap", workspace)
    122 	xmkdirall(base)
    123 
    124 	// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
    125 	for _, dir := range bootstrapDirs {
    126 		src := pathf("%s/src/%s", goroot, dir)
    127 		dst := pathf("%s/%s", base, dir)
    128 		xmkdirall(dst)
    129 		if dir == "cmd/cgo" {
    130 			// Write to src because we need the file both for bootstrap
    131 			// and for later in the main build.
    132 			mkzdefaultcc("", pathf("%s/zdefaultcc.go", src))
    133 		}
    134 	Dir:
    135 		for _, name := range xreaddirfiles(src) {
    136 			for _, pre := range ignorePrefixes {
    137 				if strings.HasPrefix(name, pre) {
    138 					continue Dir
    139 				}
    140 			}
    141 			for _, suf := range ignoreSuffixes {
    142 				if strings.HasSuffix(name, suf) {
    143 					continue Dir
    144 				}
    145 			}
    146 			srcFile := pathf("%s/%s", src, name)
    147 			dstFile := pathf("%s/%s", dst, name)
    148 			text := bootstrapRewriteFile(srcFile)
    149 			writefile(text, dstFile, 0)
    150 		}
    151 	}
    152 
    153 	// Set up environment for invoking Go 1.4 go command.
    154 	// GOROOT points at Go 1.4 GOROOT,
    155 	// GOPATH points at our bootstrap workspace,
    156 	// GOBIN is empty, so that binaries are installed to GOPATH/bin,
    157 	// and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty,
    158 	// so that Go 1.4 builds whatever kind of binary it knows how to build.
    159 	// Restore GOROOT, GOPATH, and GOBIN when done.
    160 	// Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH,
    161 	// because setup will take care of those when bootstrapBuildTools returns.
    162 
    163 	defer os.Setenv("GOROOT", os.Getenv("GOROOT"))
    164 	os.Setenv("GOROOT", goroot_bootstrap)
    165 
    166 	defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
    167 	os.Setenv("GOPATH", workspace)
    168 
    169 	defer os.Setenv("GOBIN", os.Getenv("GOBIN"))
    170 	os.Setenv("GOBIN", "")
    171 
    172 	os.Setenv("GOOS", "")
    173 	os.Setenv("GOHOSTOS", "")
    174 	os.Setenv("GOARCH", "")
    175 	os.Setenv("GOHOSTARCH", "")
    176 
    177 	// Run Go 1.4 to build binaries. Use -gcflags=-l to disable inlining to
    178 	// workaround bugs in Go 1.4's compiler. See discussion thread:
    179 	// https://groups.google.com/d/msg/golang-dev/Ss7mCKsvk8w/Gsq7VYI0AwAJ
    180 	// Use the math_big_pure_go build tag to disable the assembly in math/big
    181 	// which may contain unsupported instructions.
    182 	// Note that if we are using Go 1.10 or later as bootstrap, the -gcflags=-l
    183 	// only applies to the final cmd/go binary, but that's OK: if this is Go 1.10
    184 	// or later we don't need to disable inlining to work around bugs in the Go 1.4 compiler.
    185 	cmd := []string{
    186 		pathf("%s/bin/go", goroot_bootstrap),
    187 		"install",
    188 		"-gcflags=-l",
    189 		"-tags=math_big_pure_go compiler_bootstrap",
    190 	}
    191 	if vflag > 0 {
    192 		cmd = append(cmd, "-v")
    193 	}
    194 	if tool := os.Getenv("GOBOOTSTRAP_TOOLEXEC"); tool != "" {
    195 		cmd = append(cmd, "-toolexec="+tool)
    196 	}
    197 	cmd = append(cmd, "bootstrap/cmd/...")
    198 	run(workspace, ShowOutput|CheckExit, cmd...)
    199 
    200 	// Copy binaries into tool binary directory.
    201 	for _, name := range bootstrapDirs {
    202 		if !strings.HasPrefix(name, "cmd/") {
    203 			continue
    204 		}
    205 		name = name[len("cmd/"):]
    206 		if !strings.Contains(name, "/") {
    207 			copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
    208 		}
    209 	}
    210 
    211 	if vflag > 0 {
    212 		xprintf("\n")
    213 	}
    214 }
    215 
    216 var ssaRewriteFileSubstring = filepath.FromSlash("src/cmd/compile/internal/ssa/rewrite")
    217 
    218 // isUnneededSSARewriteFile reports whether srcFile is a
    219 // src/cmd/compile/internal/ssa/rewriteARCHNAME.go file for an
    220 // architecture that isn't for the current runtime.GOARCH.
    221 //
    222 // When unneeded is true archCaps is the rewrite base filename without
    223 // the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc.
    224 func isUnneededSSARewriteFile(srcFile string) (archCaps string, unneeded bool) {
    225 	if !strings.Contains(srcFile, ssaRewriteFileSubstring) {
    226 		return "", false
    227 	}
    228 	fileArch := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(srcFile), "rewrite"), ".go")
    229 	if fileArch == "" {
    230 		return "", false
    231 	}
    232 	b := fileArch[0]
    233 	if b == '_' || ('a' <= b && b <= 'z') {
    234 		return "", false
    235 	}
    236 	archCaps = fileArch
    237 	fileArch = strings.ToLower(fileArch)
    238 	if fileArch == strings.TrimSuffix(runtime.GOARCH, "le") {
    239 		return "", false
    240 	}
    241 	if fileArch == strings.TrimSuffix(os.Getenv("GOARCH"), "le") {
    242 		return "", false
    243 	}
    244 	return archCaps, true
    245 }
    246 
    247 func bootstrapRewriteFile(srcFile string) string {
    248 	// During bootstrap, generate dummy rewrite files for
    249 	// irrelevant architectures. We only need to build a bootstrap
    250 	// binary that works for the current runtime.GOARCH.
    251 	// This saves 6+ seconds of bootstrap.
    252 	if archCaps, ok := isUnneededSSARewriteFile(srcFile); ok {
    253 		return fmt.Sprintf(`// Code generated by go tool dist; DO NOT EDIT.
    254 
    255 package ssa
    256 
    257 func rewriteValue%s(v *Value) bool { panic("unused during bootstrap") }
    258 func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") }
    259 `, archCaps, archCaps)
    260 	}
    261 
    262 	return bootstrapFixImports(srcFile)
    263 }
    264 
    265 func bootstrapFixImports(srcFile string) string {
    266 	lines := strings.SplitAfter(readfile(srcFile), "\n")
    267 	inBlock := false
    268 	for i, line := range lines {
    269 		if strings.HasPrefix(line, "import (") {
    270 			inBlock = true
    271 			continue
    272 		}
    273 		if inBlock && strings.HasPrefix(line, ")") {
    274 			inBlock = false
    275 			continue
    276 		}
    277 		if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
    278 			inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
    279 			line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1)
    280 			for _, dir := range bootstrapDirs {
    281 				if strings.HasPrefix(dir, "cmd/") {
    282 					continue
    283 				}
    284 				line = strings.Replace(line, `"`+dir+`"`, `"bootstrap/`+dir+`"`, -1)
    285 			}
    286 			lines[i] = line
    287 		}
    288 	}
    289 
    290 	lines[0] = "// Code generated by go tool dist; DO NOT EDIT.\n// This is a bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0]
    291 
    292 	return strings.Join(lines, "")
    293 }
    294