mirror of
https://github.com/golang/go
synced 2024-11-14 14:50:23 -07:00
08a5d73940
Updates #3254. R=golang-dev, bradfitz, alex.brainman CC=golang-dev https://golang.org/cl/5783058
474 lines
10 KiB
Go
474 lines
10 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// This is a tool for packaging binary releases.
|
|
// It supports FreeBSD, Linux, and OS X.
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/base64"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
tag = flag.String("tag", "weekly", "mercurial tag to check out")
|
|
repo = flag.String("repo", "https://code.google.com/p/go", "repo URL")
|
|
|
|
username, password string // for Google Code upload
|
|
)
|
|
|
|
const (
|
|
packageMaker = "/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker"
|
|
uploadURL = "https://go.googlecode.com/files"
|
|
)
|
|
|
|
var cleanFiles = []string{
|
|
".hg",
|
|
".hgtags",
|
|
".hgignore",
|
|
"VERSION.cache",
|
|
}
|
|
|
|
var sourceCleanFiles = []string{
|
|
"bin",
|
|
"pkg",
|
|
}
|
|
|
|
func main() {
|
|
flag.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\n", os.Args[0])
|
|
flag.PrintDefaults()
|
|
os.Exit(2)
|
|
}
|
|
flag.Parse()
|
|
if flag.NArg() == 0 {
|
|
flag.Usage()
|
|
}
|
|
if err := readCredentials(); err != nil {
|
|
log.Println("readCredentials:", err)
|
|
}
|
|
for _, targ := range flag.Args() {
|
|
var b Build
|
|
if targ == "source" {
|
|
b.Source = true
|
|
} else {
|
|
p := strings.SplitN(targ, "-", 2)
|
|
if len(p) != 2 {
|
|
log.Println("Ignoring unrecognized target:", targ)
|
|
continue
|
|
}
|
|
b.OS = p[0]
|
|
b.Arch = p[1]
|
|
}
|
|
if err := b.Do(); err != nil {
|
|
log.Printf("%s: %v", targ, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type Build struct {
|
|
Source bool // if true, OS and Arch must be empty
|
|
OS string
|
|
Arch string
|
|
root string
|
|
}
|
|
|
|
func (b *Build) Do() error {
|
|
work, err := ioutil.TempDir("", "bindist")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(work)
|
|
b.root = filepath.Join(work, "go")
|
|
|
|
// Clone Go distribution and update to tag.
|
|
_, err = b.run(work, "hg", "clone", "-q", *repo, b.root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = b.run(b.root, "hg", "update", *tag)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
src := filepath.Join(b.root, "src")
|
|
if b.Source {
|
|
// Build dist tool only.
|
|
_, err = b.run(src, "bash", "make.bash", "--dist-tool")
|
|
} else {
|
|
// Build.
|
|
if b.OS == "windows" {
|
|
_, err = b.run(src, "cmd", "/C", "make.bat")
|
|
} else {
|
|
_, err = b.run(src, "bash", "make.bash")
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get version strings.
|
|
var (
|
|
version string // "weekly.2012-03-04"
|
|
fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3"
|
|
)
|
|
pat := filepath.Join(b.root, "pkg/tool/*/dist*") // trailing * for .exe
|
|
m, err := filepath.Glob(pat)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(m) == 0 {
|
|
return fmt.Errorf("couldn't find dist in %q", pat)
|
|
}
|
|
fullVersion, err = b.run("", m[0], "version")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v := bytes.SplitN(fullVersion, []byte(" "), 2)
|
|
version = string(v[0])
|
|
|
|
// Write VERSION file.
|
|
err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Clean goroot.
|
|
if err := b.clean(cleanFiles); err != nil {
|
|
return err
|
|
}
|
|
if b.Source {
|
|
if err := b.clean(sourceCleanFiles); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Create packages.
|
|
base := fmt.Sprintf("go.%s.%s-%s", version, b.OS, b.Arch)
|
|
var targs []string
|
|
switch b.OS {
|
|
case "linux", "freebsd", "":
|
|
// build tarball
|
|
targ := base + ".tar.gz"
|
|
if b.Source {
|
|
targ = fmt.Sprintf("go.%s.src", version)
|
|
}
|
|
_, err = b.run("", "tar", "czf", targ, "-C", work, "go")
|
|
targs = append(targs, targ)
|
|
case "darwin":
|
|
// arrange work so it's laid out as the dest filesystem
|
|
etc := filepath.Join(b.root, "misc/dist/darwin/etc")
|
|
_, err = b.run(work, "cp", "-r", etc, ".")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
localDir := filepath.Join(work, "usr/local")
|
|
err = os.MkdirAll(localDir, 0744)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = b.run(work, "mv", "go", localDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// build package
|
|
pm := packageMaker
|
|
if !exists(pm) {
|
|
pm = "/Developer" + pm
|
|
if !exists(pm) {
|
|
return errors.New("couldn't find PackageMaker")
|
|
}
|
|
}
|
|
targ := base + ".pkg"
|
|
scripts := filepath.Join(work, "usr/local/go/misc/dist/darwin/scripts")
|
|
_, err = b.run("", pm, "-v",
|
|
"-r", work,
|
|
"-o", targ,
|
|
"--scripts", scripts,
|
|
"--id", "com.googlecode.go",
|
|
"--title", "Go",
|
|
"--version", "1.0",
|
|
"--target", "10.5")
|
|
targs = append(targs, targ)
|
|
case "windows":
|
|
// Create ZIP file.
|
|
zip := filepath.Join(work, base+".zip")
|
|
_, err = b.run(work, "7z", "a", "-tzip", zip, "go")
|
|
// Copy zip to target file.
|
|
targ := base + ".zip"
|
|
err = cp(targ, zip)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
targs = append(targs, targ)
|
|
|
|
// Create MSI installer.
|
|
win := filepath.Join(b.root, "misc/dist/windows")
|
|
installer := filepath.Join(win, "installer.wxs")
|
|
appfiles := filepath.Join(work, "AppFiles.wxs")
|
|
msi := filepath.Join(work, "installer.msi")
|
|
// Gather files.
|
|
_, err = b.run(work, "heat", "dir", "go",
|
|
"-nologo",
|
|
"-gg", "-g1", "-srd", "-sfrag",
|
|
"-cg", "AppFiles",
|
|
"-template", "fragment",
|
|
"-dr", "INSTALLDIR",
|
|
"-var", "var.SourceDir",
|
|
"-out", appfiles)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Build package.
|
|
_, err = b.run(work, "candle",
|
|
"-nologo",
|
|
"-dVersion="+version,
|
|
"-dArch="+b.Arch,
|
|
"-dSourceDir=go",
|
|
installer, appfiles)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
appfiles = filepath.Join(work, "AppFiles.wixobj")
|
|
installer = filepath.Join(work, "installer.wixobj")
|
|
_, err = b.run(win, "light",
|
|
"-nologo",
|
|
"-ext", "WixUIExtension",
|
|
"-ext", "WixUtilExtension",
|
|
installer, appfiles,
|
|
"-o", msi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Copy installer to target file.
|
|
targ = base + ".msi"
|
|
err = cp(targ, msi)
|
|
targs = append(targs, targ)
|
|
}
|
|
if err == nil && password != "" {
|
|
for _, targ := range targs {
|
|
err = b.upload(version, targ)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (b *Build) run(dir, name string, args ...string) ([]byte, error) {
|
|
buf := new(bytes.Buffer)
|
|
cmd := exec.Command(name, args...)
|
|
cmd.Stdout = buf
|
|
cmd.Stderr = buf
|
|
cmd.Dir = dir
|
|
cmd.Env = b.env()
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "%s", buf.Bytes())
|
|
return nil, fmt.Errorf("%s %s: %v", name, strings.Join(args, " "), err)
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
var cleanEnv = []string{
|
|
"GOARCH",
|
|
"GOBIN",
|
|
"GOHOSTARCH",
|
|
"GOHOSTOS",
|
|
"GOOS",
|
|
"GOROOT",
|
|
"GOROOT_FINAL",
|
|
}
|
|
|
|
func (b *Build) env() []string {
|
|
env := os.Environ()
|
|
for i := 0; i < len(env); i++ {
|
|
for _, c := range cleanEnv {
|
|
if strings.HasPrefix(env[i], c+"=") {
|
|
env = append(env[:i], env[i+1:]...)
|
|
}
|
|
}
|
|
}
|
|
final := "/usr/local/go"
|
|
if b.OS == "windows" {
|
|
final = `c:\go`
|
|
}
|
|
env = append(env,
|
|
"GOARCH="+b.Arch,
|
|
"GOHOSTARCH="+b.Arch,
|
|
"GOHOSTOS="+b.OS,
|
|
"GOOS="+b.OS,
|
|
"GOROOT="+b.root,
|
|
"GOROOT_FINAL="+final,
|
|
)
|
|
return env
|
|
}
|
|
|
|
func (b *Build) upload(version string, filename string) error {
|
|
// Prepare upload metadata.
|
|
var labels []string
|
|
os_, arch := b.OS, b.Arch
|
|
switch b.Arch {
|
|
case "386":
|
|
arch = "32-bit"
|
|
case "amd64":
|
|
arch = "64-bit"
|
|
}
|
|
if arch != "" {
|
|
labels = append(labels, "Arch-"+b.Arch)
|
|
}
|
|
switch b.OS {
|
|
case "linux":
|
|
os_ = "Linux"
|
|
labels = append(labels, "Type-Archive", "OpSys-Linux")
|
|
case "freebsd":
|
|
os_ = "FreeBSD"
|
|
labels = append(labels, "Type-Archive", "OpSys-FreeBSD")
|
|
case "darwin":
|
|
os_ = "Mac OS X"
|
|
labels = append(labels, "Type-Installer", "OpSys-OSX")
|
|
case "windows":
|
|
os_ = "Windows"
|
|
labels = append(labels, "OpSys-Windows")
|
|
}
|
|
summary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch)
|
|
if b.OS == "windows" {
|
|
switch {
|
|
case strings.HasSuffix(filename, ".msi"):
|
|
labels = append(labels, "Type-Installer")
|
|
summary += " MSI installer"
|
|
case strings.HasSuffix(filename, ".zip"):
|
|
labels = append(labels, "Type-Archive")
|
|
summary += " ZIP archive"
|
|
}
|
|
}
|
|
if b.Source {
|
|
labels = append(labels, "Type-Source")
|
|
summary = fmt.Sprintf("Go %s (source only)", version)
|
|
}
|
|
|
|
// Open file to upload.
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Prepare multipart payload.
|
|
body := new(bytes.Buffer)
|
|
w := multipart.NewWriter(body)
|
|
if err := w.WriteField("summary", summary); err != nil {
|
|
return err
|
|
}
|
|
for _, l := range labels {
|
|
if err := w.WriteField("label", l); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
fw, err := w.CreateFormFile("filename", filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = io.Copy(fw, f); err != nil {
|
|
return err
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Send the file to Google Code.
|
|
req, err := http.NewRequest("POST", uploadURL, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
token := fmt.Sprintf("%s:%s", username, password)
|
|
token = base64.StdEncoding.EncodeToString([]byte(token))
|
|
req.Header.Set("Authorization", "Basic "+token)
|
|
req.Header.Set("Content-type", w.FormDataContentType())
|
|
|
|
resp, err := http.DefaultTransport.RoundTrip(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.StatusCode/100 != 2 {
|
|
fmt.Fprintln(os.Stderr, "upload failed")
|
|
defer resp.Body.Close()
|
|
io.Copy(os.Stderr, resp.Body)
|
|
return fmt.Errorf("upload: %s", resp.Status)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Build) clean(files []string) error {
|
|
for _, name := range files {
|
|
err := os.RemoveAll(filepath.Join(b.root, name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func exists(path string) bool {
|
|
_, err := os.Stat(path)
|
|
return err == nil
|
|
}
|
|
|
|
func readCredentials() error {
|
|
name := os.Getenv("HOME")
|
|
if runtime.GOOS == "windows" {
|
|
name = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
|
}
|
|
name = filepath.Join(name, ".gobuildkey")
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
r := bufio.NewReader(f)
|
|
for i := 0; i < 3; i++ {
|
|
b, _, err := r.ReadLine()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b = bytes.TrimSpace(b)
|
|
switch i {
|
|
case 1:
|
|
username = string(b)
|
|
case 2:
|
|
password = string(b)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cp(dst, src string) error {
|
|
sf, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer sf.Close()
|
|
df, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer df.Close()
|
|
_, err = io.Copy(df, sf)
|
|
return err
|
|
}
|