github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/misc/dist/bindist.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, OS X, and Windows. 7 package main 8 9 import ( 10 "archive/tar" 11 "archive/zip" 12 "bufio" 13 "bytes" 14 "compress/gzip" 15 "encoding/base64" 16 "flag" 17 "fmt" 18 "io" 19 "io/ioutil" 20 "log" 21 "mime/multipart" 22 "net/http" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "regexp" 27 "runtime" 28 "strings" 29 ) 30 31 var ( 32 tag = flag.String("tag", "release", "mercurial tag to check out") 33 repo = flag.String("repo", "https://code.google.com/p/go", "repo URL") 34 verbose = flag.Bool("v", false, "verbose output") 35 upload = flag.Bool("upload", true, "upload resulting files to Google Code") 36 wxsFile = flag.String("wxs", "", "path to custom installer.wxs") 37 addLabel = flag.String("label", "", "additional label to apply to file when uploading") 38 includeRace = flag.Bool("race", true, "build race detector packages") 39 versionOverride = flag.String("version", "", "override version name") 40 41 username, password string // for Google Code upload 42 ) 43 44 const ( 45 uploadURL = "https://go.googlecode.com/files" 46 godocPath = "code.google.com/p/go.tools/cmd/godoc" 47 tourPath = "code.google.com/p/go-tour" 48 ) 49 50 var preBuildCleanFiles = []string{ 51 "lib/codereview", 52 "misc/dashboard/godashboard", 53 "src/cmd/cov", 54 "src/cmd/prof", 55 "src/pkg/exp", 56 "src/pkg/old", 57 } 58 59 var cleanFiles = []string{ 60 ".hg", 61 ".hgtags", 62 ".hgignore", 63 "VERSION.cache", 64 } 65 66 var sourceCleanFiles = []string{ 67 "bin", 68 "pkg", 69 } 70 71 var tourPackages = []string{ 72 "pic", 73 "tree", 74 "wc", 75 } 76 77 var tourContent = []string{ 78 "js", 79 "prog", 80 "solutions", 81 "static", 82 "template", 83 "tour.article", 84 } 85 86 // The os-arches that support the race toolchain. 87 var raceAvailable = []string{ 88 "darwin-amd64", 89 "linux-amd64", 90 "windows-amd64", 91 } 92 93 var fileRe = regexp.MustCompile(`^(go[a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+))\.`) 94 95 func main() { 96 flag.Usage = func() { 97 fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\n", os.Args[0]) 98 flag.PrintDefaults() 99 os.Exit(2) 100 } 101 flag.Parse() 102 if flag.NArg() == 0 { 103 flag.Usage() 104 } 105 if runtime.GOOS == "windows" { 106 checkWindowsDeps() 107 } 108 109 if *upload { 110 if err := readCredentials(); err != nil { 111 log.Println("readCredentials:", err) 112 } 113 } 114 for _, targ := range flag.Args() { 115 var b Build 116 if m := fileRe.FindStringSubmatch(targ); m != nil { 117 // targ is a file name; upload it to googlecode. 118 version := m[1] 119 if m[2] == "src" { 120 b.Source = true 121 } else { 122 b.OS = m[3] 123 b.Arch = m[4] 124 } 125 if !*upload { 126 log.Printf("%s: -upload=false, skipping", targ) 127 continue 128 } 129 if err := b.Upload(version, targ); err != nil { 130 log.Printf("%s: %v", targ, err) 131 } 132 continue 133 } 134 if targ == "source" { 135 b.Source = true 136 } else { 137 p := strings.SplitN(targ, "-", 2) 138 if len(p) != 2 { 139 log.Println("Ignoring unrecognized target:", targ) 140 continue 141 } 142 b.OS = p[0] 143 b.Arch = p[1] 144 if *includeRace { 145 for _, t := range raceAvailable { 146 if t == targ { 147 b.Race = true 148 } 149 } 150 } 151 } 152 if err := b.Do(); err != nil { 153 log.Printf("%s: %v", targ, err) 154 } 155 } 156 } 157 158 type Build struct { 159 Source bool // if true, OS and Arch must be empty 160 Race bool // build race toolchain 161 OS string 162 Arch string 163 root string 164 gopath string 165 } 166 167 func (b *Build) Do() error { 168 work, err := ioutil.TempDir("", "bindist") 169 if err != nil { 170 return err 171 } 172 defer os.RemoveAll(work) 173 b.root = filepath.Join(work, "go") 174 b.gopath = work 175 176 // Clone Go distribution and update to tag. 177 _, err = b.hgCmd(work, "clone", *repo, b.root) 178 if err != nil { 179 return err 180 } 181 _, err = b.hgCmd(b.root, "update", *tag) 182 if err != nil { 183 return err 184 } 185 186 // Remove exp and old packages. 187 if err := b.clean(preBuildCleanFiles); err != nil { 188 return err 189 } 190 191 src := filepath.Join(b.root, "src") 192 if b.Source { 193 if runtime.GOOS == "windows" { 194 log.Print("Warning: running make.bash on Windows; source builds are intended to be run on a Unix machine") 195 } 196 // Build dist tool only. 197 _, err = b.run(src, "bash", "make.bash", "--dist-tool") 198 } else { 199 // Build. 200 if b.OS == "windows" { 201 _, err = b.run(src, "cmd", "/C", "make.bat") 202 } else { 203 _, err = b.run(src, "bash", "make.bash") 204 } 205 if b.Race { 206 if err != nil { 207 return err 208 } 209 goCmd := filepath.Join(b.root, "bin", "go") 210 if b.OS == "windows" { 211 goCmd += ".exe" 212 } 213 _, err = b.run(src, goCmd, "install", "-race", "std") 214 if err != nil { 215 return err 216 } 217 // Re-install std without -race, so that we're not left 218 // with a slower, race-enabled cmd/go, etc. 219 _, err = b.run(src, goCmd, "install", "-a", "std") 220 // Re-building go command leaves old versions of go.exe as go.exe~ on windows. 221 // See (*builder).copyFile in $GOROOT/src/cmd/go/build.go for details. 222 // Remove it manually. 223 if b.OS == "windows" { 224 os.Remove(goCmd + "~") 225 } 226 } 227 if err != nil { 228 return err 229 } 230 err = b.godoc() 231 if err != nil { 232 return err 233 } 234 err = b.tour() 235 } 236 if err != nil { 237 return err 238 } 239 240 // Get version strings. 241 var ( 242 version string // "weekly.2012-03-04" 243 fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3" 244 ) 245 pat := filepath.Join(b.root, "pkg/tool/*/dist*") // trailing * for .exe 246 m, err := filepath.Glob(pat) 247 if err != nil { 248 return err 249 } 250 if len(m) == 0 { 251 return fmt.Errorf("couldn't find dist in %q", pat) 252 } 253 fullVersion, err = b.run("", m[0], "version") 254 if err != nil { 255 return err 256 } 257 fullVersion = bytes.TrimSpace(fullVersion) 258 v := bytes.SplitN(fullVersion, []byte(" "), 2) 259 version = string(v[0]) 260 if *versionOverride != "" { 261 version = *versionOverride 262 } 263 264 // Write VERSION file. 265 err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644) 266 if err != nil { 267 return err 268 } 269 270 // Clean goroot. 271 if err := b.clean(cleanFiles); err != nil { 272 return err 273 } 274 if b.Source { 275 if err := b.clean(sourceCleanFiles); err != nil { 276 return err 277 } 278 } 279 280 // Create packages. 281 base := fmt.Sprintf("%s.%s-%s", version, b.OS, b.Arch) 282 if !strings.HasPrefix(base, "go") { 283 base = "go." + base 284 } 285 var targs []string 286 switch b.OS { 287 case "linux", "freebsd", "netbsd", "": 288 // build tarball 289 targ := base 290 if b.Source { 291 targ = fmt.Sprintf("%s.src", version) 292 if !strings.HasPrefix(targ, "go") { 293 targ = "go." + targ 294 } 295 } 296 targ += ".tar.gz" 297 err = makeTar(targ, work) 298 targs = append(targs, targ) 299 case "darwin": 300 // build tarball 301 targ := base + ".tar.gz" 302 err = makeTar(targ, work) 303 targs = append(targs, targ) 304 305 // build pkg 306 // arrange work so it's laid out as the dest filesystem 307 etc := filepath.Join(b.root, "misc/dist/darwin/etc") 308 _, err = b.run(work, "cp", "-r", etc, ".") 309 if err != nil { 310 return err 311 } 312 localDir := filepath.Join(work, "usr/local") 313 err = os.MkdirAll(localDir, 0755) 314 if err != nil { 315 return err 316 } 317 _, err = b.run(work, "mv", "go", localDir) 318 if err != nil { 319 return err 320 } 321 // build package 322 pkgdest, err := ioutil.TempDir("", "pkgdest") 323 if err != nil { 324 return err 325 } 326 defer os.RemoveAll(pkgdest) 327 dist := filepath.Join(runtime.GOROOT(), "misc/dist") 328 _, err = b.run("", "pkgbuild", 329 "--identifier", "com.googlecode.go", 330 "--version", "1.0", 331 "--scripts", filepath.Join(dist, "darwin/scripts"), 332 "--root", work, 333 filepath.Join(pkgdest, "com.googlecode.go.pkg")) 334 if err != nil { 335 return err 336 } 337 targ = base + ".pkg" 338 _, err = b.run("", "productbuild", 339 "--distribution", filepath.Join(dist, "darwin/Distribution"), 340 "--resources", filepath.Join(dist, "darwin/Resources"), 341 "--package-path", pkgdest, 342 targ) 343 if err != nil { 344 return err 345 } 346 targs = append(targs, targ) 347 case "windows": 348 // Create ZIP file. 349 zip := filepath.Join(work, base+".zip") 350 err = makeZip(zip, work) 351 // Copy zip to target file. 352 targ := base + ".zip" 353 err = cp(targ, zip) 354 if err != nil { 355 return err 356 } 357 targs = append(targs, targ) 358 359 // Create MSI installer. 360 win := filepath.Join(b.root, "misc/dist/windows") 361 installer := filepath.Join(win, "installer.wxs") 362 if *wxsFile != "" { 363 installer = *wxsFile 364 } 365 appfiles := filepath.Join(work, "AppFiles.wxs") 366 msi := filepath.Join(work, "installer.msi") 367 // Gather files. 368 _, err = b.run(work, "heat", "dir", "go", 369 "-nologo", 370 "-gg", "-g1", "-srd", "-sfrag", 371 "-cg", "AppFiles", 372 "-template", "fragment", 373 "-dr", "INSTALLDIR", 374 "-var", "var.SourceDir", 375 "-out", appfiles) 376 if err != nil { 377 return err 378 } 379 // Build package. 380 _, err = b.run(work, "candle", 381 "-nologo", 382 "-dVersion="+version, 383 "-dArch="+b.Arch, 384 "-dSourceDir=go", 385 installer, appfiles) 386 if err != nil { 387 return err 388 } 389 appfiles = filepath.Join(work, "AppFiles.wixobj") 390 installer = filepath.Join(work, "installer.wixobj") 391 _, err = b.run(win, "light", 392 "-nologo", 393 "-ext", "WixUIExtension", 394 "-ext", "WixUtilExtension", 395 installer, appfiles, 396 "-o", msi) 397 if err != nil { 398 return err 399 } 400 // Copy installer to target file. 401 targ = base + ".msi" 402 err = cp(targ, msi) 403 targs = append(targs, targ) 404 } 405 if err == nil && *upload { 406 for _, targ := range targs { 407 err = b.Upload(version, targ) 408 if err != nil { 409 return err 410 } 411 } 412 } 413 return err 414 } 415 416 func (b *Build) godoc() error { 417 defer func() { 418 // Clean work files from GOPATH directory. 419 for _, d := range []string{"bin", "pkg", "src"} { 420 os.RemoveAll(filepath.Join(b.gopath, d)) 421 } 422 }() 423 424 // go get the godoc package. 425 // The go tool knows to install to $GOROOT/bin. 426 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", godocPath) 427 return err 428 } 429 430 func (b *Build) tour() error { 431 defer func() { 432 // Clean work files from GOPATH directory. 433 for _, d := range []string{"bin", "pkg", "src"} { 434 os.RemoveAll(filepath.Join(b.gopath, d)) 435 } 436 }() 437 438 // go get the gotour package. 439 _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", tourPath+"/gotour") 440 if err != nil { 441 return err 442 } 443 444 // Copy all the tour content to $GOROOT/misc/tour. 445 importPath := filepath.FromSlash(tourPath) 446 tourSrc := filepath.Join(b.gopath, "src", importPath) 447 contentDir := filepath.Join(b.root, "misc", "tour") 448 if err = cpAllDir(contentDir, tourSrc, tourContent...); err != nil { 449 return err 450 } 451 452 // Copy the tour source code so it's accessible with $GOPATH pointing to $GOROOT/misc/tour. 453 if err = cpAllDir(filepath.Join(contentDir, "src", importPath), tourSrc, tourPackages...); err != nil { 454 return err 455 } 456 457 // Copy gotour binary to tool directory as "tour"; invoked as "go tool tour". 458 return cp( 459 filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"+ext()), 460 filepath.Join(b.gopath, "bin", "gotour"+ext()), 461 ) 462 } 463 464 func ext() string { 465 if runtime.GOOS == "windows" { 466 return ".exe" 467 } 468 return "" 469 } 470 471 func (b *Build) hgCmd(dir string, args ...string) ([]byte, error) { 472 return b.run(dir, "hg", append([]string{"--config", "extensions.codereview=!"}, args...)...) 473 } 474 475 func (b *Build) run(dir, name string, args ...string) ([]byte, error) { 476 buf := new(bytes.Buffer) 477 absName, err := lookPath(name) 478 if err != nil { 479 return nil, err 480 } 481 cmd := exec.Command(absName, args...) 482 var output io.Writer = buf 483 if *verbose { 484 log.Printf("Running %q %q", absName, args) 485 output = io.MultiWriter(buf, os.Stdout) 486 } 487 cmd.Stdout = output 488 cmd.Stderr = output 489 cmd.Dir = dir 490 cmd.Env = b.env() 491 if err := cmd.Run(); err != nil { 492 fmt.Fprintf(os.Stderr, "%s", buf.Bytes()) 493 return nil, fmt.Errorf("%s %s: %v", name, strings.Join(args, " "), err) 494 } 495 return buf.Bytes(), nil 496 } 497 498 var cleanEnv = []string{ 499 "GOARCH", 500 "GOBIN", 501 "GOHOSTARCH", 502 "GOHOSTOS", 503 "GOOS", 504 "GOROOT", 505 "GOROOT_FINAL", 506 "GOPATH", 507 } 508 509 func (b *Build) env() []string { 510 env := os.Environ() 511 for i := 0; i < len(env); i++ { 512 for _, c := range cleanEnv { 513 if strings.HasPrefix(env[i], c+"=") { 514 env = append(env[:i], env[i+1:]...) 515 } 516 } 517 } 518 final := "/usr/local/go" 519 if b.OS == "windows" { 520 final = `c:\go` 521 } 522 env = append(env, 523 "GOARCH="+b.Arch, 524 "GOHOSTARCH="+b.Arch, 525 "GOHOSTOS="+b.OS, 526 "GOOS="+b.OS, 527 "GOROOT="+b.root, 528 "GOROOT_FINAL="+final, 529 "GOPATH="+b.gopath, 530 ) 531 return env 532 } 533 534 func (b *Build) Upload(version string, filename string) error { 535 // Prepare upload metadata. 536 var labels []string 537 os_, arch := b.OS, b.Arch 538 switch b.Arch { 539 case "386": 540 arch = "x86 32-bit" 541 case "amd64": 542 arch = "x86 64-bit" 543 } 544 if arch != "" { 545 labels = append(labels, "Arch-"+b.Arch) 546 } 547 var opsys, ftype string // labels 548 switch b.OS { 549 case "linux": 550 os_ = "Linux" 551 opsys = "Linux" 552 case "freebsd": 553 os_ = "FreeBSD" 554 opsys = "FreeBSD" 555 case "darwin": 556 os_ = "Mac OS X" 557 opsys = "OSX" 558 case "netbsd": 559 os_ = "NetBSD" 560 opsys = "NetBSD" 561 case "windows": 562 os_ = "Windows" 563 opsys = "Windows" 564 } 565 summary := fmt.Sprintf("%s %s (%s)", version, os_, arch) 566 switch { 567 case strings.HasSuffix(filename, ".msi"): 568 ftype = "Installer" 569 summary += " MSI installer" 570 case strings.HasSuffix(filename, ".pkg"): 571 ftype = "Installer" 572 summary += " PKG installer" 573 case strings.HasSuffix(filename, ".zip"): 574 ftype = "Archive" 575 summary += " ZIP archive" 576 case strings.HasSuffix(filename, ".tar.gz"): 577 ftype = "Archive" 578 summary += " tarball" 579 } 580 if b.Source { 581 ftype = "Source" 582 summary = fmt.Sprintf("%s (source only)", version) 583 } 584 if opsys != "" { 585 labels = append(labels, "OpSys-"+opsys) 586 } 587 if ftype != "" { 588 labels = append(labels, "Type-"+ftype) 589 } 590 if *addLabel != "" { 591 labels = append(labels, *addLabel) 592 } 593 // Put "Go" prefix on summary when it doesn't already begin with "go". 594 if !strings.HasPrefix(strings.ToLower(summary), "go") { 595 summary = "Go " + summary 596 } 597 598 // Open file to upload. 599 f, err := os.Open(filename) 600 if err != nil { 601 return err 602 } 603 defer f.Close() 604 605 // Prepare multipart payload. 606 body := new(bytes.Buffer) 607 w := multipart.NewWriter(body) 608 if err := w.WriteField("summary", summary); err != nil { 609 return err 610 } 611 for _, l := range labels { 612 if err := w.WriteField("label", l); err != nil { 613 return err 614 } 615 } 616 fw, err := w.CreateFormFile("filename", filename) 617 if err != nil { 618 return err 619 } 620 if _, err = io.Copy(fw, f); err != nil { 621 return err 622 } 623 if err := w.Close(); err != nil { 624 return err 625 } 626 627 // Send the file to Google Code. 628 req, err := http.NewRequest("POST", uploadURL, body) 629 if err != nil { 630 return err 631 } 632 token := fmt.Sprintf("%s:%s", username, password) 633 token = base64.StdEncoding.EncodeToString([]byte(token)) 634 req.Header.Set("Authorization", "Basic "+token) 635 req.Header.Set("Content-type", w.FormDataContentType()) 636 637 resp, err := http.DefaultTransport.RoundTrip(req) 638 if err != nil { 639 return err 640 } 641 if resp.StatusCode/100 != 2 { 642 fmt.Fprintln(os.Stderr, "upload failed") 643 defer resp.Body.Close() 644 io.Copy(os.Stderr, resp.Body) 645 return fmt.Errorf("upload: %s", resp.Status) 646 } 647 return nil 648 } 649 650 func (b *Build) clean(files []string) error { 651 for _, name := range files { 652 err := os.RemoveAll(filepath.Join(b.root, name)) 653 if err != nil { 654 return err 655 } 656 } 657 return nil 658 } 659 660 func exists(path string) bool { 661 _, err := os.Stat(path) 662 return err == nil 663 } 664 665 func readCredentials() error { 666 name := os.Getenv("HOME") 667 if runtime.GOOS == "windows" { 668 name = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 669 } 670 name = filepath.Join(name, ".gobuildkey") 671 f, err := os.Open(name) 672 if err != nil { 673 return err 674 } 675 defer f.Close() 676 r := bufio.NewReader(f) 677 for i := 0; i < 3; i++ { 678 b, _, err := r.ReadLine() 679 if err != nil { 680 return err 681 } 682 b = bytes.TrimSpace(b) 683 switch i { 684 case 1: 685 username = string(b) 686 case 2: 687 password = string(b) 688 } 689 } 690 return nil 691 } 692 693 func cp(dst, src string) error { 694 sf, err := os.Open(src) 695 if err != nil { 696 return err 697 } 698 defer sf.Close() 699 fi, err := sf.Stat() 700 if err != nil { 701 return err 702 } 703 df, err := os.Create(dst) 704 if err != nil { 705 return err 706 } 707 defer df.Close() 708 // Windows doesn't currently implement Fchmod 709 if runtime.GOOS != "windows" { 710 if err := df.Chmod(fi.Mode()); err != nil { 711 return err 712 } 713 } 714 _, err = io.Copy(df, sf) 715 return err 716 } 717 718 func cpDir(dst, src string) error { 719 walk := func(srcPath string, info os.FileInfo, err error) error { 720 if err != nil { 721 return err 722 } 723 dstPath := filepath.Join(dst, srcPath[len(src):]) 724 if info.IsDir() { 725 return os.MkdirAll(dstPath, 0755) 726 } 727 return cp(dstPath, srcPath) 728 } 729 return filepath.Walk(src, walk) 730 } 731 732 func cpAllDir(dst, basePath string, dirs ...string) error { 733 for _, dir := range dirs { 734 if err := cpDir(filepath.Join(dst, dir), filepath.Join(basePath, dir)); err != nil { 735 return err 736 } 737 } 738 return nil 739 } 740 741 func makeTar(targ, workdir string) error { 742 f, err := os.Create(targ) 743 if err != nil { 744 return err 745 } 746 zout := gzip.NewWriter(f) 747 tw := tar.NewWriter(zout) 748 749 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 750 if !strings.HasPrefix(path, workdir) { 751 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 752 } 753 name := path[len(workdir):] 754 755 // Chop of any leading / from filename, leftover from removing workdir. 756 if strings.HasPrefix(name, "/") { 757 name = name[1:] 758 } 759 // Don't include things outside of the go subdirectory (for instance, 760 // the zip file that we're currently writing here.) 761 if !strings.HasPrefix(name, "go/") { 762 return nil 763 } 764 if *verbose { 765 log.Printf("adding to tar: %s", name) 766 } 767 target, _ := os.Readlink(path) 768 hdr, err := tar.FileInfoHeader(fi, target) 769 if err != nil { 770 return err 771 } 772 hdr.Name = name 773 hdr.Uname = "root" 774 hdr.Gname = "root" 775 hdr.Uid = 0 776 hdr.Gid = 0 777 778 // Force permissions to 0755 for executables, 0644 for everything else. 779 if fi.Mode().Perm()&0111 != 0 { 780 hdr.Mode = hdr.Mode&^0777 | 0755 781 } else { 782 hdr.Mode = hdr.Mode&^0777 | 0644 783 } 784 785 err = tw.WriteHeader(hdr) 786 if err != nil { 787 return fmt.Errorf("Error writing file %q: %v", name, err) 788 } 789 if fi.IsDir() { 790 return nil 791 } 792 r, err := os.Open(path) 793 if err != nil { 794 return err 795 } 796 defer r.Close() 797 _, err = io.Copy(tw, r) 798 return err 799 }) 800 if err != nil { 801 return err 802 } 803 if err := tw.Close(); err != nil { 804 return err 805 } 806 if err := zout.Close(); err != nil { 807 return err 808 } 809 return f.Close() 810 } 811 812 func makeZip(targ, workdir string) error { 813 f, err := os.Create(targ) 814 if err != nil { 815 return err 816 } 817 zw := zip.NewWriter(f) 818 819 err = filepath.Walk(workdir, func(path string, fi os.FileInfo, err error) error { 820 if !strings.HasPrefix(path, workdir) { 821 log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir) 822 } 823 name := path[len(workdir):] 824 825 // Convert to Unix-style named paths, as that's the 826 // type of zip file that archive/zip creates. 827 name = strings.Replace(name, "\\", "/", -1) 828 // Chop of any leading / from filename, leftover from removing workdir. 829 if strings.HasPrefix(name, "/") { 830 name = name[1:] 831 } 832 // Don't include things outside of the go subdirectory (for instance, 833 // the zip file that we're currently writing here.) 834 if !strings.HasPrefix(name, "go/") { 835 return nil 836 } 837 if *verbose { 838 log.Printf("adding to zip: %s", name) 839 } 840 fh, err := zip.FileInfoHeader(fi) 841 if err != nil { 842 return err 843 } 844 fh.Name = name 845 fh.Method = zip.Deflate 846 if fi.IsDir() { 847 fh.Name += "/" // append trailing slash 848 fh.Method = zip.Store // no need to deflate 0 byte files 849 } 850 w, err := zw.CreateHeader(fh) 851 if err != nil { 852 return err 853 } 854 if fi.IsDir() { 855 return nil 856 } 857 r, err := os.Open(path) 858 if err != nil { 859 return err 860 } 861 defer r.Close() 862 _, err = io.Copy(w, r) 863 return err 864 }) 865 if err != nil { 866 return err 867 } 868 if err := zw.Close(); err != nil { 869 return err 870 } 871 return f.Close() 872 } 873 874 type tool struct { 875 name string 876 commonDirs []string 877 } 878 879 var wixTool = tool{ 880 "http://wix.sourceforge.net/, version 3.5", 881 []string{`C:\Program Files\Windows Installer XML v3.5\bin`, 882 `C:\Program Files (x86)\Windows Installer XML v3.5\bin`}, 883 } 884 885 var hgTool = tool{ 886 "http://mercurial.selenic.com/wiki/WindowsInstall", 887 []string{`C:\Program Files\Mercurial`, 888 `C:\Program Files (x86)\Mercurial`, 889 }, 890 } 891 892 var gccTool = tool{ 893 "Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/", 894 []string{`C:\Mingw\bin`}, 895 } 896 897 var windowsDeps = map[string]tool{ 898 "gcc": gccTool, 899 "heat": wixTool, 900 "candle": wixTool, 901 "light": wixTool, 902 "cmd": {"Windows cmd.exe", nil}, 903 "hg": hgTool, 904 } 905 906 func checkWindowsDeps() { 907 for prog, help := range windowsDeps { 908 absPath, err := lookPath(prog) 909 if err != nil { 910 log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help) 911 } 912 if *verbose { 913 log.Printf("found windows dep %s at %s", prog, absPath) 914 } 915 } 916 } 917 918 func lookPath(prog string) (absPath string, err error) { 919 absPath, err = exec.LookPath(prog) 920 if err == nil { 921 return 922 } 923 t, ok := windowsDeps[prog] 924 if !ok { 925 return 926 } 927 for _, dir := range t.commonDirs { 928 for _, ext := range []string{"exe", "bat"} { 929 absPath = filepath.Join(dir, prog+"."+ext) 930 if _, err1 := os.Stat(absPath); err1 == nil { 931 err = nil 932 os.Setenv("PATH", os.Getenv("PATH")+";"+dir) 933 return 934 } 935 } 936 } 937 return 938 }