github.com/meatballhat/deppy@v0.0.0-20151116212532-116c2a9aa48d/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(), "deppy") 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 Deps. 20 21 Any go tool command can run this way, but "deppy go get" 22 is unnecessary and has been disabled. Instead, use 23 "deppy 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 'deppy go install' instead.") 41 fmt.Fprintln(os.Stderr, "Run 'deppy 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 := findDeps() 60 if dir == "" { 61 log.Fatalln("No Deps found (or in any parent directory)") 62 } 63 g, err := ReadAndLoadDeps(filepath.Join(dir, "Deps")) 64 if err != nil { 65 log.Fatalln(err) 66 } 67 gopath, err = sandboxAll(g.Deps) 68 if err != nil { 69 log.Fatalln(err) 70 } 71 return gopath 72 } 73 74 // findDeps looks for a directory entry "Deps" in the 75 // current directory or any parent, and returns the containing 76 // directory and whether the entry itself is a directory. 77 // If Deps can't be found, findDeps returns "". 78 // For any other error, it exits the program. 79 func findDeps() (dir string) { 80 wd, err := os.Getwd() 81 if err != nil { 82 log.Fatalln(err) 83 } 84 return findInParents(wd, "Deps") 85 } 86 87 // isRoot returns true iff a path is a root. 88 // On Unix: "/". 89 // On Windows: "C:\", "D:\", ... 90 func isRoot(p string) bool { 91 p = filepath.Clean(p) 92 volume := filepath.VolumeName(p) 93 94 p = strings.TrimPrefix(p, volume) 95 p = filepath.ToSlash(p) 96 97 return p == "/" 98 } 99 100 // findInParents returns the path to the directory containing name 101 // in dir or any ancestor, and whether name itself is a directory. 102 // If name cannot be found, findInParents returns the empty string. 103 func findInParents(dir, name string) (container string) { 104 for { 105 _, err := os.Stat(filepath.Join(dir, name)) 106 if os.IsNotExist(err) && isRoot(dir) { 107 return "" 108 } 109 if os.IsNotExist(err) { 110 dir = filepath.Dir(dir) 111 continue 112 } 113 if err != nil { 114 log.Fatalln(err) 115 } 116 return dir 117 } 118 } 119 120 func envNoGopath() (a []string) { 121 for _, s := range os.Environ() { 122 if !strings.HasPrefix(s, "GOPATH=") { 123 a = append(a, s) 124 } 125 } 126 return a 127 } 128 129 // sandboxAll ensures that the commits in deps are available 130 // on disk, and returns a GOPATH string that will cause them 131 // to be used. 132 func sandboxAll(a []Dependency) (gopath string, err error) { 133 var path []string 134 for _, dep := range a { 135 dir, err := sandbox(dep) 136 if err != nil { 137 return "", err 138 } 139 path = append(path, dir) 140 } 141 return strings.Join(path, ":"), nil 142 } 143 144 // sandbox ensures that commit d is available on disk, 145 // and returns a GOPATH string that will cause it to be used. 146 func sandbox(d Dependency) (gopath string, err error) { 147 if !exists(d.RepoPath()) { 148 if err = d.CreateRepo("fast", "main"); err != nil { 149 return "", fmt.Errorf("create repo: %s", err) 150 } 151 } 152 err = d.checkout() 153 if err != nil && d.FastRemotePath() != "" { 154 err = d.fetchAndCheckout("fast") 155 } 156 if err != nil { 157 err = d.fetchAndCheckout("main") 158 } 159 if err != nil { 160 return "", err 161 } 162 return d.Gopath(), nil 163 }