github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/maven/pom.go (about) 1 package maven 2 3 import ( 4 "encoding/xml" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/pkg/errors" 10 11 "github.com/fossas/fossa-cli/files" 12 "github.com/fossas/fossa-cli/graph" 13 "github.com/fossas/fossa-cli/pkg" 14 ) 15 16 // A Manifest represents a POM manifest file. 17 type Manifest struct { 18 Project xml.Name `xml:"project"` 19 Parent Parent `xml:"parent"` 20 Modules []string `xml:"modules>module"` 21 ArtifactID string `xml:"artifactId"` 22 GroupID string `xml:"groupId"` 23 Version string `xml:"version"` 24 Description string `xml:"description"` 25 Name string `xml:"name"` 26 URL string `xml:"url"` 27 Dependencies []Dependency `xml:"dependencies>dependency"` 28 DependencyManagement []Dependency `xml:"dependencyManagement>dependencies>dependency"` 29 } 30 31 type Parent struct { 32 ArtifactID string `xml:"artifactId"` 33 GroupID string `xml:"groupId"` 34 Version string `xml:"version"` 35 } 36 37 type Dependency struct { 38 GroupId string `xml:"groupId"` 39 ArtifactId string `xml:"artifactId"` 40 Version string `xml:"version"` 41 42 // Scope is where the dependency is used, such as "test" or "runtime". 43 Scope string `xml:"scope"` 44 45 Failed bool 46 } 47 48 // ID returns the dependency identifier as groupId:artifactId. 49 func (d Dependency) ID() string { 50 return d.GroupId + ":" + d.ArtifactId 51 } 52 53 // PomFileGraph returns simply the list of dependencies listed within the manifest file. 54 func PomFileGraph(target, dir string) (graph.Deps, error) { 55 pom, err := ResolveManifestFromTarget(target, dir) 56 if err != nil { 57 return graph.Deps{}, err 58 } 59 60 // Aggregate `dependencies` and `dependencyManagement` fields. 61 dependencyList := combineDependencies(pom.Dependencies, pom.DependencyManagement) 62 63 deps := graph.Deps{ 64 Direct: depsListToImports(dependencyList), 65 Transitive: make(map[pkg.ID]pkg.Package), 66 } 67 68 // From just a POM file we don't know what really depends on what, so list all imports in the graph. 69 for _, dep := range dependencyList { 70 pack := pkg.Package{ 71 ID: pkg.ID{ 72 Type: pkg.Maven, 73 Name: dep.ID(), 74 Revision: dep.Version, 75 }, 76 } 77 deps.Transitive[pack.ID] = pack 78 } 79 80 return deps, nil 81 } 82 83 // ResolveManifestFromTarget tries to determine what target is supposed to be and then reads the POM 84 // manifest file pointed to by target if it is a path to such a file or module. 85 func ResolveManifestFromTarget(target, dir string) (*Manifest, error) { 86 pomFile := filepath.Join(dir, target) 87 stat, err := os.Stat(pomFile) 88 if err != nil { 89 // target is not a path. 90 if strings.Count(target, ":") == 1 { 91 // This is likely a module ID. 92 return nil, errors.Errorf("cannot identify POM file for module %q", target) 93 } 94 return nil, errors.Wrapf(err, "manifest file for %q cannot be read", target) 95 } 96 if stat.IsDir() { 97 // We have the directory and will assume it uses the standard name for the manifest file. 98 pomFile = filepath.Join(target, "pom.xml") 99 } 100 101 var pom Manifest 102 if err := files.ReadXML(&pom, pomFile); err != nil { 103 return nil, err 104 } 105 return &pom, nil 106 } 107 108 // combineDependencies combines and dedupes two lists of Dependencies. 109 func combineDependencies(listOne, listTwo []Dependency) []Dependency { 110 mergedList := listOne 111 listOneMap := make(map[Dependency]bool) 112 for _, dep := range listOne { 113 listOneMap[dep] = true 114 } 115 for _, dep := range listTwo { 116 if _, exists := listOneMap[dep]; !exists { 117 mergedList = append(mergedList, dep) 118 } 119 } 120 return mergedList 121 }