github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/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 dest := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath)) 509 510 if strings.HasPrefix(repoPath, "golang.org/x/") { 511 // For sub-repos, fetch the old Mercurial repo; bypass "go get". 512 // DO NOT import this special case into the git tree. 513 514 if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { 515 return err 516 } 517 repo := strings.Replace(repoPath, "golang.org/x/", "https://code.google.com/p/go.", 1) 518 if _, err := b.run(b.gopath, "hg", "clone", repo, dest); err != nil { 519 return err 520 } 521 } else { 522 // Fetch the packages (without building/installing). 523 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 524 "get", "-d", repoPath+"/...") 525 if err != nil { 526 return err 527 } 528 } 529 530 // Update the repo to the specified revision. 531 var err error 532 switch { 533 case exists(filepath.Join(dest, ".git")): 534 _, err = b.run(dest, "git", "checkout", revision) 535 case exists(filepath.Join(dest, ".hg")): 536 _, err = b.run(dest, "hg", "update", revision) 537 default: 538 err = errors.New("unknown version control system") 539 } 540 return err 541 } 542 543 func (b *Build) tools() error { 544 // Fetch the go.tools repository. 545 if err := b.get(toolPath, *toolTag); err != nil { 546 return err 547 } 548 549 // Install tools. 550 args := append([]string{"install"}, toolPaths...) 551 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), args...) 552 if err != nil { 553 return err 554 } 555 556 // Copy doc.go from go.tools/cmd/$CMD to $GOROOT/src/cmd/$CMD 557 // while rewriting "package main" to "package documentation". 558 for _, p := range toolPaths { 559 d, err := ioutil.ReadFile(filepath.Join(b.gopath, "src", 560 filepath.FromSlash(p), "doc.go")) 561 if err != nil { 562 return err 563 } 564 d = bytes.Replace(d, []byte("\npackage main\n"), 565 []byte("\npackage documentation\n"), 1) 566 cmdDir := filepath.Join(b.root, "src", "cmd", path.Base(p)) 567 if err := os.MkdirAll(cmdDir, 0755); err != nil { 568 return err 569 } 570 docGo := filepath.Join(cmdDir, "doc.go") 571 if err := ioutil.WriteFile(docGo, d, 0644); err != nil { 572 return err 573 } 574 } 575 576 return nil 577 } 578 579 func (b *Build) blog() error { 580 // Fetch the blog repository. 581 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", "-d", blogPath+"/blog") 582 if err != nil { 583 return err 584 } 585 586 // Copy blog content to $GOROOT/blog. 587 blogSrc := filepath.Join(b.gopath, "src", filepath.FromSlash(blogPath)) 588 contentDir := filepath.Join(b.root, "blog") 589 return cpAllDir(contentDir, blogSrc, blogContent...) 590 } 591 592 func (b *Build) tour() error { 593 // Fetch the go-tour repository. 594 if err := b.get(tourPath, *tourTag); err != nil { 595 return err 596 } 597 598 // Build tour binary. 599 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), 600 "install", tourPath+"/gotour") 601 if err != nil { 602 return err 603 } 604 605 // Copy all the tour content to $GOROOT/misc/tour. 606 importPath := filepath.FromSlash(tourPath) 607 tourSrc := filepath.Join(b.gopath, "src", importPath) 608 contentDir := filepath.Join(b.root, "misc", "tour") 609 if err = cpAllDir(contentDir, tourSrc, tourContent...); err != nil { 610 return err 611 } 612 613 // Copy the tour source code so it's accessible with $GOPATH pointing to $GOROOT/misc/tour. 614 if err = cpAllDir(filepath.Join(contentDir, "src", importPath), tourSrc, tourPackages...); err != nil { 615 return err 616 } 617 618 // Copy gotour binary to tool directory as "tour"; invoked as "go tool tour". 619 return cp( 620 filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"+ext()), 621 filepath.Join(b.gopath, "bin", "gotour"+ext()), 622 ) 623 } 624 625 func (b *Build) cleanGopath() { 626 for _, d := range []string{"bin", "pkg", "src"} { 627 os.RemoveAll(filepath.Join(b.gopath, d)) 628 } 629 } 630 631 func ext() string { 632 if runtime.GOOS == "windows" { 633 return ".exe" 634 } 635 return "" 636 } 637 638 func (b *Build) hgCmd(dir string, args ...string) ([]byte, error) { 639 return b.run(dir, "hg", append([]string{"--config", "extensions.codereview=!"}, args...)...) 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 Kind: kind, 740 }) 741 if err != nil { 742 return err 743 } 744 u := fmt.Sprintf("%s?%s", *uploadURL, url.Values{"key": []string{builderKey}}.Encode()) 745 resp, err := http.Post(u, "application/json", bytes.NewReader(req)) 746 if err != nil { 747 return err 748 } 749 defer resp.Body.Close() 750 if resp.StatusCode != http.StatusOK { 751 return fmt.Errorf("upload status: %v", resp.Status) 752 } 753 754 return nil 755 } 756 757 type File struct { 758 Filename string 759 OS string 760 Arch string 761 Version string 762 Checksum string `datastore:",noindex"` 763 Kind string // "archive", "installer", "source" 764 } 765 766 func setupOAuthClient() error { 767 config := &oauth.Config{ 768 ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com", 769 ClientSecret: "8YLFgOhXIELWbO-NtF3iqIQz", 770 Scope: storage.DevstorageRead_writeScope, 771 AuthURL: "https://accounts.google.com/o/oauth2/auth", 772 TokenURL: "https://accounts.google.com/o/oauth2/token", 773 TokenCache: oauth.CacheFile(*tokenCache), 774 RedirectURL: "oob", 775 } 776 transport := &oauth.Transport{Config: config} 777 if token, err := config.TokenCache.Token(); err != nil { 778 url := transport.Config.AuthCodeURL("") 779 fmt.Println("Visit the following URL, obtain an authentication" + 780 "code, and enter it below.") 781 fmt.Println(url) 782 fmt.Print("Enter authentication code: ") 783 code := "" 784 if _, err := fmt.Scan(&code); err != nil { 785 return err 786 } 787 if _, err := transport.Exchange(code); err != nil { 788 return err 789 } 790 } else { 791 transport.Token = token 792 } 793 oauthClient = transport.Client() 794 return nil 795 } 796 797 func (b *Build) clean(files []string) error { 798 for _, name := range files { 799 err := os.RemoveAll(filepath.Join(b.root, name)) 800 if err != nil { 801 return err 802 } 803 } 804 return nil 805 } 806 807 func exists(path string) bool { 808 _, err := os.Stat(path) 809 return err == nil 810 } 811 812 func readCredentials() error { 813 name := os.Getenv("HOME") 814 if runtime.GOOS == "windows" { 815 name = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 816 } 817 name = filepath.Join(name, ".gobuildkey") 818 f, err := os.Open(name) 819 if err != nil { 820 return err 821 } 822 defer f.Close() 823 s := bufio.NewScanner(f) 824 if s.Scan() { 825 builderKey = s.Text() 826 } 827 return s.Err() 828 } 829 830 func cp(dst, src string) error { 831 sf, err := os.Open(src) 832 if err != nil { 833 return err 834 } 835 defer sf.Close() 836 fi, err := sf.Stat() 837 if err != nil { 838 return err 839 } 840 df, err := os.Create(dst) 841 if err != nil { 842 return err 843 } 844 defer df.Close() 845 // Windows doesn't currently implement Fchmod 846 if runtime.GOOS != "windows" { 847 if err := df.Chmod(fi.Mode()); err != nil { 848 return err 849 } 850 } 851 _, err = io.Copy(df, sf) 852 return err 853 } 854 855 func cpDir(dst, src string) error { 856 walk := func(srcPath string, info os.FileInfo, err error) error { 857 if err != nil { 858 return err 859 } 860 dstPath := filepath.Join(dst, srcPath[len(src):]) 861 if info.IsDir() { 862 return os.MkdirAll(dstPath, 0755) 863 } 864 return cp(dstPath, srcPath) 865 } 866 return filepath.Walk(src, walk) 867 } 868 869 func cpAllDir(dst, basePath string, dirs ...string) error { 870 for _, dir := range dirs { 871 if err := cpDir(filepath.Join(dst, dir), filepath.Join(basePath, dir)); err != nil { 872 return err 873 } 874 } 875 return nil 876 } 877 878 func makeTar(targ, workdir string) error { 879 f, err := os.Create(targ) 880 if err != nil { 881 return err 882 } 883 zout := gzip.NewWriter(f) 884 tw := tar.NewWriter(zout) 885 886 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 887 if !strings.HasPrefix(path, workdir) { 888 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 889 } 890 name := path[len(workdir):] 891 892 // Chop of any leading / from filename, leftover from removing workdir. 893 if strings.HasPrefix(name, "/") { 894 name = name[1:] 895 } 896 // Don't include things outside of the go subdirectory (for instance, 897 // the zip file that we're currently writing here.) 898 if !strings.HasPrefix(name, "go/") { 899 return nil 900 } 901 if *verbose { 902 log.Printf("adding to tar: %s", name) 903 } 904 target, _ := os.Readlink(path) 905 hdr, err := tar.FileInfoHeader(fi, target) 906 if err != nil { 907 return err 908 } 909 hdr.Name = name 910 hdr.Uname = "root" 911 hdr.Gname = "root" 912 hdr.Uid = 0 913 hdr.Gid = 0 914 915 // Force permissions to 0755 for executables, 0644 for everything else. 916 if fi.Mode().Perm()&0111 != 0 { 917 hdr.Mode = hdr.Mode&^0777 | 0755 918 } else { 919 hdr.Mode = hdr.Mode&^0777 | 0644 920 } 921 922 err = tw.WriteHeader(hdr) 923 if err != nil { 924 return fmt.Errorf("Error writing file %q: %v", name, err) 925 } 926 if fi.IsDir() { 927 return nil 928 } 929 r, err := os.Open(path) 930 if err != nil { 931 return err 932 } 933 defer r.Close() 934 _, err = io.Copy(tw, r) 935 return err 936 }) 937 if err != nil { 938 return err 939 } 940 if err := tw.Close(); err != nil { 941 return err 942 } 943 if err := zout.Close(); err != nil { 944 return err 945 } 946 return f.Close() 947 } 948 949 func makeZip(targ, workdir string) error { 950 f, err := os.Create(targ) 951 if err != nil { 952 return err 953 } 954 zw := zip.NewWriter(f) 955 956 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 957 if !strings.HasPrefix(path, workdir) { 958 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 959 } 960 name := path[len(workdir):] 961 962 // Convert to Unix-style named paths, as that's the 963 // type of zip file that archive/zip creates. 964 name = strings.Replace(name, "\\", "/", -1) 965 // Chop of any leading / from filename, leftover from removing workdir. 966 if strings.HasPrefix(name, "/") { 967 name = name[1:] 968 } 969 // Don't include things outside of the go subdirectory (for instance, 970 // the zip file that we're currently writing here.) 971 if !strings.HasPrefix(name, "go/") { 972 return nil 973 } 974 if *verbose { 975 log.Printf("adding to zip: %s", name) 976 } 977 fh, err := zip.FileInfoHeader(fi) 978 if err != nil { 979 return err 980 } 981 fh.Name = name 982 fh.Method = zip.Deflate 983 if fi.IsDir() { 984 fh.Name += "/" // append trailing slash 985 fh.Method = zip.Store // no need to deflate 0 byte files 986 } 987 w, err := zw.CreateHeader(fh) 988 if err != nil { 989 return err 990 } 991 if fi.IsDir() { 992 return nil 993 } 994 r, err := os.Open(path) 995 if err != nil { 996 return err 997 } 998 defer r.Close() 999 _, err = io.Copy(w, r) 1000 return err 1001 }) 1002 if err != nil { 1003 return err 1004 } 1005 if err := zw.Close(); err != nil { 1006 return err 1007 } 1008 return f.Close() 1009 } 1010 1011 type tool struct { 1012 name string 1013 commonDirs []string 1014 } 1015 1016 var wixTool = tool{ 1017 "http://wix.sourceforge.net/, version 3.5", 1018 []string{`C:\Program Files\Windows Installer XML v3.5\bin`, 1019 `C:\Program Files (x86)\Windows Installer XML v3.5\bin`}, 1020 } 1021 1022 var hgTool = tool{ 1023 "http://mercurial.selenic.com/wiki/WindowsInstall", 1024 []string{`C:\Program Files\Mercurial`, 1025 `C:\Program Files (x86)\Mercurial`, 1026 }, 1027 } 1028 1029 var gccTool = tool{ 1030 "Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/", 1031 []string{`C:\Mingw\bin`}, 1032 } 1033 1034 var windowsDeps = map[string]tool{ 1035 "gcc": gccTool, 1036 "heat": wixTool, 1037 "candle": wixTool, 1038 "light": wixTool, 1039 "cmd": {"Windows cmd.exe", nil}, 1040 "hg": hgTool, 1041 } 1042 1043 func checkWindowsDeps() { 1044 for prog, help := range windowsDeps { 1045 absPath, err := lookPath(prog) 1046 if err != nil { 1047 log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help) 1048 } 1049 if *verbose { 1050 log.Printf("found windows dep %s at %s", prog, absPath) 1051 } 1052 } 1053 } 1054 1055 func lookPath(prog string) (absPath string, err error) { 1056 absPath, err = exec.LookPath(prog) 1057 if err == nil { 1058 return 1059 } 1060 t, ok := windowsDeps[prog] 1061 if !ok { 1062 return 1063 } 1064 for _, dir := range t.commonDirs { 1065 for _, ext := range []string{"exe", "bat"} { 1066 absPath = filepath.Join(dir, prog+"."+ext) 1067 if _, err1 := os.Stat(absPath); err1 == nil { 1068 err = nil 1069 os.Setenv("PATH", os.Getenv("PATH")+";"+dir) 1070 return 1071 } 1072 } 1073 } 1074 return 1075 }