blob: ff23ec0e2b6f402518d7ca83ff676f109b1928be [file] [log] [blame]
package gps
// Manifest represents manifest-type data for a project at a particular version.
// That means dependency constraints, both for normal dependencies and for
// tests. The constraints expressed in a manifest determine the set of versions that
// are acceptable to try for a given project.
//
// Expressing a constraint in a manifest does not guarantee that a particular
// dependency will be present. It only guarantees that if packages in the
// project specified by the dependency are discovered through static analysis of
// the (transitive) import graph, then they will conform to the constraint.
//
// This does entail that manifests can express constraints on projects they do
// not themselves import. This is by design, but its implications are complex.
// See the gps docs for more information: https://github.com/sdboyer/gps/wiki
type Manifest interface {
// Returns a list of project-level constraints.
DependencyConstraints() []ProjectConstraint
// Returns a list of constraints applicable to test imports.
//
// These are applied only when tests are incorporated. Typically, that
// will only be for root manifests.
TestDependencyConstraints() []ProjectConstraint
}
// RootManifest extends Manifest to add special controls over solving that are
// only afforded to the root project.
type RootManifest interface {
Manifest
// Overrides returns a list of ProjectConstraints that will unconditionally
// supercede any ProjectConstraint declarations made in either the root
// manifest, or in any dependency's manifest.
//
// Overrides are a special control afforded only to root manifests. Tool
// users should be encouraged to use them only as a last resort; they do not
// "play well with others" (that is their express goal), and overreliance on
// them can harm the ecosystem as a whole.
Overrides() ProjectConstraints
// IngorePackages returns a set of import paths to ignore. These import
// paths can be within the root project, or part of other projects. Ignoring
// a package means that both it and its (unique) imports will be disregarded
// by all relevant solver operations.
IgnorePackages() map[string]bool
}
// SimpleManifest is a helper for tools to enumerate manifest data. It's
// generally intended for ephemeral manifests, such as those Analyzers create on
// the fly for projects with no manifest metadata, or metadata through a foreign
// tool's idioms.
type SimpleManifest struct {
Deps []ProjectConstraint
TestDeps []ProjectConstraint
}
var _ Manifest = SimpleManifest{}
// DependencyConstraints returns the project's dependencies.
func (m SimpleManifest) DependencyConstraints() []ProjectConstraint {
return m.Deps
}
// TestDependencyConstraints returns the project's test dependencies.
func (m SimpleManifest) TestDependencyConstraints() []ProjectConstraint {
return m.TestDeps
}
// simpleRootManifest exists so that we have a safe value to swap into solver
// params when a nil Manifest is provided.
//
// Also, for tests.
type simpleRootManifest struct {
c []ProjectConstraint
tc []ProjectConstraint
ovr ProjectConstraints
ig map[string]bool
}
func (m simpleRootManifest) DependencyConstraints() []ProjectConstraint {
return m.c
}
func (m simpleRootManifest) TestDependencyConstraints() []ProjectConstraint {
return m.tc
}
func (m simpleRootManifest) Overrides() ProjectConstraints {
return m.ovr
}
func (m simpleRootManifest) IgnorePackages() map[string]bool {
return m.ig
}
func (m simpleRootManifest) dup() simpleRootManifest {
m2 := simpleRootManifest{
c: make([]ProjectConstraint, len(m.c)),
tc: make([]ProjectConstraint, len(m.tc)),
ovr: ProjectConstraints{},
ig: map[string]bool{},
}
copy(m2.c, m.c)
copy(m2.tc, m.tc)
for k, v := range m.ovr {
m2.ovr[k] = v
}
for k, v := range m.ig {
m2.ig[k] = v
}
return m2
}
// prepManifest ensures a manifest is prepared and safe for use by the solver.
// This is mostly about ensuring that no outside routine can modify the manifest
// while the solver is in-flight.
//
// This is achieved by copying the manifest's data into a new SimpleManifest.
func prepManifest(m Manifest) Manifest {
if m == nil {
return SimpleManifest{}
}
deps := m.DependencyConstraints()
ddeps := m.TestDependencyConstraints()
rm := SimpleManifest{
Deps: make([]ProjectConstraint, len(deps)),
TestDeps: make([]ProjectConstraint, len(ddeps)),
}
for k, d := range deps {
rm.Deps[k] = d
}
for k, d := range ddeps {
rm.TestDeps[k] = d
}
return rm
}