blob: fc35d4a17ae700b57cbfc5a988eb57fce9f1ac90 [file] [log] [blame]
Andrew Gerrandf83f3e42015-02-02 12:05:01 +00001// Copyright 2015 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// Command release builds a Go release.
6package main
7
8import (
9 "bytes"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000010 "flag"
11 "fmt"
12 "io"
Brad Fitzpatrick4b956612015-07-07 09:57:08 -070013 "io/ioutil"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000014 "log"
15 "os"
Andrew Gerrand319667f2015-02-04 16:04:07 +000016 "path"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000017 "path/filepath"
Brad Fitzpatrick4b956612015-07-07 09:57:08 -070018 "runtime"
Andrew Gerrand319667f2015-02-04 16:04:07 +000019 "strings"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000020 "sync"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000021
Brad Fitzpatrick4b956612015-07-07 09:57:08 -070022 "golang.org/x/build"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000023 "golang.org/x/build/buildlet"
24 "golang.org/x/build/dashboard"
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000025)
26
27var (
Andrew Gerrand15af43e2015-02-11 11:47:03 +110028 target = flag.String("target", "", "If specified, build specific target platform ('linux-amd64')")
29
Andrew Gerrand319667f2015-02-04 16:04:07 +000030 rev = flag.String("rev", "", "Go revision to build")
31 toolsRev = flag.String("tools", "", "Tools revision to build")
Andrew Gerrand15af43e2015-02-11 11:47:03 +110032 tourRev = flag.String("tour", "master", "Tour revision to include")
33 blogRev = flag.String("blog", "master", "Blog revision to include")
Andrew Gerrande88955c2015-02-19 10:36:32 +110034 netRev = flag.String("net", "master", "Net revision to include")
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000035
Brad Fitzpatrick4b956612015-07-07 09:57:08 -070036 user = flag.String("user", username(), "coordinator username, appended to 'user-'")
Andrew Gerrand15af43e2015-02-11 11:47:03 +110037)
Andrew Gerrand319667f2015-02-04 16:04:07 +000038
Brad Fitzpatrick4b956612015-07-07 09:57:08 -070039var coordClient *buildlet.CoordinatorClient
40
Andrew Gerrand319667f2015-02-04 16:04:07 +000041type Build struct {
42 OS, Arch string
43
44 Race bool // Build race detector.
45 Static bool // Statically-link binaries.
46
47 Builder string // Key for dashboard.Builders.
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000048}
49
Andrew Gerrand319667f2015-02-04 16:04:07 +000050func (b *Build) String() string {
51 return fmt.Sprintf("%v-%v", b.OS, b.Arch)
52}
53
54var builds = []*Build{
55 {
56 OS: "linux",
57 Arch: "386",
58 Builder: "linux-amd64",
59 },
60 {
61 OS: "linux",
62 Arch: "amd64",
63 Race: true,
64 Static: true,
65 Builder: "linux-amd64",
66 },
67 {
68 OS: "freebsd",
69 Arch: "386",
70 Builder: "freebsd-386-gce101",
71 },
72 {
73 OS: "freebsd",
74 Arch: "amd64",
75 Race: true,
76 Builder: "freebsd-amd64-gce101",
77 },
78}
79
Andrew Gerrand15af43e2015-02-11 11:47:03 +110080const (
81 toolsRepo = "golang.org/x/tools"
82 blogRepo = "golang.org/x/blog"
83 tourRepo = "golang.org/x/tour"
84)
Andrew Gerrand319667f2015-02-04 16:04:07 +000085
86var toolPaths = []string{
87 "golang.org/x/tools/cmd/cover",
88 "golang.org/x/tools/cmd/godoc",
89 "golang.org/x/tools/cmd/vet",
Andrew Gerrand15af43e2015-02-11 11:47:03 +110090 "golang.org/x/tour/gotour",
Andrew Gerrand319667f2015-02-04 16:04:07 +000091}
Andrew Gerrandf83f3e42015-02-02 12:05:01 +000092
93var preBuildCleanFiles = []string{
94 ".gitattributes",
95 ".gitignore",
96 ".hgignore",
97 ".hgtags",
98 "misc/dashboard",
99}
100
101var postBuildCleanFiles = []string{
102 "VERSION.cache",
103}
104
105func main() {
106 flag.Parse()
107
Andrew Gerrand319667f2015-02-04 16:04:07 +0000108 if *rev == "" {
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000109 log.Fatal("must specify -rev flag")
110 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000111 if *toolsRev == "" {
112 log.Fatal("must specify -tools flag")
113 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000114
Brad Fitzpatrick4b956612015-07-07 09:57:08 -0700115 coordClient = coordinatorClient()
116
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000117 var wg sync.WaitGroup
Andrew Gerrand319667f2015-02-04 16:04:07 +0000118 for _, b := range builds {
119 b := b
120 if *target != "" && b.String() != *target {
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000121 continue
122 }
123 wg.Add(1)
124 go func() {
125 defer wg.Done()
Andrew Gerrand319667f2015-02-04 16:04:07 +0000126 b.make() // error logged by make function
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000127 }()
128 }
129 // TODO(adg): show progress of running builders
130 wg.Wait()
131}
132
Brad Fitzpatrick2742bc32015-07-03 16:20:37 -0700133func (b *Build) buildlet() (*buildlet.Client, error) {
Brad Fitzpatrick4b956612015-07-07 09:57:08 -0700134 bc, err := coordClient.CreateBuildlet(b.Builder)
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000135 if err != nil {
Brad Fitzpatrick2742bc32015-07-03 16:20:37 -0700136 return nil, err
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000137 }
Brad Fitzpatrick4b956612015-07-07 09:57:08 -0700138 bc.SetCloseFunc(func() error {
139 return bc.Destroy()
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000140 })
Brad Fitzpatrick4b956612015-07-07 09:57:08 -0700141 return bc, nil
Brad Fitzpatrick2742bc32015-07-03 16:20:37 -0700142}
143
144func (b *Build) make() (err error) {
145 bc, ok := dashboard.Builders[b.Builder]
146 if !ok {
147 return fmt.Errorf("unknown builder: %v", bc)
148 }
149
150 client, err := b.buildlet()
151 if err != nil {
152 return err
153 }
154 defer client.Close()
155 defer func() {
156 if err != nil {
157 log.Printf("%v: %v", b, err)
158 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000159 }()
160
Andrew Gerrand319667f2015-02-04 16:04:07 +0000161 work, err := client.WorkDir()
162 if err != nil {
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000163 return err
164 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000165
Andrew Gerrand319667f2015-02-04 16:04:07 +0000166 // Push source to VM
167 log.Printf("%v: Pushing source to VM.", b)
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100168 const (
169 goDir = "go"
170 goPath = "gopath"
171 )
172 for _, r := range []struct {
173 repo, rev string
174 }{
175 {"go", *rev},
176 {"tools", *toolsRev},
177 {"blog", *blogRev},
178 {"tour", *tourRev},
179 {"net", *netRev},
180 } {
181 dir := goDir
182 if r.repo != "go" {
183 dir = goPath + "/src/golang.org/x/" + r.repo
184 }
185 tar := "https://go.googlesource.com/" + r.repo + "/+archive/" + r.rev + ".tar.gz"
186 if err := client.PutTarFromURL(tar, dir); err != nil {
187 return err
188 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000189 }
190
191 log.Printf("%v: Cleaning goroot (pre-build).", b)
192 if err := client.RemoveAll(addPrefix(goDir, preBuildCleanFiles)...); err != nil {
193 return err
194 }
195
196 // Set up build environment.
197 sep := "/"
198 if b.OS == "windows" {
199 sep = "\\"
200 }
201 env := []string{
202 "GOOS=" + b.OS,
203 "GOARCH=" + b.Arch,
204 "GOHOSTOS=" + b.OS,
205 "GOHOSTARCH=" + b.Arch,
206 "GOROOT_FINAL=" + bc.GorootFinal(),
207 "GOROOT=" + work + sep + goDir,
208 "GOPATH=" + work + sep + goPath,
209 }
210 if b.Static {
211 env = append(env, "GO_DISTFLAGS=-s")
212 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000213
214 // Execute build
Andrew Gerrand319667f2015-02-04 16:04:07 +0000215 log.Printf("%v: Building.", b)
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000216 out := new(bytes.Buffer)
Andrew Gerrand319667f2015-02-04 16:04:07 +0000217 mk := filepath.Join(goDir, bc.MakeScript())
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000218 remoteErr, err := client.Exec(mk, buildlet.ExecOpts{
219 Output: out,
Andrew Gerrand319667f2015-02-04 16:04:07 +0000220 ExtraEnv: env,
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000221 })
222 if err != nil {
223 return err
224 }
225 if remoteErr != nil {
226 // TODO(adg): write log to file instead?
227 return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
228 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000229
Andrew Gerrand319667f2015-02-04 16:04:07 +0000230 goCmd := path.Join(goDir, "bin/go")
231 if b.OS == "windows" {
232 goCmd += ".exe"
233 }
234 runGo := func(args ...string) error {
235 out := new(bytes.Buffer)
236 remoteErr, err := client.Exec(goCmd, buildlet.ExecOpts{
237 Output: out,
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100238 Dir: ".", // root of buildlet work directory
Andrew Gerrand319667f2015-02-04 16:04:07 +0000239 Args: args,
240 ExtraEnv: env,
241 })
242 if err != nil {
243 return err
244 }
245 if remoteErr != nil {
246 return fmt.Errorf("go %v: %v\n%s", strings.Join(args, " "), remoteErr, out)
247 }
248 return nil
249 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000250
Andrew Gerrand319667f2015-02-04 16:04:07 +0000251 if b.Race {
252 log.Printf("%v: Building race detector.", b)
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000253
Andrew Gerrand319667f2015-02-04 16:04:07 +0000254 // Because on release branches, go install -a std is a NOP,
255 // we have to resort to delete pkg/$GOOS_$GOARCH, install -race,
256 // and then reinstall std so that we're not left with a slower,
257 // race-enabled cmd/go, etc.
258 if err := client.RemoveAll(path.Join(goDir, "pkg", b.OS+"_"+b.Arch)); err != nil {
259 return err
260 }
261 if err := runGo("tool", "dist", "install", "runtime"); err != nil {
262 return err
263 }
264 if err := runGo("install", "-race", "std"); err != nil {
265 return err
266 }
267 if err := runGo("install", "std"); err != nil {
268 return err
269 }
270 // Re-building go command leaves old versions of go.exe as go.exe~ on windows.
271 // See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details.
272 // Remove it manually.
273 if b.OS == "windows" {
274 if err := client.RemoveAll(goCmd + "~"); err != nil {
275 return err
276 }
277 }
278 }
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000279
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100280 log.Printf("%v: Building %v.", b, strings.Join(toolPaths, ", "))
281 if err := runGo(append([]string{"install"}, toolPaths...)...); err != nil {
282 return err
Andrew Gerrand319667f2015-02-04 16:04:07 +0000283 }
284
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100285 log.Printf("%v: Pushing and running releaselet.", b)
286 // TODO(adg): locate releaselet.go in GOPATH
287 const releaselet = "releaselet.go"
288 f, err := os.Open(releaselet)
289 if err != nil {
290 return err
291 }
292 err = client.Put(f, releaselet, 0666)
293 f.Close()
294 if err != nil {
295 return err
296 }
297 if err := runGo("run", releaselet); err != nil {
298 return err
299 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000300
301 log.Printf("%v: Cleaning goroot (post-build).", b)
302 // Need to delete everything except the final "go" directory,
303 // as we make the tarball relative to workdir.
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100304 cleanFiles := append(addPrefix(goDir, postBuildCleanFiles), goPath, releaselet)
Andrew Gerrand319667f2015-02-04 16:04:07 +0000305 if err := client.RemoveAll(cleanFiles...); err != nil {
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000306 return err
307 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000308
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100309 // TODO(adg): fetch msi or pkg files
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000310
311 // Download tarball
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100312 log.Printf("%v: Downloading tarball.", b)
Andrew Gerrand319667f2015-02-04 16:04:07 +0000313 tgz, err := client.GetTar(".")
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000314 if err != nil {
315 return err
316 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000317 // TODO(adg): deduce actual version
318 version := "VERSION"
319 filename := "go." + version + "." + b.String() + ".tar.gz"
Andrew Gerrand15af43e2015-02-11 11:47:03 +1100320 f, err = os.Create(filename)
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000321 if err != nil {
322 return err
323 }
324 if _, err := io.Copy(f, tgz); err != nil {
325 f.Close()
326 return err
327 }
328 if err := f.Close(); err != nil {
329 return err
330 }
Andrew Gerrand319667f2015-02-04 16:04:07 +0000331 log.Printf("%v: Wrote %q.", b, filename)
Andrew Gerrandf83f3e42015-02-02 12:05:01 +0000332
333 return nil
334}
335
Andrew Gerrand319667f2015-02-04 16:04:07 +0000336func addPrefix(prefix string, in []string) []string {
337 var out []string
338 for _, s := range in {
339 out = append(out, path.Join(prefix, s))
340 }
341 return out
342}
Brad Fitzpatrick4b956612015-07-07 09:57:08 -0700343
344func coordinatorClient() *buildlet.CoordinatorClient {
345 return &buildlet.CoordinatorClient{
346 Auth: buildlet.UserPass{
347 Username: "user-" + *user,
348 Password: userToken(),
349 },
350 Instance: build.ProdCoordinator,
351 }
352}
353
354func homeDir() string {
355 if runtime.GOOS == "windows" {
356 return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
357 }
358 return os.Getenv("HOME")
359}
360
361func configDir() string {
362 if runtime.GOOS == "windows" {
363 return filepath.Join(os.Getenv("APPDATA"), "Gomote")
364 }
365 if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
366 return filepath.Join(xdg, "gomote")
367 }
368 return filepath.Join(homeDir(), ".config", "gomote")
369}
370
371func username() string {
372 if runtime.GOOS == "windows" {
373 return os.Getenv("USERNAME")
374 }
375 return os.Getenv("USER")
376}
377
378func userToken() string {
379 if *user == "" {
380 panic("userToken called with user flag empty")
381 }
382 keyDir := configDir()
383 baseFile := "user-" + *user + ".token"
384 tokenFile := filepath.Join(keyDir, baseFile)
385 slurp, err := ioutil.ReadFile(tokenFile)
386 if os.IsNotExist(err) {
387 log.Printf("Missing file %s for user %q. Change --user or obtain a token and place it there.",
388 tokenFile, *user)
389 }
390 if err != nil {
391 log.Fatal(err)
392 }
393 return strings.TrimSpace(string(slurp))
394}