github.com/ngdinhtoan/glide@v0.12.3/tree/tree.go (about) 1 package tree 2 3 import ( 4 "container/list" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/Masterminds/glide/dependency" 10 "github.com/Masterminds/glide/msg" 11 gpath "github.com/Masterminds/glide/path" 12 "github.com/Masterminds/glide/util" 13 ) 14 15 // Display displays a tree view of the given project. 16 // 17 // FIXME: The output formatting could use some TLC. 18 func Display(b *util.BuildCtxt, basedir, myName string, level int, core bool, l *list.List) { 19 deps := walkDeps(b, basedir, myName) 20 for _, name := range deps { 21 found := findPkg(b, name, basedir) 22 if found.Loc == dependency.LocUnknown { 23 m := "glide get " + found.Name 24 msg.Puts("\t%s\t(%s)", found.Name, m) 25 continue 26 } 27 if !core && found.Loc == dependency.LocGoroot || found.Loc == dependency.LocCgo { 28 continue 29 } 30 msg.Print(strings.Repeat("|\t", level-1) + "|-- ") 31 32 f := findInList(found.Name, l) 33 if f == true { 34 msg.Puts("(Recursion) %s (%s)", found.Name, found.Path) 35 } else { 36 // Every branch in the tree is a copy to handle all the branches 37 cl := copyList(l) 38 cl.PushBack(found.Name) 39 msg.Puts("%s (%s)", found.Name, found.Path) 40 Display(b, found.Path, found.Name, level+1, core, cl) 41 } 42 } 43 } 44 45 func walkDeps(b *util.BuildCtxt, base, myName string) []string { 46 externalDeps := []string{} 47 filepath.Walk(base, func(path string, fi os.FileInfo, err error) error { 48 if err != nil { 49 return err 50 } 51 52 if !dependency.IsSrcDir(fi) { 53 if fi.IsDir() { 54 return filepath.SkipDir 55 } 56 return nil 57 } 58 59 var imps []string 60 pkg, err := b.ImportDir(path, 0) 61 if err != nil && strings.HasPrefix(err.Error(), "found packages ") { 62 // If we got here it's because a package and multiple packages 63 // declared. This is often because of an example with a package 64 // or main but +build ignore as a build tag. In that case we 65 // try to brute force the packages with a slower scan. 66 imps, _, err = dependency.IterativeScan(path) 67 if err != nil { 68 msg.Err("Error walking dependencies for %s: %s", path, err) 69 return err 70 } 71 } else if err != nil { 72 if !strings.HasPrefix(err.Error(), "no buildable Go source") { 73 msg.Warn("Error: %s (%s)", err, path) 74 // Not sure if we should return here. 75 //return err 76 } 77 } else { 78 imps = pkg.Imports 79 } 80 81 if pkg.Goroot { 82 return nil 83 } 84 85 for _, imp := range imps { 86 //if strings.HasPrefix(imp, myName) { 87 ////Info("Skipping %s because it is a subpackage of %s", imp, myName) 88 //continue 89 //} 90 if imp == myName { 91 continue 92 } 93 externalDeps = append(externalDeps, imp) 94 } 95 96 return nil 97 }) 98 return externalDeps 99 } 100 101 func findPkg(b *util.BuildCtxt, name, cwd string) *dependency.PkgInfo { 102 var fi os.FileInfo 103 var err error 104 var p string 105 106 info := &dependency.PkgInfo{ 107 Name: name, 108 } 109 110 if strings.HasPrefix(name, "./") || strings.HasPrefix(name, "../") { 111 info.Loc = dependency.LocRelative 112 return info 113 } 114 115 // Recurse backward to scan other vendor/ directories 116 // If the cwd isn't an absolute path walking upwards looking for vendor/ 117 // folders can get into an infinate loop. 118 abs, err := filepath.Abs(cwd) 119 if err != nil { 120 abs = cwd 121 } 122 if abs != "." { 123 // Previously there was a check on the loop that wd := "/". The path 124 // "/" is a POSIX path so this fails on Windows. Now the check is to 125 // make sure the same wd isn't seen twice. When the same wd happens 126 // more than once it's the beginning of looping on the same location 127 // which is the top level. 128 pwd := "" 129 for wd := abs; wd != pwd; wd = filepath.Dir(wd) { 130 pwd = wd 131 132 // Don't look for packages outside the GOPATH 133 // Note, the GOPATH may or may not end with the path separator. 134 // The output of filepath.Dir does not the the path separator on the 135 // end so we need to test both. 136 if wd == b.GOPATH || wd+string(os.PathSeparator) == b.GOPATH { 137 break 138 } 139 p = filepath.Join(wd, "vendor", name) 140 if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { 141 info.Path = p 142 info.Loc = dependency.LocVendor 143 info.Vendored = true 144 return info 145 } 146 } 147 } 148 // Check $GOPATH 149 for _, r := range strings.Split(b.GOPATH, ":") { 150 p = filepath.Join(r, "src", name) 151 if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { 152 info.Path = p 153 info.Loc = dependency.LocGopath 154 return info 155 } 156 } 157 158 // Check $GOROOT 159 for _, r := range strings.Split(b.GOROOT, ":") { 160 p = filepath.Join(r, "src", name) 161 if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) { 162 info.Path = p 163 info.Loc = dependency.LocGoroot 164 return info 165 } 166 } 167 168 // If this is "C", we're dealing with cgo 169 if name == "C" { 170 info.Loc = dependency.LocCgo 171 } else if name == "appengine" || name == "appengine_internal" || 172 strings.HasPrefix(name, "appengine/") || 173 strings.HasPrefix(name, "appengine_internal/") { 174 // Appengine is a special case when it comes to Go builds. It is a local 175 // looking package only available within appengine. It's a special case 176 // where Google products are playing with each other. 177 // https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath 178 info.Loc = dependency.LocAppengine 179 } else if name == "context" || name == "net/http/httptrace" { 180 // context and net/http/httptrace are packages being added to 181 // the Go 1.7 standard library. Some packages, such as golang.org/x/net 182 // are importing it with build flags in files for go1.7. Need to detect 183 // this and handle it. 184 info.Loc = dependency.LocGoroot 185 } 186 187 return info 188 } 189 190 // copyList copies an existing list to a new list. 191 func copyList(l *list.List) *list.List { 192 n := list.New() 193 for e := l.Front(); e != nil; e = e.Next() { 194 n.PushBack(e.Value.(string)) 195 } 196 return n 197 } 198 199 // findInList searches a list haystack for a string needle. 200 func findInList(n string, l *list.List) bool { 201 for e := l.Front(); e != nil; e = e.Next() { 202 if e.Value.(string) == n { 203 return true 204 } 205 } 206 207 return false 208 }