github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/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 "akaros-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 err := b.Do(); err != nil { 210 log.Printf("%s: %v", targ, err) 211 ok = false 212 } 213 } 214 if !ok { 215 os.Exit(1) 216 } 217 } 218 219 type Build struct { 220 Source bool // if true, OS and Arch must be empty 221 Race bool // build race toolchain 222 OS string 223 Arch string 224 Label string 225 root string 226 gopath string 227 static bool // if true, build statically linked toolchain 228 } 229 230 func (b *Build) Do() error { 231 work, err := ioutil.TempDir("", "makerelease") 232 if err != nil { 233 return err 234 } 235 defer os.RemoveAll(work) 236 b.root = filepath.Join(work, "go") 237 b.gopath = work 238 239 // Clone Go distribution and update to tag. 240 _, err = b.hgCmd(work, "clone", *repo, b.root) 241 if err != nil { 242 return err 243 } 244 _, err = b.hgCmd(b.root, "update", *tag) 245 if err != nil { 246 return err 247 } 248 249 // Remove exp and old packages. 250 if err := b.clean(preBuildCleanFiles); err != nil { 251 return err 252 } 253 254 src := filepath.Join(b.root, "src") 255 if b.Source { 256 if runtime.GOOS == "windows" { 257 log.Print("Warning: running make.bash on Windows; source builds are intended to be run on a Unix machine") 258 } 259 // Build dist tool only. 260 _, err = b.run(src, "bash", "make.bash", "--dist-tool") 261 } else { 262 // Build. 263 if b.OS == "windows" { 264 _, err = b.run(src, "cmd", "/C", "make.bat") 265 } else { 266 _, err = b.run(src, "bash", "make.bash") 267 } 268 if b.Race { 269 if err != nil { 270 return err 271 } 272 goCmd := filepath.Join(b.root, "bin", "go") 273 if b.OS == "windows" { 274 goCmd += ".exe" 275 } 276 _, err = b.run(src, goCmd, "install", "-race", "std") 277 if err != nil { 278 return err 279 } 280 // Re-install std without -race, so that we're not left 281 // with a slower, race-enabled cmd/go, etc. 282 _, err = b.run(src, goCmd, "install", "-a", "std") 283 // Re-building go command leaves old versions of go.exe as go.exe~ on windows. 284 // See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details. 285 // Remove it manually. 286 if b.OS == "windows" { 287 os.Remove(goCmd + "~") 288 } 289 } 290 if err != nil { 291 return err 292 } 293 err = b.extras() 294 } 295 if err != nil { 296 return err 297 } 298 299 // Get version strings. 300 var ( 301 version string // "weekly.2012-03-04" 302 fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3" 303 ) 304 pat := filepath.Join(b.root, "pkg/tool/*/dist*") // trailing * for .exe 305 m, err := filepath.Glob(pat) 306 if err != nil { 307 return err 308 } 309 if len(m) == 0 { 310 return fmt.Errorf("couldn't find dist in %q", pat) 311 } 312 fullVersion, err = b.run("", m[0], "version") 313 if err != nil { 314 return err 315 } 316 fullVersion = bytes.TrimSpace(fullVersion) 317 v := bytes.SplitN(fullVersion, []byte(" "), 2) 318 version = string(v[0]) 319 if *versionOverride != "" { 320 version = *versionOverride 321 } 322 323 // Write VERSION file. 324 err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644) 325 if err != nil { 326 return err 327 } 328 329 // Clean goroot. 330 if err := b.clean(cleanFiles); err != nil { 331 return err 332 } 333 if b.Source { 334 if err := b.clean(sourceCleanFiles); err != nil { 335 return err 336 } 337 } 338 339 // Create packages. 340 base := fmt.Sprintf("%s.%s-%s", version, b.OS, b.Arch) 341 if b.Label != "" { 342 base += "-" + b.Label 343 } 344 if !strings.HasPrefix(base, "go") { 345 base = "go." + base 346 } 347 var targs []string 348 switch b.OS { 349 case "akaros", "linux", "freebsd", "netbsd", "": 350 // build tarball 351 targ := base 352 if b.Source { 353 targ = fmt.Sprintf("%s.src", version) 354 if !strings.HasPrefix(targ, "go") { 355 targ = "go." + targ 356 } 357 } 358 targ += ".tar.gz" 359 err = makeTar(targ, work) 360 targs = append(targs, targ) 361 case "darwin": 362 // build tarball 363 targ := base + ".tar.gz" 364 err = makeTar(targ, work) 365 targs = append(targs, targ) 366 367 makerelease := filepath.Join(runtime.GOROOT(), "misc/makerelease") 368 369 // build pkg 370 // arrange work so it's laid out as the dest filesystem 371 etc := filepath.Join(makerelease, "darwin/etc") 372 _, err = b.run(work, "cp", "-r", etc, ".") 373 if err != nil { 374 return err 375 } 376 localDir := filepath.Join(work, "usr/local") 377 err = os.MkdirAll(localDir, 0755) 378 if err != nil { 379 return err 380 } 381 _, err = b.run(work, "mv", "go", localDir) 382 if err != nil { 383 return err 384 } 385 // build package 386 pkgdest, err := ioutil.TempDir("", "pkgdest") 387 if err != nil { 388 return err 389 } 390 defer os.RemoveAll(pkgdest) 391 _, err = b.run("", "pkgbuild", 392 "--identifier", "com.googlecode.go", 393 "--version", version, 394 "--scripts", filepath.Join(makerelease, "darwin/scripts"), 395 "--root", work, 396 filepath.Join(pkgdest, "com.googlecode.go.pkg")) 397 if err != nil { 398 return err 399 } 400 targ = base + ".pkg" 401 _, err = b.run("", "productbuild", 402 "--distribution", filepath.Join(makerelease, "darwin/Distribution"), 403 "--resources", filepath.Join(makerelease, "darwin/Resources"), 404 "--package-path", pkgdest, 405 targ) 406 if err != nil { 407 return err 408 } 409 targs = append(targs, targ) 410 case "windows": 411 // Create ZIP file. 412 zip := filepath.Join(work, base+".zip") 413 err = makeZip(zip, work) 414 // Copy zip to target file. 415 targ := base + ".zip" 416 err = cp(targ, zip) 417 if err != nil { 418 return err 419 } 420 targs = append(targs, targ) 421 422 // Create MSI installer. 423 win := filepath.Join(runtime.GOROOT(), "misc/makerelease/windows") 424 installer := filepath.Join(win, "installer.wxs") 425 appfiles := filepath.Join(work, "AppFiles.wxs") 426 msi := filepath.Join(work, "installer.msi") 427 // Gather files. 428 _, err = b.run(work, "heat", "dir", "go", 429 "-nologo", 430 "-gg", "-g1", "-srd", "-sfrag", 431 "-cg", "AppFiles", 432 "-template", "fragment", 433 "-dr", "INSTALLDIR", 434 "-var", "var.SourceDir", 435 "-out", appfiles) 436 if err != nil { 437 return err 438 } 439 // Build package. 440 _, err = b.run(work, "candle", 441 "-nologo", 442 "-dGoVersion="+version, 443 "-dWixGoVersion="+wixVersion(version), 444 "-dArch="+b.Arch, 445 "-dSourceDir=go", 446 installer, appfiles) 447 if err != nil { 448 return err 449 } 450 appfiles = filepath.Join(work, "AppFiles.wixobj") 451 installer = filepath.Join(work, "installer.wixobj") 452 _, err = b.run(win, "light", 453 "-nologo", 454 "-ext", "WixUIExtension", 455 "-ext", "WixUtilExtension", 456 installer, appfiles, 457 "-o", msi) 458 if err != nil { 459 return err 460 } 461 // Copy installer to target file. 462 targ = base + ".msi" 463 err = cp(targ, msi) 464 targs = append(targs, targ) 465 } 466 if err == nil && *upload { 467 for _, targ := range targs { 468 err = b.Upload(version, targ) 469 if err != nil { 470 return fmt.Errorf("uploading %s: %v", targ, err) 471 } 472 } 473 } 474 return err 475 } 476 477 var versionRe = regexp.MustCompile(`^go([0-9]+(\.[0-9]+)*)`) 478 479 // The Microsoft installer requires version format major.minor.build 480 // (http://msdn.microsoft.com/en-us/library/aa370859%28v=vs.85%29.aspx). 481 // Where the major and minor field has a maximum value of 255 and build 65535. 482 // The offical Go version format is goMAJOR.MINOR.PATCH at $GOROOT/VERSION. 483 // It's based on the Mercurial tag. Remove prefix and suffix to make the 484 // installer happy. 485 func wixVersion(v string) string { 486 m := versionRe.FindStringSubmatch(v) 487 if m == nil { 488 return "0.0.0" 489 } 490 return m[1] 491 } 492 493 // extras fetches the go.tools, go.blog, and go-tour repositories, 494 // builds them and copies the resulting binaries and static assets 495 // to the new GOROOT. 496 func (b *Build) extras() error { 497 defer b.cleanGopath() 498 499 if err := b.tools(); err != nil { 500 return err 501 } 502 if err := b.blog(); err != nil { 503 return err 504 } 505 return b.tour() 506 } 507 508 func (b *Build) get(repoPath, revision string) error { 509 dest := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath)) 510 511 if strings.HasPrefix(repoPath, "golang.org/x/") { 512 // For sub-repos, fetch the old Mercurial repo; bypass "go get". 513 // DO NOT import this special case into the git tree. 514 515 if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { 516 return err 517 } 518 repo := strings.Replace(repoPath, "golang.org/x/", "https://code.google.com/p/go.", 1) 519 if _, err := b.run(b.gopath, "hg", "clone", repo, dest); err != nil { 520 return err 521 } 522 } else { 523 // Fetch the packages (without building/installing). 524 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 525 "get", "-d", repoPath+"/...") 526 if err != nil { 527 return err 528 } 529 } 530 531 // Update the repo to the specified revision. 532 var err error 533 switch { 534 case exists(filepath.Join(dest, ".git")): 535 _, err = b.run(dest, "git", "checkout", revision) 536 case exists(filepath.Join(dest, ".hg")): 537 _, err = b.run(dest, "hg", "update", revision) 538 default: 539 err = errors.New("unknown version control system") 540 } 541 return err 542 } 543 544 func (b *Build) tools() error { 545 // Fetch the go.tools repository. 546 if err := b.get(toolPath, *toolTag); err != nil { 547 return err 548 } 549 550 // Install tools. 551 args := append([]string{"install"}, toolPaths...) 552 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), args...) 553 if err != nil { 554 return err 555 } 556 557 // Copy doc.go from go.tools/cmd/$CMD to $GOROOT/src/cmd/$CMD 558 // while rewriting "package main" to "package documentation". 559 for _, p := range toolPaths { 560 d, err := ioutil.ReadFile(filepath.Join(b.gopath, "src", 561 filepath.FromSlash(p), "doc.go")) 562 if err != nil { 563 return err 564 } 565 d = bytes.Replace(d, []byte("\npackage main\n"), 566 []byte("\npackage documentation\n"), 1) 567 cmdDir := filepath.Join(b.root, "src", "cmd", path.Base(p)) 568 if err := os.MkdirAll(cmdDir, 0755); err != nil { 569 return err 570 } 571 docGo := filepath.Join(cmdDir, "doc.go") 572 if err := ioutil.WriteFile(docGo, d, 0644); err != nil { 573 return err 574 } 575 } 576 577 return nil 578 } 579 580 func (b *Build) blog() error { 581 // Fetch the blog repository. 582 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", "-d", blogPath+"/blog") 583 if err != nil { 584 return err 585 } 586 587 // Copy blog content to $GOROOT/blog. 588 blogSrc := filepath.Join(b.gopath, "src", filepath.FromSlash(blogPath)) 589 contentDir := filepath.Join(b.root, "blog") 590 return cpAllDir(contentDir, blogSrc, blogContent...) 591 } 592 593 func (b *Build) tour() error { 594 // Fetch the go-tour repository. 595 if err := b.get(tourPath, *tourTag); err != nil { 596 return err 597 } 598 599 // Build tour binary. 600 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 601 "install", tourPath+"/gotour") 602 if err != nil { 603 return err 604 } 605 606 // Copy all the tour content to $GOROOT/misc/tour. 607 importPath := filepath.FromSlash(tourPath) 608 tourSrc := filepath.Join(b.gopath, "src", importPath) 609 contentDir := filepath.Join(b.root, "misc", "tour") 610 if err = cpAllDir(contentDir, tourSrc, tourContent...); err != nil { 611 return err 612 } 613 614 // Copy the tour source code so it's accessible with $GOPATH pointing to $GOROOT/misc/tour. 615 if err = cpAllDir(filepath.Join(contentDir, "src", importPath), tourSrc, tourPackages...); err != nil { 616 return err 617 } 618 619 // Copy gotour binary to tool directory as "tour"; invoked as "go tool tour". 620 return cp( 621 filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"+ext()), 622 filepath.Join(b.gopath, "bin", "gotour"+ext()), 623 ) 624 } 625 626 func (b *Build) cleanGopath() { 627 for _, d := range []string{"bin", "pkg", "src"} { 628 os.RemoveAll(filepath.Join(b.gopath, d)) 629 } 630 } 631 632 func ext() string { 633 if runtime.GOOS == "windows" { 634 return ".exe" 635 } 636 return "" 637 } 638 639 func (b *Build) hgCmd(dir string, args ...string) ([]byte, error) { 640 return b.run(dir, "hg", append([]string{"--config", "extensions.codereview=!"}, args...)...) 641 } 642 643 func (b *Build) run(dir, name string, args ...string) ([]byte, error) { 644 buf := new(bytes.Buffer) 645 absName, err := lookPath(name) 646 if err != nil { 647 return nil, err 648 } 649 cmd := exec.Command(absName, args...) 650 var output io.Writer = buf 651 if *verbose { 652 log.Printf("Running %q %q", absName, args) 653 output = io.MultiWriter(buf, os.Stdout) 654 } 655 cmd.Stdout = output 656 cmd.Stderr = output 657 cmd.Dir = dir 658 cmd.Env = b.env() 659 if err := cmd.Run(); err != nil { 660 fmt.Fprintf(os.Stderr, "%s", buf.Bytes()) 661 return nil, fmt.Errorf("%s %s: %v", name, strings.Join(args, " "), err) 662 } 663 return buf.Bytes(), nil 664 } 665 666 var cleanEnv = []string{ 667 "GOARCH", 668 "GOBIN", 669 "GOHOSTARCH", 670 "GOHOSTOS", 671 "GOOS", 672 "GOROOT", 673 "GOROOT_FINAL", 674 "GOPATH", 675 } 676 677 func (b *Build) env() []string { 678 env := os.Environ() 679 for i := 0; i < len(env); i++ { 680 for _, c := range cleanEnv { 681 if strings.HasPrefix(env[i], c+"=") { 682 env = append(env[:i], env[i+1:]...) 683 } 684 } 685 } 686 final := "/usr/local/go" 687 if b.OS == "windows" { 688 final = `c:\go` 689 } 690 env = append(env, 691 "GOARCH="+b.Arch, 692 "GOHOSTARCH="+b.Arch, 693 "GOHOSTOS="+b.OS, 694 "GOOS="+b.OS, 695 "GOROOT="+b.root, 696 "GOROOT_FINAL="+final, 697 "GOPATH="+b.gopath, 698 ) 699 if b.static { 700 env = append(env, "GO_DISTFLAGS=-s") 701 } 702 return env 703 } 704 705 func (b *Build) Upload(version string, filename string) error { 706 file, err := ioutil.ReadFile(filename) 707 if err != nil { 708 return err 709 } 710 711 svc, err := storage.New(oauthClient) 712 if err != nil { 713 return err 714 } 715 obj := &storage.Object{ 716 Acl: []*storage.ObjectAccessControl{{Entity: "allUsers", Role: "READER"}}, 717 Name: filename, 718 } 719 _, err = svc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do() 720 if err != nil { 721 return err 722 } 723 724 sum := fmt.Sprintf("%x", sha1.Sum(file)) 725 kind := "unknown" 726 switch { 727 case b.Source: 728 kind = "source" 729 case strings.HasSuffix(filename, ".tar.gz"), strings.HasSuffix(filename, ".zip"): 730 kind = "archive" 731 case strings.HasSuffix(filename, ".msi"), strings.HasSuffix(filename, ".pkg"): 732 kind = "installer" 733 } 734 req, err := json.Marshal(File{ 735 Filename: filename, 736 Version: version, 737 OS: b.OS, 738 Arch: b.Arch, 739 Checksum: sum, 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 Kind string // "archive", "installer", "source" 765 } 766 767 func setupOAuthClient() error { 768 config := &oauth.Config{ 769 ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com", 770 ClientSecret: "8YLFgOhXIELWbO-NtF3iqIQz", 771 Scope: storage.DevstorageRead_writeScope, 772 AuthURL: "https://accounts.google.com/o/oauth2/auth", 773 TokenURL: "https://accounts.google.com/o/oauth2/token", 774 TokenCache: oauth.CacheFile(*tokenCache), 775 RedirectURL: "oob", 776 } 777 transport := &oauth.Transport{Config: config} 778 if token, err := config.TokenCache.Token(); err != nil { 779 url := transport.Config.AuthCodeURL("") 780 fmt.Println("Visit the following URL, obtain an authentication" + 781 "code, and enter it below.") 782 fmt.Println(url) 783 fmt.Print("Enter authentication code: ") 784 code := "" 785 if _, err := fmt.Scan(&code); err != nil { 786 return err 787 } 788 if _, err := transport.Exchange(code); err != nil { 789 return err 790 } 791 } else { 792 transport.Token = token 793 } 794 oauthClient = transport.Client() 795 return nil 796 } 797 798 func (b *Build) clean(files []string) error { 799 for _, name := range files { 800 err := os.RemoveAll(filepath.Join(b.root, name)) 801 if err != nil { 802 return err 803 } 804 } 805 return nil 806 } 807 808 func exists(path string) bool { 809 _, err := os.Stat(path) 810 return err == nil 811 } 812 813 func readCredentials() error { 814 name := os.Getenv("HOME") 815 if runtime.GOOS == "windows" { 816 name = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 817 } 818 name = filepath.Join(name, ".gobuildkey") 819 f, err := os.Open(name) 820 if err != nil { 821 return err 822 } 823 defer f.Close() 824 s := bufio.NewScanner(f) 825 if s.Scan() { 826 builderKey = s.Text() 827 } 828 return s.Err() 829 } 830 831 func cp(dst, src string) error { 832 sf, err := os.Open(src) 833 if err != nil { 834 return err 835 } 836 defer sf.Close() 837 fi, err := sf.Stat() 838 if err != nil { 839 return err 840 } 841 df, err := os.Create(dst) 842 if err != nil { 843 return err 844 } 845 defer df.Close() 846 // Windows doesn't currently implement Fchmod 847 if runtime.GOOS != "windows" { 848 if err := df.Chmod(fi.Mode()); err != nil { 849 return err 850 } 851 } 852 _, err = io.Copy(df, sf) 853 return err 854 } 855 856 func cpDir(dst, src string) error { 857 walk := func(srcPath string, info os.FileInfo, err error) error { 858 if err != nil { 859 return err 860 } 861 dstPath := filepath.Join(dst, srcPath[len(src):]) 862 if info.IsDir() { 863 return os.MkdirAll(dstPath, 0755) 864 } 865 return cp(dstPath, srcPath) 866 } 867 return filepath.Walk(src, walk) 868 } 869 870 func cpAllDir(dst, basePath string, dirs ...string) error { 871 for _, dir := range dirs { 872 if err := cpDir(filepath.Join(dst, dir), filepath.Join(basePath, dir)); err != nil { 873 return err 874 } 875 } 876 return nil 877 } 878 879 func makeTar(targ, workdir string) error { 880 f, err := os.Create(targ) 881 if err != nil { 882 return err 883 } 884 zout := gzip.NewWriter(f) 885 tw := tar.NewWriter(zout) 886 887 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 888 if !strings.HasPrefix(path, workdir) { 889 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 890 } 891 name := path[len(workdir):] 892 893 // Chop of any leading / from filename, leftover from removing workdir. 894 if strings.HasPrefix(name, "/") { 895 name = name[1:] 896 } 897 // Don't include things outside of the go subdirectory (for instance, 898 // the zip file that we're currently writing here.) 899 if !strings.HasPrefix(name, "go/") { 900 return nil 901 } 902 if *verbose { 903 log.Printf("adding to tar: %s", name) 904 } 905 target, _ := os.Readlink(path) 906 hdr, err := tar.FileInfoHeader(fi, target) 907 if err != nil { 908 return err 909 } 910 hdr.Name = name 911 hdr.Uname = "root" 912 hdr.Gname = "root" 913 hdr.Uid = 0 914 hdr.Gid = 0 915 916 // Force permissions to 0755 for executables, 0644 for everything else. 917 if fi.Mode().Perm()&0111 != 0 { 918 hdr.Mode = hdr.Mode&^0777 | 0755 919 } else { 920 hdr.Mode = hdr.Mode&^0777 | 0644 921 } 922 923 err = tw.WriteHeader(hdr) 924 if err != nil { 925 return fmt.Errorf("Error writing file %q: %v", name, err) 926 } 927 if fi.IsDir() { 928 return nil 929 } 930 r, err := os.Open(path) 931 if err != nil { 932 return err 933 } 934 defer r.Close() 935 _, err = io.Copy(tw, r) 936 return err 937 }) 938 if err != nil { 939 return err 940 } 941 if err := tw.Close(); err != nil { 942 return err 943 } 944 if err := zout.Close(); err != nil { 945 return err 946 } 947 return f.Close() 948 } 949 950 func makeZip(targ, workdir string) error { 951 f, err := os.Create(targ) 952 if err != nil { 953 return err 954 } 955 zw := zip.NewWriter(f) 956 957 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 958 if !strings.HasPrefix(path, workdir) { 959 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 960 } 961 name := path[len(workdir):] 962 963 // Convert to Unix-style named paths, as that's the 964 // type of zip file that archive/zip creates. 965 name = strings.Replace(name, "\\", "/", -1) 966 // Chop of any leading / from filename, leftover from removing workdir. 967 if strings.HasPrefix(name, "/") { 968 name = name[1:] 969 } 970 // Don't include things outside of the go subdirectory (for instance, 971 // the zip file that we're currently writing here.) 972 if !strings.HasPrefix(name, "go/") { 973 return nil 974 } 975 if *verbose { 976 log.Printf("adding to zip: %s", name) 977 } 978 fh, err := zip.FileInfoHeader(fi) 979 if err != nil { 980 return err 981 } 982 fh.Name = name 983 fh.Method = zip.Deflate 984 if fi.IsDir() { 985 fh.Name += "/" // append trailing slash 986 fh.Method = zip.Store // no need to deflate 0 byte files 987 } 988 w, err := zw.CreateHeader(fh) 989 if err != nil { 990 return err 991 } 992 if fi.IsDir() { 993 return nil 994 } 995 r, err := os.Open(path) 996 if err != nil { 997 return err 998 } 999 defer r.Close() 1000 _, err = io.Copy(w, r) 1001 return err 1002 }) 1003 if err != nil { 1004 return err 1005 } 1006 if err := zw.Close(); err != nil { 1007 return err 1008 } 1009 return f.Close() 1010 } 1011 1012 type tool struct { 1013 name string 1014 commonDirs []string 1015 } 1016 1017 var wixTool = tool{ 1018 "http://wix.sourceforge.net/, version 3.5", 1019 []string{`C:\Program Files\Windows Installer XML v3.5\bin`, 1020 `C:\Program Files (x86)\Windows Installer XML v3.5\bin`}, 1021 } 1022 1023 var hgTool = tool{ 1024 "http://mercurial.selenic.com/wiki/WindowsInstall", 1025 []string{`C:\Program Files\Mercurial`, 1026 `C:\Program Files (x86)\Mercurial`, 1027 }, 1028 } 1029 1030 var gccTool = tool{ 1031 "Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/", 1032 []string{`C:\Mingw\bin`}, 1033 } 1034 1035 var windowsDeps = map[string]tool{ 1036 "gcc": gccTool, 1037 "heat": wixTool, 1038 "candle": wixTool, 1039 "light": wixTool, 1040 "cmd": {"Windows cmd.exe", nil}, 1041 "hg": hgTool, 1042 } 1043 1044 func checkWindowsDeps() { 1045 for prog, help := range windowsDeps { 1046 absPath, err := lookPath(prog) 1047 if err != nil { 1048 log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help) 1049 } 1050 if *verbose { 1051 log.Printf("found windows dep %s at %s", prog, absPath) 1052 } 1053 } 1054 } 1055 1056 func lookPath(prog string) (absPath string, err error) { 1057 absPath, err = exec.LookPath(prog) 1058 if err == nil { 1059 return 1060 } 1061 t, ok := windowsDeps[prog] 1062 if !ok { 1063 return 1064 } 1065 for _, dir := range t.commonDirs { 1066 for _, ext := range []string{"exe", "bat"} { 1067 absPath = filepath.Join(dir, prog+"."+ext) 1068 if _, err1 := os.Stat(absPath); err1 == nil { 1069 err = nil 1070 os.Setenv("PATH", os.Getenv("PATH")+";"+dir) 1071 return 1072 } 1073 } 1074 } 1075 return 1076 }