github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/carthage/carthage.go (about) 1 // A `BuildTarget` for Carthage is the path to the directory with the Cartfile. 2 package carthage 3 4 import ( 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "github.com/apex/log" 10 "github.com/mitchellh/mapstructure" 11 12 "github.com/fossas/fossa-cli/buildtools/carthage" 13 "github.com/fossas/fossa-cli/exec" 14 "github.com/fossas/fossa-cli/files" 15 "github.com/fossas/fossa-cli/graph" 16 "github.com/fossas/fossa-cli/module" 17 "github.com/fossas/fossa-cli/pkg" 18 ) 19 20 type Analyzer struct { 21 CarthageCmd string 22 CarthageVersion string 23 24 Carthage carthage.Carthage 25 26 Module module.Module 27 Options Options 28 } 29 30 type Options struct{} 31 32 func New(m module.Module) (*Analyzer, error) { 33 // Set Carthage context variables 34 cartCmd, cartVersion, err := exec.Which("version", os.Getenv("CARTHAGE_BINARY"), "carthage") 35 if err != nil { 36 log.Debug("could not find Carthage binary (try setting $CARTHAGE_BINARY") 37 } 38 39 // Parse and validate options. 40 var options Options 41 err = mapstructure.Decode(m.Options, &options) 42 if err != nil { 43 return nil, err 44 } 45 log.WithField("options", options).Debug("parsed analyzer options") 46 47 analyzer := Analyzer{ 48 CarthageCmd: cartCmd, 49 CarthageVersion: cartVersion, 50 51 Carthage: carthage.Carthage{ 52 Bin: cartCmd, 53 }, 54 55 Options: options, 56 Module: m, 57 } 58 59 log.WithField("analyzer", analyzer).Debug("constructed analyzer") 60 return &analyzer, nil 61 } 62 63 // Discover constructs modules in all directories with a `Cartfile`. 64 func Discover(dir string, options map[string]interface{}) ([]module.Module, error) { 65 var modules []module.Module 66 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 67 if err != nil { 68 log.WithError(err).WithField("path", path).Debug("error while walking for discovery") 69 return err 70 } 71 72 moduleName := filepath.Base(path) 73 74 if !info.IsDir() && (info.Name() == "Cartfile" || info.Name() == "Cartfile.private") { 75 // If the same directory has both a Cartfile and Cartfile.private, we will just pick the Cartfile 76 if info.Name() == "Cartfile.private" { 77 cartfilePath := filepath.Join(filepath.Dir(path), "Cartfile") 78 cartfileExists, err := files.Exists(cartfilePath) 79 if err != nil { 80 log.Debugf("Error searching for Cartfile: %s (%s)", path, err.Error()) 81 } 82 83 if cartfileExists { 84 return nil // This will get picked up before/later if it exists in the same directory 85 } 86 } 87 88 log.Debugf("Found Carthage package: %s (%s)", path, moduleName) 89 relPath, _ := filepath.Rel(dir, path) 90 modules = append(modules, module.Module{ 91 Name: moduleName, 92 Type: pkg.Carthage, 93 BuildTarget: filepath.Dir(relPath), 94 Dir: filepath.Dir(relPath), 95 }) 96 } 97 98 return nil 99 }) 100 101 if err != nil { 102 return nil, fmt.Errorf("Could not find Carthage package manifests: %s", err.Error()) 103 } 104 105 return modules, nil 106 } 107 108 // IsBuilt checks whether file `Cartfile.resolved` exists, and also if a `Carthage` folder exists 109 func (a *Analyzer) IsBuilt() (bool, error) { 110 log.Debugf("Checking Carthage build: %#v", a.Module) 111 hasResolvedCartfile, err := files.Exists(filepath.Join(a.Module.Dir, "Cartfile.resolved")) 112 113 if err != nil { 114 log.Warnf("Error checking Carthage build: %#v", err.Error()) 115 return false, err 116 } 117 118 hasCarthageFolder, err := files.ExistsFolder(a.Module.Dir, "Carthage") 119 if err != nil { 120 log.Warnf("Error checking Carthage build: %#v", err.Error()) 121 return false, err 122 } 123 124 isBuilt := hasCarthageFolder && hasResolvedCartfile 125 126 log.Debugf("Done checking Carthage build: %#v", isBuilt) 127 return isBuilt, nil 128 } 129 130 func (a *Analyzer) Clean() error { 131 return files.Rm(a.Module.Dir, "Carthage") 132 } 133 134 func (a *Analyzer) Build() error { 135 return a.Carthage.Install(a.Module.Dir) 136 } 137 138 func (a *Analyzer) Analyze() (graph.Deps, error) { 139 log.Debugf("Running Carthage analysis: %#v", a.Module) 140 cartfileResolvedExists, err := files.Exists(filepath.Join(a.Module.Dir, "Cartfile.resolved")) 141 if !cartfileResolvedExists || err != nil { 142 return graph.Deps{}, fmt.Errorf("The file Cartfile.resolved could not be found in the directory: %s, Carthage analysis requires the existence of a Cartfile.resolved file in order to determine information about the dependencies used to build your project.", a.Module.Dir) 143 } 144 145 resolvedCartfile, err := carthage.FromResolvedCartfile("ROOT", a.Module.Dir) 146 if err != nil { 147 return graph.Deps{}, err 148 } 149 cartfilePath := filepath.Join(a.Module.Dir, "Cartfile.resolved") 150 // Set direct dependencies. 151 var imports []pkg.Import 152 for _, dep := range resolvedCartfile.Dependencies { 153 imports = append(imports, pkg.Import{ 154 Target: dep.String(), 155 Resolved: pkg.ID{ 156 Type: pkg.Carthage, 157 Name: dep.Name, 158 Revision: dep.Revision, 159 Location: cartfilePath, 160 }, 161 }) 162 } 163 164 // Set transitive dependencies. 165 transitiveDeps := make(map[pkg.ID]pkg.Package) 166 carthage.RecurseDeps(transitiveDeps, resolvedCartfile) 167 168 log.Debugf("Done running Carthage analysis: %#v", transitiveDeps) 169 170 return graph.Deps{ 171 Direct: imports, 172 Transitive: transitiveDeps, 173 }, nil 174 175 }