gps/manifest.go

Summary

Maintainability
A
0 mins
Test Coverage
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gps

import "github.com/golang/dep/gps/pkgtree"

// Manifest represents manifest-type data for a project at a particular version.
// 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() ProjectConstraints
}

// 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
    // supersede 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

    // IgnoredPackages returns a pkgtree.IgnoredRuleset, which comprises a set
    // of import paths, or import path patterns, that are to be ignored during
    // solving. These ignored 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.
    //
    // It is an error to include a package in both the ignored and required
    // sets.
    IgnoredPackages() *pkgtree.IgnoredRuleset

    // RequiredPackages returns a set of import paths to require. These packages
    // are required to be present in any solution. The list can include main
    // packages.
    //
    // It is meaningless to specify packages that are within the
    // PackageTree of the ProjectRoot (though not an error, because the
    // RootManifest itself does not report a ProjectRoot).
    //
    // It is an error to include a package in both the ignored and required
    // sets.
    RequiredPackages() 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 ProjectConstraints
}

var _ Manifest = SimpleManifest{}

// DependencyConstraints returns the project's dependencies.
func (m SimpleManifest) DependencyConstraints() ProjectConstraints {
    return m.Deps
}

// simpleRootManifest exists so that we have a safe value to swap into solver
// params when a nil Manifest is provided.
type simpleRootManifest struct {
    c, ovr ProjectConstraints
    ig     *pkgtree.IgnoredRuleset
    req    map[string]bool
}

func (m simpleRootManifest) DependencyConstraints() ProjectConstraints {
    return m.c
}
func (m simpleRootManifest) Overrides() ProjectConstraints {
    return m.ovr
}
func (m simpleRootManifest) IgnoredPackages() *pkgtree.IgnoredRuleset {
    return m.ig
}
func (m simpleRootManifest) RequiredPackages() map[string]bool {
    return m.req
}

// 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, but it also filters out any empty
// ProjectProperties.
//
// This is achieved by copying the manifest's data into a new SimpleManifest.
func prepManifest(m Manifest) SimpleManifest {
    if m == nil {
        return SimpleManifest{}
    }

    deps := m.DependencyConstraints()

    rm := SimpleManifest{
        Deps: make(ProjectConstraints, len(deps)),
    }

    for k, d := range deps {
        // A zero-value ProjectProperties is equivalent to one with an
        // anyConstraint{} in terms of how the solver will treat it. However, we
        // normalize between these two by omitting such instances entirely, as
        // it negates some possibility for false mismatches in input hashing.
        if d.Constraint == nil {
            if d.Source == "" {
                continue
            }
            d.Constraint = anyConstraint{}
        }

        rm.Deps[k] = d
    }

    return rm
}