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 `