github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/dist/buildtool.go (about)

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