github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/golang/golang.go (about)

     1  // Package golang implements the analyzer for Go.
     2  //
     3  // A `BuildTarget` in Go is defined as an import path (see `go help importpath`
     4  // for details).
     5  //
     6  // This package is implemented by externally calling both the `go` tool and any
     7  // external build tools.
     8  //
     9  // FAQ
    10  //
    11  // 1. Why not use `go/build`, or a library like `KyleBanks/depth`?
    12  //
    13  // The `go` tool's interface is incredibly stable over different releases, but
    14  // the internals are not. Using these libraries causes crashes when analyzing
    15  // code that is compiled using a different version of Go. (This was how the
    16  // analyzer was originally implemented.)
    17  package golang
    18  
    19  import (
    20  	"os"
    21  
    22  	"github.com/apex/log"
    23  	"github.com/mitchellh/mapstructure"
    24  
    25  	"github.com/fossas/fossa-cli/analyzers/golang/resolver"
    26  	"github.com/fossas/fossa-cli/buildtools/gocmd"
    27  	"github.com/fossas/fossa-cli/exec"
    28  	"github.com/fossas/fossa-cli/module"
    29  )
    30  
    31  // An Analyzer contains structs used in the analysis of Go packages. It
    32  // implements analyzer.Analyzer.
    33  type Analyzer struct {
    34  	Go        gocmd.Go
    35  	GoVersion string
    36  
    37  	Module  module.Module
    38  	Options Options
    39  
    40  	BuildTags []string
    41  
    42  	// These caches prevent redundant filesystem lookups and execs, and help a lot
    43  	// when dealing with nested vendoring.
    44  	resolverCache map[string]resolver.Resolver
    45  	projectCache  map[string]Project
    46  }
    47  
    48  // Options set analyzer options for Go modules.
    49  type Options struct {
    50  	Tags                      []string `mapstructure:"tags"`                         // specify individual build configurations
    51  	AllTags                   bool     `mapstructure:"all-tags"`                     // Turn on all fossa default build tags
    52  	Strategy                  string   `mapstructure:"strategy"`                     // See the Go analyzer documentation.
    53  	LockfilePath              string   `mapstructure:"lockfile"`                     // For non-standard lockfile locations with strategies `manifest:*`.
    54  	ManifestPath              string   `mapstructure:"manifest"`                     // For non-standard manifest locations with strategies `manifest:*`.
    55  	AllowUnresolved           bool     `mapstructure:"allow-unresolved"`             // Allow unresolved revisions.
    56  	AllowUnresolvedPrefix     string   `mapstructure:"allow-unresolved-prefix"`      // If set, allows unresolved revisions for packages whose import path's prefix matches. Multiple space-delimited prefixes can be specified.
    57  	AllowNestedVendor         bool     `mapstructure:"allow-nested-vendor"`          // Allows vendor folders to be nested and attempts to resolve using parent lockfile lookup.
    58  	AllowDeepVendor           bool     `mapstructure:"allow-deep-vendor"`            // Allows nested vendored dependencies to be resolved using ancestor lockfiles farther than their direct parent.
    59  	AllowExternalVendor       bool     `mapstructure:"allow-external-vendor"`        // Allows reading vendor lockfiles of other projects.
    60  	AllowExternalVendorPrefix string   `mapstructure:"allow-external-vendor-prefix"` // If set, allow reading vendor lockfiles of projects whose import path's prefix matches. Multiple space-delimited prefixes can be specified.
    61  	SkipImportTracing         bool     `mapstructure:"skip-tracing"`                 // Skips dependency tracing.
    62  	SkipProject               bool     `mapstructure:"skip-project"`                 // Skips project detection.
    63  }
    64  
    65  var osTags = []string{"windows", "linux", "freebsd", "android", "darwin", "dragonfly", "nacl", "netbsd", "openbsd", "plan9", "solaris"}
    66  var archTags = []string{"386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "s390", "s390x", "sparc", "sparc64"}
    67  
    68  // New constructs an Analyzer.
    69  func New(m module.Module) (*Analyzer, error) {
    70  	log.Debugf("%#v", m)
    71  
    72  	// Parse and validate options.
    73  	var options Options
    74  	err := mapstructure.Decode(m.Options, &options)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	log.WithField("options", options).Debug("parsed analyzer options")
    79  
    80  	// Construct analyzer.
    81  	cmd, version, err := exec.Which("version", os.Getenv("FOSSA_GO_CMD"), "go")
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	// Compile a list of all user requested build constraints.
    87  	tags := options.Tags
    88  	if options.AllTags {
    89  		tags = append(tags, osTags...)
    90  		tags = append(tags, archTags...)
    91  	}
    92  	// Include the current build setup.
    93  	tags = append(tags, "")
    94  
    95  	return &Analyzer{
    96  		Go: gocmd.Go{
    97  			Cmd: cmd,
    98  		},
    99  		GoVersion: version,
   100  
   101  		Module:  m,
   102  		Options: options,
   103  
   104  		BuildTags: tags,
   105  
   106  		resolverCache: make(map[string]resolver.Resolver),
   107  		projectCache:  make(map[string]Project),
   108  	}, nil
   109  }
   110  
   111  // Clean runs `go clean $PKG`.
   112  func (a *Analyzer) Clean() error {
   113  	m := a.Module
   114  	return a.Go.Clean([]string{m.BuildTarget})
   115  }
   116  
   117  // Build runs `go build $PKG`.
   118  func (a *Analyzer) Build() error {
   119  	m := a.Module
   120  	return a.Go.Build([]string{m.BuildTarget})
   121  }
   122  
   123  // IsBuilt runs `go list $PKG` and checks for errors.
   124  func (a *Analyzer) IsBuilt() (bool, error) {
   125  	m := a.Module
   126  	log.Debugf("%#v", m)
   127  	pkg, err := a.Go.ListOne(m.BuildTarget, nil)
   128  	if err != nil {
   129  		return false, err
   130  	}
   131  	return pkg.Error == nil, nil
   132  }