github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/sbt/parse.go (about) 1 package sbt 2 3 import ( 4 "regexp" 5 "strings" 6 7 "github.com/apex/log" 8 "github.com/fossas/fossa-cli/pkg" 9 ) 10 11 type GraphML struct { 12 Graph Graph `xml:"graph"` 13 } 14 15 type Graph struct { 16 Nodes []Node `xml:"node"` 17 Edges []Edge `xml:"edge"` 18 } 19 20 type Node struct { 21 ID string `xml:"id,attr"` 22 } 23 24 type Edge struct { 25 Source string `xml:"source,attr"` 26 Target string `xml:"target,attr"` 27 } 28 29 func ParseDependencyGraph(graph Graph, evicted string) (pkg.Imports, pkg.Deps, error) { 30 log.WithFields(log.Fields{ 31 "graph": graph, 32 "evicted": evicted, 33 }).Debug("parsing XML graph with evictions") 34 35 replacements := ParseEvicted(evicted) 36 37 deps := make(map[pkg.ID]pkg.Imports) 38 for _, edge := range graph.Edges { 39 source := ParsePackageID(edge.Source) 40 target := ParsePackageID(edge.Target) 41 42 log.WithField("source", source).Debug("parsing graph edge") 43 44 _, ok := deps[source] 45 if !ok { 46 deps[source] = pkg.Imports{} 47 } 48 49 _, ok = deps[target] 50 if !ok { 51 deps[target] = pkg.Imports{} 52 } 53 54 replacement, ok := replacements[target] 55 if ok { 56 target = replacement 57 } 58 59 deps[source] = append(deps[source], pkg.Import{ 60 Target: target.String(), 61 Resolved: target, 62 }) 63 } 64 65 pkgs := make(pkg.Deps) 66 for id, imports := range deps { 67 pkgs[id] = pkg.Package{ 68 ID: id, 69 Imports: imports, 70 } 71 } 72 73 root := ParsePackageID(graph.Nodes[0].ID) 74 imports := deps[root] 75 delete(pkgs, root) 76 77 return imports, pkgs, nil 78 } 79 80 func ParseEvicted(evicted string) map[pkg.ID]pkg.ID { 81 replacements := make(map[pkg.ID]pkg.ID) 82 r := regexp.MustCompile("^\\[info\\] \\* (.*?) is selected over (.*?)$") 83 for _, line := range strings.Split(evicted, "\n") { 84 matches := r.FindStringSubmatch(line) 85 if matches != nil { 86 target := ParsePackageID(matches[1]) 87 if strings.HasPrefix(matches[2], "{") && strings.HasSuffix(matches[2], "}") { 88 // Handle multiple versions. 89 versions := strings.Split(strings.Trim(matches[2], "{}"), ", ") 90 for _, version := range versions { 91 source := target 92 source.Revision = version 93 replacements[source] = target 94 } 95 } else { 96 // Handle single version. 97 source := target 98 source.Revision = matches[2] 99 replacements[source] = target 100 } 101 } 102 } 103 return replacements 104 } 105 106 func ParsePackageID(packageID string) pkg.ID { 107 r := regexp.MustCompile("([^:\\s]+):([^:\\s]+):([^:\\s]+).*") 108 matches := r.FindStringSubmatch(packageID) 109 return pkg.ID{ 110 Type: pkg.Scala, 111 Name: matches[1] + ":" + matches[2], 112 Revision: matches[3], 113 } 114 }