github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/scala/scala.go (about) 1 // Package scala implements Scala analysis. 2 // 3 // A `BuildTarget` for scala is project:configuration. 4 package scala 5 6 import ( 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/apex/log" 12 "github.com/mitchellh/mapstructure" 13 "github.com/pkg/errors" 14 15 "github.com/fossas/fossa-cli/buildtools/sbt" 16 "github.com/fossas/fossa-cli/exec" 17 "github.com/fossas/fossa-cli/files" 18 "github.com/fossas/fossa-cli/graph" 19 "github.com/fossas/fossa-cli/module" 20 "github.com/fossas/fossa-cli/pkg" 21 ) 22 23 type Analyzer struct { 24 SBTCmd string 25 SBTVersion string 26 27 JavaCmd string 28 JavaVersion string 29 30 SBT sbt.SBT 31 Module module.Module 32 Options Options 33 } 34 35 type Options struct{} 36 37 func New(m module.Module) (*Analyzer, error) { 38 // Set Java context variables 39 javaCmd, javaVersion, err := exec.Which("-version", os.Getenv("JAVA_BINARY"), "java") 40 if err != nil { 41 // Is JAVA_HOME set? 42 // Is $FOSSA_JAVA_BINARY set? 43 log.WithError(err).Warn("could not find Java binary") 44 } 45 46 // Set SBT context variables 47 sbtCmd, sbtVersion, err := exec.WhichArgs([]string{"-no-colors", "about"}, os.Getenv("SBT_BINARY"), "sbt") 48 if err != nil { 49 // Is FOSSA_SBT_BINARY set? 50 log.WithError(err).Warn("could not find SBT binary") 51 } 52 53 // Parse and validate options. 54 var options Options 55 err = mapstructure.Decode(m.Options, &options) 56 if err != nil { 57 return nil, err 58 } 59 log.WithField("options", options).Debug("decoded SBT analyzer options") 60 61 analyzer := Analyzer{ 62 JavaCmd: javaCmd, 63 JavaVersion: javaVersion, 64 65 SBTCmd: sbtCmd, 66 SBTVersion: sbtVersion, 67 68 SBT: sbt.SBT{ 69 Bin: sbtCmd, 70 }, 71 Module: m, 72 Options: options, 73 } 74 75 log.WithField("analyzer", analyzer).Debug("constructed SBT analyzer") 76 return &analyzer, nil 77 } 78 79 func Discover(dir string, options map[string]interface{}) ([]module.Module, error) { 80 log.WithField("dir", dir).Debug("discovering modules") 81 82 // Construct SBT instance (for listing projects). 83 sbtCmd, _, err := exec.WhichArgs([]string{"-no-colors", "about"}, os.Getenv("SBT_BINARY"), "sbt") 84 if err != nil { 85 return nil, nil 86 } 87 sbt := sbt.SBT{ 88 Bin: sbtCmd, 89 } 90 91 var modules []module.Module 92 err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 93 log.WithField("path", path).Debug("discovering modules") 94 95 if err != nil { 96 log.WithError(err).WithField("path", path).Debug("error while walking for discovery") 97 return err 98 } 99 100 if info.IsDir() { 101 log.Debug("path is folder") 102 ok, err := files.Exists(path, "build.sbt") 103 if err != nil { 104 return err 105 } 106 if !ok { 107 return nil 108 } 109 log.Debug("Path has build.sbt") 110 111 dir := filepath.Dir(path) 112 projects, err := sbt.Projects(dir) 113 if err != nil { 114 log.WithError(err).Debug("could not get modules at path") 115 return err 116 } 117 118 for _, p := range projects { 119 modules = append(modules, module.Module{ 120 Name: p, 121 Type: pkg.Scala, 122 BuildTarget: p + ":compile", 123 Dir: dir, 124 }) 125 } 126 log.WithField("path", path).Debug("skipping") 127 // Don't continue recursing, because anything else is probably a 128 // subproject. 129 return filepath.SkipDir 130 } 131 132 return nil 133 }) 134 135 if err != nil { 136 return nil, errors.Wrap(err, "could not find Scala projects") 137 } 138 139 return modules, nil 140 } 141 142 func (a *Analyzer) Clean() error { 143 project, configuration := ParseTarget(a.Module.BuildTarget) 144 return a.SBT.Clean(a.Module.Dir, project, configuration) 145 } 146 147 func (a *Analyzer) Build() error { 148 project, configuration := ParseTarget(a.Module.BuildTarget) 149 return a.SBT.Compile(a.Module.Dir, project, configuration) 150 } 151 152 // IsBuilt checks whether `mvn dependency:list` produces an error. 153 func (a *Analyzer) IsBuilt() (bool, error) { 154 project, configuration := ParseTarget(a.Module.BuildTarget) 155 output, err := a.SBT.DependencyList(a.Module.Dir, project, configuration) 156 if err != nil { 157 if strings.Contains(output, "Could not find artifact") { 158 return false, nil 159 } 160 return false, err 161 } 162 return output != "", nil 163 } 164 165 func (a *Analyzer) Analyze() (graph.Deps, error) { 166 log.WithField("module", a.Module).Debug("analyzing module") 167 168 project, configuration := ParseTarget(a.Module.BuildTarget) 169 imports, deps, err := a.SBT.DependencyTree(a.Module.Dir, project, configuration) 170 if err != nil { 171 return graph.Deps{}, err 172 } 173 174 return graph.Deps{ 175 Direct: imports, 176 Transitive: deps, 177 }, nil 178 } 179 180 func ParseTarget(target string) (project string, configuration string) { 181 splits := strings.Split(target, ":") 182 project = splits[0] 183 configuration = splits[1] 184 return project, configuration 185 }