github.com/aca02djr/gb@v0.4.1/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 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 }