github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/dashboard/builder/env.go (about) 1 // Copyright 2013 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 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "strings" 17 18 "golang.org/x/tools/go/vcs" 19 ) 20 21 // builderEnv represents the environment that a Builder will run tests in. 22 type builderEnv interface { 23 // setup sets up the builder environment and returns the directory to run the buildCmd in. 24 setup(repo *Repo, workpath, hash string, envv []string) (string, error) 25 } 26 27 // goEnv represents the builderEnv for the main Go repo. 28 type goEnv struct { 29 goos, goarch string 30 } 31 32 func (b *Builder) envv() []string { 33 if runtime.GOOS == "windows" { 34 return b.envvWindows() 35 } 36 37 var e []string 38 if *buildTool == "go" { 39 e = []string{ 40 "GOOS=" + b.goos, 41 "GOARCH=" + b.goarch, 42 "GOROOT_FINAL=/usr/local/go", 43 } 44 switch b.goos { 45 case "android", "nacl": 46 // Cross compile. 47 default: 48 // If we are building, for example, linux/386 on a linux/amd64 machine we want to 49 // make sure that the whole build is done as a if this were compiled on a real 50 // linux/386 machine. In other words, we want to not do a cross compilation build. 51 // To do this we set GOHOSTOS and GOHOSTARCH to override the detection in make.bash. 52 // 53 // The exception to this rule is when we are doing nacl/android builds. These are by 54 // definition always cross compilation, and we have support built into cmd/go to be 55 // able to handle this case. 56 e = append(e, "GOHOSTOS="+b.goos, "GOHOSTARCH="+b.goarch) 57 } 58 } 59 60 for _, k := range extraEnv() { 61 if s, ok := getenvOk(k); ok { 62 e = append(e, k+"="+s) 63 } 64 } 65 return e 66 } 67 68 func (b *Builder) envvWindows() []string { 69 var start map[string]string 70 if *buildTool == "go" { 71 start = map[string]string{ 72 "GOOS": b.goos, 73 "GOHOSTOS": b.goos, 74 "GOARCH": b.goarch, 75 "GOHOSTARCH": b.goarch, 76 "GOROOT_FINAL": `c:\go`, 77 "GOBUILDEXIT": "1", // exit all.bat with completion status. 78 } 79 } 80 81 for _, name := range extraEnv() { 82 if s, ok := getenvOk(name); ok { 83 start[name] = s 84 } 85 } 86 if b.goos == "windows" { 87 switch b.goarch { 88 case "amd64": 89 start["PATH"] = `c:\TDM-GCC-64\bin;` + start["PATH"] 90 case "386": 91 start["PATH"] = `c:\TDM-GCC-32\bin;` + start["PATH"] 92 } 93 } 94 skip := map[string]bool{ 95 "GOBIN": true, 96 "GOPATH": true, 97 "GOROOT": true, 98 "INCLUDE": true, 99 "LIB": true, 100 } 101 var e []string 102 for name, v := range start { 103 e = append(e, name+"="+v) 104 skip[name] = true 105 } 106 for _, kv := range os.Environ() { 107 s := strings.SplitN(kv, "=", 2) 108 name := strings.ToUpper(s[0]) 109 switch { 110 case name == "": 111 // variables, like "=C:=C:\", just copy them 112 e = append(e, kv) 113 case !skip[name]: 114 e = append(e, kv) 115 skip[name] = true 116 } 117 } 118 return e 119 } 120 121 // setup for a goEnv clones the main go repo to workpath/go at the provided hash 122 // and returns the path workpath/go/src, the location of all go build scripts. 123 func (env *goEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) { 124 goworkpath := filepath.Join(workpath, "go") 125 if err := repo.Export(goworkpath, hash); err != nil { 126 return "", fmt.Errorf("error exporting repository: %s", err) 127 } 128 // Write out VERSION file if it does not already exist. 129 vFile := filepath.Join(goworkpath, "VERSION") 130 if _, err := os.Stat(vFile); os.IsNotExist(err) { 131 if err := ioutil.WriteFile(vFile, []byte(hash), 0644); err != nil { 132 return "", fmt.Errorf("error writing VERSION file: %s", err) 133 } 134 } 135 return filepath.Join(goworkpath, "src"), nil 136 } 137 138 // gccgoEnv represents the builderEnv for the gccgo compiler. 139 type gccgoEnv struct{} 140 141 // setup for a gccgoEnv clones the gofrontend repo to workpath/go at the hash 142 // and clones the latest GCC branch to repo.Path/gcc. The gccgo sources are 143 // replaced with the updated sources in the gofrontend repo and gcc gets 144 // gets configured and built in workpath/gcc-objdir. The path to 145 // workpath/gcc-objdir is returned. 146 func (env *gccgoEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) { 147 gccpath := filepath.Join(repo.Path, "gcc") 148 149 // get a handle to Git vcs.Cmd for pulling down GCC from the mirror. 150 git := vcs.ByCmd("git") 151 152 // only pull down gcc if we don't have a local copy. 153 if _, err := os.Stat(gccpath); err != nil { 154 if err := timeout(*cmdTimeout, func() error { 155 // pull down a working copy of GCC. 156 157 cloneCmd := []string{ 158 "clone", 159 // This is just a guess since there are ~6000 commits to 160 // GCC per year. It's likely there will be enough history 161 // to cross-reference the Gofrontend commit against GCC. 162 // The disadvantage would be if the commit being built is more than 163 // a year old; in this case, the user should make a clone that has 164 // the full history. 165 "--depth", "6000", 166 // We only care about the master branch. 167 "--branch", "master", "--single-branch", 168 *gccPath, 169 } 170 171 // Clone Kind Clone Time(Dry run) Clone Size 172 // --------------------------------------------------------------- 173 // Full Clone 10 - 15 min 2.2 GiB 174 // Master Branch 2 - 3 min 1.5 GiB 175 // Full Clone(shallow) 1 min 900 MiB 176 // Master Branch(shallow) 40 sec 900 MiB 177 // 178 // The shallow clones have the same size, which is expected, 179 // but the full shallow clone will only have 6000 commits 180 // spread across all branches. There are ~50 branches. 181 return run(exec.Command("git", cloneCmd...), runEnv(envv), allOutput(os.Stdout), runDir(repo.Path)) 182 }); err != nil { 183 return "", err 184 } 185 } 186 187 if err := git.Download(gccpath); err != nil { 188 return "", err 189 } 190 191 // get the modified files for this commit. 192 193 var buf bytes.Buffer 194 if err := run(exec.Command("hg", "status", "--no-status", "--change", hash), 195 allOutput(&buf), runDir(repo.Path), runEnv(envv)); err != nil { 196 return "", fmt.Errorf("Failed to find the modified files for %s: %s", hash, err) 197 } 198 modifiedFiles := strings.Split(buf.String(), "\n") 199 var isMirrored bool 200 for _, f := range modifiedFiles { 201 if strings.HasPrefix(f, "go/") || strings.HasPrefix(f, "libgo/") { 202 isMirrored = true 203 break 204 } 205 } 206 207 // use git log to find the corresponding commit to sync to in the gcc mirror. 208 // If the files modified in the gofrontend are mirrored to gcc, we expect a 209 // commit with a similar description in the gcc mirror. If the files modified are 210 // not mirrored, e.g. in support/, we can sync to the most recent gcc commit that 211 // occurred before those files were modified to verify gccgo's status at that point. 212 logCmd := []string{ 213 "log", 214 "-1", 215 "--format=%H", 216 } 217 var errMsg string 218 if isMirrored { 219 commitDesc, err := repo.Master.VCS.LogAtRev(repo.Path, hash, "{desc|firstline|escape}") 220 if err != nil { 221 return "", err 222 } 223 224 quotedDesc := regexp.QuoteMeta(string(commitDesc)) 225 logCmd = append(logCmd, "--grep", quotedDesc, "--regexp-ignore-case", "--extended-regexp") 226 errMsg = fmt.Sprintf("Failed to find a commit with a similar description to '%s'", string(commitDesc)) 227 } else { 228 commitDate, err := repo.Master.VCS.LogAtRev(repo.Path, hash, "{date|rfc3339date}") 229 if err != nil { 230 return "", err 231 } 232 233 logCmd = append(logCmd, "--before", string(commitDate)) 234 errMsg = fmt.Sprintf("Failed to find a commit before '%s'", string(commitDate)) 235 } 236 237 buf.Reset() 238 if err := run(exec.Command("git", logCmd...), runEnv(envv), allOutput(&buf), runDir(gccpath)); err != nil { 239 return "", fmt.Errorf("%s: %s", errMsg, err) 240 } 241 gccRev := buf.String() 242 if gccRev == "" { 243 return "", fmt.Errorf(errMsg) 244 } 245 246 // checkout gccRev 247 // TODO(cmang): Fix this to work in parallel mode. 248 if err := run(exec.Command("git", "reset", "--hard", strings.TrimSpace(gccRev)), runEnv(envv), runDir(gccpath)); err != nil { 249 return "", fmt.Errorf("Failed to checkout commit at revision %s: %s", gccRev, err) 250 } 251 252 // make objdir to work in 253 gccobjdir := filepath.Join(workpath, "gcc-objdir") 254 if err := os.Mkdir(gccobjdir, mkdirPerm); err != nil { 255 return "", err 256 } 257 258 // configure GCC with substituted gofrontend and libgo 259 if err := run(exec.Command(filepath.Join(gccpath, "configure"), 260 "--enable-languages=c,c++,go", 261 "--disable-bootstrap", 262 "--disable-multilib", 263 ), runEnv(envv), runDir(gccobjdir)); err != nil { 264 return "", fmt.Errorf("Failed to configure GCC: %v", err) 265 } 266 267 // build gcc 268 if err := run(exec.Command("make", *gccOpts), runTimeout(*buildTimeout), runEnv(envv), runDir(gccobjdir)); err != nil { 269 return "", fmt.Errorf("Failed to build GCC: %s", err) 270 } 271 272 return gccobjdir, nil 273 } 274 275 func getenvOk(k string) (v string, ok bool) { 276 v = os.Getenv(k) 277 if v != "" { 278 return v, true 279 } 280 keq := k + "=" 281 for _, kv := range os.Environ() { 282 if kv == keq { 283 return "", true 284 } 285 } 286 return "", false 287 } 288 289 // extraEnv returns environment variables that need to be copied from 290 // the gobuilder's environment to the envv of its subprocesses. 291 func extraEnv() []string { 292 extra := []string{ 293 "GOARM", 294 "GO386", 295 "CGO_ENABLED", 296 "CC", 297 "CC_FOR_TARGET", 298 "PATH", 299 "TMPDIR", 300 "USER", 301 } 302 if runtime.GOOS == "plan9" { 303 extra = append(extra, "objtype", "cputype", "path") 304 } 305 return extra 306 }