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
12package main
13
14import (
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.
32var 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/compile",
39	"cmd/compile/internal/amd64",
40	"cmd/compile/internal/arm",
41	"cmd/compile/internal/arm64",
42	"cmd/compile/internal/gc",
43	"cmd/compile/internal/mips",
44	"cmd/compile/internal/mips64",
45	"cmd/compile/internal/ppc64",
46	"cmd/compile/internal/types",
47	"cmd/compile/internal/s390x",
48	"cmd/compile/internal/ssa",
49	"cmd/compile/internal/syntax",
50	"cmd/compile/internal/x86",
51	"cmd/internal/bio",
52	"cmd/internal/gcprog",
53	"cmd/internal/dwarf",
54	"cmd/internal/objabi",
55	"cmd/internal/obj",
56	"cmd/internal/obj/arm",
57	"cmd/internal/obj/arm64",
58	"cmd/internal/obj/mips",
59	"cmd/internal/obj/ppc64",
60	"cmd/internal/obj/s390x",
61	"cmd/internal/obj/x86",
62	"cmd/internal/src",
63	"cmd/internal/sys",
64	"cmd/link",
65	"cmd/link/internal/amd64",
66	"cmd/link/internal/arm",
67	"cmd/link/internal/arm64",
68	"cmd/link/internal/ld",
69	"cmd/link/internal/mips",
70	"cmd/link/internal/mips64",
71	"cmd/link/internal/ppc64",
72	"cmd/link/internal/s390x",
73	"cmd/link/internal/x86",
74	"debug/pe",
75	"math/big",
76	"math/bits",
77}
78
79// File prefixes that are ignored by go/build anyway, and cause
80// problems with editor generated temporary files (#18931).
81var ignorePrefixes = []string{
82	".",
83	"_",
84}
85
86// File suffixes that use build tags introduced since Go 1.4.
87// These must not be copied into the bootstrap build directory.
88var ignoreSuffixes = []string{
89	"_arm64.s",
90	"_arm64.go",
91}
92
93func bootstrapBuildTools() {
94	goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
95	if goroot_bootstrap == "" {
96		goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME"))
97	}
98	xprintf("##### Building Go toolchain using %s.\n", goroot_bootstrap)
99
100	mkzbootstrap(pathf("%s/src/cmd/internal/objabi/zbootstrap.go", goroot))
101
102	// Use $GOROOT/pkg/bootstrap as the bootstrap workspace root.
103	// We use a subdirectory of $GOROOT/pkg because that's the
104	// space within $GOROOT where we store all generated objects.
105	// We could use a temporary directory outside $GOROOT instead,
106	// but it is easier to debug on failure if the files are in a known location.
107	workspace := pathf("%s/pkg/bootstrap", goroot)
108	xremoveall(workspace)
109	base := pathf("%s/src/bootstrap", workspace)
110	xmkdirall(base)
111
112	// Copy source code into $GOROOT/pkg/bootstrap and rewrite import paths.
113	for _, dir := range bootstrapDirs {
114		src := pathf("%s/src/%s", goroot, dir)
115		dst := pathf("%s/%s", base, dir)
116		xmkdirall(dst)
117	Dir:
118		for _, name := range xreaddirfiles(src) {
119			for _, pre := range ignorePrefixes {
120				if strings.HasPrefix(name, pre) {
121					continue Dir
122				}
123			}
124			for _, suf := range ignoreSuffixes {
125				if strings.HasSuffix(name, suf) {
126					continue Dir
127				}
128			}
129			srcFile := pathf("%s/%s", src, name)
130			dstFile := pathf("%s/%s", dst, name)
131			text := readfile(srcFile)
132			text = bootstrapRewriteFile(text, srcFile)
133			writefile(text, dstFile, 0)
134		}
135	}
136
137	// Set up environment for invoking Go 1.4 go command.
138	// GOROOT points at Go 1.4 GOROOT,
139	// GOPATH points at our bootstrap workspace,
140	// GOBIN is empty, so that binaries are installed to GOPATH/bin,
141	// and GOOS, GOHOSTOS, GOARCH, and GOHOSTOS are empty,
142	// so that Go 1.4 builds whatever kind of binary it knows how to build.
143	// Restore GOROOT, GOPATH, and GOBIN when done.
144	// Don't bother with GOOS, GOHOSTOS, GOARCH, and GOHOSTARCH,
145	// because setup will take care of those when bootstrapBuildTools returns.
146
147	defer os.Setenv("GOROOT", os.Getenv("GOROOT"))
148	os.Setenv("GOROOT", goroot_bootstrap)
149
150	defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
151	os.Setenv("GOPATH", workspace)
152
153	defer os.Setenv("GOBIN", os.Getenv("GOBIN"))
154	os.Setenv("GOBIN", "")
155
156	os.Setenv("GOOS", "")
157	os.Setenv("GOHOSTOS", "")
158	os.Setenv("GOARCH", "")
159	os.Setenv("GOHOSTARCH", "")
160
161	// Run Go 1.4 to build binaries. Use -gcflags=-l to disable inlining to
162	// workaround bugs in Go 1.4's compiler. See discussion thread:
163	// https://groups.google.com/d/msg/golang-dev/Ss7mCKsvk8w/Gsq7VYI0AwAJ
164	// Use the math_big_pure_go build tag to disable the assembly in math/big
165	// which may contain unsupported instructions.
166	cmd := []string{
167		pathf("%s/bin/go", goroot_bootstrap),
168		"install",
169		"-gcflags=-l",
170		"-tags=math_big_pure_go",
171		"-v",
172	}
173	if tool := os.Getenv("GOBOOTSTRAP_TOOLEXEC"); tool != "" {
174		cmd = append(cmd, "-toolexec="+tool)
175	}
176	cmd = append(cmd, "bootstrap/cmd/...")
177	run(workspace, ShowOutput|CheckExit, cmd...)
178
179	// Copy binaries into tool binary directory.
180	for _, name := range bootstrapDirs {
181		if !strings.HasPrefix(name, "cmd/") {
182			continue
183		}
184		name = name[len("cmd/"):]
185		if !strings.Contains(name, "/") {
186			copyfile(pathf("%s/%s%s", tooldir, name, exe), pathf("%s/bin/%s%s", workspace, name, exe), writeExec)
187		}
188	}
189
190	xprintf("\n")
191}
192
193var ssaRewriteFileSubstring = filepath.ToSlash("src/cmd/compile/internal/ssa/rewrite")
194
195// isUnneededSSARewriteFile reports whether srcFile is a
196// src/cmd/compile/internal/ssa/rewriteARCHNAME.go file for an
197// architecture that isn't for the current runtime.GOARCH.
198//
199// When unneeded is true archCaps is the rewrite base filename without
200// the "rewrite" prefix or ".go" suffix: AMD64, 386, ARM, ARM64, etc.
201func isUnneededSSARewriteFile(srcFile string) (archCaps string, unneeded bool) {
202	if !strings.Contains(srcFile, ssaRewriteFileSubstring) {
203		return "", false
204	}
205	fileArch := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(srcFile), "rewrite"), ".go")
206	if fileArch == "" {
207		return "", false
208	}
209	b := fileArch[0]
210	if b == '_' || ('a' <= b && b <= 'z') {
211		return "", false
212	}
213	archCaps = fileArch
214	fileArch = strings.ToLower(fileArch)
215	if fileArch == strings.TrimSuffix(runtime.GOARCH, "le") {
216		return "", false
217	}
218	if fileArch == strings.TrimSuffix(os.Getenv("GOARCH"), "le") {
219		return "", false
220	}
221	return archCaps, true
222}
223
224func bootstrapRewriteFile(text, srcFile string) string {
225	// During bootstrap, generate dummy rewrite files for
226	// irrelevant architectures. We only need to build a bootstrap
227	// binary that works for the current runtime.GOARCH.
228	// This saves 6+ seconds of bootstrap.
229	if archCaps, ok := isUnneededSSARewriteFile(srcFile); ok {
230		return fmt.Sprintf(`package ssa
231
232func rewriteValue%s(v *Value) bool { panic("unused during bootstrap") }
233func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") }
234`, archCaps, archCaps)
235	}
236
237	return bootstrapFixImports(text, srcFile)
238}
239
240func bootstrapFixImports(text, srcFile string) string {
241	lines := strings.SplitAfter(text, "\n")
242	inBlock := false
243	for i, line := range lines {
244		if strings.HasPrefix(line, "import (") {
245			inBlock = true
246			continue
247		}
248		if inBlock && strings.HasPrefix(line, ")") {
249			inBlock = false
250			continue
251		}
252		if strings.HasPrefix(line, `import "`) || strings.HasPrefix(line, `import . "`) ||
253			inBlock && (strings.HasPrefix(line, "\t\"") || strings.HasPrefix(line, "\t. \"")) {
254			line = strings.Replace(line, `"cmd/`, `"bootstrap/cmd/`, -1)
255			for _, dir := range bootstrapDirs {
256				if strings.HasPrefix(dir, "cmd/") {
257					continue
258				}
259				line = strings.Replace(line, `"`+dir+`"`, `"bootstrap/`+dir+`"`, -1)
260			}
261			lines[i] = line
262		}
263	}
264
265	lines[0] = "// Do not edit. Bootstrap copy of " + srcFile + "\n\n//line " + srcFile + ":1\n" + lines[0]
266
267	return strings.Join(lines, "")
268}
269