Documentation ¶
Overview ¶
Package golang implements the analyzer for Go.
A `BuildTarget` in Go is defined as an import path (see `go help importpath` for details).
This package is implemented by externally calling both the `go` tool and any external build tools.
FAQ ¶
1. Why not use `go/build`, or a library like `KyleBanks/depth`?
The `go` tool's interface is incredibly stable over different releases, but the internals are not. Using these libraries causes crashes when analyzing code that is compiled using a different version of Go. (This was how the analyzer was originally implemented.)
Index ¶
- Variables
- func Discover(dir string, opts map[string]interface{}) ([]module.Module, error)
- func ImportPath(dir string) (string, error)
- func LockfileIn(dirname string) (resolver.Type, error)
- func NearestLockfile(dirname string) (resolver.Type, string, error)
- func UnresolvedImport(gopkg gocmd.Package) pkg.Import
- func Unvendor(importpath string) string
- func VendorParent(dirname string) string
- type Analyzer
- func (a *Analyzer) Analyze() (graph.Deps, error)
- func (a *Analyzer) Build() error
- func (a *Analyzer) Clean() error
- func (a *Analyzer) Dir(importpath string) (string, error)
- func (a *Analyzer) IsBuilt() (bool, error)
- func (a *Analyzer) ParentOK(project Project, gopkg gocmd.Package, dir string) bool
- func (a *Analyzer) Project(pkg string) (Project, error)
- func (a *Analyzer) ResolverFromLockfile(tool resolver.Type, dir string) (resolver.Resolver, error)
- func (a *Analyzer) Revision(project Project, r resolver.Resolver, gopkg gocmd.Package) (pkg.Import, error)
- func (a *Analyzer) RevisionContextual(project Project, gopkg gocmd.Package) (pkg.Import, error)
- func (a *Analyzer) RevisionContextualLookup(project Project, gopkg gocmd.Package, dir string) (pkg.Import, error)
- func (a *Analyzer) RevisionDirect(project Project, r resolver.Resolver, gopkg gocmd.Package) (pkg.Import, error)
- func (a *Analyzer) UnresolvedOK(project Project, gopkg gocmd.Package) bool
- type DiscoverOptions
- type Options
- type Project
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoLockfileInDir = errors.New("could not find lockfile in directory") ErrNoNearestLockfile = errors.New("could not find nearest lockfile of directory") )
Errors that occur while finding lockfiles.
var (
ErrNoGOPATH = errors.New("no $GOPATH set")
)
Errors that occur while running utilities.
Functions ¶
func ImportPath ¶
ImportPath returns the import path of a package located at the directory.
func LockfileIn ¶
LockfileIn returns the type of lockfile within a directory, or ErrNoLockfileInDir if none is found.
func NearestLockfile ¶
NearestLockfile returns the type and directory of the nearest lockfile in an ancestor directory, or ErrNoNearestLockfile if none is found.
func UnresolvedImport ¶
UnresolvedImport returns the default (non-zero) pkg.Import for an unresolved gocmd.Package.
func VendorParent ¶
VendorParent returns the directory that contains a vendored directory, or "." if none exists.
Types ¶
type Analyzer ¶
type Analyzer struct { Go gocmd.Go GoVersion string Module module.Module Options Options // contains filtered or unexported fields }
An Analyzer contains structs used in the analysis of Go packages. It implements analyzer.Analyzer.
func (*Analyzer) Analyze ¶
Analyze builds a dependency graph using go list and then looks up revisions using tool-specific lockfiles.
func (*Analyzer) ParentOK ¶
ParentOK returns true if looking up the lockfile of gopkg in dir is allowed.
A package may be looked up in a directory if:
- The directory is within the main project.
- The directory is not within the main project, but we allow external project vendoring.
- The directory is not within the main project, but an allowed external vendoring import path prefix is a prefix of the import path that a package at the directory would have.
func (*Analyzer) Project ¶
Project calculates the project containing any Go package.
This function searches upwards from the Go package's directory, looking for lockfiles of supported dependency management tools. If none are found, it fails.
The rationale for this design is that the packages in a "project" are versioned together. There are two reasonable ways to capture the notion of "versioned together":
- The nearest lockfile. The nearest lockfile to the package probably locks the dependencies of the package.
- The nearest VCS repository. The nearest VCS repository probably contains the current "project" being worked on. The only common exception to this is monorepos, in which case all the contents of the repository are probably internal, so allowing packages within the repository to be unresolved is probably acceptable.
There are a couple issues with both of these:
- The nearest lockfile is not guaranteed to exist. When it does, it's not guaranteed to be _the_ semantic lockfile for the package -- this is merely a very common convention, not a requirement.
- The package is not guaranteed to be in a VCS repository. When it is, the repository might be extremely weird. One example of this is a repository containing the entire $GOPATH (which is a reasonable convention that some early adopters of Go used).
This function tries its best to mitigate both of these issues:
- The nearest lockfile is used for resolving versions. This is a very strong convention.
- The nearest VCS repository is used for determining allowed unresolved import paths. This is also a very strong convention.
Both of these assumptions can be overridden by the user.
func (*Analyzer) ResolverFromLockfile ¶
ResolverFromLockfile loads and caches lockfile resolvers.
func (*Analyzer) Revision ¶
func (a *Analyzer) Revision(project Project, r resolver.Resolver, gopkg gocmd.Package) (pkg.Import, error)
Revision resolves a revision, returning buildtools.ErrNoRevisionForPackage when no revision is found (unless a revision is not required).
func (*Analyzer) RevisionContextual ¶
RevisionContextual resolve a revision by looking up its revision in the lockfile of a vendoring parent. This supports complex use cases, such as multi-project vendoring (Docker), and nested vendor folders (Hashicorp):
- Multi-project example: docker/docker-ce does not vendor all of its dependencies within the project. Instead, it uses unvendored imports of docker/docker, which vendors imports of its own set of dependencies. This means that we need to do a lookup in docker/docker's lockfile to resolve a transitive dependency of docker/docker-ce.
- Nested vendor folder example: hashicorp/consul has nested vendor folders. This means that resolving a transitive dependency may require looking at the lockfile of the dependency that vendors it, or looking further up at any of the ancestors in the vendoring chain.
In theory, this is only used for setups that _build very carefully_, but are otherwise prone to exploding. In practice, this seems to be required a lot.
func (*Analyzer) RevisionContextualLookup ¶
func (a *Analyzer) RevisionContextualLookup(project Project, gopkg gocmd.Package, dir string) (pkg.Import, error)
RevisionContextualLookup attempts to resolve a revision of a package using the lockfile in a specific directory.
func (*Analyzer) RevisionDirect ¶
func (a *Analyzer) RevisionDirect(project Project, r resolver.Resolver, gopkg gocmd.Package) (pkg.Import, error)
RevisionDirect resolves a revision by looking up its revision in the project's lockfile. This works for most conventional projects.
func (*Analyzer) UnresolvedOK ¶
UnresolvedOK returns true if failing to resolve the revision of a package is a non-fatal (rather than fatal) error.
Packages require a resolved revision unless any of the following are true:
- The package is part of the standard library.
- The package is internal.
- The package is within the project folder, but not under the vendor folder.
- A package at the project's folder would have an import path that is a prefix of the package path.
- Options.AllowUnresolved is true.
- Options.AllowUnresolvedPrefix is set one of its paths is a prefix of the package's import path.
These exceptions are generally either "part of" (i.e. versioned as a unit with) the project, or otherwise don't have revisions.
type DiscoverOptions ¶
type Options ¶
type Options struct { BuildOS string `mapstructure:"os"` // Target build OS (for build tags). BuildArch string `mapstructure:"arch"` // Target build architecture (for build tags). Strategy string `mapstructure:"strategy"` // See the Go analyzer documentation. LockfilePath string `mapstructure:"lockfile"` // For non-standard lockfile locations with strategies `manifest:*`. AllowUnresolved bool `mapstructure:"allow-unresolved"` // Allow unresolved revisions. 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. AllowNestedVendor bool `mapstructure:"allow-nested-vendor"` // Allows vendor folders to be nested and attempts to resolve using parent lockfile lookup. AllowDeepVendor bool `mapstructure:"allow-deep-vendor"` // Allows nested vendored dependencies to be resolved using ancestor lockfiles farther than their direct parent. AllowExternalVendor bool `mapstructure:"allow-external-vendor"` // Allows reading vendor lockfiles of other projects. 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. SkipImportTracing bool `mapstructure:"skip-tracing"` // Skips dependency tracing. SkipProject bool `mapstructure:"skip-project"` // Skips project detection. }
Options set analyzer options for Go modules.
type Project ¶
type Project struct { Tool resolver.Type // Name of the dependency management tool used by the project, if any. Manifest string // Absolute path to the tool's manifest file for this project, if any. Dir string // Absolute path of the first-party code folder, if any. ImportPath string // Import path prefix of project code. }
A Project is a single folder that forms a coherent "project" for a developer and is versioned as a single unit. It may contain multiple Go packages.