github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/okbuck/okbuck.go (about) 1 package okbuck 2 3 import ( 4 "encoding/json" 5 "strings" 6 7 "github.com/fossas/fossa-cli/graph" 8 "github.com/fossas/fossa-cli/pkg" 9 ) 10 11 // OkBuck defines an interface for all OkBuck tool implementations. 12 type OkBuck interface { 13 Deps(string) (graph.Deps, error) 14 } 15 16 // Setup implements OkBuck and defines how to retrieve OkBuck output. 17 type Setup struct { 18 Target string 19 Cmd func(...string) (string, error) 20 } 21 22 // Target represents an OkBuck build target. 23 type Target struct { 24 BinaryJar string `json:"binaryJar"` 25 Aar string `json:"aar"` 26 MavenCoordinates string `json:"mavenCoords"` 27 } 28 29 // Dependency holds revision information after parsing output. 30 type Dependency struct { 31 Jar string 32 Name string 33 Revision string 34 } 35 36 // New creates a new OkBuck instance that calls the okbuck build tool directly. 37 func New(target string) OkBuck { 38 return Setup{ 39 Target: target, 40 Cmd: Cmd, 41 } 42 } 43 44 // Deps finds the dependencies of an OkBuck target or classpath using 45 // the supplied command and returns the dependency graph. 46 func (b Setup) Deps(classpath string) (graph.Deps, error) { 47 out, err := b.Cmd("targets", b.Target, "--json") 48 if err != nil { 49 return graph.Deps{}, err 50 } 51 52 var targets []Target 53 err = json.Unmarshal([]byte(out), &targets) 54 if err != nil { 55 return graph.Deps{}, err 56 } 57 58 dependencies := depsFromTargets(targets) 59 60 if classpath != "" { 61 out, err := b.Cmd("audit", "classpath", classpath) 62 if err != nil { 63 return graph.Deps{}, err 64 } 65 jars := classpathJars(out) 66 return graphFromJars(jars, dependencies), nil 67 } 68 69 depGraph := graph.Deps{ 70 Direct: []pkg.Import{}, 71 Transitive: make(map[pkg.ID]pkg.Package), 72 } 73 for _, dep := range dependencies { 74 id := pkg.ID{ 75 Type: pkg.Maven, 76 Name: dep.Name, 77 Revision: dep.Revision, 78 } 79 depGraph.Direct = append(depGraph.Direct, pkg.Import{Resolved: id}) 80 depGraph.Transitive[id] = pkg.Package{ 81 ID: id, 82 Imports: []pkg.Import{}, 83 } 84 } 85 86 return depGraph, nil 87 } 88 89 // depsFromTargets takes an OkBuck target list and creates a list of 90 // Dependencies that can be used to build a graph.Deps object. 91 func depsFromTargets(targets []Target) []Dependency { 92 var deps []Dependency 93 for _, target := range targets { 94 splitDep := strings.Split(target.MavenCoordinates, ":") 95 if len(splitDep) == 4 { 96 deps = append(deps, Dependency{ 97 Jar: conditionJar(target), 98 Name: splitDep[0] + ":" + splitDep[1], 99 Revision: splitDep[3], 100 }) 101 } 102 } 103 return deps 104 } 105 106 // classpathJars reads output from `buck audit classpath` and 107 // finds the unique jar identifies. Examples: 108 // .okbuck/android/constraint/__package.aar#aar_prebuilt_jar__/classes.jar --> package.aar 109 // .okbuck/android/constraint/__package.jar__/package.jar --> package.jar 110 func classpathJars(cmdOut string) []string { 111 jarList := strings.Split(cmdOut, "\n") 112 deps := []string{} 113 for _, jar := range jarList { 114 jarFromLine := strings.Split(jar, "_") 115 if len(jarFromLine) >= 3 { 116 jarFromLine = strings.Split(jarFromLine[2], "#") 117 } 118 deps = append(deps, jarFromLine[0]) 119 } 120 return deps 121 } 122 123 func graphFromJars(jarList []string, dependencies []Dependency) graph.Deps { 124 depGraph := graph.Deps{ 125 Direct: []pkg.Import{}, 126 Transitive: make(map[pkg.ID]pkg.Package), 127 } 128 129 jarMap := mapJars(dependencies) 130 for _, jar := range jarList { 131 dep := jarMap[jar] 132 if dep != (Dependency{}) { 133 id := pkg.ID{ 134 Type: pkg.Maven, 135 Name: dep.Name, 136 Revision: dep.Revision, 137 } 138 depGraph.Direct = append(depGraph.Direct, pkg.Import{Resolved: id}) 139 depGraph.Transitive[id] = pkg.Package{ 140 ID: id, 141 Imports: []pkg.Import{}, 142 } 143 } 144 } 145 return depGraph 146 } 147 148 // mapJars creates a mapping of a jar to the full Dependency. 149 func mapJars(dependencies []Dependency) map[string]Dependency { 150 jarMap := make(map[string]Dependency) 151 for _, dependency := range dependencies { 152 jarMap[dependency.Jar] = dependency 153 } 154 return jarMap 155 } 156 157 // conditionJar determines what an OkBuck targets jar is. 158 // "aar" : ":package.aar__downloaded" -> package.aar 159 // "binaryJar" : ":package.jar__downloaded" -> package.jar 160 func conditionJar(target Target) string { 161 jar := target.BinaryJar 162 if target.Aar != "" { 163 jar = target.Aar 164 } 165 jar = strings.TrimPrefix(jar, ":") 166 jar = strings.Split(jar, "_")[0] 167 return jar 168 }