diff options
Diffstat (limited to 'go/tools/builders/stdlib.go')
-rw-r--r-- | go/tools/builders/stdlib.go | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/go/tools/builders/stdlib.go b/go/tools/builders/stdlib.go new file mode 100644 index 00000000..d7b2bf0b --- /dev/null +++ b/go/tools/builders/stdlib.go @@ -0,0 +1,169 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "fmt" + "go/build" + "os" + "path/filepath" + "regexp" + "strings" +) + +// stdlib builds the standard library in the appropriate mode into a new goroot. +func stdlib(args []string) error { + // process the args + flags := flag.NewFlagSet("stdlib", flag.ExitOnError) + goenv := envFlags(flags) + out := flags.String("out", "", "Path to output go root") + race := flags.Bool("race", false, "Build in race mode") + shared := flags.Bool("shared", false, "Build in shared mode") + dynlink := flags.Bool("dynlink", false, "Build in dynlink mode") + var packages multiFlag + flags.Var(&packages, "package", "Packages to build") + var gcflags quoteMultiFlag + flags.Var(&gcflags, "gcflags", "Go compiler flags") + if err := flags.Parse(args); err != nil { + return err + } + if err := goenv.checkFlags(); err != nil { + return err + } + goroot := os.Getenv("GOROOT") + if goroot == "" { + return fmt.Errorf("GOROOT not set") + } + output := abs(*out) + + // Fail fast if cgo is required but a toolchain is not configured. + if os.Getenv("CGO_ENABLED") == "1" && filepath.Base(os.Getenv("CC")) == "vc_installation_error.bat" { + return fmt.Errorf(`cgo is required, but a C toolchain has not been configured. +You may need to use the flags --cpu=x64_windows --compiler=mingw-gcc.`) + } + + // Link in the bare minimum needed to the new GOROOT + if err := replicate(goroot, output, replicatePaths("src", "pkg/tool", "pkg/include")); err != nil { + return err + } + + output, err := processPath(output) + if err != nil { + return err + } + + // Now switch to the newly created GOROOT + os.Setenv("GOROOT", output) + + // Create a temporary cache directory. "go build" requires this starting + // in Go 1.12. + cachePath := filepath.Join(output, ".gocache") + os.Setenv("GOCACHE", cachePath) + defer os.RemoveAll(cachePath) + + // Disable modules for the 'go install' command. Depending on the sandboxing + // mode, there may be a go.mod file in a parent directory which will turn + // modules on in "auto" mode. + os.Setenv("GO111MODULE", "off") + + // Make sure we have an absolute path to the C compiler. + // TODO(#1357): also take absolute paths of includes and other paths in flags. + os.Setenv("CC", quotePathIfNeeded(abs(os.Getenv("CC")))) + + // Ensure paths are absolute. + absPaths := []string{} + for _, path := range filepath.SplitList(os.Getenv("PATH")) { + absPaths = append(absPaths, abs(path)) + } + os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator))) + + sandboxPath := abs(".") + + // Strip path prefix from source files in debug information. + os.Setenv("CGO_CFLAGS", os.Getenv("CGO_CFLAGS")+" "+strings.Join(defaultCFlags(output), " ")) + os.Setenv("CGO_LDFLAGS", os.Getenv("CGO_LDFLAGS")+" "+strings.Join(defaultLdFlags(), " ")) + + // Allow flags in CGO_LDFLAGS that wouldn't pass the security check. + // Workaround for golang.org/issue/42565. + var b strings.Builder + sep := "" + cgoLdflags, _ := splitQuoted(os.Getenv("CGO_LDFLAGS")) + for _, f := range cgoLdflags { + b.WriteString(sep) + sep = "|" + b.WriteString(regexp.QuoteMeta(f)) + // If the flag if -framework, the flag value needs to be in the same + // condition. + if f == "-framework" { + sep = " " + } + } + os.Setenv("CGO_LDFLAGS_ALLOW", b.String()) + os.Setenv("GODEBUG", "installgoroot=all") + + // Build the commands needed to build the std library in the right mode + // NOTE: the go command stamps compiled .a files with build ids, which are + // cryptographic sums derived from the inputs. This prevents us from + // creating reproducible builds because the build ids are hashed from + // CGO_CFLAGS, which frequently contains absolute paths. As a workaround, + // we strip the build ids, since they won't be used after this. + installArgs := goenv.goCmd("install", "-toolexec", abs(os.Args[0])+" filterbuildid") + if len(build.Default.BuildTags) > 0 { + installArgs = append(installArgs, "-tags", strings.Join(build.Default.BuildTags, ",")) + } + + ldflags := []string{"-trimpath", sandboxPath} + asmflags := []string{"-trimpath", output} + if *race { + installArgs = append(installArgs, "-race") + } + if *shared { + gcflags = append(gcflags, "-shared") + ldflags = append(ldflags, "-shared") + asmflags = append(asmflags, "-shared") + } + if *dynlink { + gcflags = append(gcflags, "-dynlink") + ldflags = append(ldflags, "-dynlink") + asmflags = append(asmflags, "-dynlink") + } + + // Since Go 1.10, an all= prefix indicates the flags should apply to the package + // and its dependencies, rather than just the package itself. This was the + // default behavior before Go 1.10. + allSlug := "" + for _, t := range build.Default.ReleaseTags { + if t == "go1.10" { + allSlug = "all=" + break + } + } + installArgs = append(installArgs, "-gcflags="+allSlug+strings.Join(gcflags, " ")) + installArgs = append(installArgs, "-ldflags="+allSlug+strings.Join(ldflags, " ")) + installArgs = append(installArgs, "-asmflags="+allSlug+strings.Join(asmflags, " ")) + + // Modifying CGO flags to use only absolute path + // because go is having its own sandbox, all CGO flags must use absolute path + if err := absEnv(cgoEnvVars, cgoAbsEnvFlags); err != nil { + return fmt.Errorf("error modifying cgo environment to absolute path: %v", err) + } + + installArgs = append(installArgs, packages...) + if err := goenv.runCommand(installArgs); err != nil { + return err + } + return nil +} |