Commits

Yoshifumi YAMAGUCHI  committed c97ebba Merge

Merge branch 'master' of ssh://bitbucket.org/ymotongpoo/goenv

  • Participants
  • Parent commits 58d62ff, 73b466e

Comments (0)

Files changed (10)

 2. Install
 ==========
 
+2.1 Fast installation
+---------------------
+
+If you are already using Go and have exising GOROOT with built Go binaries and you do not need to install Go binaries with goof command, install genv with fast-install.sh.
+
 To install ``genv``, get all code for it and put built program into appropriate directory::
 
    $ git clone https://bitbucket.org/ymotongpoo/genv
    $ cd genv
-   $ go build -o genv main.go
+   $ go build -o genv *.go
    $ chmod +x genv
    $ cp genv /path/to/bindir
 
 or you can run a following command for procedure all above::
 
    $ export GENVTARGET=/path/to/install/target
-   $ curl -L https://bitbucket.org/ymotongpoo/genv/raw/master/install.sh | sh
+   $ curl -L https://bitbucket.org/ymotongpoo/genv/raw/master/shellscipts/fast-install.sh | bash
+
+
+2.2 Go initial installation
+---------------------------
+
+If you're fine with installing Go from the source code and genv at the same time, use initial-install.sh::
+
+   $ curl -L https://bitbucket.org/ymotongpoo/genv/raw/master/shellscipts/initial-install.sh | bash
+
+You have to set following variables beforehand:
+
+* ``GENVGOROOT``: Go binary install target. All Go binaries are installed under this directory. 
+* ``GENVTARGET``: genv install target. genv and genvwrapper.sh are installed here.
+* ``GENVHOME``: All workspaces created with ``goof`` command are here.
 
 
 3. Usage
 ========
 
+3.1 Basics
+----------
+
 ``genv`` is quite simple tool and usage is as following::
 
-   genv [-go GOROOT] [-deps <additional GOPATH>] envname
+   genv [-go GOROOT] [-deps <additional GOPATH>] [-gae] envname
+   genv -freeze
+   genv -install filename
+   genv -version
 
 ``-go`` option is used when we need to use different Go binary.
 
    (go:foo) % deactivate
    % 
 
-4. Support tool
-===============
+``-freeze`` option is used to confirm external packages that Go sources under current ``GOPATH`` are depending upon, like this::
+
+   (go:foo) % genv -freeze
+   bitbucket.org/zombiezen/gopdf/pdf
+   code.google.com/p/draw2d/draw2d
+   code.google.com/p/draw2d/postscript
+   code.google.com/p/draw2d/wingui
+   code.google.com/p/freetype-go/freetype
+   ...
+
+Outputs from ``genv -freeze`` can be utilized in ``genv -install`` command, which enables bulk ``go get`` command::
+
+   (go:foo) % genv -freeze > requirements.txt
+   (go:foo) % genv -install requirements.txt
+   [1/12] installing 'bitbucket.org/zombiezen/gopdf/pdf' ... done.
+   [2/12] installing 'code.google.com/p/draw2d/draw2d' ... done.
+   [3/12] installing 'code.google.com/p/draw2d/postscript' ... already installed.
+   [4/12] installing 'code.google.com/p/draw2d/wingui' ... already installed.
+   [5/12] installing 'code.google.com/p/freetype-go/freetype' ... already installed.
+   ...
+
+3.2 GAE/Go support
+------------------
+
+``genv`` also supports creating GAE/Go enrionments. With ``-gae`` option, it generates ``app.yaml`` and initial Go file for GAE/Go as well as ``activate`` script.
+For instance, if you create a workspace ``gaetest`` with this option, directory sructure of a generate workspace would be::
+
+   % genv -gae gaetest
+   % tree gaetest 
+   gaetest
+   ├── activate
+   └── src
+          ├── app.yaml
+          └── gaetest.go
+
+
+4. Support tool (``goof``)
+==========================
+
+4.1 Basics
+----------
 
-To enhance the power of ``genv``, genvwrapper.sh is quite useful for managing Go environment.
+``goof`` -- genv wrapper for goofy gophers -- command is provided from genvwrapper.sh to enhance the power of ``genv``.
 install.sh will put genvwrapper.sh in the same directory as ``genv`` command is installed.
-To use genvwrapper.sh, load functions there with ``source`` command or add following line in your .bashrc::
+To use ``goof`` command, load functions there with ``source`` command or add following line in your .bashrc::
 
    export GENVHOME=/path/to/environment_root
    source /path/to/genvwrapper.sh
 
-genvwrapper.sh offers 4 commands.
+``goof`` command has follwing 5 subcommands.
 
-``mkgenv`` creates environment with specified environment name under $GENVHOME::
+``make`` creates environment with specified environment name under $GENVHOME::
 
-   % mkgenv foo
+   % goof make foo
    (go:foo) % 
 
-``showgenv`` shows all existing environments. Environment with '*' mark is current environment::
+``show`` shows all existing environments. Environment with '*' mark is current environment::
 
-   (go:foo) % showgenv
+   (go:foo) % goof show
    bar
    foo *
 
-``gworkon`` switches current enviroment to speficied environment::
+``workon`` switches current enviroment to speficied environment::
 
-   (go:foo) % gworkon bar
+   (go:foo) % goof workon bar
    (go:bar) %
    bar *
    foo
 
 To delete unnecessary environment, run ``rmgenv`` with the environment name::
 
-   (go:bar) % rmgenv foo
+   (go:bar) % goof remove foo
    bar *
 
+``freeze`` is just a alias for ``genv -freeze``::
+
+   (go:bar) % goof freeze
+   bitbucket.org/zombiezen/gopdf/pdf
+   code.google.com/p/draw2d/draw2d
+   code.google.com/p/draw2d/postscript
+   code.google.com/p/draw2d/wingui
+   code.google.com/p/freetype-go/freetype
+   ...
+
+
+4.2 Go install support
+----------------------
+
+From genv version 0.4.0, goof supports installing Go itself. In order to ``goinstall`` subcommand, set ``GENVGOROOT`` environment variable::
+
+   % export GENVGOROOT=/opt/genv
+
+``goinstall`` subcommand works with tag of the Go repository, such as "go1.0.3", "release.r60" and "weekly.2012-01-27".
+For first call of ``goof goinstall``, it fetches all source from Go repository into $GENVGOROOT::
+
+   % goof goinstall go1.0.3
+   [goof] Go source code initial checkout.
+   [goof] 'go1.0.3' is installed in /opt/genv/go1.0.3
+
+All installed Go binaries are shown with ``goinstall`` subcommand without arguments::
+
+   % goof goinstall
+     /opt/genv/go
+     /opt/genv/go1.0.2
+   * /opt/genv/go1.0.3
+
+You can also uninstall Go binaries with ``-u`` option before version number::
+
+   % goof goinstall -u go1.0.2
+   [goof] uninstalled version 'go1.0.2'
+
+When you need to confirm all tags, give "tags" as an argument::
+
+   % goof goinstall tags
+   tip                            15392:7bc28d72d49c
+   release                        13677:2d8bc3c94ecb
+   go1.0.3                        13677:2d8bc3c94ecb
+   go1.0.2                        13230:5e806355a9e1
+   go1.0.1                        12994:2ccfd4b451d3
+   go1                            12872:920e9d1ffd1f
+   ...
+
+``goof go`` replaces current GOROOT with specified Go version::
+
+   % goof go go1.0.2
+   % goof goinstall
+     /opt/genv/go
+     /opt/genv/go1.0.2
+   * /opt/genv/go1.0.3
+
+
+
 5. Contribution
 ===============
 
 ==========
 
 ``genv`` is an open source project with BSD-style license. See ``LICENSE`` file for details.
-
+// Copyright 2012 Yoshifumi Yamaguchi, All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package main
+
+import (
+	"bytes"
+	"os/exec"
+	"strings"
+)
+
+const goListTemplate = `'{{ range $pkg := .Deps}}{{ printf "%s\n" $pkg }}{{ end }}'`
+
+// envDependency shows all depending external packages.
+func envDependency(paths []string) ([]string, error) {
+	imports := []string{}
+	for _, p := range paths {
+		cmd := exec.Command("go", "list", "-f", goListTemplate, p)
+		out, err := cmd.Output()
+		if err != nil {
+			return nil, err
+		}
+		data := bytes.NewBuffer(out).String()
+		lines := strings.Split(data, "\n")
+		if err != nil {
+			return nil, err
+		}
+		for _, l := range lines {
+			if hasURL(l) {
+				imports = append(imports, l)
+			}
+		}
+	}
+	return imports, nil
+}
+
+
+// hasURL returns package name and whether the package is external or not.
+func hasURL(pkg string) bool {
+	topDir := strings.Split(pkg, "/")[0]
+	if strings.Contains(topDir, ".") || strings.HasPrefix(topDir, "localhost") {
+		return true
+	}
+	return false
+}

File genvwrapper.sh

-#!/bin/sh
-# Copyright 2012 Yoshifumi Yamaguchi, All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-#
-#
-# genvwrapper.sh -- genv utility wrapper functions
-#
-# To use genvwrapper.sh, add following line in your .bashrc:
-#
-#   source /path/to/genvwrapper.sh
-#
-# install.sh installs this script into the same install target as 'genv'.
-# Default is '/usr/local/bin'.
-
-if [ -z "$GENVHOME" ]; then
-  echo "please set environment variable 'GENVHOME' to use genvwrapper!"
-  return 0
-fi
-if [ ! -x "$(which genv)" ]; then
-  echo "please put binary 'genv' under PATH to use genvwrapper!"
-  return 0
-fi
-
-declare -x GENVHOME
-
-typeset -f showgenv
-typeset -f mkgenv
-typeset -f rmgenv
-typeset -f gworkon
-
-function showgenv() {
-  for dir in "$GENVHOME"/*; do
-    if [ -d "$dir" ]; then
-      local envname=${dir##*/}
-	  local prefix="  "
-      if [ "$envname" = "$GENVNAME" ]; then
-	    prefix="* "
-      fi
-      echo "$prefix$envname"
-      unset envname
-	  unset prefix
-    fi
-  done
-}
-
-function mkgenv() {
-  if [ -z "$1" ]; then
-    echo "usage: mkgenv envname"
-    return 1
-  fi
-
-  local envhome
-  local args
-  until [ -z "$1" ]; do
-    case "$1" in
-      "-go" | "--go" | "-deps" | "--deps")
-        args="$args $1 $2"
-        shift 2
-        ;;
-      *)
-        envhome="$GENVHOME/$1"
-        shift
-        ;;
-    esac
-  done
-  eval "genv $args $envhome"
-  if [ -f "$envhome/activate" ]; then
-    source "$envhome/activate"
-  fi
-  cd "$envhome"
-  return 0
-}
-
-function rmgenv() {
-  if [ -z "$1" ]; then
-    echo "usage: rmgenv envname"
-    return 0
-  fi
-
-  local envhome="$GENVHOME/$1"
-  if [ -d "$envhome" ]; then
-    if [ "$GENVNAME" = "$1" ]; then
-      deactivate
-    fi
-
-    if [ "$PWD" = "$envhome" ]; then
-      cd "$GENVHOME"
-    fi
-    rm -rf "$envhome"
-  else
-    echo "confirm 'GENVHOME' or envname"
-    echo "GENVHOME=$GENVHOME"
-    showgenv
-    return 1
-  fi
-  return 0
-}
-
-function gworkon() {
-  if [ -z "$1" ]; then
-    showgenv
-    return 0
-  fi
-
-  if [ -n "$1" ]; then
-    if [ -f "$GENVHOME/$1/activate" ]; then
-      source "$GENVHOME/$1/activate"
-      cd "$GENVHOME/$1"
-    fi
-  fi
-  return 0
-}
+// Copyright 2012 Yoshifumi Yamaguchi, All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io"
+	"os"
+	"os/exec"
+	"path/filepath"
+)
+
+// readLines returns strings separated by newline character.
+func readLines(r io.Reader) ([]string, error) {
+	var (
+		err    error
+		part   []byte
+		prefix bool
+	)
+	reader := bufio.NewReader(r)
+	buffer := bytes.NewBuffer(make([]byte, 0))
+	lines := []string{}
+	for {
+		if part, prefix, err = reader.ReadLine(); err != nil {
+			break
+		}
+		buffer.Write(part)
+		if !prefix {
+			lines = append(lines, buffer.String())
+			buffer.Reset()
+		}
+	}
+	if err == io.EOF {
+		err = nil
+	} else {
+		return nil, err
+	}
+	return lines, nil
+}
+
+
+// bulkGet kicks 'go get' for each packages in data in r.
+// each line should only have package name.
+func bulkGet(r io.Reader) error {
+	lines, err := readLines(r)
+	if err != nil {
+		return err
+	}
+
+	gopath := os.Getenv("GOPATH")
+	total := len(lines)
+	errors := []struct {
+		Err     error
+		Package string
+	}{}
+
+	for i, l := range lines {
+		// Confirm if given packages is installed already
+		fmt.Printf("[%d/%d] installing '%s' ... ", i+1, total, l)
+		for _, dir := range filepath.SplitList(gopath) {
+			sourceDir := filepath.Join(dir, "src")
+			err = doExists(filepath.Join(sourceDir, l))
+			if err == nil {
+				fmt.Printf("already installed.\n")
+				continue
+			} else {
+				// Run `go get` command
+				cmd := exec.Command("go", "get", l)
+				err = cmd.Run()
+				if err != nil {
+					e := struct {
+						Err     error
+						Package string
+					}{
+						Err:     err,
+						Package: l,
+					}
+					errors = append(errors, e)
+					fmt.Printf("error.\n")
+				} else {
+					fmt.Printf("done.\n")
+				}
+			}
+		}
+	}
+
+	// Show all errors during bulk installation
+	if len(errors) > 0 {
+		fmt.Println("check following packages can be get by 'go get'")
+		for _, e := range errors {
+			fmt.Printf("%s : %s\n", e.Package, e.Err)
+		}
+	}
+	return nil
+}

File install.sh

-#!/bin/sh
-#
-# Environment variable GENVTARGET is install target of `genv`
-# Default install target i /usr/local/bin.
-
-git clone https://bitbucket.org/ymotongpoo/genv.git
-cd genv
-go build -o genv main.go
-chmod +x genv
-
-if [ -z $GENVTARGET ]; then
-  target="/usr/local/bin"
-elif [ -d "$GENVTARGET" ]; then
-  target="$GENVTARGET"
-else
-  echo "no such a directory: $GENVTARGET"
-  exit 1
-fi
-
-cp genv "$target"
-
-
 import (
 	"flag"
 	"fmt"
+	"log"
 	"os"
 	"path/filepath"
 	"strings"
 	"text/template"
 )
 
-const Version = "0.2.5"
+const Version = "0.4.1"
 
 var (
 	version bool
 	goroot  string
 	deps    string
+	gae     bool
+	freeze  bool
+	install string
 )
 
-const activateTemplateText = `#!/bin/sh
-function deactivate() {
-  if [ -n "$GENVNAME" ]; then
-    unset GENVNAME
-  fi
-
-  if [ -n "$_OLD_GOROOT" ]; then
-    export GOROOT="$_OLD_GOROOT"
-    unset _OLD_GOROOT
-  fi
-
-  if [ -n "$_OLD_PS1" ]; then
-    export PS1="$_OLD_PS1"
-    unset _OLD_PS1
-  fi
-
-  export GOPATH="$_OLD_GOPATH"
- 
-  if [ "$1" != "init" ]; then
-    unset -f deactivate
-  fi
-}
-
-deactivate init
-
-export _OLD_GOPATH="$GOPATH"
-export _OLD_GOROOT="$GOROOT"
-export _OLD_PS1="$PS1"
-export GENVNAME="{{.ENVNAME}}"
-export PS1="{{.PS1}}"
-export GOPATH={{.GOPATH}}
-{{if .GOROOT}}export GOROOT={{.GOROOT}}{{end}}
-`
-
-var activateTemplate = template.Must(template.New("activate").Parse(activateTemplateText))
-
+// usage shows genv command usage and command options.
 func usage() {
 	versionText := "genv -- Go language environment manager (ver. " + Version + ")"
 	usageText := `
-usage: genv [options] envname
+usage: genv [-go <GOROOT>] [-deps <additonal GOPATH>] path
+       genv -freeze
+       genv -install <requrements file>
        genv -version
 
 options:
-  -go: specify GOROOT
-  -deps: add dependent package paths in GOPATH
+  -go:		specify GOROOT
+  -deps:	add dependent package paths in GOPATH
+  -freeze:	show a list of external pacakges imported in Go sources under GOPATH
+  -install:	bulk install external packages listed in requrements file
 `
 	fmt.Println(versionText + usageText)
 }
 
+// doExists confirms whether provided directory exists or not.
 func doExists(dir string) error {
 	_, err := os.Stat(dir)
 	if err != nil {
 	return nil
 }
 
+// ynQuestion put yes/no prompt on the terminal.
 func ynQuestion(q string) bool {
 	fmt.Printf("%s", q)
 	var input string
 	return false
 }
 
-func createScript(envhome string, scriptName string, t *template.Template, p interface{}) (*os.File, error) {
-	scriptPath := filepath.Join(envhome, scriptName)
+// createScript generate script with template text and a struct inside envhome.
+func createScript(scriptPath string, t *template.Template, p interface{}) (*os.File, error) {
 	file, err := os.Create(scriptPath)
 	if err != nil {
 		return nil, err
 	return file, nil
 }
 
+
+func createGAEFiles(envpath, envname, filename string, t *template.Template) error {
+	parameters := struct{
+		EnvName string
+	}{
+		EnvName: envname,
+	}
+	scriptPath := filepath.Join(filepath.Join(envpath, "src"), filename)
+	_, err := createScript(scriptPath, t, parameters)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// makeEnv creates `src` directory inside envpath and generate an `activate` script
+// under envhome.
 func makeEnv(envpath, goroot, deps string) (string, error) {
 	var err error
 	if goroot != "" {
 		}
 	}
 
+	// make envhome dir
 	var absEnvPath string
 	trimedPath := strings.TrimLeft(envpath, " ")
 	if filepath.IsAbs(trimedPath) {
 		gopath += ":" + deps
 	}
 
+	// make `src` directory under envhome
 	sourceDir := filepath.Join(absEnvPath, "src")
 	if err := os.MkdirAll(sourceDir, os.FileMode(0775)); err != nil {
 		return "", err
 	}
 
+	// generate `activate` script
 	parameters := &struct {
 		PS1     string
 		GOPATH  string
 		parameters.GOROOT = goroot
 	}
 
-	_, err = createScript(absEnvPath, "activate", activateTemplate, parameters)
+	scriptPath := filepath.Join(absEnvPath, "activate")
+	_, err = createScript(scriptPath , activateTemplate, parameters)
 	if err != nil {
-		return "", nil
+		return "", err
+	}
+
+	// GAE/Go
+	if gae {
+		err = createGAEFiles(absEnvPath, envname, "app.yaml", appYamlTemplate)
+		err = createGAEFiles(absEnvPath, envname, envname+".go", appGoTemplate)
+		if err != nil {
+			return "", err
+		}
 	}
 
 	return absEnvPath, nil
 }
 
 func init() {
-	flag.BoolVar(&version, "version", false, "show version")
-	flag.StringVar(&goroot, "go", "", "specify GOROOT")
-	flag.StringVar(&deps, "deps", "", "add dependent package paths in GOPATH")
+	flag.BoolVar(&version, "version", false, "Show version")
+	flag.StringVar(&goroot, "go", "", "Specify GOROOT")
+	flag.StringVar(&deps, "deps", "", "Add dependent package paths in GOPATH")
+	flag.BoolVar(&gae, "gae", false, "Create Google App Engine for Go workspace")
+	flag.BoolVar(&freeze, "freeze", false, "show all imported packages in GOPATH")
+	flag.StringVar(&install, "install", "", "assing requirements.txt for bulk 'go get'")
 }
 
 func main() {
 		return
 	}
 
+	// freeze command
+	if freeze {
+		var paths []string
+		// If no argument given, genv recognize GOPATH/src as target dirs.
+		// Otherwise, arguments are target dirs.
+		if flag.NArg() == 0 {
+			gopath := os.Getenv("GOPATH")
+			for _, p := range filepath.SplitList(gopath) {
+				paths = append(paths, filepath.Join(p, "src"))
+			}
+		} else { 
+			for i := 0; i < flag.NArg(); i++ {
+				paths = append(paths, flag.Arg(i))
+			}
+		}
+
+		imports, err := envDependency(paths)
+		if err != nil {
+			log.Printf("[genv] confirm given paths has importable go files: %v", paths)
+			log.Fatalf("[genv] %v\n", err)
+		}
+
+		for _, p := range imports {
+			fmt.Println(p)
+		}
+		return
+	}
+
+	// install command
+	if len(install) > 0 {
+		file, err := os.Open(install)
+		if err != nil {
+			log.Fatalf("[genv] %v\n", err)
+		}
+		defer file.Close()
+		err = bulkGet(file)
+		if err != nil {
+			log.Fatalf("[genv] %v\n", err)
+		}
+		return
+	}
+
+	// normal genv command
 	if flag.NArg() == 0 {
 		fmt.Println("Error: assign envname for genv\n")
 		usage()
 
 	envpath, err := makeEnv(flag.Arg(0), goroot, deps)
 	if err != nil {
-		fmt.Printf("%v\n", err)
+		log.Fatalf("[genv] %v\n", err)
 	}
 	if len(envpath) > 0 {
 		fmt.Printf("Environment %s created!\n", envpath)

File shellscripts/fast-install.sh

+#!/bin/sh
+#
+# Environment variable GENVTARGET is install target of `genv`
+# Default install target i /usr/local/bin.
+
+if ! type git 2>&1 > /dev/null; then
+  echo "[genv] To get genv, install git"
+  exit 0
+fi
+git clone https://bitbucket.org/ymotongpoo/genv.git
+cd genv
+go build -o genv *.go
+chmod +x genv
+
+if [ -z $GENVTARGET ]; then
+  target="/usr/local/bin"
+elif [ -d "$GENVTARGET" ]; then
+  target="$GENVTARGET"
+else
+  echo "no such a directory: $GENVTARGET"
+  exit 0
+fi
+
+cp genv "$target"
+
+

File shellscripts/genvwrapper.sh

+#!/bin/sh
+# Copyright 2012 Yoshifumi Yamaguchi, All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+#
+#
+# genvwrapper.sh -- genv utility wrapper functions
+#
+# This script provides "goof" command, which is army knife command set for genv.
+#
+# To use "goof" command, add following line in your .bashrc:
+#
+#   source /path/to/genvwrapper.sh
+#
+# install.sh installs this script into the same install target as 'genv'.
+# Default is '/usr/local/bin'.
+
+declare -x GENVHOME
+
+# main command
+typeset -f goof
+
+# private functions
+typeset -f _goof_show
+typeset -f _goof_make
+typeset -f _goof_remove
+typeset -f _goof_workon
+typeset -f _goof_freeze
+typeset -f _goof_install
+typeset -f _goof_showgo
+typeset -f _goof_goinstall
+typeset -f _goof_go
+typeset -f _goof_usage
+typeset -f _goof_validator
+
+function goof() {
+  case "$1" in
+    "show")
+      shift
+      _goof_show
+      ;;
+    "workon")
+      shift
+      _goof_workon $*
+      ;;
+    "make")
+      shift
+      _goof_make $*
+      ;;
+    "freeze")
+      shift
+      _goof_freeze
+      ;;
+    "install")
+      shift
+      _goof_install $*
+      ;;
+    "remove")
+      shift
+      _goof_remove $*
+      ;;
+    "goinstall")
+      shift
+      _goof_goinstall $*
+      ;;
+    "go")
+      shift
+      _goof_go $*
+      ;;
+    *)
+      _goof_usage $*
+      ;;
+  esac
+}
+
+function _goof_usage() {
+  cat <<"EOF"
+goof -- genv wrapper for goofy gophers
+
+usage: goof make [genv options] <envname>
+       goof workon <envname>
+       goof remove <envname>
+       goof show
+       goof freeze
+       goof install <filename>
+       goof goinstall [-u] <version>
+       goof go [version]
+
+a list of goof commands:
+ make		Create a new Go workspace
+ workon		Switch the environment to specified workspace
+ remove		Delete a specified workspace
+ show		Show a list of existing workspaces
+ freeze		Show all depending external packages in all Go sources under directories in GOPATH
+ install	Bulk 'go get' packages listed in given text file into GOPATH/src
+ goinstall	Build and install Go binary of given version
+ go			Replace GOROOT with specified Go version binaries
+EOF
+  return 0
+}
+
+
+# goof_validator checks all requred enviroment variables
+function _goof_varidator() {
+  if [ -z "$GENVHOME" ]; then
+    echo "please set environment variable 'GENVHOME' to use genvwrapper!"
+    return 1
+  fi
+  if [ ! -x "$(which genv)" ]; then
+    echo "please put binary 'genv' under PATH to use genvwrapper!"
+    return 1
+  fi
+  return 0
+}
+
+
+# goof_show returns all environment under $GENVHOME
+# TODO(ymotongpoo): check existance of `activate` script.
+function _goof_show() {
+  if _goof_varidator; then
+    return 1
+  fi
+  for dir in "$GENVHOME"/*; do
+    if [ -d "$dir" ]; then
+      local envname=${dir##*/}
+      local prefix="  "
+      if [ "$envname" = "$GENVNAME" ]; then
+        prefix="* "
+      fi
+      echo "$prefix$envname"
+      unset envname
+      unset prefix
+    fi
+  done
+  return 0
+}
+
+# goof_make is short of normal 'genv' command.
+# all options are passed to genv, but a target directory is
+# going to be under $GENVHOME.
+function _goof_make() {
+  if [ -z "$1" ]; then
+    _goof_usage
+    return 1
+  fi
+
+  if _goof_varidator; then
+    return 1
+  fi
+
+  local envhome
+  local args
+  until [ -z "$1" ]; do
+    case "$1" in
+      "-go" | "--go")
+        if [ -n "$GENVGOROOT" ]; then
+          if [ -d "$GENVGOROOT/$2" ]; then
+            args="$args $1 $GENVGOROOT/$2"
+            shift 2
+          else
+            echo "[goof] $GENVGOROOT/$2 does not exist."
+            return 1
+          fi
+        else
+          args="$args $1 $2"
+          shift 2
+        fi
+        ;;
+      "-deps" | "--deps")
+        args="$args $1 $2"
+        shift 2
+        ;;
+      "-gae")
+        args="$args -gae"
+        shift
+        ;;
+      *)
+        envhome="$GENVHOME/$1"
+        shift
+        ;;
+    esac
+  done
+  eval "genv $args $envhome"
+  if [ -f "$envhome/activate" ]; then
+    source "$envhome/activate"
+  fi
+  cd "$envhome"
+  return 0
+}
+
+# goof_remove delets given workspace.
+function _goof_remove() {
+  if [ -z "$1" ]; then
+    _goof_usage
+    return 0
+  fi
+
+  if _goof_varidator; then
+    return 1
+  fi
+
+  local envhome="$GENVHOME/$1"
+  if [ -d "$envhome" ]; then
+    if [ "$GENVNAME" = "$1" ]; then
+      deactivate
+    fi
+
+    if [ "$PWD" = "$envhome" ]; then
+      cd "$GENVHOME"
+    fi
+    rm -rf "$envhome"
+  else
+    echo "confirm 'GENVHOME' or envname"
+    echo "GENVHOME=$GENVHOME"
+    goof_show
+    return 1
+  fi
+  return 0
+}
+
+# goof_workon is short cut for activating workspace and 
+# changing directory to the workspace.
+function _goof_workon() {
+  if [ -z "$1" ]; then
+    _goof_show
+    return 0
+  fi
+
+  if _goof_varidator; then
+    return 1
+  fi
+
+  if [ -n "$1" ]; then
+    if [ -f "$GENVHOME/$1/activate" ]; then
+      source "$GENVHOME/$1/activate"
+      cd "$GENVHOME/$1"
+    fi
+  fi
+  return 0
+}
+
+# goof_freeze is an alias of 'genv -freeze'
+# if envname is given, freeze that workspace.
+function _goof_freeze() {
+  if [ -z "$1" ]; then
+    eval "genv -freeze"
+  fi
+
+  if _goof_varidator; then
+    return 1
+  fi
+
+  if [ -n "$1" ]; then
+    local currenv=$GENVNAME
+    if [ -f "$GENVHOME/$1/activate" ]; then
+      source "$GENVHOME/$1/activate"
+      eval "genv -freeze"
+      eval "_workon $currenv"
+    else
+      echo "Is envronment '$1' genv workspace?"
+      _goof_show
+    fi
+  fi
+}
+
+# goof_install is an alias of 'genv -install'. 
+# if 1st argument is null, try to find 'requirements.txt' under 
+# current workspace, and install packages in it if it is.
+function _goof_install() {
+  if _goof_varidator; then
+    return 1
+  fi
+
+  if [ -z "$1" ]; then
+    local requirements="$GENVHOME/$GENVNAME/requirements.txt"
+    if [ -f "$requirements" ]; then
+      eval "genv -install $requirements"
+      return 0
+    else
+      _goof_usage
+      return 1
+    fi
+  fi
+
+  if [ -f "$1" ]; then
+    eval "genv -install $1"
+  fi
+  return 0
+}
+
+
+function _goof_showgo() {
+  # if no argument given, it shows available Go versions
+  for dir in "$GENVGOROOT"/*; do
+    if [ -d "$dir" ]; then
+      local prefix="  "
+      if [ "$dir" = "$GOROOT" ]; then
+        prefix="* "
+      fi
+      if [ "$dir" = "$GENVGOROOT/go" ]; then
+        dir="$dir (repository)"
+      fi
+      echo "$prefix$dir"
+      unset prefix
+    fi
+  done
+  return 0
+}
+
+
+# goof_goinstall installs Go with specific version
+function _goof_goinstall() {
+  # check required environment variables are set
+  if [ -z "$GENVGOROOT" ]; then
+    echo "[goof] set enviroment variable GENVGOROOT to use 'goof goinstall' command"
+    return 1
+  fi
+
+  if [ ! -d "$GENVGOROOT" ]; then
+    echo "[goof] no such directory: $GENVGOROOT"
+    return 1
+  fi
+
+  if [ -z "$1" ]; then
+    _goof_showgo
+    return 0
+  fi
+  # check uninstall command
+  if [ "$1" = "-u" ]; then
+    if [ -d "$GENVGOROOT/$2" ]; then
+      rm -rf "$GENVGOROOT/$2"
+      echo "[goof] uninstalled version '$2'"
+      return 0
+    else
+      echo "[goof] cannot unintall version '$2'. not installed."
+      return 1
+    fi
+  fi
+
+  # check mercurial
+  if ! type hg > /dev/null; then
+    echo "[goof] 'goof goinstall' requires mercurial."
+    echo "[goof] Install it from http://mercurial.selenic.com/"
+    return 1
+  fi
+
+  # build and copy directory
+  if [ -d "$GENVGOROOT/$1" ]; then
+    echo "[goof] Go version '$1' already exists."
+  else
+    local repo=$GENVGOROOT/go
+    # initial checkout
+    echo "[goof] check repository existence."
+    if [ ! -d "$repo" ]; then
+      echo "[goof] Go source code initial checkout."
+      eval "hg clone -q https://code.google.com/p/go/ --cwd $GENVGOROOT"
+    fi
+    cd $repo
+    if [ ! -d "$repo/logs" ]; then
+      mkdir "$repo/logs"
+    fi
+
+    # check tags
+    if [ "$1" = "tags" ]; then
+      hg tags
+      return 0
+    fi
+
+    # build and copy
+    local datetime=`date +"%Y%m%d%H%M%S"`
+    local logfile="$repo/logs/$1-$datetime.log"
+    if eval "hg checkout $1 &> $logfile"; then
+      cd src
+      echo "[goof] building '$1'."
+      if ./all.bash &>> "$logfile"; then
+        cd "$repo"
+        echo "[goof] copying '$1'."
+        if rsync -av --exclude='.hg*' . "$GENVGOROOT/$1" &>> "$logfile"; then
+          echo "[goof] '$1' is installed in $GENVGOROOT/$1"
+        fi
+      else
+        echo "see build log file: $logfile"
+        return 1
+      fi
+    else
+      echo "check tag name: $1"
+    fi
+  fi
+}
+
+# goof_go changes current GOROOT in specified version
+function _goof_go() {
+  if [ -z "$GENVGOROOT" ]; then
+    echo "[goof] set enviroment variable GENVGOROOT to use 'goof go' command"
+    return 1
+  fi
+  if [ -z "$1" -o ! -d "$GENVGOROOT/$1" ]; then
+    _goof_showgo
+    return 1
+  fi
+
+  export _PREV_GOROOT="$GOROOT"
+  export GOROOT="$GENVGOROOT/$1"
+  echo "[goof] changed GOROOT=$GENVGOROOT/$1"
+  return 0
+}
+

File shellscripts/initial-install.sh

+#!/usr/bin/env bash
+#
+# Environment variable GENVGOROOT, GENVTARGET is install target of `genv`
+# Default install target i /usr/local/bin.
+
+if [ -z "$GENVGOROOT" ]; then
+  echo "[genv] set GENVGOROOT environment variable. (Go binary install target)"
+  exit 0
+fi
+
+if [ -z "$GENVTARGET" ]; then
+  echo "[genv] set GENVTARGET environment variable. (genv binary install target)"
+  exit 0
+fi
+
+cd /tmp
+
+local downloader
+if type curl &> /dev/null; then
+  downloader="curl -O"
+elif type wget &> /dev/null; then
+  downloader="wget"
+else
+  echo "[genv] `curl` or `wget` is required to fetch files."
+  exit 0
+fi
+
+if $downloader "https://bitbucket.org/ymotongpoo/genv/raw/master/shellscripts/genvwrapper.sh"; then
+  if [ -f "genvwrapper.sh" ]; then
+    source genvwrapper.sh
+  else
+    echo "[genv] cannot read genvwrapper.sh"
+    exit 0
+  fi
+else
+  echo "[genv] cannot download genvwrapper.sh"
+  exit 0
+fi
+
+if goof goinstall default; then
+  if [ -d "$GENVGOROOT/default" ]; then
+    echo "[genv] add following lines in your .barshrc/.zshrc"
+    echo "wxport GOROOT=$GENVGOROOT/default"
+    echo "export PATH=$PATH:$GOROOT/bin"
+    export GOROOT="$GENVGOROOT/default"
+    export PATH=$GOROOT/bin:$PATH
+  fi
+else
+  echo "[genv] `goof goinstall` command does not work."
+  exit 0
+fi
+
+if ! type git 2>&1 > /dev/null; then
+  echo "[genv] To get genv, install git"
+  exit 0 
+fi
+
+echo "[genv] cloning genv from GitHub"
+if git clone https://bitbucket.org/ymotongpoo/genv.git; then
+  cd genv
+  if ! go build -o genv; then
+    echo "[genv] cannot build genv"
+  fi
+  if ! cp genv "$GENVTARGET"; then
+    echo "[genv] cannot copy genv. check permission of '$GENVTARGET'"
+  fi
+  if ! cp shellscripts/genvwrapper.sh $GENVTARGET; then
+    echo "[genv] failed copying genvwrapper.sh. check permission of '$GENVTARGET'"
+  fi
+else
+  echo "[genv] cannot clone genv repository"
+  exit 0
+fi

File templates.go

+// Copyright 2012 Yoshifumi Yamaguchi, All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package main
+
+import (
+	"text/template"
+)
+
+// activateTemplateText is a template for create `activate` file
+// for a new Go workspace.
+const activateTemplateText = `#!/bin/sh
+function deactivate() {
+  if [ -n "$GENVNAME" ]; then
+    unset GENVNAME
+  fi
+
+  if [ -n "$_OLD_GOROOT" ]; then
+    export GOROOT="$_OLD_GOROOT"
+    unset _OLD_GOROOT
+  fi
+
+  if [ -n "$_OLD_PS1" ]; then
+    export PS1="$_OLD_PS1"
+    unset _OLD_PS1
+  fi
+
+  if [ -n "$_OLD_PATH" ]; then
+    export PATH="$_OLD_PATH"
+    unset _OLD_PATH
+  fi
+
+  export GOPATH="$_OLD_GOPATH"
+ 
+  if [ "$1" != "init" ]; then
+    unset -f deactivate
+  fi
+}
+
+deactivate init
+
+export _OLD_PATH="$PATH"
+export _OLD_GOPATH="$GOPATH"
+export _OLD_GOROOT="$GOROOT"
+export _OLD_PS1="$PS1"
+export GENVNAME="{{.ENVNAME}}"
+export PS1="{{.PS1}}"
+export GOPATH={{.GOPATH}}
+export PATH="$GOPATH/bin":$PATH
+{{if .GOROOT}}export GOROOT={{.GOROOT}}{{end}}
+`
+
+var activateTemplate = template.Must(template.New("activate").Parse(activateTemplateText))
+
+// appYamlTemplateText is a template for create `app.yaml` file
+// for a new GAE/Go workspace.
+const appYamlTemplateText = `application: {{ .EnvName }}    # change here
+version: 1
+runtime: go
+api_version: go1
+
+handlers:
+- url: /.*
+  script: _go_app
+`
+
+var appYamlTemplate = template.Must(template.New("app.yaml").Parse(appYamlTemplateText))
+
+// appGoTemplateText is a template for create `<appname>.go` file
+// for a new GAE/Go workspace.
+const appGoTemplateText = `package {{ .EnvName }}
+
+import (
+	"net/http"
+)
+
+func init() {
+	http.HandleFunc("/", handler)
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	// some code
+}
+`
+
+var appGoTemplate = template.Must(template.New("app.yaml").Parse(appGoTemplateText))