| package tree |
| |
| import ( |
| "container/list" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "github.com/Masterminds/glide/dependency" |
| "github.com/Masterminds/glide/msg" |
| gpath "github.com/Masterminds/glide/path" |
| "github.com/Masterminds/glide/util" |
| ) |
| |
| // Display displays a tree view of the given project. |
| // |
| // FIXME: The output formatting could use some TLC. |
| func Display(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.Loc == dependency.LocUnknown { |
| m := "glide get " + found.Name |
| msg.Puts("\t%s\t(%s)", found.Name, m) |
| continue |
| } |
| if !core && found.Loc == dependency.LocGoroot || found.Loc == dependency.LocCgo { |
| continue |
| } |
| msg.Print(strings.Repeat("|\t", level-1) + "|-- ") |
| |
| f := findInList(found.Name, l) |
| if f == true { |
| msg.Puts("(Recursion) %s (%s)", 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) |
| msg.Puts("%s (%s)", found.Name, found.Path) |
| Display(b, found.Path, found.Name, level+1, core, cl) |
| } |
| } |
| } |
| |
| func walkDeps(b *util.BuildCtxt, base, myName string) []string { |
| externalDeps := []string{} |
| filepath.Walk(base, func(path string, fi os.FileInfo, err error) error { |
| if err != nil { |
| return err |
| } |
| |
| if !dependency.IsSrcDir(fi) { |
| if fi.IsDir() { |
| return filepath.SkipDir |
| } |
| return nil |
| } |
| |
| var imps []string |
| pkg, err := b.ImportDir(path, 0) |
| if err != nil && strings.HasPrefix(err.Error(), "found packages ") { |
| // If we got here it's because a package and multiple packages |
| // declared. This is often because of an example with a package |
| // or main but +build ignore as a build tag. In that case we |
| // try to brute force the packages with a slower scan. |
| imps, _, err = dependency.IterativeScan(path) |
| if err != nil { |
| msg.Err("Error walking dependencies for %s: %s", path, err) |
| return err |
| } |
| } else if err != nil { |
| if !strings.HasPrefix(err.Error(), "no buildable Go source") { |
| msg.Warn("Error: %s (%s)", err, path) |
| // Not sure if we should return here. |
| //return err |
| } |
| } else { |
| imps = pkg.Imports |
| } |
| |
| if pkg.Goroot { |
| return nil |
| } |
| |
| for _, imp := range imps { |
| //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 findPkg(b *util.BuildCtxt, name, cwd string) *dependency.PkgInfo { |
| var fi os.FileInfo |
| var err error |
| var p string |
| |
| info := &dependency.PkgInfo{ |
| Name: name, |
| } |
| |
| if strings.HasPrefix(name, "./") || strings.HasPrefix(name, "../") { |
| info.Loc = dependency.LocRelative |
| return info |
| } |
| |
| // 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 != "." { |
| // Previously there was a check on the loop that wd := "/". The path |
| // "/" is a POSIX path so this fails on Windows. Now the check is to |
| // make sure the same wd isn't seen twice. When the same wd happens |
| // more than once it's the beginning of looping on the same location |
| // which is the top level. |
| pwd := "" |
| for wd := abs; wd != pwd; wd = filepath.Dir(wd) { |
| pwd = wd |
| |
| // Don't look for packages outside the GOPATH |
| // Note, the GOPATH may or may not end with the path separator. |
| // The output of filepath.Dir does not the the path separator on the |
| // end so we need to test both. |
| if wd == b.GOPATH || wd+string(os.PathSeparator) == b.GOPATH { |
| break |
| } |
| p = filepath.Join(wd, "vendor", name) |
| if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { |
| info.Path = p |
| info.Loc = dependency.LocVendor |
| 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() || gpath.IsLink(fi)) { |
| info.Path = p |
| info.Loc = dependency.LocGopath |
| 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() || gpath.IsLink(fi)) { |
| info.Path = p |
| info.Loc = dependency.LocGoroot |
| return info |
| } |
| } |
| |
| // If this is "C", we're dealing with cgo |
| if name == "C" { |
| info.Loc = dependency.LocCgo |
| } else if name == "appengine" || name == "appengine_internal" || |
| strings.HasPrefix(name, "appengine/") || |
| strings.HasPrefix(name, "appengine_internal/") { |
| // Appengine is a special case when it comes to Go builds. It is a local |
| // looking package only available within appengine. It's a special case |
| // where Google products are playing with each other. |
| // https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath |
| info.Loc = dependency.LocAppengine |
| } else if name == "context" || name == "net/http/httptrace" { |
| // context and net/http/httptrace are packages being added to |
| // the Go 1.7 standard library. Some packages, such as golang.org/x/net |
| // are importing it with build flags in files for go1.7. Need to detect |
| // this and handle it. |
| info.Loc = dependency.LocGoroot |
| } |
| |
| return info |
| } |
| |
| // copyList copies an existing list to a new list. |
| 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 |
| } |
| |
| // findInList searches a list haystack for a string needle. |
| 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 |
| } |