1// Copyright 2018 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "flag" 19 "fmt" 20 "go/build" 21 "os" 22 "path/filepath" 23 "regexp" 24 "strings" 25) 26 27// stdlib builds the standard library in the appropriate mode into a new goroot. 28func stdlib(args []string) error { 29 // process the args 30 flags := flag.NewFlagSet("stdlib", flag.ExitOnError) 31 goenv := envFlags(flags) 32 out := flags.String("out", "", "Path to output go root") 33 race := flags.Bool("race", false, "Build in race mode") 34 shared := flags.Bool("shared", false, "Build in shared mode") 35 dynlink := flags.Bool("dynlink", false, "Build in dynlink mode") 36 var packages multiFlag 37 flags.Var(&packages, "package", "Packages to build") 38 var gcflags quoteMultiFlag 39 flags.Var(&gcflags, "gcflags", "Go compiler flags") 40 if err := flags.Parse(args); err != nil { 41 return err 42 } 43 if err := goenv.checkFlags(); err != nil { 44 return err 45 } 46 goroot := os.Getenv("GOROOT") 47 if goroot == "" { 48 return fmt.Errorf("GOROOT not set") 49 } 50 output := abs(*out) 51 52 // Fail fast if cgo is required but a toolchain is not configured. 53 if os.Getenv("CGO_ENABLED") == "1" && filepath.Base(os.Getenv("CC")) == "vc_installation_error.bat" { 54 return fmt.Errorf(`cgo is required, but a C toolchain has not been configured. 55You may need to use the flags --cpu=x64_windows --compiler=mingw-gcc.`) 56 } 57 58 // Link in the bare minimum needed to the new GOROOT 59 if err := replicate(goroot, output, replicatePaths("src", "pkg/tool", "pkg/include")); err != nil { 60 return err 61 } 62 63 output, err := processPath(output) 64 if err != nil { 65 return err 66 } 67 68 // Now switch to the newly created GOROOT 69 os.Setenv("GOROOT", output) 70 71 // Create a temporary cache directory. "go build" requires this starting 72 // in Go 1.12. 73 cachePath := filepath.Join(output, ".gocache") 74 os.Setenv("GOCACHE", cachePath) 75 defer os.RemoveAll(cachePath) 76 77 // Disable modules for the 'go install' command. Depending on the sandboxing 78 // mode, there may be a go.mod file in a parent directory which will turn 79 // modules on in "auto" mode. 80 os.Setenv("GO111MODULE", "off") 81 82 // Make sure we have an absolute path to the C compiler. 83 // TODO(#1357): also take absolute paths of includes and other paths in flags. 84 os.Setenv("CC", quotePathIfNeeded(abs(os.Getenv("CC")))) 85 86 // Ensure paths are absolute. 87 absPaths := []string{} 88 for _, path := range filepath.SplitList(os.Getenv("PATH")) { 89 absPaths = append(absPaths, abs(path)) 90 } 91 os.Setenv("PATH", strings.Join(absPaths, string(os.PathListSeparator))) 92 93 sandboxPath := abs(".") 94 95 // Strip path prefix from source files in debug information. 96 os.Setenv("CGO_CFLAGS", os.Getenv("CGO_CFLAGS")+" "+strings.Join(defaultCFlags(output), " ")) 97 os.Setenv("CGO_LDFLAGS", os.Getenv("CGO_LDFLAGS")+" "+strings.Join(defaultLdFlags(), " ")) 98 99 // Allow flags in CGO_LDFLAGS that wouldn't pass the security check. 100 // Workaround for golang.org/issue/42565. 101 var b strings.Builder 102 sep := "" 103 cgoLdflags, _ := splitQuoted(os.Getenv("CGO_LDFLAGS")) 104 for _, f := range cgoLdflags { 105 b.WriteString(sep) 106 sep = "|" 107 b.WriteString(regexp.QuoteMeta(f)) 108 // If the flag if -framework, the flag value needs to be in the same 109 // condition. 110 if f == "-framework" { 111 sep = " " 112 } 113 } 114 os.Setenv("CGO_LDFLAGS_ALLOW", b.String()) 115 os.Setenv("GODEBUG", "installgoroot=all") 116 117 // Build the commands needed to build the std library in the right mode 118 // NOTE: the go command stamps compiled .a files with build ids, which are 119 // cryptographic sums derived from the inputs. This prevents us from 120 // creating reproducible builds because the build ids are hashed from 121 // CGO_CFLAGS, which frequently contains absolute paths. As a workaround, 122 // we strip the build ids, since they won't be used after this. 123 installArgs := goenv.goCmd("install", "-toolexec", abs(os.Args[0])+" filterbuildid") 124 if len(build.Default.BuildTags) > 0 { 125 installArgs = append(installArgs, "-tags", strings.Join(build.Default.BuildTags, ",")) 126 } 127 128 ldflags := []string{"-trimpath", sandboxPath} 129 asmflags := []string{"-trimpath", output} 130 if *race { 131 installArgs = append(installArgs, "-race") 132 } 133 if *shared { 134 gcflags = append(gcflags, "-shared") 135 ldflags = append(ldflags, "-shared") 136 asmflags = append(asmflags, "-shared") 137 } 138 if *dynlink { 139 gcflags = append(gcflags, "-dynlink") 140 ldflags = append(ldflags, "-dynlink") 141 asmflags = append(asmflags, "-dynlink") 142 } 143 144 // Since Go 1.10, an all= prefix indicates the flags should apply to the package 145 // and its dependencies, rather than just the package itself. This was the 146 // default behavior before Go 1.10. 147 allSlug := "" 148 for _, t := range build.Default.ReleaseTags { 149 if t == "go1.10" { 150 allSlug = "all=" 151 break 152 } 153 } 154 installArgs = append(installArgs, "-gcflags="+allSlug+strings.Join(gcflags, " ")) 155 installArgs = append(installArgs, "-ldflags="+allSlug+strings.Join(ldflags, " ")) 156 installArgs = append(installArgs, "-asmflags="+allSlug+strings.Join(asmflags, " ")) 157 158 // Modifying CGO flags to use only absolute path 159 // because go is having its own sandbox, all CGO flags must use absolute path 160 if err := absEnv(cgoEnvVars, cgoAbsEnvFlags); err != nil { 161 return fmt.Errorf("error modifying cgo environment to absolute path: %v", err) 162 } 163 164 installArgs = append(installArgs, packages...) 165 if err := goenv.runCommand(installArgs); err != nil { 166 return err 167 } 168 return nil 169} 170