github.com/goguardian/glide@v0.0.0-20160311175917-84255172e124/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 !dependency.IsSrcDir(fi) {
    49  			if fi.IsDir() {
    50  				return filepath.SkipDir
    51  			}
    52  			return nil
    53  		}
    54  
    55  		var imps []string
    56  		pkg, err := b.ImportDir(path, 0)
    57  		if err != nil && strings.HasPrefix(err.Error(), "found packages ") {
    58  			// If we got here it's because a package and multiple packages
    59  			// declared. This is often because of an example with a package
    60  			// or main but +build ignore as a build tag. In that case we
    61  			// try to brute force the packages with a slower scan.
    62  			imps, err = dependency.IterativeScan(path)
    63  			if err != nil {
    64  				msg.Err("Error walking dependencies for %s: %s", path, err)
    65  				return err
    66  			}
    67  		} else if err != nil {
    68  			if !strings.HasPrefix(err.Error(), "no buildable Go source") {
    69  				msg.Warn("Error: %s (%s)", err, path)
    70  				// Not sure if we should return here.
    71  				//return err
    72  			}
    73  		} else {
    74  			imps = pkg.Imports
    75  		}
    76  
    77  		if pkg.Goroot {
    78  			return nil
    79  		}
    80  
    81  		for _, imp := range imps {
    82  			//if strings.HasPrefix(imp, myName) {
    83  			////Info("Skipping %s because it is a subpackage of %s", imp, myName)
    84  			//continue
    85  			//}
    86  			if imp == myName {
    87  				continue
    88  			}
    89  			externalDeps = append(externalDeps, imp)
    90  		}
    91  
    92  		return nil
    93  	})
    94  	return externalDeps
    95  }
    96  
    97  func findPkg(b *util.BuildCtxt, name, cwd string) *dependency.PkgInfo {
    98  	var fi os.FileInfo
    99  	var err error
   100  	var p string
   101  
   102  	info := &dependency.PkgInfo{
   103  		Name: name,
   104  	}
   105  
   106  	// Recurse backward to scan other vendor/ directories
   107  	// If the cwd isn't an absolute path walking upwards looking for vendor/
   108  	// folders can get into an infinate loop.
   109  	abs, err := filepath.Abs(cwd)
   110  	if err != nil {
   111  		abs = cwd
   112  	}
   113  	if abs != "." {
   114  		// Previously there was a check on the loop that wd := "/". The path
   115  		// "/" is a POSIX path so this fails on Windows. Now the check is to
   116  		// make sure the same wd isn't seen twice. When the same wd happens
   117  		// more than once it's the beginning of looping on the same location
   118  		// which is the top level.
   119  		pwd := ""
   120  		for wd := abs; wd != pwd; wd = filepath.Dir(wd) {
   121  			pwd = wd
   122  
   123  			// Don't look for packages outside the GOPATH
   124  			// Note, the GOPATH may or may not end with the path separator.
   125  			// The output of filepath.Dir does not the the path separator on the
   126  			// end so we need to test both.
   127  			if wd == b.GOPATH || wd+string(os.PathSeparator) == b.GOPATH {
   128  				break
   129  			}
   130  			p = filepath.Join(wd, "vendor", name)
   131  			if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) {
   132  				info.Path = p
   133  				info.Loc = dependency.LocVendor
   134  				info.Vendored = true
   135  				return info
   136  			}
   137  		}
   138  	}
   139  	// Check $GOPATH
   140  	for _, r := range strings.Split(b.GOPATH, ":") {
   141  		p = filepath.Join(r, "src", name)
   142  		if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) {
   143  			info.Path = p
   144  			info.Loc = dependency.LocGopath
   145  			return info
   146  		}
   147  	}
   148  
   149  	// Check $GOROOT
   150  	for _, r := range strings.Split(b.GOROOT, ":") {
   151  		p = filepath.Join(r, "src", name)
   152  		if fi, err = os.Stat(p); err == nil && (fi.IsDir() || gpath.IsLink(fi)) {
   153  			info.Path = p
   154  			info.Loc = dependency.LocGoroot
   155  			return info
   156  		}
   157  	}
   158  
   159  	// If this is "C", we're dealing with cgo
   160  	if name == "C" {
   161  		info.Loc = dependency.LocCgo
   162  	} else if name == "appengine" || name == "appengine_internal" ||
   163  		strings.HasPrefix(name, "appengine/") ||
   164  		strings.HasPrefix(name, "appengine_internal/") {
   165  		// Appengine is a special case when it comes to Go builds. It is a local
   166  		// looking package only available within appengine. It's a special case
   167  		// where Google products are playing with each other.
   168  		// https://blog.golang.org/the-app-engine-sdk-and-workspaces-gopath
   169  		info.Loc = dependency.LocAppengine
   170  	}
   171  
   172  	return info
   173  }
   174  
   175  // copyList copies an existing list to a new list.
   176  func copyList(l *list.List) *list.List {
   177  	n := list.New()
   178  	for e := l.Front(); e != nil; e = e.Next() {
   179  		n.PushBack(e.Value.(string))
   180  	}
   181  	return n
   182  }
   183  
   184  // findInList searches a list haystack for a string needle.
   185  func findInList(n string, l *list.List) bool {
   186  	for e := l.Front(); e != nil; e = e.Next() {
   187  		if e.Value.(string) == n {
   188  			return true
   189  		}
   190  	}
   191  
   192  	return false
   193  }