LEFT | RIGHT |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package main | 5 package main |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "flag" | 9 "flag" |
10 "fmt" | 10 "fmt" |
(...skipping 22 matching lines...) Expand all Loading... |
33 var extraEnv = []string{ | 33 var extraEnv = []string{ |
34 "CC", | 34 "CC", |
35 "GOARM", | 35 "GOARM", |
36 "GOHOSTARCH", | 36 "GOHOSTARCH", |
37 "GOHOSTOS", | 37 "GOHOSTOS", |
38 "PATH", | 38 "PATH", |
39 "TMPDIR", | 39 "TMPDIR", |
40 } | 40 } |
41 | 41 |
42 type Builder struct { | 42 type Builder struct { |
| 43 goroot *Repo |
43 name string | 44 name string |
44 goos, goarch string | 45 goos, goarch string |
45 key string | 46 key string |
46 } | 47 } |
47 | 48 |
48 var ( | 49 var ( |
49 buildroot = flag.String("buildroot", defaultBuildRoot(), "Directory
under which to build") | 50 buildroot = flag.String("buildroot", defaultBuildRoot(), "Directory
under which to build") |
50 dashboard = flag.String("dashboard", "build.golang.org", "Go Dashbo
ard Host") | 51 dashboard = flag.String("dashboard", "build.golang.org", "Go Dashbo
ard Host") |
51 buildRelease = flag.Bool("release", false, "Build and upload binary re
lease archives") | 52 buildRelease = flag.Bool("release", false, "Build and upload binary re
lease archives") |
52 buildRevision = flag.String("rev", "", "Build specified revision and ex
it") | 53 buildRevision = flag.String("rev", "", "Build specified revision and ex
it") |
53 buildCmd = flag.String("cmd", filepath.Join(".", allCmd), "Build c
ommand (specify relative to go/src/)") | 54 buildCmd = flag.String("cmd", filepath.Join(".", allCmd), "Build c
ommand (specify relative to go/src/)") |
54 failAll = flag.Bool("fail", false, "fail all builds") | 55 failAll = flag.Bool("fail", false, "fail all builds") |
55 parallel = flag.Bool("parallel", false, "Build multiple targets in
parallel") | 56 parallel = flag.Bool("parallel", false, "Build multiple targets in
parallel") |
56 buildTimeout = flag.Duration("buildTimeout", 60*time.Minute, "Maximum
time to wait for builds and tests") | 57 buildTimeout = flag.Duration("buildTimeout", 60*time.Minute, "Maximum
time to wait for builds and tests") |
57 cmdTimeout = flag.Duration("cmdTimeout", 5*time.Minute, "Maximum tim
e to wait for an external command") | 58 cmdTimeout = flag.Duration("cmdTimeout", 5*time.Minute, "Maximum tim
e to wait for an external command") |
58 commitInterval = flag.Duration("commitInterval", 1*time.Minute, "Time to
wait between polling for new commits (0 disables commit poller)") | 59 commitInterval = flag.Duration("commitInterval", 1*time.Minute, "Time to
wait between polling for new commits (0 disables commit poller)") |
59 verbose = flag.Bool("v", false, "verbose") | 60 verbose = flag.Bool("v", false, "verbose") |
60 ) | 61 ) |
61 | 62 |
62 var ( | 63 var ( |
63 binaryTagRe = regexp.MustCompile(`^(release\.r|weekly\.)[0-9\-.]+`) | 64 binaryTagRe = regexp.MustCompile(`^(release\.r|weekly\.)[0-9\-.]+`) |
64 releaseRe = regexp.MustCompile(`^release\.r[0-9\-.]+`) | 65 releaseRe = regexp.MustCompile(`^release\.r[0-9\-.]+`) |
65 allCmd = "all" + suffix | 66 allCmd = "all" + suffix |
66 cleanCmd = "clean" + suffix | 67 cleanCmd = "clean" + suffix |
67 suffix = defaultSuffix() | 68 suffix = defaultSuffix() |
68 | |
69 goroot *Repo | |
70 ) | 69 ) |
71 | 70 |
72 func main() { | 71 func main() { |
73 flag.Usage = func() { | 72 flag.Usage = func() { |
74 fmt.Fprintf(os.Stderr, "usage: %s goos-goarch...\n", os.Args[0]) | 73 fmt.Fprintf(os.Stderr, "usage: %s goos-goarch...\n", os.Args[0]) |
75 flag.PrintDefaults() | 74 flag.PrintDefaults() |
76 os.Exit(2) | 75 os.Exit(2) |
77 } | 76 } |
78 flag.Parse() | 77 flag.Parse() |
79 if len(flag.Args()) == 0 { | 78 if len(flag.Args()) == 0 { |
80 flag.Usage() | 79 flag.Usage() |
81 } | 80 } |
82 » goroot = &Repo{ | 81 » goroot := &Repo{ |
83 Path: filepath.Join(*buildroot, "goroot"), | 82 Path: filepath.Join(*buildroot, "goroot"), |
84 } | 83 } |
85 | 84 |
86 // set up work environment, use existing enviroment if possible | 85 // set up work environment, use existing enviroment if possible |
87 if goroot.Exists() { | 86 if goroot.Exists() { |
88 log.Print("Found old workspace, will use it") | 87 log.Print("Found old workspace, will use it") |
89 } else { | 88 } else { |
90 if err := os.RemoveAll(*buildroot); err != nil { | 89 if err := os.RemoveAll(*buildroot); err != nil { |
91 log.Fatalf("Error removing build root (%s): %s", *buildr
oot, err) | 90 log.Fatalf("Error removing build root (%s): %s", *buildr
oot, err) |
92 } | 91 } |
93 if err := os.Mkdir(*buildroot, mkdirPerm); err != nil { | 92 if err := os.Mkdir(*buildroot, mkdirPerm); err != nil { |
94 log.Fatalf("Error making build root (%s): %s", *buildroo
t, err) | 93 log.Fatalf("Error making build root (%s): %s", *buildroo
t, err) |
95 } | 94 } |
96 var err error | 95 var err error |
97 goroot, err = RemoteRepo(hgUrl).Clone(goroot.Path, "tip") | 96 goroot, err = RemoteRepo(hgUrl).Clone(goroot.Path, "tip") |
98 if err != nil { | 97 if err != nil { |
99 log.Fatal("Error cloning repository:", err) | 98 log.Fatal("Error cloning repository:", err) |
100 } | 99 } |
101 } | 100 } |
102 | 101 |
103 // set up builders | 102 // set up builders |
104 builders := make([]*Builder, len(flag.Args())) | 103 builders := make([]*Builder, len(flag.Args())) |
105 » for i, builder := range flag.Args() { | 104 » for i, name := range flag.Args() { |
106 » » b, err := NewBuilder(builder) | 105 » » b, err := NewBuilder(goroot, name) |
107 if err != nil { | 106 if err != nil { |
108 log.Fatal(err) | 107 log.Fatal(err) |
109 } | 108 } |
110 builders[i] = b | 109 builders[i] = b |
111 } | 110 } |
112 | 111 |
113 if *failAll { | 112 if *failAll { |
114 failMode(builders) | 113 failMode(builders) |
115 return | 114 return |
116 } | 115 } |
117 | 116 |
118 // if specified, build revision and return | 117 // if specified, build revision and return |
119 if *buildRevision != "" { | 118 if *buildRevision != "" { |
120 hash, err := goroot.FullHash(*buildRevision) | 119 hash, err := goroot.FullHash(*buildRevision) |
121 if err != nil { | 120 if err != nil { |
122 log.Fatal("Error finding revision: ", err) | 121 log.Fatal("Error finding revision: ", err) |
123 } | 122 } |
124 for _, b := range builders { | 123 for _, b := range builders { |
125 if err := b.buildHash(hash); err != nil { | 124 if err := b.buildHash(hash); err != nil { |
126 log.Println(err) | 125 log.Println(err) |
127 } | 126 } |
128 } | 127 } |
129 return | 128 return |
130 } | 129 } |
131 | 130 |
132 // Start commit watcher | 131 // Start commit watcher |
133 » go commitWatcher() | 132 » go commitWatcher(goroot) |
134 | 133 |
135 // go continuous build mode | 134 // go continuous build mode |
136 // check for new commits and build them | 135 // check for new commits and build them |
137 for { | 136 for { |
138 built := false | 137 built := false |
139 t := time.Now() | 138 t := time.Now() |
140 if *parallel { | 139 if *parallel { |
141 done := make(chan bool) | 140 done := make(chan bool) |
142 for _, b := range builders { | 141 for _, b := range builders { |
143 go func(b *Builder) { | 142 go func(b *Builder) { |
(...skipping 28 matching lines...) Expand all Loading... |
172 for _, b := range builders { | 171 for _, b := range builders { |
173 built = b.failBuild() || built | 172 built = b.failBuild() || built |
174 } | 173 } |
175 // stop if there was nothing to fail | 174 // stop if there was nothing to fail |
176 if !built { | 175 if !built { |
177 break | 176 break |
178 } | 177 } |
179 } | 178 } |
180 } | 179 } |
181 | 180 |
182 func NewBuilder(builder string) (*Builder, error) { | 181 func NewBuilder(goroot *Repo, name string) (*Builder, error) { |
183 » b := &Builder{name: builder} | 182 » b := &Builder{ |
| 183 » » goroot: goroot, |
| 184 » » name: name, |
| 185 » } |
184 | 186 |
185 // get goos/goarch from builder string | 187 // get goos/goarch from builder string |
186 » s := strings.SplitN(builder, "-", 3) | 188 » s := strings.SplitN(b.name, "-", 3) |
187 if len(s) >= 2 { | 189 if len(s) >= 2 { |
188 b.goos, b.goarch = s[0], s[1] | 190 b.goos, b.goarch = s[0], s[1] |
189 } else { | 191 } else { |
190 » » return nil, fmt.Errorf("unsupported builder form: %s", builder) | 192 » » return nil, fmt.Errorf("unsupported builder form: %s", name) |
191 } | 193 } |
192 | 194 |
193 // read keys from keyfile | 195 // read keys from keyfile |
194 fn := "" | 196 fn := "" |
195 if runtime.GOOS == "windows" { | 197 if runtime.GOOS == "windows" { |
196 fn = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") | 198 fn = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") |
197 } else { | 199 } else { |
198 fn = os.Getenv("HOME") | 200 fn = os.Getenv("HOME") |
199 } | 201 } |
200 fn = filepath.Join(fn, ".gobuildkey") | 202 fn = filepath.Join(fn, ".gobuildkey") |
(...skipping 14 matching lines...) Expand all Loading... |
215 func (b *Builder) build() bool { | 217 func (b *Builder) build() bool { |
216 hash, err := b.todo("build-go-commit", "", "") | 218 hash, err := b.todo("build-go-commit", "", "") |
217 if err != nil { | 219 if err != nil { |
218 log.Println(err) | 220 log.Println(err) |
219 return false | 221 return false |
220 } | 222 } |
221 if hash == "" { | 223 if hash == "" { |
222 return false | 224 return false |
223 } | 225 } |
224 | 226 |
225 » // Look for hash locally before running hg pull. | 227 » if err := b.buildHash(hash); err != nil { |
226 » log.Println("looking for hash", hash) | |
227 » if _, err := goroot.FullHash(hash[:12]); err != nil { | |
228 » » // Don't have hash, so run hg pull. | |
229 » » log.Printf("Repo: %v does not have hash %s, pulling", goroot, ha
sh) | |
230 » » if err := goroot.Pull(); err != nil { | |
231 » » » log.Println("hg pull failed:", err) | |
232 » » » return false | |
233 » » } | |
234 » } | |
235 | |
236 » err = b.buildHash(hash) | |
237 » if err != nil { | |
238 log.Println(err) | 228 log.Println(err) |
239 } | 229 } |
240 return true | 230 return true |
241 } | 231 } |
242 | 232 |
243 func (b *Builder) buildHash(hash string) error { | 233 func (b *Builder) buildHash(hash string) error { |
244 log.Println(b.name, "building", hash) | 234 log.Println(b.name, "building", hash) |
245 | 235 |
246 // create place in which to do work | 236 // create place in which to do work |
247 workpath := filepath.Join(*buildroot, b.name+"-"+hash[:12]) | 237 workpath := filepath.Join(*buildroot, b.name+"-"+hash[:12]) |
248 if err := os.Mkdir(workpath, mkdirPerm); err != nil { | 238 if err := os.Mkdir(workpath, mkdirPerm); err != nil { |
249 return err | 239 return err |
250 } | 240 } |
251 defer os.RemoveAll(workpath) | 241 defer os.RemoveAll(workpath) |
252 | 242 |
253 // pull before cloning to ensure we have the revision | 243 // pull before cloning to ensure we have the revision |
254 » if err := goroot.Pull(); err != nil { | 244 » if err := b.goroot.Pull(); err != nil { |
255 return err | 245 return err |
256 } | 246 } |
257 | 247 |
258 // clone repo at specified revision | 248 // clone repo at specified revision |
259 » if _, err := goroot.Clone(filepath.Join(workpath, "go"), hash); err != n
il { | 249 » if _, err := b.goroot.Clone(filepath.Join(workpath, "go"), hash); err !=
nil { |
260 return err | 250 return err |
261 } | 251 } |
262 | 252 |
263 srcDir := filepath.Join(workpath, "go", "src") | 253 srcDir := filepath.Join(workpath, "go", "src") |
264 | 254 |
265 // build | 255 // build |
266 var buildlog bytes.Buffer | 256 var buildlog bytes.Buffer |
267 logfile := filepath.Join(workpath, "build.log") | 257 logfile := filepath.Join(workpath, "build.log") |
268 f, err := os.Create(logfile) | 258 f, err := os.Create(logfile) |
269 if err != nil { | 259 if err != nil { |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 s, err := os.Stat(name) | 451 s, err := os.Stat(name) |
462 return err == nil && s.IsDir() | 452 return err == nil && s.IsDir() |
463 } | 453 } |
464 | 454 |
465 func isFile(name string) bool { | 455 func isFile(name string) bool { |
466 s, err := os.Stat(name) | 456 s, err := os.Stat(name) |
467 return err == nil && !s.IsDir() | 457 return err == nil && !s.IsDir() |
468 } | 458 } |
469 | 459 |
470 // commitWatcher polls hg for new commits and tells the dashboard about them. | 460 // commitWatcher polls hg for new commits and tells the dashboard about them. |
471 func commitWatcher() { | 461 func commitWatcher(goroot *Repo) { |
472 if *commitInterval == 0 { | 462 if *commitInterval == 0 { |
473 log.Printf("commitInterval is %s, disabling commitWatcher", *com
mitInterval) | 463 log.Printf("commitInterval is %s, disabling commitWatcher", *com
mitInterval) |
474 return | 464 return |
475 } | 465 } |
476 // Create builder just to get master key. | 466 // Create builder just to get master key. |
477 » b, err := NewBuilder("mercurial-commit") | 467 » b, err := NewBuilder(goroot, "mercurial-commit") |
478 if err != nil { | 468 if err != nil { |
479 log.Fatal(err) | 469 log.Fatal(err) |
480 } | 470 } |
481 key := b.key | 471 key := b.key |
482 | 472 |
483 for { | 473 for { |
484 if *verbose { | 474 if *verbose { |
485 log.Printf("poll...") | 475 log.Printf("poll...") |
486 } | 476 } |
487 // Main Go repository. | 477 // Main Go repository. |
488 » » commitPoll(key, "") | 478 » » commitPoll(goroot, "", key) |
489 // Go sub-repositories. | 479 // Go sub-repositories. |
490 for _, pkg := range dashboardPackages("subrepo") { | 480 for _, pkg := range dashboardPackages("subrepo") { |
491 » » » commitPoll(key, pkg) | 481 » » » pkgroot := &Repo{ |
| 482 » » » » Path: filepath.Join(*buildroot, pkg), |
| 483 » » » } |
| 484 » » » commitPoll(pkgroot, pkg, key) |
492 } | 485 } |
493 if *verbose { | 486 if *verbose { |
494 log.Printf("sleep...") | 487 log.Printf("sleep...") |
495 } | 488 } |
496 time.Sleep(*commitInterval) | 489 time.Sleep(*commitInterval) |
497 } | 490 } |
498 } | 491 } |
499 | 492 |
| 493 // logByHash is a cache of all Mercurial revisions we know about, |
| 494 // indexed by full hash. |
| 495 var logByHash = map[string]*HgLog{} |
| 496 |
500 // commitPoll pulls any new revisions from the hg server | 497 // commitPoll pulls any new revisions from the hg server |
501 // and tells the server about them. | 498 // and tells the server about them. |
502 func commitPoll(key, pkg string) { | 499 func commitPoll(repo *Repo, pkg, key string) { |
503 » pkgRoot := goroot | 500 » if !repo.Exists() { |
504 | 501 » » var err error |
505 » if pkg != "" { | 502 » » repo, err = RemoteRepo(repoURL(pkg)).Clone(repo.Path, "tip") |
506 » » pkgRoot = &Repo{ | 503 » » if err != nil { |
507 » » » Path: filepath.Join(*buildroot, pkg), | 504 » » » log.Printf("%s: hg clone failed: %v", pkg, err) |
508 » » } | 505 » » » if err := os.RemoveAll(repo.Path); err != nil { |
509 » » if !pkgRoot.Exists() { | 506 » » » » log.Printf("%s: %v", pkg, err) |
510 » » » var err error | |
511 » » » pkgRoot, err = RemoteRepo(repoURL(pkg)).Clone(pkgRoot.Pa
th, "tip") | |
512 » » » if err != nil { | |
513 » » » » log.Printf("%s: hg clone failed: %v", pkg, err) | |
514 » » » » if err := os.RemoveAll(pkgRoot.Path); err != nil
{ | |
515 » » » » » log.Printf("%s: %v", pkg, err) | |
516 » » » » } | |
517 » » » » return | |
518 } | 507 } |
519 } | 508 } |
520 } | |
521 | |
522 if err := pkgRoot.Pull(); err != nil { | |
523 log.Printf("hg pull: %v", err) | |
524 return | 509 return |
525 } | 510 } |
526 | 511 |
527 » logs, err := pkgRoot.Log() | 512 » logs, err := repo.Log() // repo.Log calls repo.Pull internally |
528 if err != nil { | 513 if err != nil { |
529 log.Printf("hg log: %v", err) | 514 log.Printf("hg log: %v", err) |
530 return | 515 return |
531 } | 516 } |
532 | 517 |
533 // Pass 1. Fill in parents and add new log entries to logsByHash. | 518 // Pass 1. Fill in parents and add new log entries to logsByHash. |
534 // Empty parent means take parent from next log entry. | 519 // Empty parent means take parent from next log entry. |
535 // Non-empty parent has form 1234:hashhashhash; we want full hash. | 520 // Non-empty parent has form 1234:hashhashhash; we want full hash. |
536 for i := range logs { | 521 for i := range logs { |
537 l := &logs[i] | 522 l := &logs[i] |
538 if l.Parent == "" && i+1 < len(logs) { | 523 if l.Parent == "" && i+1 < len(logs) { |
539 l.Parent = logs[i+1].Hash | 524 l.Parent = logs[i+1].Hash |
540 } else if l.Parent != "" { | 525 } else if l.Parent != "" { |
541 » » » l.Parent, _ = pkgRoot.FullHash(l.Parent) | 526 » » » l.Parent, _ = repo.FullHash(l.Parent) |
542 } | 527 } |
543 if *verbose { | 528 if *verbose { |
544 log.Printf("hg log %s: %s < %s\n", pkg, l.Hash, l.Parent
) | 529 log.Printf("hg log %s: %s < %s\n", pkg, l.Hash, l.Parent
) |
545 } | 530 } |
546 if logByHash[l.Hash] == nil { | 531 if logByHash[l.Hash] == nil { |
547 // Make copy to avoid pinning entire slice when only one
entry is new. | 532 // Make copy to avoid pinning entire slice when only one
entry is new. |
548 t := *l | 533 t := *l |
549 logByHash[t.Hash] = &t | 534 logByHash[t.Hash] = &t |
550 } | 535 } |
551 } | 536 } |
552 | 537 |
553 » for i := range logs { | 538 » for _, l := range logs { |
554 » » l := &logs[i] | |
555 addCommit(pkg, l.Hash, key) | 539 addCommit(pkg, l.Hash, key) |
556 } | 540 } |
557 } | 541 } |
558 | 542 |
559 // addCommit adds the commit with the named hash to the dashboard. | 543 // addCommit adds the commit with the named hash to the dashboard. |
560 // key is the secret key for authentication to the dashboard. | 544 // key is the secret key for authentication to the dashboard. |
561 // It avoids duplicate effort. | 545 // It avoids duplicate effort. |
562 func addCommit(pkg, hash, key string) bool { | 546 func addCommit(pkg, hash, key string) bool { |
563 l := logByHash[hash] | 547 l := logByHash[hash] |
564 if l == nil { | 548 if l == nil { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 return v, true | 619 return v, true |
636 } | 620 } |
637 keq := k + "=" | 621 keq := k + "=" |
638 for _, kv := range os.Environ() { | 622 for _, kv := range os.Environ() { |
639 if kv == keq { | 623 if kv == keq { |
640 return "", true | 624 return "", true |
641 } | 625 } |
642 } | 626 } |
643 return "", false | 627 return "", false |
644 } | 628 } |
LEFT | RIGHT |