github.com/alexanderthaller/godep@v0.0.0-20141231210904-0baa7ea46402/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  	log.Println(strings.TrimSpace(noSourceCodeWarning))
    67  	g, err := ReadAndLoadGodeps(filepath.Join(dir, "Godeps"))
    68  	if err != nil {
    69  		log.Fatalln(err)
    70  	}
    71  	gopath, err = sandboxAll(g.Deps)
    72  	if err != nil {
    73  		log.Fatalln(err)
    74  	}
    75  	return gopath
    76  }
    77  
    78  // findGodeps looks for a directory entry "Godeps" in the
    79  // current directory or any parent, and returns the containing
    80  // directory and whether the entry itself is a directory.
    81  // If Godeps can't be found, findGodeps returns "".
    82  // For any other error, it exits the program.
    83  func findGodeps() (dir string, isDir bool) {
    84  	wd, err := os.Getwd()
    85  	if err != nil {
    86  		log.Fatalln(err)
    87  	}
    88  	return findInParents(wd, "Godeps")
    89  }
    90  
    91  // isRoot returns true iff a path is a root.
    92  // On Unix: "/".
    93  // On Windows: "C:\", "D:\", ...
    94  func isRoot(p string) bool {
    95  	p = filepath.Clean(p)
    96  	volume := filepath.VolumeName(p)
    97  
    98  	p = strings.TrimPrefix(p, volume)
    99  	p = filepath.ToSlash(p)
   100  
   101  	return p == "/"
   102  }
   103  
   104  // findInParents returns the path to the directory containing name
   105  // in dir or any ancestor, and whether name itself is a directory.
   106  // If name cannot be found, findInParents returns the empty string.
   107  func findInParents(dir, name string) (container string, isDir bool) {
   108  	for {
   109  		fi, err := os.Stat(filepath.Join(dir, name))
   110  		if os.IsNotExist(err) && isRoot(dir) {
   111  			return "", false
   112  		}
   113  		if os.IsNotExist(err) {
   114  			dir = filepath.Dir(dir)
   115  			continue
   116  		}
   117  		if err != nil {
   118  			log.Fatalln(err)
   119  		}
   120  		return dir, fi.IsDir()
   121  	}
   122  }
   123  
   124  func envNoGopath() (a []string) {
   125  	for _, s := range os.Environ() {
   126  		if !strings.HasPrefix(s, "GOPATH=") {
   127  			a = append(a, s)
   128  		}
   129  	}
   130  	return a
   131  }
   132  
   133  // sandboxAll ensures that the commits in deps are available
   134  // on disk, and returns a GOPATH string that will cause them
   135  // to be used.
   136  func sandboxAll(a []Dependency) (gopath string, err error) {
   137  	var path []string
   138  	for _, dep := range a {
   139  		dir, err := sandbox(dep)
   140  		if err != nil {
   141  			return "", err
   142  		}
   143  		path = append(path, dir)
   144  	}
   145  	return strings.Join(path, ":"), nil
   146  }
   147  
   148  // sandbox ensures that commit d is available on disk,
   149  // and returns a GOPATH string that will cause it to be used.
   150  func sandbox(d Dependency) (gopath string, err error) {
   151  	if !exists(d.RepoPath()) {
   152  		if err = d.CreateRepo("fast", "main"); err != nil {
   153  			return "", fmt.Errorf("create repo: %s", err)
   154  		}
   155  	}
   156  	err = d.checkout()
   157  	if err != nil && d.FastRemotePath() != "" {
   158  		err = d.fetchAndCheckout("fast")
   159  	}
   160  	if err != nil {
   161  		err = d.fetchAndCheckout("main")
   162  	}
   163  	if err != nil {
   164  		return "", err
   165  	}
   166  	return d.Gopath(), nil
   167  }
   168  
   169  const noSourceCodeWarning = `
   170  warning: outdated Godeps missing source code
   171  
   172  The ability to read this format will be removed in the future.
   173  See http://goo.gl/RpYs8e for a discussion of the upcoming removal.
   174  
   175  To avoid this warning, ask the maintainer of this package to run
   176  'godep save' without flag -copy.
   177  `