| package vendor | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"go/build" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | // Pkg describes a Go package. | 
 | type Pkg struct { | 
 | 	*Depset | 
 | 	*build.Package | 
 | } | 
 |  | 
 | // Depset describes a set of related Go packages. | 
 | type Depset struct { | 
 | 	Root   string | 
 | 	Prefix string | 
 | 	Pkgs   map[string]*Pkg | 
 | } | 
 |  | 
 | // LoadPaths returns a map of paths to Depsets. | 
 | func LoadPaths(paths ...struct{ Root, Prefix string }) (map[string]*Depset, error) { | 
 | 	m := make(map[string]*Depset) | 
 | 	for _, p := range paths { | 
 | 		set, err := LoadTree(p.Root, p.Prefix) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		m[set.Root] = set | 
 | 	} | 
 | 	return m, nil | 
 | } | 
 |  | 
 | // LoadTree parses a tree of source files into a map of *pkgs. | 
 | func LoadTree(root string, prefix string) (*Depset, error) { | 
 | 	d := Depset{ | 
 | 		Root:   root, | 
 | 		Prefix: prefix, | 
 | 		Pkgs:   make(map[string]*Pkg), | 
 | 	} | 
 | 	fn := func(dir string, fi os.FileInfo) error { | 
 | 		importpath := filepath.Join(prefix, dir[len(root)+1:]) | 
 |  | 
 | 		// if we're at the root of a tree, skip it | 
 | 		if importpath == "" { | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		p, err := loadPackage(&d, dir) | 
 | 		if err != nil { | 
 | 			if _, ok := err.(*build.NoGoError); ok { | 
 | 				return nil | 
 | 			} | 
 | 			return fmt.Errorf("loadPackage(%q, %q): %v", dir, importpath, err) | 
 | 		} | 
 | 		p.ImportPath = filepath.ToSlash(importpath) | 
 | 		if p != nil { | 
 | 			d.Pkgs[p.ImportPath] = p | 
 | 		} | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	// handle root of the tree | 
 | 	fi, err := os.Stat(root) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	if err := fn(root+string(filepath.Separator), fi); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	// walk sub directories | 
 | 	err = eachDir(root, fn) | 
 | 	return &d, err | 
 | } | 
 |  | 
 | func loadPackage(d *Depset, dir string) (*Pkg, error) { | 
 | 	p := Pkg{ | 
 | 		Depset: d, | 
 | 	} | 
 | 	var err error | 
 |  | 
 | 	// expolit local import logic | 
 | 	p.Package, err = build.ImportDir(dir, build.ImportComment) | 
 | 	return &p, err | 
 | } | 
 |  | 
 | func eachDir(dir string, fn func(string, os.FileInfo) error) error { | 
 | 	f, err := os.Open(dir) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	defer f.Close() | 
 | 	files, err := f.Readdir(-1) | 
 | 	for _, fi := range files { | 
 | 		if !fi.IsDir() { | 
 | 			continue | 
 | 		} | 
 | 		if strings.HasPrefix(fi.Name(), "_") || strings.HasPrefix(fi.Name(), ".") || fi.Name() == "testdata" { | 
 | 			continue | 
 | 		} | 
 | 		path := filepath.Join(dir, fi.Name()) | 
 | 		if err := fn(path, fi); err != nil { | 
 | 			return err | 
 | 		} | 
 | 		if err := eachDir(path, fn); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } |