github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/haskell/discover.go (about) 1 package haskell 2 3 import ( 4 "os" 5 "path/filepath" 6 "strings" 7 8 "github.com/apex/log" 9 10 "github.com/fossas/fossa-cli/errors" 11 "github.com/fossas/fossa-cli/module" 12 "github.com/fossas/fossa-cli/pkg" 13 ) 14 15 func NewModule(moduleName string, relativeDir string, strategy Strategy) module.Module { 16 return module.Module{ 17 Name: moduleName, 18 Type: pkg.Haskell, 19 BuildTarget: relativeDir, 20 Dir: relativeDir, 21 Options: map[string]interface{}{"strategy": strategy}, 22 } 23 } 24 25 func Discover(baseDir string, options map[string]interface{}) ([]module.Module, error) { 26 // List of discovered projects. Projects can be defined in 27 // `cabal.project` or `stack.yaml` files 28 projects := make(map[string]module.Module) 29 // List of discovered cabal files. These will be used in the case that: 30 // - No projects are found; or 31 // - A project doesn't exist in the current or a parent directory 32 cabalFiles := make(map[string]module.Module) 33 34 err := filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error { 35 if err != nil { 36 log.WithError(err).WithField("filename", path).Debug("failed to access path") 37 return err 38 } 39 40 if info.IsDir() { 41 return nil 42 } 43 44 moduleName := filepath.Base(path) 45 relativeDir, _ := filepath.Rel(baseDir, filepath.Dir(path)) 46 47 if info.Name() == "cabal.project" { 48 projects[relativeDir] = NewModule(moduleName, relativeDir, CabalInstall) 49 } else if info.Name() == "stack.yaml" { 50 projects[relativeDir] = NewModule(moduleName, relativeDir, Stack) 51 } else if strings.HasSuffix(info.Name(), ".cabal") { 52 cabalFiles[relativeDir] = NewModule(moduleName, relativeDir, CabalInstall) 53 } 54 55 return nil 56 }) 57 58 if err != nil { 59 return nil, errors.Wrap(err, "could not find Haskell package manifests") 60 } 61 62 // Prune .cabal files where parent projects are present 63 for cabalFilePath := range cabalFiles { 64 for projectPath := range projects { 65 // TODO: parse cabal.project and stack.yaml instead of using their paths? 66 rel, err := filepath.Rel(projectPath, cabalFilePath) 67 // filepath.Rel will produce a path containing ".." when the cabal 68 // file isn't in a subdirectory of the project 69 if err == nil && !strings.Contains(rel, "..") { 70 delete(cabalFiles, cabalFilePath) 71 break 72 } 73 } 74 } 75 76 var modules []module.Module 77 for _, m := range projects { 78 modules = append(modules, m) 79 } 80 for _, m := range cabalFiles { 81 modules = append(modules, m) 82 } 83 84 return modules, nil 85 }