github.com/zach-klippenstein/go@v0.0.0-20150108044943-fcfbeb3adf58/misc/makerelease/makerelease.go (about) 1 // Copyright 2012 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 // This is a tool for packaging binary releases. 6 // It supports FreeBSD, Linux, NetBSD, OpenBSD, OS X, and Windows. 7 package main 8 9 import ( 10 "archive/tar" 11 "archive/zip" 12 "bufio" 13 "bytes" 14 "compress/gzip" 15 "crypto/sha1" 16 "encoding/json" 17 "errors" 18 "flag" 19 "fmt" 20 "io" 21 "io/ioutil" 22 "log" 23 "net/http" 24 "net/url" 25 "os" 26 "os/exec" 27 "path" 28 "path/filepath" 29 "regexp" 30 "runtime" 31 "strings" 32 33 "code.google.com/p/goauth2/oauth" 34 storage "code.google.com/p/google-api-go-client/storage/v1" 35 ) 36 37 var ( 38 tag = flag.String("tag", "release", "mercurial tag to check out") 39 toolTag = flag.String("tool", defaultToolTag, "go.tools tag to check out") 40 tourTag = flag.String("tour", defaultTourTag, "go-tour tag to check out") 41 repo = flag.String("repo", "https://code.google.com/p/go", "repo URL") 42 verbose = flag.Bool("v", false, "verbose output") 43 upload = flag.Bool("upload", false, "upload resulting files to Google Code") 44 addLabel = flag.String("label", "", "additional label to apply to file when uploading") 45 includeRace = flag.Bool("race", true, "build race detector packages") 46 versionOverride = flag.String("version", "", "override version name") 47 staticToolchain = flag.Bool("static", true, "try to build statically linked toolchain (only supported on ELF targets)") 48 tokenCache = flag.String("token", defaultCacheFile, "Authentication token cache file") 49 storageBucket = flag.String("bucket", "golang", "Cloud Storage Bucket") 50 uploadURL = flag.String("upload_url", defaultUploadURL, "Upload URL") 51 52 defaultCacheFile = filepath.Join(os.Getenv("HOME"), ".makerelease-request-token") 53 defaultUploadURL = "http://golang.org/dl/upload" 54 ) 55 56 const ( 57 blogPath = "golang.org/x/blog" 58 toolPath = "golang.org/x/tools" 59 tourPath = "code.google.com/p/go-tour" 60 defaultToolTag = "release-branch.go1.4" 61 defaultTourTag = "release-branch.go1.4" 62 ) 63 64 // Import paths for tool commands. 65 // These must be the command that cmd/go knows to install to $GOROOT/bin 66 // or $GOROOT/pkg/tool. 67 var toolPaths = []string{ 68 "golang.org/x/tools/cmd/cover", 69 "golang.org/x/tools/cmd/godoc", 70 "golang.org/x/tools/cmd/vet", 71 } 72 73 var preBuildCleanFiles = []string{ 74 "lib/codereview", 75 "misc/dashboard/godashboard", 76 "src/cmd/cov", 77 "src/cmd/prof", 78 "src/exp", 79 "src/old", 80 } 81 82 var cleanFiles = []string{ 83 ".hg", 84 ".hgtags", 85 ".hgignore", 86 "VERSION.cache", 87 } 88 89 var sourceCleanFiles = []string{ 90 "bin", 91 "pkg", 92 } 93 94 var tourPackages = []string{ 95 "pic", 96 "tree", 97 "wc", 98 } 99 100 var tourContent = []string{ 101 "content", 102 "solutions", 103 "static", 104 "template", 105 } 106 107 var blogContent = []string{ 108 "content", 109 "template", 110 } 111 112 // The os-arches that support the race toolchain. 113 var raceAvailable = []string{ 114 "darwin-amd64", 115 "linux-amd64", 116 "windows-amd64", 117 } 118 119 // The OSes that support building statically linked toolchain 120 // Only ELF platforms are supported. 121 var staticLinkAvailable = []string{ 122 "linux", 123 "freebsd", 124 "openbsd", 125 "netbsd", 126 } 127 128 var fileRe = regexp.MustCompile(`^(go[a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+)(?:-([a-z0-9.]+))?)\.(tar\.gz|zip|pkg|msi)$`) 129 130 // OAuth2-authenticated HTTP client used to make calls to Cloud Storage. 131 var oauthClient *http.Client 132 133 // Builder key as specified in ~/.gobuildkey 134 var builderKey string 135 136 func main() { 137 flag.Usage = func() { 138 fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\n", os.Args[0]) 139 flag.PrintDefaults() 140 os.Exit(2) 141 } 142 flag.Parse() 143 if flag.NArg() == 0 { 144 flag.Usage() 145 } 146 if runtime.GOOS == "windows" { 147 checkWindowsDeps() 148 } 149 150 if *upload { 151 if err := readCredentials(); err != nil { 152 log.Fatalln("readCredentials:", err) 153 } 154 if err := setupOAuthClient(); err != nil { 155 log.Fatalln("setupOAuthClient:", err) 156 } 157 } 158 ok := true 159 for _, targ := range flag.Args() { 160 var b Build 161 if m := fileRe.FindStringSubmatch(targ); m != nil { 162 // targ is a file name; upload it to googlecode. 163 version := m[1] 164 if m[2] == "src" { 165 b.Source = true 166 } else { 167 b.OS = m[3] 168 b.Arch = m[4] 169 b.Label = m[5] 170 } 171 if !*upload { 172 log.Printf("%s: -upload=false, skipping", targ) 173 continue 174 } 175 if err := b.Upload(version, targ); err != nil { 176 log.Printf("uploading %s: %v", targ, err) 177 } 178 continue 179 } 180 if targ == "source" { 181 b.Source = true 182 } else { 183 p := strings.SplitN(targ, "-", 3) 184 if len(p) < 2 { 185 log.Println("Ignoring unrecognized target:", targ) 186 continue 187 } 188 b.OS = p[0] 189 b.Arch = p[1] 190 if len(p) >= 3 { 191 b.Label = p[2] 192 } 193 if *includeRace { 194 for _, t := range raceAvailable { 195 if t == targ || strings.HasPrefix(targ, t+"-") { 196 b.Race = true 197 } 198 } 199 } 200 if *staticToolchain { 201 for _, os := range staticLinkAvailable { 202 if b.OS == os { 203 b.static = true 204 } 205 } 206 } 207 } 208 if err := b.Do(); err != nil { 209 log.Printf("%s: %v", targ, err) 210 ok = false 211 } 212 } 213 if !ok { 214 os.Exit(1) 215 } 216 } 217 218 type Build struct { 219 Source bool // if true, OS and Arch must be empty 220 Race bool // build race toolchain 221 OS string 222 Arch string 223 Label string 224 root string 225 gopath string 226 static bool // if true, build statically linked toolchain 227 } 228 229 func (b *Build) Do() error { 230 work, err := ioutil.TempDir("", "makerelease") 231 if err != nil { 232 return err 233 } 234 defer os.RemoveAll(work) 235 b.root = filepath.Join(work, "go") 236 b.gopath = work 237 238 // Clone Go distribution and update to tag. 239 _, err = b.hgCmd(work, "clone", *repo, b.root) 240 if err != nil { 241 return err 242 } 243 _, err = b.hgCmd(b.root, "update", *tag) 244 if err != nil { 245 return err 246 } 247 248 // Remove exp and old packages. 249 if err := b.clean(preBuildCleanFiles); err != nil { 250 return err 251 } 252 253 src := filepath.Join(b.root, "src") 254 if b.Source { 255 if runtime.GOOS == "windows" { 256 log.Print("Warning: running make.bash on Windows; source builds are intended to be run on a Unix machine") 257 } 258 // Build dist tool only. 259 _, err = b.run(src, "bash", "make.bash", "--dist-tool") 260 } else { 261 // Build. 262 if b.OS == "windows" { 263 _, err = b.run(src, "cmd", "/C", "make.bat") 264 } else { 265 _, err = b.run(src, "bash", "make.bash") 266 } 267 if b.Race { 268 if err != nil { 269 return err 270 } 271 goCmd := filepath.Join(b.root, "bin", "go") 272 if b.OS == "windows" { 273 goCmd += ".exe" 274 } 275 _, err = b.run(src, goCmd, "install", "-race", "std") 276 if err != nil { 277 return err 278 } 279 // Re-install std without -race, so that we're not left 280 // with a slower, race-enabled cmd/go, etc. 281 _, err = b.run(src, goCmd, "install", "-a", "std") 282 // Re-building go command leaves old versions of go.exe as go.exe~ on windows. 283 // See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details. 284 // Remove it manually. 285 if b.OS == "windows" { 286 os.Remove(goCmd + "~") 287 } 288 } 289 if err != nil { 290 return err 291 } 292 err = b.extras() 293 } 294 if err != nil { 295 return err 296 } 297 298 // Get version strings. 299 var ( 300 version string // "weekly.2012-03-04" 301 fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3" 302 ) 303 pat := filepath.Join(b.root, "pkg/tool/*/dist*") // trailing * for .exe 304 m, err := filepath.Glob(pat) 305 if err != nil { 306 return err 307 } 308 if len(m) == 0 { 309 return fmt.Errorf("couldn't find dist in %q", pat) 310 } 311 fullVersion, err = b.run("", m[0], "version") 312 if err != nil { 313 return err 314 } 315 fullVersion = bytes.TrimSpace(fullVersion) 316 v := bytes.SplitN(fullVersion, []byte(" "), 2) 317 version = string(v[0]) 318 if *versionOverride != "" { 319 version = *versionOverride 320 } 321 322 // Write VERSION file. 323 err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644) 324 if err != nil { 325 return err 326 } 327 328 // Clean goroot. 329 if err := b.clean(cleanFiles); err != nil { 330 return err 331 } 332 if b.Source { 333 if err := b.clean(sourceCleanFiles); err != nil { 334 return err 335 } 336 } 337 338 // Create packages. 339 base := fmt.Sprintf("%s.%s-%s", version, b.OS, b.Arch) 340 if b.Label != "" { 341 base += "-" + b.Label 342 } 343 if !strings.HasPrefix(base, "go") { 344 base = "go." + base 345 } 346 var targs []string 347 switch b.OS { 348 case "linux", "freebsd", "netbsd", "": 349 // build tarball 350 targ := base 351 if b.Source { 352 targ = fmt.Sprintf("%s.src", version) 353 if !strings.HasPrefix(targ, "go") { 354 targ = "go." + targ 355 } 356 } 357 targ += ".tar.gz" 358 err = makeTar(targ, work) 359 targs = append(targs, targ) 360 case "darwin": 361 // build tarball 362 targ := base + ".tar.gz" 363 err = makeTar(targ, work) 364 targs = append(targs, targ) 365 366 makerelease := filepath.Join(runtime.GOROOT(), "misc/makerelease") 367 368 // build pkg 369 // arrange work so it's laid out as the dest filesystem 370 etc := filepath.Join(makerelease, "darwin/etc") 371 _, err = b.run(work, "cp", "-r", etc, ".") 372 if err != nil { 373 return err 374 } 375 localDir := filepath.Join(work, "usr/local") 376 err = os.MkdirAll(localDir, 0755) 377 if err != nil { 378 return err 379 } 380 _, err = b.run(work, "mv", "go", localDir) 381 if err != nil { 382 return err 383 } 384 // build package 385 pkgdest, err := ioutil.TempDir("", "pkgdest") 386 if err != nil { 387 return err 388 } 389 defer os.RemoveAll(pkgdest) 390 _, err = b.run("", "pkgbuild", 391 "--identifier", "com.googlecode.go", 392 "--version", version, 393 "--scripts", filepath.Join(makerelease, "darwin/scripts"), 394 "--root", work, 395 filepath.Join(pkgdest, "com.googlecode.go.pkg")) 396 if err != nil { 397 return err 398 } 399 targ = base + ".pkg" 400 _, err = b.run("", "productbuild", 401 "--distribution", filepath.Join(makerelease, "darwin/Distribution"), 402 "--resources", filepath.Join(makerelease, "darwin/Resources"), 403 "--package-path", pkgdest, 404 targ) 405 if err != nil { 406 return err 407 } 408 targs = append(targs, targ) 409 case "windows": 410 // Create ZIP file. 411 zip := filepath.Join(work, base+".zip") 412 err = makeZip(zip, work) 413 // Copy zip to target file. 414 targ := base + ".zip" 415 err = cp(targ, zip) 416 if err != nil { 417 return err 418 } 419 targs = append(targs, targ) 420 421 // Create MSI installer. 422 win := filepath.Join(runtime.GOROOT(), "misc/makerelease/windows") 423 installer := filepath.Join(win, "installer.wxs") 424 appfiles := filepath.Join(work, "AppFiles.wxs") 425 msi := filepath.Join(work, "installer.msi") 426 // Gather files. 427 _, err = b.run(work, "heat", "dir", "go", 428 "-nologo", 429 "-gg", "-g1", "-srd", "-sfrag", 430 "-cg", "AppFiles", 431 "-template", "fragment", 432 "-dr", "INSTALLDIR", 433 "-var", "var.SourceDir", 434 "-out", appfiles) 435 if err != nil { 436 return err 437 } 438 // Build package. 439 _, err = b.run(work, "candle", 440 "-nologo", 441 "-dGoVersion="+version, 442 "-dWixGoVersion="+wixVersion(version), 443 "-dArch="+b.Arch, 444 "-dSourceDir=go", 445 installer, appfiles) 446 if err != nil { 447 return err 448 } 449 appfiles = filepath.Join(work, "AppFiles.wixobj") 450 installer = filepath.Join(work, "installer.wixobj") 451 _, err = b.run(win, "light", 452 "-nologo", 453 "-ext", "WixUIExtension", 454 "-ext", "WixUtilExtension", 455 installer, appfiles, 456 "-o", msi) 457 if err != nil { 458 return err 459 } 460 // Copy installer to target file. 461 targ = base + ".msi" 462 err = cp(targ, msi) 463 targs = append(targs, targ) 464 } 465 if err == nil && *upload { 466 for _, targ := range targs { 467 err = b.Upload(version, targ) 468 if err != nil { 469 return fmt.Errorf("uploading %s: %v", targ, err) 470 } 471 } 472 } 473 return err 474 } 475 476 var versionRe = regexp.MustCompile(`^go([0-9]+(\.[0-9]+)*)`) 477 478 // The Microsoft installer requires version format major.minor.build 479 // (http://msdn.microsoft.com/en-us/library/aa370859%28v=vs.85%29.aspx). 480 // Where the major and minor field has a maximum value of 255 and build 65535. 481 // The offical Go version format is goMAJOR.MINOR.PATCH at $GOROOT/VERSION. 482 // It's based on the Mercurial tag. Remove prefix and suffix to make the 483 // installer happy. 484 func wixVersion(v string) string { 485 m := versionRe.FindStringSubmatch(v) 486 if m == nil { 487 return "0.0.0" 488 } 489 return m[1] 490 } 491 492 // extras fetches the go.tools, go.blog, and go-tour repositories, 493 // builds them and copies the resulting binaries and static assets 494 // to the new GOROOT. 495 func (b *Build) extras() error { 496 defer b.cleanGopath() 497 498 if err := b.tools(); err != nil { 499 return err 500 } 501 if err := b.blog(); err != nil { 502 return err 503 } 504 return b.tour() 505 } 506 507 func (b *Build) get(repoPath, revision string) error { 508 // Fetch the packages (without building/installing). 509 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 510 "get", "-d", repoPath+"/...") 511 if err != nil { 512 return err 513 } 514 515 // Update the repo to the specified revision. 516 dest := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath)) 517 switch { 518 case exists(filepath.Join(dest, ".git")): 519 _, err = b.run(dest, "git", "checkout", revision) 520 case exists(filepath.Join(dest, ".hg")): 521 _, err = b.run(dest, "hg", "update", revision) 522 default: 523 err = errors.New("unknown version control system") 524 } 525 return err 526 } 527 528 func (b *Build) tools() error { 529 // Fetch the go.tools repository. 530 if err := b.get(toolPath, *toolTag); err != nil { 531 return err 532 } 533 534 // Install tools. 535 args := append([]string{"install"}, toolPaths...) 536 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), args...) 537 if err != nil { 538 return err 539 } 540 541 // Copy doc.go from go.tools/cmd/$CMD to $GOROOT/src/cmd/$CMD 542 // while rewriting "package main" to "package documentation". 543 for _, p := range toolPaths { 544 d, err := ioutil.ReadFile(filepath.Join(b.gopath, "src", 545 filepath.FromSlash(p), "doc.go")) 546 if err != nil { 547 return err 548 } 549 d = bytes.Replace(d, []byte("\npackage main\n"), 550 []byte("\npackage documentation\n"), 1) 551 cmdDir := filepath.Join(b.root, "src", "cmd", path.Base(p)) 552 if err := os.MkdirAll(cmdDir, 0755); err != nil { 553 return err 554 } 555 docGo := filepath.Join(cmdDir, "doc.go") 556 if err := ioutil.WriteFile(docGo, d, 0644); err != nil { 557 return err 558 } 559 } 560 561 return nil 562 } 563 564 func (b *Build) blog() error { 565 // Fetch the blog repository. 566 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", "-d", blogPath+"/blog") 567 if err != nil { 568 return err 569 } 570 571 // Copy blog content to $GOROOT/blog. 572 blogSrc := filepath.Join(b.gopath, "src", filepath.FromSlash(blogPath)) 573 contentDir := filepath.Join(b.root, "blog") 574 return cpAllDir(contentDir, blogSrc, blogContent...) 575 } 576 577 func (b *Build) tour() error { 578 // Fetch the go-tour repository. 579 if err := b.get(tourPath, *tourTag); err != nil { 580 return err 581 } 582 583 // Build tour binary. 584 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 585 "install", tourPath+"/gotour") 586 if err != nil { 587 return err 588 } 589 590 // Copy all the tour content to $GOROOT/misc/tour. 591 importPath := filepath.FromSlash(tourPath) 592 tourSrc := filepath.Join(b.gopath, "src", importPath) 593 contentDir := filepath.Join(b.root, "misc", "tour") 594 if err = cpAllDir(contentDir, tourSrc, tourContent...); err != nil { 595 return err 596 } 597 598 // Copy the tour source code so it's accessible with $GOPATH pointing to $GOROOT/misc/tour. 599 if err = cpAllDir(filepath.Join(contentDir, "src", importPath), tourSrc, tourPackages...); err != nil { 600 return err 601 } 602 603 // Copy gotour binary to tool directory as "tour"; invoked as "go tool tour". 604 return cp( 605 filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"+ext()), 606 filepath.Join(b.gopath, "bin", "gotour"+ext()), 607 ) 608 } 609 610 func (b *Build) cleanGopath() { 611 for _, d := range []string{"bin", "pkg", "src"} { 612 os.RemoveAll(filepath.Join(b.gopath, d)) 613 } 614 } 615 616 func ext() string { 617 if runtime.GOOS == "windows" { 618 return ".exe" 619 } 620 return "" 621 } 622 623 func (b *Build) hgCmd(dir string, args ...string) ([]byte, error) { 624 return b.run(dir, "hg", append([]string{"--config", "extensions.codereview=!"}, args...)...) 625 } 626 627 func (b *Build) run(dir, name string, args ...string) ([]byte, error) { 628 buf := new(bytes.Buffer) 629 absName, err := lookPath(name) 630 if err != nil { 631 return nil, err 632 } 633 cmd := exec.Command(absName, args...) 634 var output io.Writer = buf 635 if *verbose { 636 log.Printf("Running %q %q", absName, args) 637 output = io.MultiWriter(buf, os.Stdout) 638 } 639 cmd.Stdout = output 640 cmd.Stderr = output 641 cmd.Dir = dir 642 cmd.Env = b.env() 643 if err := cmd.Run(); err != nil { 644 fmt.Fprintf(os.Stderr, "%s", buf.Bytes()) 645 return nil, fmt.Errorf("%s %s: %v", name, strings.Join(args, " "), err) 646 } 647 return buf.Bytes(), nil 648 } 649 650 var cleanEnv = []string{ 651 "GOARCH", 652 "GOBIN", 653 "GOHOSTARCH", 654 "GOHOSTOS", 655 "GOOS", 656 "GOROOT", 657 "GOROOT_FINAL", 658 "GOPATH", 659 } 660 661 func (b *Build) env() []string { 662 env := os.Environ() 663 for i := 0; i < len(env); i++ { 664 for _, c := range cleanEnv { 665 if strings.HasPrefix(env[i], c+"=") { 666 env = append(env[:i], env[i+1:]...) 667 } 668 } 669 } 670 final := "/usr/local/go" 671 if b.OS == "windows" { 672 final = `c:\go` 673 } 674 env = append(env, 675 "GOARCH="+b.Arch, 676 "GOHOSTARCH="+b.Arch, 677 "GOHOSTOS="+b.OS, 678 "GOOS="+b.OS, 679 "GOROOT="+b.root, 680 "GOROOT_FINAL="+final, 681 "GOPATH="+b.gopath, 682 ) 683 if b.static { 684 env = append(env, "GO_DISTFLAGS=-s") 685 } 686 return env 687 } 688 689 func (b *Build) Upload(version string, filename string) error { 690 file, err := ioutil.ReadFile(filename) 691 if err != nil { 692 return err 693 } 694 695 svc, err := storage.New(oauthClient) 696 if err != nil { 697 return err 698 } 699 obj := &storage.Object{ 700 Acl: []*storage.ObjectAccessControl{{Entity: "allUsers", Role: "READER"}}, 701 Name: filename, 702 } 703 _, err = svc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do() 704 if err != nil { 705 return err 706 } 707 708 sum := fmt.Sprintf("%x", sha1.Sum(file)) 709 kind := "unknown" 710 switch { 711 case b.Source: 712 kind = "source" 713 case strings.HasSuffix(filename, ".tar.gz"), strings.HasSuffix(filename, ".zip"): 714 kind = "archive" 715 case strings.HasSuffix(filename, ".msi"), strings.HasSuffix(filename, ".pkg"): 716 kind = "installer" 717 } 718 req, err := json.Marshal(File{ 719 Filename: filename, 720 Version: version, 721 OS: b.OS, 722 Arch: b.Arch, 723 Checksum: sum, 724 Kind: kind, 725 }) 726 if err != nil { 727 return err 728 } 729 u := fmt.Sprintf("%s?%s", *uploadURL, url.Values{"key": []string{builderKey}}.Encode()) 730 resp, err := http.Post(u, "application/json", bytes.NewReader(req)) 731 if err != nil { 732 return err 733 } 734 defer resp.Body.Close() 735 if resp.StatusCode != http.StatusOK { 736 return fmt.Errorf("upload status: %v", resp.Status) 737 } 738 739 return nil 740 } 741 742 type File struct { 743 Filename string 744 OS string 745 Arch string 746 Version string 747 Checksum string `datastore:",noindex"` 748 Kind string // "archive", "installer", "source" 749 } 750 751 func setupOAuthClient() error { 752 config := &oauth.Config{ 753 ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com", 754 ClientSecret: "8YLFgOhXIELWbO-NtF3iqIQz", 755 Scope: storage.DevstorageRead_writeScope, 756 AuthURL: "https://accounts.google.com/o/oauth2/auth", 757 TokenURL: "https://accounts.google.com/o/oauth2/token", 758 TokenCache: oauth.CacheFile(*tokenCache), 759 RedirectURL: "oob", 760 } 761 transport := &oauth.Transport{Config: config} 762 if token, err := config.TokenCache.Token(); err != nil { 763 url := transport.Config.AuthCodeURL("") 764 fmt.Println("Visit the following URL, obtain an authentication" + 765 "code, and enter it below.") 766 fmt.Println(url) 767 fmt.Print("Enter authentication code: ") 768 code := "" 769 if _, err := fmt.Scan(&code); err != nil { 770 return err 771 } 772 if _, err := transport.Exchange(code); err != nil { 773 return err 774 } 775 } else { 776 transport.Token = token 777 } 778 oauthClient = transport.Client() 779 return nil 780 } 781 782 func (b *Build) clean(files []string) error { 783 for _, name := range files { 784 err := os.RemoveAll(filepath.Join(b.root, name)) 785 if err != nil { 786 return err 787 } 788 } 789 return nil 790 } 791 792 func exists(path string) bool { 793 _, err := os.Stat(path) 794 return err == nil 795 } 796 797 func readCredentials() error { 798 name := os.Getenv("HOME") 799 if runtime.GOOS == "windows" { 800 name = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 801 } 802 name = filepath.Join(name, ".gobuildkey") 803 f, err := os.Open(name) 804 if err != nil { 805 return err 806 } 807 defer f.Close() 808 s := bufio.NewScanner(f) 809 if s.Scan() { 810 builderKey = s.Text() 811 } 812 return s.Err() 813 } 814 815 func cp(dst, src string) error { 816 sf, err := os.Open(src) 817 if err != nil { 818 return err 819 } 820 defer sf.Close() 821 fi, err := sf.Stat() 822 if err != nil { 823 return err 824 } 825 df, err := os.Create(dst) 826 if err != nil { 827 return err 828 } 829 defer df.Close() 830 // Windows doesn't currently implement Fchmod 831 if runtime.GOOS != "windows" { 832 if err := df.Chmod(fi.Mode()); err != nil { 833 return err 834 } 835 } 836 _, err = io.Copy(df, sf) 837 return err 838 } 839 840 func cpDir(dst, src string) error { 841 walk := func(srcPath string, info os.FileInfo, err error) error { 842 if err != nil { 843 return err 844 } 845 dstPath := filepath.Join(dst, srcPath[len(src):]) 846 if info.IsDir() { 847 return os.MkdirAll(dstPath, 0755) 848 } 849 return cp(dstPath, srcPath) 850 } 851 return filepath.Walk(src, walk) 852 } 853 854 func cpAllDir(dst, basePath string, dirs ...string) error { 855 for _, dir := range dirs { 856 if err := cpDir(filepath.Join(dst, dir), filepath.Join(basePath, dir)); err != nil { 857 return err 858 } 859 } 860 return nil 861 } 862 863 func makeTar(targ, workdir string) error { 864 f, err := os.Create(targ) 865 if err != nil { 866 return err 867 } 868 zout := gzip.NewWriter(f) 869 tw := tar.NewWriter(zout) 870 871 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 872 if !strings.HasPrefix(path, workdir) { 873 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 874 } 875 name := path[len(workdir):] 876 877 // Chop of any leading / from filename, leftover from removing workdir. 878 if strings.HasPrefix(name, "/") { 879 name = name[1:] 880 } 881 // Don't include things outside of the go subdirectory (for instance, 882 // the zip file that we're currently writing here.) 883 if !strings.HasPrefix(name, "go/") { 884 return nil 885 } 886 if *verbose { 887 log.Printf("adding to tar: %s", name) 888 } 889 target, _ := os.Readlink(path) 890 hdr, err := tar.FileInfoHeader(fi, target) 891 if err != nil { 892 return err 893 } 894 hdr.Name = name 895 hdr.Uname = "root" 896 hdr.Gname = "root" 897 hdr.Uid = 0 898 hdr.Gid = 0 899 900 // Force permissions to 0755 for executables, 0644 for everything else. 901 if fi.Mode().Perm()&0111 != 0 { 902 hdr.Mode = hdr.Mode&^0777 | 0755 903 } else { 904 hdr.Mode = hdr.Mode&^0777 | 0644 905 } 906 907 err = tw.WriteHeader(hdr) 908 if err != nil { 909 return fmt.Errorf("Error writing file %q: %v", name, err) 910 } 911 if fi.IsDir() { 912 return nil 913 } 914 r, err := os.Open(path) 915 if err != nil { 916 return err 917 } 918 defer r.Close() 919 _, err = io.Copy(tw, r) 920 return err 921 }) 922 if err != nil { 923 return err 924 } 925 if err := tw.Close(); err != nil { 926 return err 927 } 928 if err := zout.Close(); err != nil { 929 return err 930 } 931 return f.Close() 932 } 933 934 func makeZip(targ, workdir string) error { 935 f, err := os.Create(targ) 936 if err != nil { 937 return err 938 } 939 zw := zip.NewWriter(f) 940 941 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 942 if !strings.HasPrefix(path, workdir) { 943 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 944 } 945 name := path[len(workdir):] 946 947 // Convert to Unix-style named paths, as that's the 948 // type of zip file that archive/zip creates. 949 name = strings.Replace(name, "\\", "/", -1) 950 // Chop of any leading / from filename, leftover from removing workdir. 951 if strings.HasPrefix(name, "/") { 952 name = name[1:] 953 } 954 // Don't include things outside of the go subdirectory (for instance, 955 // the zip file that we're currently writing here.) 956 if !strings.HasPrefix(name, "go/") { 957 return nil 958 } 959 if *verbose { 960 log.Printf("adding to zip: %s", name) 961 } 962 fh, err := zip.FileInfoHeader(fi) 963 if err != nil { 964 return err 965 } 966 fh.Name = name 967 fh.Method = zip.Deflate 968 if fi.IsDir() { 969 fh.Name += "/" // append trailing slash 970 fh.Method = zip.Store // no need to deflate 0 byte files 971 } 972 w, err := zw.CreateHeader(fh) 973 if err != nil { 974 return err 975 } 976 if fi.IsDir() { 977 return nil 978 } 979 r, err := os.Open(path) 980 if err != nil { 981 return err 982 } 983 defer r.Close() 984 _, err = io.Copy(w, r) 985 return err 986 }) 987 if err != nil { 988 return err 989 } 990 if err := zw.Close(); err != nil { 991 return err 992 } 993 return f.Close() 994 } 995 996 type tool struct { 997 name string 998 commonDirs []string 999 } 1000 1001 var wixTool = tool{ 1002 "http://wix.sourceforge.net/, version 3.5", 1003 []string{`C:\Program Files\Windows Installer XML v3.5\bin`, 1004 `C:\Program Files (x86)\Windows Installer XML v3.5\bin`}, 1005 } 1006 1007 var hgTool = tool{ 1008 "http://mercurial.selenic.com/wiki/WindowsInstall", 1009 []string{`C:\Program Files\Mercurial`, 1010 `C:\Program Files (x86)\Mercurial`, 1011 }, 1012 } 1013 1014 var gccTool = tool{ 1015 "Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/", 1016 []string{`C:\Mingw\bin`}, 1017 } 1018 1019 var windowsDeps = map[string]tool{ 1020 "gcc": gccTool, 1021 "heat": wixTool, 1022 "candle": wixTool, 1023 "light": wixTool, 1024 "cmd": {"Windows cmd.exe", nil}, 1025 "hg": hgTool, 1026 } 1027 1028 func checkWindowsDeps() { 1029 for prog, help := range windowsDeps { 1030 absPath, err := lookPath(prog) 1031 if err != nil { 1032 log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help) 1033 } 1034 if *verbose { 1035 log.Printf("found windows dep %s at %s", prog, absPath) 1036 } 1037 } 1038 } 1039 1040 func lookPath(prog string) (absPath string, err error) { 1041 absPath, err = exec.LookPath(prog) 1042 if err == nil { 1043 return 1044 } 1045 t, ok := windowsDeps[prog] 1046 if !ok { 1047 return 1048 } 1049 for _, dir := range t.commonDirs { 1050 for _, ext := range []string{"exe", "bat"} { 1051 absPath = filepath.Join(dir, prog+"."+ext) 1052 if _, err1 := os.Stat(absPath); err1 == nil { 1053 err = nil 1054 os.Setenv("PATH", os.Getenv("PATH")+";"+dir) 1055 return 1056 } 1057 } 1058 } 1059 return 1060 }