github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/maven/maven.go (about) 1 // Package maven implements Maven analysis. 2 // 3 // A `BuildTarget` for Maven is either a Maven project ID (groupId:artifactId), or a path to a directory in 4 // which there is a "pom.xml" file, or a path to a POM file. 5 package maven 6 7 import ( 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/apex/log" 13 "github.com/mitchellh/mapstructure" 14 "github.com/pkg/errors" 15 16 "github.com/fossas/fossa-cli/buildtools/maven" 17 "github.com/fossas/fossa-cli/exec" 18 "github.com/fossas/fossa-cli/files" 19 "github.com/fossas/fossa-cli/graph" 20 "github.com/fossas/fossa-cli/module" 21 "github.com/fossas/fossa-cli/pkg" 22 ) 23 24 type Analyzer struct { 25 Maven maven.Maven 26 Module module.Module 27 Options Options 28 } 29 30 type Options struct { 31 Binary string `mapstructure:"bin"` 32 Command string `mapstructure:"cmd"` 33 // Strategy can be "pom-file", "maven-tree", or empty. 34 Strategy string `mapstructure:"strategy"` 35 } 36 37 func New(m module.Module) (*Analyzer, error) { 38 log.Debugf("%#v", m.Options) 39 40 // Decode options. 41 var options Options 42 err := mapstructure.Decode(m.Options, &options) 43 if err != nil { 44 return nil, err 45 } 46 47 // Get Maven binary. 48 mvnBin, _, err := exec.Which("--version", options.Binary, os.Getenv("MAVEN_BINARY"), "mvn") 49 if err != nil { 50 log.Warnf("Could not find Maven binary: %s", err.Error()) 51 } 52 53 analyzer := Analyzer{ 54 Maven: maven.Maven{ 55 Cmd: mvnBin, 56 }, 57 Module: m, 58 Options: options, 59 } 60 61 log.WithField("analyzer", analyzer).Debug("constructed analyzer") 62 return &analyzer, nil 63 } 64 65 func Discover(dir string, options map[string]interface{}) ([]module.Module, error) { 66 log.WithField("dir", dir).Debug("discovering modules") 67 var modules []module.Module 68 checked := make(map[string]bool) 69 70 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 71 if err != nil { 72 log.WithError(err).WithField("path", path).Debug("error while walking for discovery") 73 return err 74 } 75 76 if info.IsDir() { 77 ok, err := files.Exists(path, "pom.xml") 78 if err != nil { 79 return err 80 } 81 if !ok { 82 return nil 83 } 84 85 submodules, err := maven.Modules(filepath.Join(path, "pom.xml"), path, checked) 86 if err != nil { 87 log.WithError(err).Debugf("could not get modules at path %s", path) 88 return err 89 } 90 91 for _, m := range submodules { 92 modules = append(modules, module.Module{ 93 Name: m.Name, 94 Type: pkg.Maven, 95 BuildTarget: m.Target, 96 Dir: m.Dir, 97 }) 98 } 99 // Continue recursing because there may be modules that are not declared under the current module. 100 } 101 102 return nil 103 }) 104 105 if err != nil { 106 return nil, errors.Wrap(err, "could not find Maven projects") 107 } 108 109 return modules, nil 110 } 111 112 func (a *Analyzer) Clean() error { 113 return a.Maven.Clean(a.Module.Dir) 114 } 115 116 func (a *Analyzer) Build() error { 117 return a.Maven.Compile(a.Module.Dir) 118 } 119 120 // IsBuilt checks whether `mvn dependency:list` returns without error. 121 func (a *Analyzer) IsBuilt() (bool, error) { 122 output, err := a.Maven.DependencyList(a.Module.Dir, a.Module.BuildTarget) 123 if err != nil { 124 if strings.Contains(output, "Could not find artifact") { 125 return false, nil 126 } 127 return false, err 128 } 129 return output != "", nil 130 } 131 132 func (a *Analyzer) Analyze() (graph.Deps, error) { 133 log.WithField("module", a.Module).Debug("analyzing module") 134 135 switch a.Options.Strategy { 136 case "pom-file": 137 return maven.PomFileGraph(a.Module.BuildTarget, a.Module.Dir) 138 case "maven-tree": 139 return a.Maven.DependencyTree(a.Module.Dir, a.Module.BuildTarget) 140 default: 141 if a.Options.Command != "" { 142 output, _, err := exec.Shell(exec.Cmd{ 143 Command: a.Options.Command, 144 }) 145 if err != nil { 146 // Because this was a custom shell command, we do not fall back to any other strategies. 147 return graph.Deps{}, err 148 } 149 return maven.ParseDependencyTree(output) 150 } 151 152 deps, err := a.Maven.DependencyTree(a.Module.Dir, a.Module.BuildTarget) 153 if err != nil { 154 log.Warnf( 155 "Could not use Maven to determine dependencies for %q: %v. Falling back to use manifest file.", 156 a.Module.Name, 157 err, 158 ) 159 } else if len(deps.Direct) == 0 { 160 log.Warnf("Maven did not find dependencies for %q. Falling back to use manifest file.", 161 a.Module.Name) 162 } else { 163 return deps, nil 164 } 165 166 return maven.PomFileGraph(a.Module.BuildTarget, a.Module.Dir) 167 } 168 }