| // 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") |
| pgoprofile := flags.String("pgoprofile", "", "Build with pgo using the given pprof file") |
| 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. |
| 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 *pgoprofile != "" { |
| installArgs = append(installArgs, "-pgo", abs(*pgoprofile)) |
| } |
| 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, " ")) |
| |
| // Modify 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 |
| } |