github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/cmd/gb/depset.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"go/build"
     6  	"path/filepath"
     7  	"runtime"
     8  
     9  	"github.com/constabulary/gb"
    10  	"github.com/constabulary/gb/cmd"
    11  	"github.com/constabulary/gb/internal/vendor"
    12  )
    13  
    14  func init() {
    15  	registerCommand(&cmd.Command{
    16  		Name: "depset",
    17  		Run:  depset,
    18  	})
    19  }
    20  
    21  func depset(ctx *gb.Context, args []string) error {
    22  	paths := []struct {
    23  		Root, Prefix string
    24  	}{
    25  		{filepath.Join(runtime.GOROOT(), "src"), ""},
    26  		{filepath.Join(ctx.Projectdir(), "src"), ""},
    27  	}
    28  	m, err := vendor.ReadManifest(filepath.Join("vendor", "manifest"))
    29  	if err != nil {
    30  		return err
    31  	}
    32  	for _, d := range m.Dependencies {
    33  		paths = append(paths, struct{ Root, Prefix string }{filepath.Join(ctx.Projectdir(), "vendor", "src", filepath.FromSlash(d.Importpath)), filepath.FromSlash(d.Importpath)})
    34  	}
    35  
    36  	dsm, err := vendor.LoadPaths(paths...)
    37  	if err != nil {
    38  		return err
    39  	}
    40  	for _, set := range dsm {
    41  		fmt.Printf("%s (%s)\n", set.Root, set.Prefix)
    42  		for _, p := range set.Pkgs {
    43  			fmt.Printf("\t%s (%s)\n", p.ImportPath, p.Name)
    44  			fmt.Printf("\t\timports: %s\n", p.Imports)
    45  		}
    46  	}
    47  
    48  	root := paths[1] // $PROJECT/src
    49  	rs := dsm[root.Root].Pkgs
    50  
    51  	fmt.Println("missing:")
    52  	for missing := range findMissing(pkgs(rs), dsm) {
    53  		fmt.Printf("\t%s\n", missing)
    54  	}
    55  
    56  	fmt.Println("orphaned:")
    57  	for orphan := range findOrphaned(pkgs(rs), dsm) {
    58  		fmt.Printf("\t%s\n", orphan)
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  func pkgs(m map[string]*vendor.Pkg) []*vendor.Pkg {
    65  	var p []*vendor.Pkg
    66  	for _, v := range m {
    67  		p = append(p, v)
    68  	}
    69  	return p
    70  }
    71  
    72  func findMissing(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool {
    73  	missing := make(map[string]bool)
    74  	imports := make(map[string]*vendor.Pkg)
    75  	for _, s := range dsm {
    76  		for _, p := range s.Pkgs {
    77  			imports[p.ImportPath] = p
    78  		}
    79  	}
    80  
    81  	// make fake C package for cgo
    82  	imports["C"] = &vendor.Pkg{
    83  		Depset: nil, // probably a bad idea
    84  		Package: &build.Package{
    85  			Name: "C",
    86  		},
    87  	}
    88  	stk := make(map[string]bool)
    89  	push := func(v string) {
    90  		if stk[v] {
    91  			panic(fmt.Sprintln("import loop:", v, stk))
    92  		}
    93  		stk[v] = true
    94  	}
    95  	pop := func(v string) {
    96  		if !stk[v] {
    97  			panic(fmt.Sprintln("impossible pop:", v, stk))
    98  		}
    99  		delete(stk, v)
   100  	}
   101  
   102  	// checked records import paths who's dependencies are all present
   103  	checked := make(map[string]bool)
   104  
   105  	var fn func(string)
   106  	fn = func(importpath string) {
   107  		p, ok := imports[importpath]
   108  		if !ok {
   109  			missing[importpath] = true
   110  			return
   111  		}
   112  
   113  		// have we already walked this arm, if so, skip it
   114  		if checked[importpath] {
   115  			return
   116  		}
   117  
   118  		sz := len(missing)
   119  		push(importpath)
   120  		for _, i := range p.Imports {
   121  			if i == importpath {
   122  				continue
   123  			}
   124  			fn(i)
   125  		}
   126  
   127  		// if the size of the missing map has not changed
   128  		// this entire subtree is complete, mark it as such
   129  		if len(missing) == sz {
   130  			checked[importpath] = true
   131  		}
   132  		pop(importpath)
   133  	}
   134  	for _, pkg := range pkgs {
   135  		fn(pkg.ImportPath)
   136  	}
   137  	return missing
   138  }
   139  
   140  func findOrphaned(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool {
   141  	missing := make(map[string]bool)
   142  	imports := make(map[string]*vendor.Pkg)
   143  	for _, s := range dsm {
   144  		for _, p := range s.Pkgs {
   145  			imports[p.ImportPath] = p
   146  		}
   147  	}
   148  
   149  	orphans := make(map[string]bool)
   150  	for k := range dsm {
   151  		orphans[k] = true
   152  	}
   153  
   154  	// make fake C package for cgo
   155  	imports["C"] = &vendor.Pkg{
   156  		Depset: new(vendor.Depset),
   157  		Package: &build.Package{
   158  			Name: "C",
   159  		},
   160  	}
   161  	stk := make(map[string]bool)
   162  	push := func(v string) {
   163  		if stk[v] {
   164  			panic(fmt.Sprintln("import loop:", v, stk))
   165  		}
   166  		stk[v] = true
   167  	}
   168  	pop := func(v string) {
   169  		if !stk[v] {
   170  			panic(fmt.Sprintln("impossible pop:", v, stk))
   171  		}
   172  		delete(stk, v)
   173  	}
   174  
   175  	// checked records import paths who's dependencies are all present
   176  	checked := make(map[string]bool)
   177  
   178  	var fn func(string)
   179  	fn = func(importpath string) {
   180  		p, ok := imports[importpath]
   181  		if !ok {
   182  			missing[importpath] = true
   183  			return
   184  		}
   185  
   186  		// delete the pkg's depset, as it is referenced
   187  		delete(orphans, p.Depset.Root)
   188  
   189  		// have we already walked this arm, if so, skip it
   190  		if checked[importpath] {
   191  			return
   192  		}
   193  
   194  		sz := len(missing)
   195  		push(importpath)
   196  		for _, i := range p.Imports {
   197  			if i == importpath {
   198  				continue
   199  			}
   200  			fn(i)
   201  		}
   202  
   203  		// if the size of the missing map has not changed
   204  		// this entire subtree is complete, mark it as such
   205  		if len(missing) == sz {
   206  			checked[importpath] = true
   207  		}
   208  		pop(importpath)
   209  	}
   210  	for _, pkg := range pkgs {
   211  		fn(pkg.ImportPath)
   212  	}
   213  	return orphans
   214  }