github.com/vladimirvivien/godep@v0.0.0-20160710170555-3c0ccb9a2415/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "go/build" 7 "io" 8 "log" 9 "os" 10 "path/filepath" 11 "runtime/pprof" 12 "strings" 13 "text/template" 14 ) 15 16 var ( 17 cpuprofile string 18 verbose bool // Verbose flag for commands that support it 19 debug bool // Debug flag for commands that support it 20 majorGoVersion string 21 VendorExperiment bool 22 sep string 23 ) 24 25 // Command is an implementation of a godep command 26 // like godep save or godep go. 27 type Command struct { 28 // Run runs the command. 29 // The args are the arguments after the command name. 30 Run func(cmd *Command, args []string) 31 32 // Name of the command 33 Name string 34 35 // Args the command would expect 36 Args string 37 38 // Short is the short description shown in the 'godep help' output. 39 Short string 40 41 // Long is the long message shown in the 42 // 'godep help <this-command>' output. 43 Long string 44 45 // Flag is a set of flags specific to this command. 46 Flag flag.FlagSet 47 48 // OnlyInGOPATH limits this command to being run only while inside of a GOPATH 49 OnlyInGOPATH bool 50 } 51 52 // UsageExit prints usage information and exits. 53 func (c *Command) UsageExit() { 54 fmt.Fprintf(os.Stderr, "Args: godep %s [-v] [-d] %s\n\n", c.Name, c.Args) 55 fmt.Fprintf(os.Stderr, "Run 'godep help %s' for help.\n", c.Name) 56 os.Exit(2) 57 } 58 59 // Commands lists the available commands and help topics. 60 // The order here is the order in which they are printed 61 // by 'godep help'. 62 var commands = []*Command{ 63 cmdSave, 64 cmdGo, 65 cmdGet, 66 cmdPath, 67 cmdRestore, 68 cmdUpdate, 69 cmdDiff, 70 cmdVersion, 71 } 72 73 // VendorExperiment is the Go 1.5 vendor directory experiment flag, see 74 // https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff 75 // Honor the env var unless the project already has an old school godep workspace 76 func determineVendor(v string) bool { 77 go15ve := os.Getenv("GO15VENDOREXPERIMENT") 78 ev := (v == "go1.5" && go15ve == "1") || 79 (v == "go1.6" && go15ve != "0") || 80 (v == "go1.7") || 81 (strings.HasPrefix(v, "devel") && go15ve != "0") 82 83 ws := filepath.Join("Godeps", "_workspace") 84 s, err := os.Stat(ws) 85 if err == nil && s.IsDir() { 86 log.Printf("WARNING: Godep workspaces (./Godeps/_workspace) are deprecated and support for them will be removed when go1.8 is released.") 87 if ev { 88 log.Printf("WARNING: Go version (%s) & $GO15VENDOREXPERIMENT=%s wants to enable the vendor experiment, but disabling because a Godep workspace (%s) exists\n", v, go15ve, ws) 89 } 90 return false 91 } 92 93 return ev 94 } 95 96 func main() { 97 log.SetFlags(0) 98 log.SetPrefix("godep: ") 99 log.SetOutput(os.Stderr) 100 101 flag.Usage = usageExit 102 flag.Parse() 103 args := flag.Args() 104 if len(args) < 1 { 105 usageExit() 106 } 107 108 if args[0] == "help" { 109 help(args[1:]) 110 return 111 } 112 113 var err error 114 majorGoVersion, err = goVersion() 115 if err != nil { 116 log.Fatal(err) 117 } 118 119 for _, cmd := range commands { 120 if cmd.Name == args[0] { 121 if cmd.OnlyInGOPATH { 122 checkInGOPATH() 123 } 124 125 VendorExperiment = determineVendor(majorGoVersion) 126 // sep is the signature set of path elements that 127 // precede the original path of an imported package. 128 sep = defaultSep(VendorExperiment) 129 130 cmd.Flag.BoolVar(&verbose, "v", false, "enable verbose output") 131 cmd.Flag.BoolVar(&debug, "d", false, "enable debug output") 132 cmd.Flag.StringVar(&cpuprofile, "cpuprofile", "", "Write cpu profile to this file") 133 cmd.Flag.Usage = func() { cmd.UsageExit() } 134 cmd.Flag.Parse(args[1:]) 135 136 debugln("versionString()", versionString()) 137 debugln("majorGoVersion", majorGoVersion) 138 debugln("VendorExperiment", VendorExperiment) 139 debugln("sep", sep) 140 141 if cpuprofile != "" { 142 f, err := os.Create(cpuprofile) 143 if err != nil { 144 log.Fatal(err) 145 } 146 pprof.StartCPUProfile(f) 147 defer pprof.StopCPUProfile() 148 } 149 cmd.Run(cmd, cmd.Flag.Args()) 150 return 151 } 152 } 153 154 fmt.Fprintf(os.Stderr, "godep: unknown command %q\n", args[0]) 155 fmt.Fprintf(os.Stderr, "Run 'godep help' for usage.\n") 156 os.Exit(2) 157 } 158 159 func subPath(sub, path string) bool { 160 ls := strings.ToLower(sub) 161 lp := strings.ToLower(path) 162 if ls == lp { 163 return false 164 } 165 return strings.HasPrefix(ls, lp) 166 } 167 168 func checkInGOPATH() { 169 pwd, err := os.Getwd() 170 if err != nil { 171 log.Fatal("Unable to determine current working directory", err) 172 } 173 dirs := build.Default.SrcDirs() 174 for _, p := range dirs { 175 if ok := subPath(pwd, p); ok { 176 return 177 } 178 } 179 180 log.Println("[WARNING]: godep should only be used inside a valid go package directory and") 181 log.Println("[WARNING]: may not function correctly. You are probably outside of your $GOPATH.") 182 log.Printf("[WARNING]:\tCurrent Directory: %s\n", pwd) 183 log.Printf("[WARNING]:\t$GOPATH: %s\n", os.Getenv("GOPATH")) 184 } 185 186 var usageTemplate = ` 187 Godep is a tool for managing Go package dependencies. 188 189 Usage: 190 191 godep command [arguments] 192 193 The commands are: 194 {{range .}} 195 {{.Name | printf "%-8s"}} {{.Short}}{{end}} 196 197 Use "godep help [command]" for more information about a command. 198 ` 199 200 var helpTemplate = ` 201 Args: godep {{.Name}} [-v] [-d] {{.Args}} 202 203 {{.Long | trim}} 204 205 If -v is given, verbose output is enabled. 206 207 If -d is given, debug output is enabled (you probably don't want this, see -v). 208 209 ` 210 211 func help(args []string) { 212 if len(args) == 0 { 213 printUsage(os.Stdout) 214 return 215 } 216 if len(args) != 1 { 217 fmt.Fprintf(os.Stderr, "usage: godep help command\n\n") 218 fmt.Fprintf(os.Stderr, "Too many arguments given.\n") 219 os.Exit(2) 220 } 221 for _, cmd := range commands { 222 if cmd.Name == args[0] { 223 tmpl(os.Stdout, helpTemplate, cmd) 224 return 225 } 226 } 227 } 228 229 func usageExit() { 230 printUsage(os.Stderr) 231 os.Exit(2) 232 } 233 234 func printUsage(w io.Writer) { 235 tmpl(w, usageTemplate, commands) 236 } 237 238 // tmpl executes the given template text on data, writing the result to w. 239 func tmpl(w io.Writer, text string, data interface{}) { 240 t := template.New("top") 241 t.Funcs(template.FuncMap{ 242 "trim": strings.TrimSpace, 243 }) 244 template.Must(t.Parse(strings.TrimSpace(text) + "\n\n")) 245 if err := t.Execute(w, data); err != nil { 246 panic(err) 247 } 248 }