github.com/sc0rp1us/gb@v0.4.1-0.20160319180011-4ba8cf1baa5a/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/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 keys(m map[string]bool) []string { 65 var s []string 66 for k := range m { 67 s = append(s, k) 68 } 69 return s 70 } 71 72 func pkgs(m map[string]*vendor.Pkg) []*vendor.Pkg { 73 var p []*vendor.Pkg 74 for _, v := range m { 75 p = append(p, v) 76 } 77 return p 78 } 79 80 func findMissing(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { 81 missing := make(map[string]bool) 82 imports := make(map[string]*vendor.Pkg) 83 for _, s := range dsm { 84 for _, p := range s.Pkgs { 85 imports[p.ImportPath] = p 86 } 87 } 88 89 // make fake C package for cgo 90 imports["C"] = &vendor.Pkg{ 91 Depset: nil, // probably a bad idea 92 Package: &build.Package{ 93 Name: "C", 94 }, 95 } 96 stk := make(map[string]bool) 97 push := func(v string) { 98 if stk[v] { 99 panic(fmt.Sprintln("import loop:", v, stk)) 100 } 101 stk[v] = true 102 } 103 pop := func(v string) { 104 if !stk[v] { 105 panic(fmt.Sprintln("impossible pop:", v, stk)) 106 } 107 delete(stk, v) 108 } 109 110 // checked records import paths who's dependencies are all present 111 checked := make(map[string]bool) 112 113 var fn func(string) 114 fn = func(importpath string) { 115 p, ok := imports[importpath] 116 if !ok { 117 missing[importpath] = true 118 return 119 } 120 121 // have we already walked this arm, if so, skip it 122 if checked[importpath] { 123 return 124 } 125 126 sz := len(missing) 127 push(importpath) 128 for _, i := range p.Imports { 129 if i == importpath { 130 continue 131 } 132 fn(i) 133 } 134 135 // if the size of the missing map has not changed 136 // this entire subtree is complete, mark it as such 137 if len(missing) == sz { 138 checked[importpath] = true 139 } 140 pop(importpath) 141 } 142 for _, pkg := range pkgs { 143 fn(pkg.ImportPath) 144 } 145 return missing 146 } 147 148 func findOrphaned(pkgs []*vendor.Pkg, dsm map[string]*vendor.Depset) map[string]bool { 149 missing := make(map[string]bool) 150 imports := make(map[string]*vendor.Pkg) 151 for _, s := range dsm { 152 for _, p := range s.Pkgs { 153 imports[p.ImportPath] = p 154 } 155 } 156 157 orphans := make(map[string]bool) 158 for k := range dsm { 159 orphans[k] = true 160 } 161 162 // make fake C package for cgo 163 imports["C"] = &vendor.Pkg{ 164 Depset: new(vendor.Depset), 165 Package: &build.Package{ 166 Name: "C", 167 }, 168 } 169 stk := make(map[string]bool) 170 push := func(v string) { 171 if stk[v] { 172 panic(fmt.Sprintln("import loop:", v, stk)) 173 } 174 stk[v] = true 175 } 176 pop := func(v string) { 177 if !stk[v] { 178 panic(fmt.Sprintln("impossible pop:", v, stk)) 179 } 180 delete(stk, v) 181 } 182 183 // checked records import paths who's dependencies are all present 184 checked := make(map[string]bool) 185 186 var fn func(string) 187 fn = func(importpath string) { 188 p, ok := imports[importpath] 189 if !ok { 190 missing[importpath] = true 191 return 192 } 193 194 // delete the pkg's depset, as it is referenced 195 delete(orphans, p.Depset.Root) 196 197 // have we already walked this arm, if so, skip it 198 if checked[importpath] { 199 return 200 } 201 202 sz := len(missing) 203 push(importpath) 204 for _, i := range p.Imports { 205 if i == importpath { 206 continue 207 } 208 fn(i) 209 } 210 211 // if the size of the missing map has not changed 212 // this entire subtree is complete, mark it as such 213 if len(missing) == sz { 214 checked[importpath] = true 215 } 216 pop(importpath) 217 } 218 for _, pkg := range pkgs { 219 fn(pkg.ImportPath) 220 } 221 return orphans 222 }