aboutsummaryrefslogtreecommitdiff
path: root/go/tools/builders/stdlib.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/tools/builders/stdlib.go')
-rw-r--r--go/tools/builders/stdlib.go169
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
+}