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  }