From 984780a589fa46197674a6b6e56a4647954ec08a Mon Sep 17 00:00:00 2001 From: Andrew Gerrand Date: Thu, 1 Mar 2012 15:49:37 +1100 Subject: [PATCH] misc/dist: implement binary distribution scripts in go R=golang-dev, r, alex.brainman, r, mike.rosset CC=golang-dev https://golang.org/cl/5697050 --- misc/dist/README | 4 - misc/dist/bindist.go | 304 +++++++++++++++++++++++++++++++++++++ misc/dist/darwin/README | 3 - misc/dist/darwin/dist.bash | 69 --------- misc/dist/linux/dist.bash | 55 ------- 5 files changed, 304 insertions(+), 131 deletions(-) delete mode 100644 misc/dist/README create mode 100644 misc/dist/bindist.go delete mode 100644 misc/dist/darwin/README delete mode 100755 misc/dist/darwin/dist.bash delete mode 100755 misc/dist/linux/dist.bash diff --git a/misc/dist/README b/misc/dist/README deleted file mode 100644 index 06136c4c58c..00000000000 --- a/misc/dist/README +++ /dev/null @@ -1,4 +0,0 @@ -This directory contains the binary distribution packaging scripts for the -supported GOOSes. - -To build a package, run $GOOS/dist.bash. diff --git a/misc/dist/bindist.go b/misc/dist/bindist.go new file mode 100644 index 00000000000..f307d9b7682 --- /dev/null +++ b/misc/dist/bindist.go @@ -0,0 +1,304 @@ +// 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 ( + "bytes" + "encoding/base64" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "os" + "os/exec" + "path/filepath" + "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", +} + +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() + } + readCredentials() + for _, targ := range flag.Args() { + p := strings.SplitN(targ, "-", 2) + if len(p) != 2 { + log.Println("Ignoring unrecognized target:", targ) + continue + } + b := Build{OS: p[0], Arch: p[1]} + if err := b.Do(); err != nil { + log.Printf("%s: %v", targ, err) + } + } +} + +type Build struct { + 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 + } + + // Build. + _, err = b.run(filepath.Join(work, "go/src"), "bash", "make.bash") + if err != nil { + return err + } + + // Get version string. + version, err := b.run("", filepath.Join(b.root, "bin/go"), "version") + if err != nil { + return err + } + v := bytes.SplitN(version, []byte(" "), 4) + version = bytes.Join(v[2:], []byte(" ")) + + // Write VERSION file. + err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), version, 0644) + if err != nil { + return err + } + + // Clean goroot. + for _, name := range cleanFiles { + err = os.RemoveAll(filepath.Join(b.root, name)) + if err != nil { + return err + } + } + + // Create packages. + targ := fmt.Sprintf("go.%s.%s-%s", v[2], b.OS, b.Arch) + switch b.OS { + case "linux", "freebsd": + // build tarball + targ += ".tar.gz" + _, err = b.run("", "tar", "czf", targ, "-C", work, "go") + 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 += ".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") + } + if err == nil && password != "" { + err = b.upload(string(v[2]), targ) + } + 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:]...) + } + } + } + env = append(env, + "GOARCH="+b.Arch, + "GOHOSTARCH="+b.Arch, + "GOHOSTOS="+b.OS, + "GOOS="+b.OS, + "GOROOT="+b.root, + "GOROOT_FINAL=/usr/local/go", + ) + return env +} + +func (b *Build) upload(version string, filename string) error { + // Prepare upload metadata. + labels := []string{"Arch-" + b.Arch} + os_, arch := b.OS, b.Arch + switch b.Arch { + case "386": + arch = "32-bit" + case "amd64": + arch = "64-bit" + } + 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") + } + summary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch) + + // 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 exists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +func readCredentials() { + name := filepath.Join(os.Getenv("HOME"), ".gobuildkey") + c, err := ioutil.ReadFile(name) + if err != nil { + log.Println("readCredentials:", err) + return + } + v := bytes.Split(c, []byte("\n")) + if len(v) >= 3 { + username, password = string(v[1]), string(v[2]) + } +} diff --git a/misc/dist/darwin/README b/misc/dist/darwin/README deleted file mode 100644 index 25aeb8ca63a..00000000000 --- a/misc/dist/darwin/README +++ /dev/null @@ -1,3 +0,0 @@ -Use dist.bash to construct a package file (Go.pkg) for installation on OS X. - -This script depends on PackageMaker (Developer Tools). diff --git a/misc/dist/darwin/dist.bash b/misc/dist/darwin/dist.bash deleted file mode 100755 index adade2e2239..00000000000 --- a/misc/dist/darwin/dist.bash +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# Copyright 2011 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. - -set -e - -if ! test -f ../../../src/all.bash; then - echo >&2 "dist.bash must be run from $GOROOT/misc/dist/darwin" - exit 1 -fi - -echo >&2 "Locating PackageMaker..." -PM=/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker -if [ ! -x $PM ]; then - PM=/Developer$PM - if [ ! -x $PM ]; then - echo >&2 "could not find PackageMaker; aborting" - exit 1 - fi -fi -echo >&2 " Found: $PM" - -BUILD=/tmp/go.build.tmp -ROOT=`hg root` -export GOROOT=$BUILD/root/usr/local/go -export GOROOT_FINAL=/usr/local/go - -echo >&2 "Removing old images" -rm -f *.pkg *.dmg - -echo >&2 "Preparing temporary directory" -rm -rf $BUILD -mkdir -p $BUILD -trap "rm -rf $BUILD" 0 - -echo >&2 "Copying go source distribution" -mkdir -p $BUILD/root/usr/local -cp -r $ROOT $GOROOT -cp -r etc $BUILD/root/etc - -pushd $GOROOT > /dev/null - -echo >&2 "Detecting version..." -pushd src > /dev/null -./make.bash --dist-tool > /dev/null -../bin/tool/dist version > /dev/null -popd > /dev/null -mv VERSION.cache VERSION -VERSION="$(cat VERSION | awk '{ print $1 }')" -echo >&2 " Version: $VERSION" - -echo >&2 "Pruning Mercurial metadata" -rm -rf .hg .hgignore .hgtags - -echo >&2 "Building Go" -pushd src -./all.bash 2>&1 | sed "s/^/ /" >&2 -popd > /dev/null - -popd > /dev/null - -echo >&2 "Building package" -$PM -v -r $BUILD/root -o "go.darwin.$VERSION.pkg" \ - --scripts scripts \ - --id com.googlecode.go \ - --title Go \ - --version "0.1" \ - --target "10.5" diff --git a/misc/dist/linux/dist.bash b/misc/dist/linux/dist.bash deleted file mode 100755 index 9270782ad92..00000000000 --- a/misc/dist/linux/dist.bash +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -e - -TAG=$1 -if [ "$TAG" == "" ]; then - echo >&2 'usage: dist.bash ' - exit 2 -fi - -GOOS=${GOOS:-linux} -GOARCH=${GOARCH:-amd64} - -ROOT=/tmp/godist.linux.$GOARCH -rm -rf $ROOT -mkdir -p $ROOT -pushd $ROOT>/dev/null - -# clone Go distribution -echo "Preparing new GOROOT" -hg clone -q https://code.google.com/p/go go -pushd go > /dev/null -hg update $TAG - -# get version -pushd src > /dev/null -echo "Building dist tool to get VERSION" -./make.bash --dist-tool 2>&1 | sed 's/^/ /' >&2 -../bin/tool/dist version > ../VERSION -popd > /dev/null -VERSION="$(cat VERSION | awk '{ print $1 }')" -echo " Version: $VERSION" - -# remove mercurial stuff -rm -rf .hg* - -# build Go -echo "Building Go" -unset GOROOT -export GOOS -export GOARCH -export GOROOT_FINAL=/usr/local/go -pushd src > /dev/null -./all.bash 2>&1 | sed 's/^/ /' >&2 -popd > /dev/null -popd > /dev/null - -# tar it up -DEST=go.$VERSION.$GOOS-$GOARCH.tar.gz -echo "Writing tarball: $ROOT/$DEST" -tar czf $DEST go -popd > /dev/null