cmd/release: build race detector, godoc, cover, and vet
Change-Id: Iec3edcfdadedee882866c7747e84bb119d5ab591
Reviewed-on: https://go-review.googlesource.com/3881
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/cmd/release/release.go b/cmd/release/release.go
index 6a5aad1..c060e15 100644
--- a/cmd/release/release.go
+++ b/cmd/release/release.go
@@ -13,7 +13,9 @@
"io"
"log"
"os"
+ "path"
"path/filepath"
+ "strings"
"sync"
"time"
@@ -25,23 +27,61 @@
)
var (
- revision = flag.String("rev", "", "Go revision to build")
+ rev = flag.String("rev", "", "Go revision to build")
+ toolsRev = flag.String("tools", "", "Tools revision to build")
project = flag.String("project", "symbolic-datum-552", "Google Cloud Project")
zone = flag.String("zone", "us-central1-a", "Compute Engine zone")
+ target = flag.String("target", "", "If specified, build specific target platform")
)
-var builders = []string{
- "darwin-386",
- "darwin-amd64",
- "freebsd-386",
- "freebsd-amd64",
- "linux-386",
- "linux-amd64",
- "windows-386",
- "windows-amd64",
+const timeout = time.Hour
+
+type Build struct {
+ OS, Arch string
+
+ Race bool // Build race detector.
+ Static bool // Statically-link binaries.
+
+ Builder string // Key for dashboard.Builders.
}
-const timeout = time.Hour
+func (b *Build) String() string {
+ return fmt.Sprintf("%v-%v", b.OS, b.Arch)
+}
+
+var builds = []*Build{
+ {
+ OS: "linux",
+ Arch: "386",
+ Builder: "linux-amd64",
+ },
+ {
+ OS: "linux",
+ Arch: "amd64",
+ Race: true,
+ Static: true,
+ Builder: "linux-amd64",
+ },
+ {
+ OS: "freebsd",
+ Arch: "386",
+ Builder: "freebsd-386-gce101",
+ },
+ {
+ OS: "freebsd",
+ Arch: "amd64",
+ Race: true,
+ Builder: "freebsd-amd64-gce101",
+ },
+}
+
+const toolsRepo = "golang.org/x/tools"
+
+var toolPaths = []string{
+ "golang.org/x/tools/cmd/cover",
+ "golang.org/x/tools/cmd/godoc",
+ "golang.org/x/tools/cmd/vet",
+}
var preBuildCleanFiles = []string{
".gitattributes",
@@ -58,31 +98,37 @@
func main() {
flag.Parse()
- if *revision == "" {
+ if *rev == "" {
log.Fatal("must specify -rev flag")
}
+ if *toolsRev == "" {
+ log.Fatal("must specify -tools flag")
+ }
var wg sync.WaitGroup
- for _, name := range builders {
- b, ok := dashboard.Builders[name]
- if !ok {
- log.Printf("unknown builder %q", name)
+ for _, b := range builds {
+ b := b
+ if *target != "" && b.String() != *target {
continue
}
wg.Add(1)
go func() {
defer wg.Done()
- if err := makeRelease(b); err != nil {
- log.Printf("makeRelease(%q): %v", b.Name, err)
- }
+ b.make() // error logged by make function
}()
}
// TODO(adg): show progress of running builders
wg.Wait()
}
-func makeRelease(bc dashboard.BuildConfig) (err error) {
+func (b *Build) make() (err error) {
+ bc, ok := dashboard.Builders[b.Builder]
+ if !ok {
+ return fmt.Errorf("unknown builder: %v", bc)
+ }
+
// Start VM
+ log.Printf("%v: Starting VM.", b)
keypair, err := buildlet.NewKeyPair()
if err != nil {
return err
@@ -95,44 +141,80 @@
DeleteIn: timeout,
Description: fmt.Sprintf("release buildlet for %s", os.Getenv("USER")),
OnInstanceRequested: func() {
- log.Printf("%v: Sent create request. Waiting for operation.", instance)
+ log.Printf("%v: Sent create request. Waiting for operation.", b)
},
OnInstanceCreated: func() {
- log.Printf("%v: Instance created.", instance)
+ log.Printf("%v: Instance created.", b)
},
})
if err != nil {
return err
}
- log.Printf("%v: Instance up.", instance)
+ log.Printf("%v: Instance %v up.", b, instance)
defer func() {
- log.Printf("%v: Destroying VM.", instance)
+ if err != nil {
+ log.Printf("%v: %v", b, err)
+ }
+ log.Printf("%v: Destroying VM.", b)
err := client.DestroyVM(projTokenSource(), *project, *zone, instance)
if err != nil {
- log.Printf("%v: Destroying VM: %v", instance, err)
+ log.Printf("%v: Destroying VM: %v", b, err)
}
}()
- // Push source to VM
- const dir = "go"
- tar := "https://go.googlesource.com/go/+archive/" + *revision + ".tar.gz"
- if err := client.PutTarFromURL(tar, dir); err != nil {
+ work, err := client.WorkDir()
+ if err != nil {
return err
}
- log.Printf("%v: Pushed source to VM.", instance)
- if err := client.RemoveAll(preBuildCleanFiles...); err != nil {
+ const (
+ goDir = "go"
+ goPath = "gopath"
+ toolsDir = goPath + "/src/" + toolsRepo
+ )
+
+ // Push source to VM
+ log.Printf("%v: Pushing source to VM.", b)
+ tar := "https://go.googlesource.com/go/+archive/" + *rev + ".tar.gz"
+ if err := client.PutTarFromURL(tar, goDir); err != nil {
return err
}
- log.Printf("%v: Cleaned repo (pre-build).", instance)
+ tar = "https://go.googlesource.com/tools/+archive/" + *toolsRev + ".tar.gz"
+ if err := client.PutTarFromURL(tar, toolsDir); err != nil {
+ return err
+ }
+
+ log.Printf("%v: Cleaning goroot (pre-build).", b)
+ if err := client.RemoveAll(addPrefix(goDir, preBuildCleanFiles)...); err != nil {
+ return err
+ }
+
+ // Set up build environment.
+ sep := "/"
+ if b.OS == "windows" {
+ sep = "\\"
+ }
+ env := []string{
+ "GOOS=" + b.OS,
+ "GOARCH=" + b.Arch,
+ "GOHOSTOS=" + b.OS,
+ "GOHOSTARCH=" + b.Arch,
+ "GOROOT_FINAL=" + bc.GorootFinal(),
+ "GOROOT=" + work + sep + goDir,
+ "GOPATH=" + work + sep + goPath,
+ }
+ if b.Static {
+ env = append(env, "GO_DISTFLAGS=-s")
+ }
// Execute build
+ log.Printf("%v: Building.", b)
out := new(bytes.Buffer)
- mk := filepath.Join(dir, bc.MakeScript())
+ mk := filepath.Join(goDir, bc.MakeScript())
remoteErr, err := client.Exec(mk, buildlet.ExecOpts{
Output: out,
- ExtraEnv: []string{"GOROOT_FINAL=" + bc.GorootFinal()},
+ ExtraEnv: env,
})
if err != nil {
return err
@@ -141,27 +223,85 @@
// TODO(adg): write log to file instead?
return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
}
- log.Printf("%v: Build complete.", instance)
- // TODO: build race-enabled tool chain
+ goCmd := path.Join(goDir, "bin/go")
+ if b.OS == "windows" {
+ goCmd += ".exe"
+ }
+ runGo := func(args ...string) error {
+ out := new(bytes.Buffer)
+ remoteErr, err := client.Exec(goCmd, buildlet.ExecOpts{
+ Output: out,
+ Args: args,
+ ExtraEnv: env,
+ })
+ if err != nil {
+ return err
+ }
+ if remoteErr != nil {
+ return fmt.Errorf("go %v: %v\n%s", strings.Join(args, " "), remoteErr, out)
+ }
+ return nil
+ }
- // TODO: check out tools
- // TODO: build godoc, vet, cover
+ if b.Race {
+ log.Printf("%v: Building race detector.", b)
- // TODO: check out blog, add to misc
- // TODO: check out tour, add to misc
+ // Because on release branches, go install -a std is a NOP,
+ // we have to resort to delete pkg/$GOOS_$GOARCH, install -race,
+ // and then reinstall std so that we're not left with a slower,
+ // race-enabled cmd/go, etc.
+ if err := client.RemoveAll(path.Join(goDir, "pkg", b.OS+"_"+b.Arch)); err != nil {
+ return err
+ }
+ if err := runGo("tool", "dist", "install", "runtime"); err != nil {
+ return err
+ }
+ if err := runGo("install", "-race", "std"); err != nil {
+ return err
+ }
+ if err := runGo("install", "std"); err != nil {
+ return err
+ }
+ // Re-building go command leaves old versions of go.exe as go.exe~ on windows.
+ // See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details.
+ // Remove it manually.
+ if b.OS == "windows" {
+ if err := client.RemoveAll(goCmd + "~"); err != nil {
+ return err
+ }
+ }
+ }
- if err := client.RemoveAll(postBuildCleanFiles...); err != nil {
+ for _, p := range toolPaths {
+ log.Printf("%v: Building %v.", b, p)
+ if err := runGo("install", p); err != nil {
+ return err
+ }
+ }
+
+ // TODO(adg): check out blog, add to misc
+ // TODO(adg): check out tour, add to misc
+
+ log.Printf("%v: Cleaning goroot (post-build).", b)
+ // Need to delete everything except the final "go" directory,
+ // as we make the tarball relative to workdir.
+ cleanFiles := append(addPrefix(goDir, postBuildCleanFiles), goPath)
+ if err := client.RemoveAll(cleanFiles...); err != nil {
return err
}
- log.Printf("%v: Cleaned repo (post-build).", instance)
+
+ // TODO(adg): build msi files on windows and pkg files on osx
// Download tarball
- tgz, err := client.GetTar(dir)
+ log.Printf("%v: Downloading tarball", b)
+ tgz, err := client.GetTar(".")
if err != nil {
return err
}
- filename := "go-version-" + bc.Name + ".tar.gz"
+ // TODO(adg): deduce actual version
+ version := "VERSION"
+ filename := "go." + version + "." + b.String() + ".tar.gz"
f, err := os.Create(filename)
if err != nil {
return err
@@ -173,7 +313,7 @@
if err := f.Close(); err != nil {
return err
}
- log.Printf("%v: Wrote %q.", instance, filename)
+ log.Printf("%v: Wrote %q.", b, filename)
return nil
}
@@ -194,3 +334,11 @@
}
return fmt.Sprintf("%x", buf)
}
+
+func addPrefix(prefix string, in []string) []string {
+ var out []string
+ for _, s := range in {
+ out = append(out, path.Join(prefix, s))
+ }
+ return out
+}