blob: 510a345a5389c1848878816e072cb39aef7ed2ba [file] [log] [blame]
// 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
}