|  | package cmd | 
|  |  | 
|  | import ( | 
|  | "container/list" | 
|  | "fmt" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "sort" | 
|  | "strings" | 
|  |  | 
|  | "github.com/Masterminds/cookoo" | 
|  | "github.com/Masterminds/glide/dependency" | 
|  | "github.com/Masterminds/glide/msg" | 
|  | "github.com/Masterminds/glide/util" | 
|  | ) | 
|  |  | 
|  | // Tree prints a tree representing dependencies. | 
|  | func Tree(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { | 
|  | buildContext, err := util.GetBuildContext() | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | showcore := p.Get("showcore", false).(bool) | 
|  | basedir := p.Get("dir", ".").(string) | 
|  | myName := guessPackageName(buildContext, basedir) | 
|  |  | 
|  | if basedir == "." { | 
|  | var err error | 
|  | basedir, err = os.Getwd() | 
|  | if err != nil { | 
|  | Error("Could not get working directory") | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  |  | 
|  | fmt.Println(myName) | 
|  | l := list.New() | 
|  | l.PushBack(myName) | 
|  | displayTree(buildContext, basedir, myName, 1, showcore, l) | 
|  | return nil, nil | 
|  | } | 
|  |  | 
|  | // ListDeps lists all of the dependencies of the current project. | 
|  | // | 
|  | // Params: | 
|  | //  - dir (string): basedir | 
|  | //  - deep (bool): whether to do a deep scan or a shallow scan | 
|  | // | 
|  | // Returns: | 
|  | // | 
|  | func ListDeps(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { | 
|  | basedir := p.Get("dir", ".").(string) | 
|  | deep := p.Get("deep", true).(bool) | 
|  |  | 
|  | basedir, err := filepath.Abs(basedir) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | r, err := dependency.NewResolver(basedir) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}} | 
|  | r.Handler = h | 
|  |  | 
|  | sortable, err := r.ResolveLocal(deep) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | sort.Strings(sortable) | 
|  |  | 
|  | fmt.Println("INSTALLED packages:") | 
|  | for _, k := range sortable { | 
|  | v, err := filepath.Rel(basedir, k) | 
|  | if err != nil { | 
|  | msg.Warn("Failed to Rel path: %s", err) | 
|  | v = k | 
|  | } | 
|  | fmt.Printf("\t%s\n", v) | 
|  | } | 
|  |  | 
|  | if len(h.Missing) > 0 { | 
|  | fmt.Println("\nMISSING packages:") | 
|  | for _, pkg := range h.Missing { | 
|  | fmt.Printf("\t%s\n", pkg) | 
|  | } | 
|  | } | 
|  | if len(h.Gopath) > 0 { | 
|  | fmt.Println("\nGOPATH packages:") | 
|  | for _, pkg := range h.Gopath { | 
|  | fmt.Printf("\t%s\n", pkg) | 
|  | } | 
|  | } | 
|  |  | 
|  | return nil, nil | 
|  | } | 
|  |  | 
|  | func listDeps(b *util.BuildCtxt, info map[string]*pinfo, name, path string) { | 
|  | found := findPkg(b, name, path) | 
|  | switch found.PType { | 
|  | case ptypeUnknown: | 
|  | info[name] = found | 
|  | break | 
|  | case ptypeGoroot, ptypeCgo: | 
|  | break | 
|  | default: | 
|  | info[name] = found | 
|  | for _, i := range walkDeps(b, found.Path, found.Name) { | 
|  | // Only walk the deps that are not already found to avoid | 
|  | // infinite recursion. | 
|  | if _, f := info[found.Name]; f == false { | 
|  | listDeps(b, info, i, found.Path) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func displayTree(b *util.BuildCtxt, basedir, myName string, level int, core bool, l *list.List) { | 
|  | deps := walkDeps(b, basedir, myName) | 
|  | for _, name := range deps { | 
|  | found := findPkg(b, name, basedir) | 
|  | if found.PType == ptypeUnknown { | 
|  | msg := "glide get " + found.Name | 
|  | fmt.Printf("\t%s\t(%s)\n", found.Name, msg) | 
|  | continue | 
|  | } | 
|  | if !core && found.PType == ptypeGoroot || found.PType == ptypeCgo { | 
|  | continue | 
|  | } | 
|  | fmt.Print(strings.Repeat("\t", level)) | 
|  |  | 
|  | f := findInList(found.Name, l) | 
|  | if f == true { | 
|  | fmt.Printf("(Recursion) %s   (%s)\n", found.Name, found.Path) | 
|  | } else { | 
|  | // Every branch in the tree is a copy to handle all the branches | 
|  | cl := copyList(l) | 
|  | cl.PushBack(found.Name) | 
|  | fmt.Printf("%s   (%s)\n", found.Name, found.Path) | 
|  | displayTree(b, found.Path, found.Name, level+1, core, cl) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | type ptype int8 | 
|  |  | 
|  | const ( | 
|  | ptypeUnknown ptype = iota | 
|  | ptypeLocal | 
|  | ptypeVendor | 
|  | ptypeGopath | 
|  | ptypeGoroot | 
|  | ptypeCgo | 
|  | ) | 
|  |  | 
|  | func ptypeString(t ptype) string { | 
|  | switch t { | 
|  | case ptypeLocal: | 
|  | return "local" | 
|  | case ptypeVendor: | 
|  | return "vendored" | 
|  | case ptypeGopath: | 
|  | return "gopath" | 
|  | case ptypeGoroot: | 
|  | return "core" | 
|  | case ptypeCgo: | 
|  | return "cgo" | 
|  | default: | 
|  | return "missing" | 
|  | } | 
|  | } | 
|  |  | 
|  | type pinfo struct { | 
|  | Name, Path string | 
|  | PType      ptype | 
|  | Vendored   bool | 
|  | } | 
|  |  | 
|  | func findPkg(b *util.BuildCtxt, name, cwd string) *pinfo { | 
|  | var fi os.FileInfo | 
|  | var err error | 
|  | var p string | 
|  |  | 
|  | info := &pinfo{ | 
|  | Name: name, | 
|  | } | 
|  |  | 
|  | // Recurse backward to scan other vendor/ directories | 
|  | // If the cwd isn't an absolute path walking upwards looking for vendor/ | 
|  | // folders can get into an infinate loop. | 
|  | abs, err := filepath.Abs(cwd) | 
|  | if err != nil { | 
|  | abs = cwd | 
|  | } | 
|  | if abs != "." { | 
|  | for wd := abs; wd != "/"; wd = filepath.Dir(wd) { | 
|  |  | 
|  | // Don't look for packages outside the GOPATH | 
|  | if wd == b.GOPATH { | 
|  | break | 
|  | } | 
|  | p = filepath.Join(wd, "vendor", name) | 
|  | if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) { | 
|  | info.Path = p | 
|  | info.PType = ptypeVendor | 
|  | info.Vendored = true | 
|  | return info | 
|  | } | 
|  | } | 
|  | } | 
|  | // Check $GOPATH | 
|  | for _, r := range strings.Split(b.GOPATH, ":") { | 
|  | p = filepath.Join(r, "src", name) | 
|  | if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) { | 
|  | info.Path = p | 
|  | info.PType = ptypeGopath | 
|  | return info | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check $GOROOT | 
|  | for _, r := range strings.Split(b.GOROOT, ":") { | 
|  | p = filepath.Join(r, "src", name) | 
|  | if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) { | 
|  | info.Path = p | 
|  | info.PType = ptypeGoroot | 
|  | return info | 
|  | } | 
|  | } | 
|  |  | 
|  | // Finally, if this is "C", we're dealing with cgo | 
|  | if name == "C" { | 
|  | info.PType = ptypeCgo | 
|  | } | 
|  |  | 
|  | return info | 
|  | } | 
|  |  | 
|  | func isLink(fi os.FileInfo) bool { | 
|  | return fi.Mode()&os.ModeSymlink == os.ModeSymlink | 
|  | } | 
|  |  | 
|  | func walkDeps(b *util.BuildCtxt, base, myName string) []string { | 
|  | externalDeps := []string{} | 
|  | filepath.Walk(base, func(path string, fi os.FileInfo, err error) error { | 
|  | if excludeSubtree(path, fi) { | 
|  | if fi.IsDir() { | 
|  | return filepath.SkipDir | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | pkg, err := b.ImportDir(path, 0) | 
|  | if err != nil { | 
|  | if !strings.HasPrefix(err.Error(), "no buildable Go source") { | 
|  | Warn("Error: %s (%s)", err, path) | 
|  | // Not sure if we should return here. | 
|  | //return err | 
|  | } | 
|  | } | 
|  |  | 
|  | if pkg.Goroot { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | for _, imp := range pkg.Imports { | 
|  | //if strings.HasPrefix(imp, myName) { | 
|  | ////Info("Skipping %s because it is a subpackage of %s", imp, myName) | 
|  | //continue | 
|  | //} | 
|  | if imp == myName { | 
|  | continue | 
|  | } | 
|  | externalDeps = append(externalDeps, imp) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | }) | 
|  | return externalDeps | 
|  | } | 
|  |  | 
|  | func excludeSubtree(path string, fi os.FileInfo) bool { | 
|  | top := filepath.Base(path) | 
|  |  | 
|  | if !fi.IsDir() && !isLink(fi) { | 
|  | return true | 
|  | } | 
|  |  | 
|  | // Provisionally, we'll skip vendor. We definitely | 
|  | // should skip testdata. | 
|  | if top == "vendor" || top == "testdata" { | 
|  | return true | 
|  | } | 
|  |  | 
|  | // Skip anything that starts with _ | 
|  | if strings.HasPrefix(top, "_") || (strings.HasPrefix(top, ".") && top != ".") { | 
|  | return true | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | func copyList(l *list.List) *list.List { | 
|  | n := list.New() | 
|  | for e := l.Front(); e != nil; e = e.Next() { | 
|  | n.PushBack(e.Value.(string)) | 
|  | } | 
|  | return n | 
|  | } | 
|  |  | 
|  | func findInList(n string, l *list.List) bool { | 
|  | for e := l.Front(); e != nil; e = e.Next() { | 
|  | if e.Value.(string) == n { | 
|  | return true | 
|  | } | 
|  | } | 
|  |  | 
|  | return false | 
|  | } |