github.com/elubow/godep@v0.0.0-20140525002653-983ff9241cea/go.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  )
    11  
    12  var spool = filepath.Join(os.TempDir(), "godep")
    13  
    14  var cmdGo = &Command{
    15  	Usage: "go command [arguments]",
    16  	Short: "run the go tool in a sandbox",
    17  	Long: `
    18  Go runs the go tool in a temporary GOPATH sandbox
    19  with the dependencies listed in file Godeps.
    20  
    21  Any go tool command can run this way, but "godep go get"
    22  is unnecessary and has been disabled. Instead, use
    23  "godep go install".
    24  `,
    25  	Run: runGo,
    26  }
    27  
    28  // Set up a sandbox and run the go tool. The sandbox is built
    29  // out of specific checked-out revisions of repos. We keep repos
    30  // and revs materialized on disk under the assumption that disk
    31  // space is cheap and plentiful, and writing files is slow.
    32  // Everything is kept in the spool directory.
    33  func runGo(cmd *Command, args []string) {
    34  	gopath := prepareGopath()
    35  	if s := os.Getenv("GOPATH"); s != "" {
    36  		gopath += string(os.PathListSeparator) + os.Getenv("GOPATH")
    37  	}
    38  	if len(args) > 0 && args[0] == "get" {
    39  		log.Printf("invalid subcommand: %q", "go get")
    40  		fmt.Fprintln(os.Stderr, "Use 'godep go install' instead.")
    41  		fmt.Fprintln(os.Stderr, "Run 'godep help go' for usage.")
    42  		os.Exit(2)
    43  	}
    44  	c := exec.Command("go", args...)
    45  	c.Env = append(envNoGopath(), "GOPATH="+gopath)
    46  	c.Stdin = os.Stdin
    47  	c.Stdout = os.Stdout
    48  	c.Stderr = os.Stderr
    49  	err := c.Run()
    50  	if err != nil {
    51  		log.Fatalln("go", err)
    52  	}
    53  }
    54  
    55  // prepareGopath reads dependency information from the filesystem
    56  // entry name, fetches any necessary code, and returns a gopath
    57  // causing the specified dependencies to be used.
    58  func prepareGopath() (gopath string) {
    59  	dir, isDir := findGodeps()
    60  	if dir == "" {
    61  		log.Fatalln("No Godeps found (or in any parent directory)")
    62  	}
    63  	if isDir {
    64  		return filepath.Join(dir, "Godeps", "_workspace")
    65  	}
    66  	g, err := ReadAndLoadGodeps(filepath.Join(dir, "Godeps"))
    67  	if err != nil {
    68  		log.Fatalln(err)
    69  	}
    70  	gopath, err = sandboxAll(g.Deps)
    71  	if err != nil {
    72  		log.Fatalln(err)
    73  	}
    74  	return gopath
    75  }
    76  
    77  // findGodeps looks for a directory entry "Godeps" in the
    78  // current directory or any parent, and returns the containing
    79  // directory and whether the entry itself is a directory.
    80  // If Godeps can't be found, findGodeps returns "".
    81  // For any other error, it exits the program.
    82  func findGodeps() (dir string, isDir bool) {
    83  	wd, err := os.Getwd()
    84  	if err != nil {
    85  		log.Fatalln(err)
    86  	}
    87  	return findInParents(wd, "Godeps")
    88  }
    89  
    90  // findInParents returns the path to the directory containing name
    91  // in dir or any ancestor, and whether name itself is a directory.
    92  // If name cannot be found, findInParents returns the empty string.
    93  func findInParents(dir, name string) (container string, isDir bool) {
    94  	for {
    95  		fi, err := os.Stat(filepath.Join(dir, name))
    96  		if os.IsNotExist(err) && dir == "/" {
    97  			return "", false
    98  		}
    99  		if os.IsNotExist(err) {
   100  			dir = filepath.Dir(dir)
   101  			continue
   102  		}
   103  		if err != nil {
   104  			log.Fatalln(err)
   105  		}
   106  		return dir, fi.IsDir()
   107  	}
   108  }
   109  
   110  func envNoGopath() (a []string) {
   111  	for _, s := range os.Environ() {
   112  		if !strings.HasPrefix(s, "GOPATH=") {
   113  			a = append(a, s)
   114  		}
   115  	}
   116  	return a
   117  }
   118  
   119  // sandboxAll ensures that the commits in deps are available
   120  // on disk, and returns a GOPATH string that will cause them
   121  // to be used.
   122  func sandboxAll(a []Dependency) (gopath string, err error) {
   123  	var path []string
   124  	for _, dep := range a {
   125  		dir, err := sandbox(dep)
   126  		if err != nil {
   127  			return "", err
   128  		}
   129  		path = append(path, dir)
   130  	}
   131  	return strings.Join(path, ":"), nil
   132  }
   133  
   134  // sandbox ensures that commit d is available on disk,
   135  // and returns a GOPATH string that will cause it to be used.
   136  func sandbox(d Dependency) (gopath string, err error) {
   137  	if !exists(d.RepoPath()) {
   138  		if err = d.CreateRepo("fast", "main"); err != nil {
   139  			return "", fmt.Errorf("create repo: %s", err)
   140  		}
   141  	}
   142  	err = d.checkout()
   143  	if err != nil && d.FastRemotePath() != "" {
   144  		err = d.fetchAndCheckout("fast")
   145  	}
   146  	if err != nil {
   147  		err = d.fetchAndCheckout("main")
   148  	}
   149  	if err != nil {
   150  		return "", err
   151  	}
   152  	return d.Gopath(), nil
   153  }