| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "runtime" |
| "strings" |
| |
| "code.google.com/p/go.tools/go/vcs" |
| ) |
| |
| // These variables are copied from the gobuilder's environment |
| // to the envv of its subprocesses. |
| var extraEnv = []string{ |
| "GOARM", |
| |
| // For Unix derivatives. |
| "CC", |
| "PATH", |
| "TMPDIR", |
| "USER", |
| |
| // For Plan 9. |
| "objtype", |
| "cputype", |
| "path", |
| } |
| |
| // builderEnv represents the environment that a Builder will run tests in. |
| type builderEnv interface { |
| // setup sets up the builder environment and returns the directory to run the buildCmd in. |
| setup(repo *Repo, workpath, hash string, envv []string) (string, error) |
| } |
| |
| // goEnv represents the builderEnv for the main Go repo. |
| type goEnv struct { |
| goos, goarch string |
| } |
| |
| func (b *Builder) envv() []string { |
| if runtime.GOOS == "windows" { |
| return b.envvWindows() |
| } |
| |
| var e []string |
| if *buildTool == "go" { |
| e = []string{ |
| "GOOS=" + b.goos, |
| "GOHOSTOS=" + b.goos, |
| "GOARCH=" + b.goarch, |
| "GOHOSTARCH=" + b.goarch, |
| "GOROOT_FINAL=/usr/local/go", |
| } |
| } |
| |
| for _, k := range extraEnv { |
| if s, ok := getenvOk(k); ok { |
| e = append(e, k+"="+s) |
| } |
| } |
| return e |
| } |
| |
| func (b *Builder) envvWindows() []string { |
| var start map[string]string |
| if *buildTool == "go" { |
| start = map[string]string{ |
| "GOOS": b.goos, |
| "GOHOSTOS": b.goos, |
| "GOARCH": b.goarch, |
| "GOHOSTARCH": b.goarch, |
| "GOROOT_FINAL": `c:\go`, |
| "GOBUILDEXIT": "1", // exit all.bat with completion status. |
| } |
| } |
| |
| for _, name := range extraEnv { |
| if s, ok := getenvOk(name); ok { |
| start[name] = s |
| } |
| } |
| skip := map[string]bool{ |
| "GOBIN": true, |
| "GOROOT": true, |
| "INCLUDE": true, |
| "LIB": true, |
| } |
| var e []string |
| for name, v := range start { |
| e = append(e, name+"="+v) |
| skip[name] = true |
| } |
| for _, kv := range os.Environ() { |
| s := strings.SplitN(kv, "=", 2) |
| name := strings.ToUpper(s[0]) |
| switch { |
| case name == "": |
| // variables, like "=C:=C:\", just copy them |
| e = append(e, kv) |
| case !skip[name]: |
| e = append(e, kv) |
| skip[name] = true |
| } |
| } |
| return e |
| } |
| |
| // setup for a goEnv clones the main go repo to workpath/go at the provided hash |
| // and returns the path workpath/go/src, the location of all go build scripts. |
| func (env *goEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) { |
| goworkpath := filepath.Join(workpath, "go") |
| if _, err := repo.Clone(goworkpath, hash); err != nil { |
| return "", fmt.Errorf("error cloning repository: %s", err) |
| } |
| return filepath.Join(goworkpath, "src"), nil |
| } |
| |
| // gccgoEnv represents the builderEnv for the gccgo compiler. |
| type gccgoEnv struct{} |
| |
| // setup for a gccgoEnv clones the gofrontend repo to workpath/go at the hash |
| // and clones the latest GCC branch to repo.Path/gcc. The gccgo sources are |
| // replaced with the updated sources in the gofrontend repo and gcc gets |
| // gets configured and built in workpath/gcc-objdir. The path to |
| // workpath/gcc-objdir is returned. |
| func (env *gccgoEnv) setup(repo *Repo, workpath, hash string, envv []string) (string, error) { |
| gofrontendpath := filepath.Join(workpath, "gofrontend") |
| gccpath := filepath.Join(repo.Path, "gcc") |
| gccgopath := filepath.Join(gccpath, "gcc", "go", "gofrontend") |
| gcclibgopath := filepath.Join(gccpath, "libgo") |
| |
| // get a handle to SVN vcs.Cmd for pulling down GCC. |
| svn := vcs.ByCmd("svn") |
| |
| // only pull down gcc if we don't have a local copy. |
| if _, err := os.Stat(gccpath); err != nil { |
| if err := timeout(*cmdTimeout, func() error { |
| // pull down a working copy of GCC. |
| return svn.Create(gccpath, *gccPath) |
| }); err != nil { |
| return "", err |
| } |
| } else { |
| // make sure to remove gccgopath and gcclibgopath before |
| // updating the repo to avoid file clobbering. |
| if err := os.RemoveAll(gccgopath); err != nil { |
| return "", err |
| } |
| if err := os.RemoveAll(gcclibgopath); err != nil { |
| return "", err |
| } |
| } |
| if err := svn.Download(gccpath); err != nil { |
| return "", err |
| } |
| |
| // clone gofrontend repo at specified revision |
| if _, err := repo.Clone(gofrontendpath, hash); err != nil { |
| return "", err |
| } |
| |
| // remove gccgopath and gcclibgopath before copying over gofrontend. |
| if err := os.RemoveAll(gccgopath); err != nil { |
| return "", err |
| } |
| if err := os.RemoveAll(gcclibgopath); err != nil { |
| return "", err |
| } |
| |
| // copy gofrontend and libgo to appropriate locations |
| if err := copyDir(filepath.Join(gofrontendpath, "go"), gccgopath); err != nil { |
| return "", fmt.Errorf("Failed to copy gofrontend/go to gcc/go/gofrontend: %s\n", err) |
| } |
| if err := copyDir(filepath.Join(gofrontendpath, "libgo"), gcclibgopath); err != nil { |
| return "", fmt.Errorf("Failed to copy gofrontend/libgo to gcc/libgo: %s\n", err) |
| } |
| |
| // make objdir to work in |
| gccobjdir := filepath.Join(workpath, "gcc-objdir") |
| if err := os.Mkdir(gccobjdir, mkdirPerm); err != nil { |
| return "", err |
| } |
| |
| // configure GCC with substituted gofrontend and libgo |
| gccConfigCmd := []string{filepath.Join(gccpath, "configure"), "--enable-languages=c,c++,go", "--disable-bootstrap"} |
| if _, err := runOutput(*cmdTimeout, envv, ioutil.Discard, gccobjdir, gccConfigCmd...); err != nil { |
| return "", fmt.Errorf("Failed to configure GCC: %s", err) |
| } |
| |
| // build gcc |
| if _, err := runOutput(*buildTimeout, envv, ioutil.Discard, gccobjdir, "make"); err != nil { |
| return "", fmt.Errorf("Failed to build GCC: %s", err) |
| } |
| |
| return gccobjdir, nil |
| } |
| |
| // copyDir copies the src directory into the dst |
| func copyDir(src, dst string) error { |
| return filepath.Walk(src, func(path string, f os.FileInfo, err error) error { |
| dstPath := strings.Replace(path, src, dst, 1) |
| if f.IsDir() { |
| return os.Mkdir(dstPath, mkdirPerm) |
| } |
| |
| srcFile, err := os.Open(path) |
| if err != nil { |
| return err |
| } |
| defer srcFile.Close() |
| |
| dstFile, err := os.Create(dstPath) |
| if err != nil { |
| return err |
| } |
| |
| if _, err := io.Copy(dstFile, srcFile); err != nil { |
| return err |
| } |
| return dstFile.Close() |
| }) |
| } |
| |
| func getenvOk(k string) (v string, ok bool) { |
| v = os.Getenv(k) |
| if v != "" { |
| return v, true |
| } |
| keq := k + "=" |
| for _, kv := range os.Environ() { |
| if kv == keq { |
| return "", true |
| } |
| } |
| return "", false |
| } |