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