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