]> Cypherpunks repositories - gostls13.git/commitdiff
misc/dist: implement binary distribution scripts in go
authorAndrew Gerrand <adg@golang.org>
Thu, 1 Mar 2012 04:49:37 +0000 (15:49 +1100)
committerAndrew Gerrand <adg@golang.org>
Thu, 1 Mar 2012 04:49:37 +0000 (15:49 +1100)
R=golang-dev, r, alex.brainman, r, mike.rosset
CC=golang-dev
https://golang.org/cl/5697050

misc/dist/README [deleted file]
misc/dist/bindist.go [new file with mode: 0644]
misc/dist/darwin/README [deleted file]
misc/dist/darwin/dist.bash [deleted file]
misc/dist/linux/dist.bash [deleted file]

diff --git a/misc/dist/README b/misc/dist/README
deleted file mode 100644 (file)
index 06136c4..0000000
+++ /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 (file)
index 0000000..f307d9b
--- /dev/null
@@ -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 (file)
index 25aeb8c..0000000
+++ /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 (executable)
index adade2e..0000000
+++ /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 (executable)
index 9270782..0000000
+++ /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 <tag>'
-       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