| // Copyright 2015 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. |
| |
| // racebuild builds the race runtime (syso files) on all supported OSes using gomote. |
| // Usage: |
| // $ racebuild -rev <llvm_revision> -goroot <path_to_go_repo> |
| package main |
| |
| import ( |
| "archive/tar" |
| "bytes" |
| "compress/gzip" |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "log" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "regexp" |
| "strings" |
| "sync" |
| ) |
| |
| var ( |
| flagGoroot = flag.String("goroot", "", "path to Go repository to update (required)") |
| flagRev = flag.String("rev", "", "llvm compiler-rt git revision from http://llvm.org/git/compiler-rt.git (required)") |
| ) |
| |
| // TODO: use buildlet package instead of calling out to gomote. |
| var platforms = []*Platform{ |
| &Platform{ |
| OS: "freebsd", |
| Arch: "amd64", |
| Type: "freebsd-amd64-race", |
| Script: `#!/usr/bin/env bash |
| set -e |
| git clone https://go.googlesource.com/go |
| git clone http://llvm.org/git/compiler-rt.git |
| (cd compiler-rt && git checkout $REV) |
| (cd compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh) |
| cp compiler-rt/lib/tsan/go/race_freebsd_amd64.syso go/src/runtime/race |
| (cd go/src && ./race.bash) |
| `, |
| }, |
| &Platform{ |
| OS: "darwin", |
| Arch: "amd64", |
| Type: "darwin-amd64-10_10", |
| Script: `#!/usr/bin/env bash |
| set -e |
| git clone https://go.googlesource.com/go |
| git clone http://llvm.org/git/compiler-rt.git |
| (cd compiler-rt && git checkout $REV) |
| (cd compiler-rt/lib/tsan/go && CC=clang ./buildgo.sh) |
| cp compiler-rt/lib/tsan/go/race_darwin_amd64.syso go/src/runtime/race |
| (cd go/src && ./race.bash) |
| `, |
| }, |
| &Platform{ |
| OS: "linux", |
| Arch: "amd64", |
| Type: "linux-amd64-race", |
| Script: `#!/usr/bin/env bash |
| set -e |
| apt-get update |
| apt-get install -y git g++ |
| git clone https://go.googlesource.com/go |
| git clone http://llvm.org/git/compiler-rt.git |
| (cd compiler-rt && git checkout $REV) |
| (cd compiler-rt/lib/tsan/go && ./buildgo.sh) |
| cp compiler-rt/lib/tsan/go/race_linux_amd64.syso go/src/runtime/race |
| (cd go/src && ./race.bash) |
| `, |
| }, |
| &Platform{ |
| OS: "windows", |
| Arch: "amd64", |
| Type: "windows-amd64-race", |
| Script: ` |
| git clone https://go.googlesource.com/go |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| git clone http://llvm.org/git/compiler-rt.git |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| cd compiler-rt |
| git checkout %REV% |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| cd .. |
| cd compiler-rt/lib/tsan/go |
| call build.bat |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| cd ../../../.. |
| xcopy compiler-rt\lib\tsan\go\race_windows_amd64.syso go\src\runtime\race\race_windows_amd64.syso /Y |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| cd go/src |
| call race.bat |
| if %errorlevel% neq 0 exit /b %errorlevel% |
| `, |
| }, |
| } |
| |
| func main() { |
| flag.Parse() |
| if *flagRev == "" || *flagGoroot == "" { |
| flag.PrintDefaults() |
| os.Exit(1) |
| } |
| |
| // Update revision in the README file. |
| // Do this early to check goroot correctness. |
| readmeFile := filepath.Join(*flagGoroot, "src", "runtime", "race", "README") |
| readme, err := ioutil.ReadFile(readmeFile) |
| if err != nil { |
| log.Fatalf("bad -goroot? %v", err) |
| } |
| readmeRev := regexp.MustCompile("Current runtime is built on rev ([0-9,a-z]+)\\.").FindSubmatchIndex(readme) |
| if readmeRev == nil { |
| log.Fatalf("failed to find current revision in src/runtime/race/README") |
| } |
| readme = bytes.Replace(readme, readme[readmeRev[2]:readmeRev[3]], []byte(*flagRev), -1) |
| if err := ioutil.WriteFile(readmeFile, readme, 0640); err != nil { |
| log.Fatalf("failed to write README file: %v", err) |
| } |
| |
| // Start build on all platforms in parallel. |
| var wg sync.WaitGroup |
| wg.Add(len(platforms)) |
| for _, p := range platforms { |
| p := p |
| go func() { |
| defer wg.Done() |
| p.Err = p.Build() |
| if p.Err != nil { |
| p.Err = fmt.Errorf("failed: %v", p.Err) |
| log.Printf("%v: %v", p.Name, p.Err) |
| } |
| }() |
| } |
| wg.Wait() |
| |
| // Duplicate results, they can get lost in the log. |
| ok := true |
| log.Printf("---") |
| for _, p := range platforms { |
| if p.Err == nil { |
| log.Printf("%v: ok", p.Name) |
| continue |
| } |
| ok = false |
| log.Printf("%v: %v", p.Name, p.Err) |
| } |
| if !ok { |
| os.Exit(1) |
| } |
| } |
| |
| type Platform struct { |
| OS string |
| Arch string |
| Name string // something for logging |
| Type string // gomote instance type |
| Inst string // actual gomote instance name |
| Err error |
| Log *os.File |
| Script string |
| } |
| |
| func (p *Platform) Build() error { |
| p.Name = fmt.Sprintf("%v-%v", p.OS, p.Arch) |
| |
| // Open log file. |
| var err error |
| p.Log, err = ioutil.TempFile("", p.Name) |
| if err != nil { |
| return fmt.Errorf("failed to create log file: %v", err) |
| } |
| defer p.Log.Close() |
| log.Printf("%v: logging to %v", p.Name, p.Log.Name()) |
| |
| // Create gomote instance (or reuse an existing instance for debugging). |
| if p.Inst == "" { |
| // Creation sometimes fails with transient errors like: |
| // "buildlet didn't come up at http://10.240.0.13 in 3m0s". |
| var createErr error |
| for i := 0; i < 10; i++ { |
| inst, err := p.Gomote("create", p.Type) |
| if err != nil { |
| log.Printf("%v: instance creation failed, retrying", p.Name) |
| createErr = err |
| continue |
| } |
| p.Inst = strings.Trim(string(inst), " \t\n") |
| break |
| } |
| if p.Inst == "" { |
| return createErr |
| } |
| } |
| defer p.Gomote("destroy", p.Inst) |
| log.Printf("%s: using instance %v", p.Name, p.Inst) |
| |
| // put14 |
| if _, err := p.Gomote("put14", p.Inst); err != nil { |
| return err |
| } |
| |
| // Execute the script. |
| script, err := ioutil.TempFile("", "racebuild") |
| if err != nil { |
| return fmt.Errorf("failed to create temp file: %v", err) |
| } |
| defer func() { |
| script.Close() |
| os.Remove(script.Name()) |
| }() |
| if _, err := script.Write([]byte(p.Script)); err != nil { |
| return fmt.Errorf("failed to write temp file: %v", err) |
| } |
| script.Close() |
| targetName := "script.bash" |
| if p.OS == "windows" { |
| targetName = "script.bat" |
| } |
| if _, err := p.Gomote("put", "-mode=0700", p.Inst, script.Name(), targetName); err != nil { |
| return err |
| } |
| if _, err := p.Gomote("run", "-e=REV="+*flagRev, p.Inst, targetName); err != nil { |
| return err |
| } |
| |
| // The script is supposed to leave updated runtime at that path. Copy it out. |
| syso := fmt.Sprintf("race_%v_%s.syso", p.OS, p.Arch) |
| targz, err := p.Gomote("gettar", "-dir=go/src/runtime/race/"+syso, p.Inst) |
| if err != nil { |
| return err |
| } |
| |
| // Untar the runtime and write it to goroot. |
| if err := p.WriteSyso(filepath.Join(*flagGoroot, "src", "runtime", "race", syso), targz); err != nil { |
| return fmt.Errorf("%v", err) |
| } |
| |
| log.Printf("%v: build completed", p.Name) |
| return nil |
| } |
| |
| func (p *Platform) WriteSyso(sysof string, targz []byte) error { |
| // Ungzip. |
| gzipr, err := gzip.NewReader(bytes.NewReader(targz)) |
| if err != nil { |
| return fmt.Errorf("failed to read gzip archive: %v", err) |
| } |
| defer gzipr.Close() |
| tr := tar.NewReader(gzipr) |
| if _, err := tr.Next(); err != nil { |
| return fmt.Errorf("failed to read tar archive: %v", err) |
| } |
| |
| // Copy the file. |
| syso, err := os.Create(sysof) |
| if err != nil { |
| return fmt.Errorf("failed to open race runtime: %v", err) |
| } |
| defer syso.Close() |
| if _, err := io.Copy(syso, tr); err != nil { |
| return fmt.Errorf("failed to write race runtime: %v", err) |
| } |
| return nil |
| } |
| |
| func (p *Platform) Gomote(args ...string) ([]byte, error) { |
| log.Printf("%v: gomote %v", p.Name, args) |
| fmt.Fprintf(p.Log, "$ gomote %v\n", args) |
| output, err := exec.Command("gomote", args...).CombinedOutput() |
| if err != nil || args[0] != "gettar" { |
| p.Log.Write(output) |
| } |
| fmt.Fprintf(p.Log, "\n\n") |
| if err != nil { |
| err = fmt.Errorf("gomote %v failed: %v", args, err) |
| } |
| return output, err |
| } |