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