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  }