github.com/avvmoto/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  }