зеркало из https://github.com/golang/dep.git
493 строки
14 KiB
Go
493 строки
14 KiB
Go
package gps
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type errorLevel uint8
|
|
|
|
// TODO(sdboyer) consistent, sensible way of handling 'type' and 'severity' - or figure
|
|
// out that they're not orthogonal and collapse into just 'type'
|
|
|
|
const (
|
|
warning errorLevel = 1 << iota
|
|
mustResolve
|
|
cannotResolve
|
|
)
|
|
|
|
func a2vs(a atom) string {
|
|
if a.v == rootRev || a.v == nil {
|
|
return "(root)"
|
|
}
|
|
|
|
return fmt.Sprintf("%s@%s", a.id.errString(), a.v)
|
|
}
|
|
|
|
type traceError interface {
|
|
traceString() string
|
|
}
|
|
|
|
type noVersionError struct {
|
|
pn ProjectIdentifier
|
|
fails []failedVersion
|
|
}
|
|
|
|
func (e *noVersionError) Error() string {
|
|
if len(e.fails) == 0 {
|
|
return fmt.Sprintf("No versions found for project %q.", e.pn.ProjectRoot)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "No versions of %s met constraints:", e.pn.ProjectRoot)
|
|
for _, f := range e.fails {
|
|
fmt.Fprintf(&buf, "\n\t%s: %s", f.v, f.f.Error())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (e *noVersionError) traceString() string {
|
|
if len(e.fails) == 0 {
|
|
return fmt.Sprintf("No versions found")
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "No versions of %s met constraints:", e.pn.ProjectRoot)
|
|
for _, f := range e.fails {
|
|
if te, ok := f.f.(traceError); ok {
|
|
fmt.Fprintf(&buf, "\n %s: %s", f.v, te.traceString())
|
|
} else {
|
|
fmt.Fprintf(&buf, "\n %s: %s", f.v, f.f.Error())
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// disjointConstraintFailure occurs when attempting to introduce an atom that
|
|
// itself has an acceptable version, but one of its dependency constraints is
|
|
// disjoint with one or more dependency constraints already active for that
|
|
// identifier.
|
|
type disjointConstraintFailure struct {
|
|
// goal is the dependency with the problematic constraint, forcing us to
|
|
// reject the atom that introduces it.
|
|
goal dependency
|
|
// failsib is the list of active dependencies that are disjoint with the
|
|
// goal dependency. This will be at least one, but may not be all of the
|
|
// active dependencies.
|
|
failsib []dependency
|
|
// nofailsib is the list of active dependencies that are NOT disjoint with
|
|
// the goal dependency. The total of nofailsib and failsib will always be
|
|
// the total number of active dependencies on target identifier.
|
|
nofailsib []dependency
|
|
// c is the current constraint on the target identifier. It is intersection
|
|
// of all the active dependencies' constraints.
|
|
c Constraint
|
|
}
|
|
|
|
func (e *disjointConstraintFailure) Error() string {
|
|
if len(e.failsib) == 1 {
|
|
str := "Could not introduce %s, as it has a dependency on %s with constraint %s, which has no overlap with existing constraint %s from %s"
|
|
return fmt.Sprintf(str, a2vs(e.goal.depender), e.goal.dep.Ident.errString(), e.goal.dep.Constraint.String(), e.failsib[0].dep.Constraint.String(), a2vs(e.failsib[0].depender))
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
var sibs []dependency
|
|
if len(e.failsib) > 1 {
|
|
sibs = e.failsib
|
|
|
|
str := "Could not introduce %s, as it has a dependency on %s with constraint %s, which has no overlap with the following existing constraints:\n"
|
|
fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.goal.dep.Ident.errString(), e.goal.dep.Constraint.String())
|
|
} else {
|
|
sibs = e.nofailsib
|
|
|
|
str := "Could not introduce %s, as it has a dependency on %s with constraint %s, which does not overlap with the intersection of existing constraints from other currently selected packages:\n"
|
|
fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.goal.dep.Ident.errString(), e.goal.dep.Constraint.String())
|
|
}
|
|
|
|
for _, c := range sibs {
|
|
fmt.Fprintf(&buf, "\t%s from %s\n", c.dep.Constraint.String(), a2vs(c.depender))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (e *disjointConstraintFailure) traceString() string {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "constraint %s on %s disjoint with other dependers:\n", e.goal.dep.Constraint.String(), e.goal.dep.Ident.errString())
|
|
for _, f := range e.failsib {
|
|
fmt.Fprintf(
|
|
&buf,
|
|
"%s from %s (no overlap)\n",
|
|
f.dep.Constraint.String(),
|
|
a2vs(f.depender),
|
|
)
|
|
}
|
|
for _, f := range e.nofailsib {
|
|
fmt.Fprintf(
|
|
&buf,
|
|
"%s from %s (some overlap)\n",
|
|
f.dep.Constraint.String(),
|
|
a2vs(f.depender),
|
|
)
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// Indicates that an atom could not be introduced because one of its dep
|
|
// constraints does not admit the currently-selected version of the target
|
|
// project.
|
|
type constraintNotAllowedFailure struct {
|
|
// The dependency with the problematic constraint that could not be
|
|
// introduced.
|
|
goal dependency
|
|
// The (currently selected) version of the target project that was not
|
|
// admissible by the goal dependency.
|
|
v Version
|
|
}
|
|
|
|
func (e *constraintNotAllowedFailure) Error() string {
|
|
return fmt.Sprintf(
|
|
"Could not introduce %s, as it has a dependency on %s with constraint %s, which does not allow the currently selected version of %s",
|
|
a2vs(e.goal.depender),
|
|
e.goal.dep.Ident.errString(),
|
|
e.goal.dep.Constraint,
|
|
e.v,
|
|
)
|
|
}
|
|
|
|
func (e *constraintNotAllowedFailure) traceString() string {
|
|
return fmt.Sprintf(
|
|
"%s depends on %s with %s, but that's already selected at %s",
|
|
a2vs(e.goal.depender),
|
|
e.goal.dep.Ident.ProjectRoot,
|
|
e.goal.dep.Constraint,
|
|
e.v,
|
|
)
|
|
}
|
|
|
|
// versionNotAllowedFailure describes a failure where an atom is rejected
|
|
// because its version is not allowed by current constraints.
|
|
//
|
|
// (This is one of the more straightforward types of failures)
|
|
type versionNotAllowedFailure struct {
|
|
// goal is the atom that was rejected by current constraints.
|
|
goal atom
|
|
// failparent is the list of active dependencies that caused the atom to be
|
|
// rejected. Note that this only includes dependencies that actually
|
|
// rejected the atom, which will be at least one, but may not be all the
|
|
// active dependencies on the atom's identifier.
|
|
failparent []dependency
|
|
// c is the current constraint on the atom's identifier. This is the intersection
|
|
// of all active dependencies' constraints.
|
|
c Constraint
|
|
}
|
|
|
|
func (e *versionNotAllowedFailure) Error() string {
|
|
if len(e.failparent) == 1 {
|
|
return fmt.Sprintf(
|
|
"Could not introduce %s, as it is not allowed by constraint %s from project %s.",
|
|
a2vs(e.goal),
|
|
e.failparent[0].dep.Constraint.String(),
|
|
e.failparent[0].depender.id.errString(),
|
|
)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintf(&buf, "Could not introduce %s, as it is not allowed by constraints from the following projects:\n", a2vs(e.goal))
|
|
|
|
for _, f := range e.failparent {
|
|
fmt.Fprintf(&buf, "\t%s from %s\n", f.dep.Constraint.String(), a2vs(f.depender))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (e *versionNotAllowedFailure) traceString() string {
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintf(&buf, "%s not allowed by constraint %s:\n", a2vs(e.goal), e.c.String())
|
|
for _, f := range e.failparent {
|
|
fmt.Fprintf(&buf, " %s from %s\n", f.dep.Constraint.String(), a2vs(f.depender))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type missingSourceFailure struct {
|
|
goal ProjectIdentifier
|
|
prob string
|
|
}
|
|
|
|
func (e *missingSourceFailure) Error() string {
|
|
return fmt.Sprintf(e.prob, e.goal)
|
|
}
|
|
|
|
type badOptsFailure string
|
|
|
|
func (e badOptsFailure) Error() string {
|
|
return string(e)
|
|
}
|
|
|
|
type sourceMismatchFailure struct {
|
|
// The ProjectRoot over which there is disagreement about where it should be
|
|
// sourced from
|
|
shared ProjectRoot
|
|
// The current value for the network source
|
|
current string
|
|
// The mismatched value for the network source
|
|
mismatch string
|
|
// The currently selected dependencies which have agreed upon/established
|
|
// the given network source
|
|
sel []dependency
|
|
// The atom with the constraint that has the new, incompatible network source
|
|
prob atom
|
|
}
|
|
|
|
func (e *sourceMismatchFailure) Error() string {
|
|
var cur []string
|
|
for _, c := range e.sel {
|
|
cur = append(cur, string(c.depender.id.ProjectRoot))
|
|
}
|
|
|
|
str := "Could not introduce %s, as it depends on %s from %s, but %s is already marked as coming from %s by %s"
|
|
return fmt.Sprintf(str, a2vs(e.prob), e.shared, e.mismatch, e.shared, e.current, strings.Join(cur, ", "))
|
|
}
|
|
|
|
func (e *sourceMismatchFailure) traceString() string {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "disagreement on network addr for %s:\n", e.shared)
|
|
|
|
fmt.Fprintf(&buf, " %s from %s\n", e.mismatch, e.prob.id.errString())
|
|
for _, dep := range e.sel {
|
|
fmt.Fprintf(&buf, " %s from %s\n", e.current, dep.depender.id.errString())
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
type errDeppers struct {
|
|
err error
|
|
deppers []atom
|
|
}
|
|
|
|
// checkeeHasProblemPackagesFailure indicates that the goal atom was rejected
|
|
// because one or more of the packages required by its deppers had errors.
|
|
//
|
|
// "errors" includes package nonexistence, which is indicated by a nil err in
|
|
// the corresponding errDeppers failpkg map value.
|
|
//
|
|
// checkeeHasProblemPackagesFailure complements depHasProblemPackagesFailure;
|
|
// one or the other could appear to describe the same fundamental issue,
|
|
// depending on the order in which dependencies were visited.
|
|
type checkeeHasProblemPackagesFailure struct {
|
|
// goal is the atom that was rejected due to problematic packages.
|
|
goal atom
|
|
// failpkg is a map of package names to the error describing the problem
|
|
// with them, plus a list of the selected atoms that require that package.
|
|
failpkg map[string]errDeppers
|
|
}
|
|
|
|
func (e *checkeeHasProblemPackagesFailure) Error() string {
|
|
var buf bytes.Buffer
|
|
indent := ""
|
|
|
|
if len(e.failpkg) > 1 {
|
|
indent = "\t"
|
|
fmt.Fprintf(
|
|
&buf, "Could not introduce %s due to multiple problematic subpackages:\n",
|
|
a2vs(e.goal),
|
|
)
|
|
}
|
|
|
|
for pkg, errdep := range e.failpkg {
|
|
var cause string
|
|
if errdep.err == nil {
|
|
cause = "is missing"
|
|
} else {
|
|
cause = fmt.Sprintf("does not contain usable Go code (%T).", errdep.err)
|
|
}
|
|
|
|
if len(e.failpkg) == 1 {
|
|
fmt.Fprintf(
|
|
&buf, "Could not introduce %s, as its subpackage %s %s.",
|
|
a2vs(e.goal),
|
|
pkg,
|
|
cause,
|
|
)
|
|
} else {
|
|
fmt.Fprintf(&buf, "\tSubpackage %s %s.", pkg, cause)
|
|
}
|
|
|
|
if len(errdep.deppers) == 1 {
|
|
fmt.Fprintf(
|
|
&buf, " (Package is required by %s.)",
|
|
a2vs(errdep.deppers[0]),
|
|
)
|
|
} else {
|
|
fmt.Fprintf(&buf, " Package is required by:")
|
|
for _, pa := range errdep.deppers {
|
|
fmt.Fprintf(&buf, "\n%s\t%s", indent, a2vs(pa))
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (e *checkeeHasProblemPackagesFailure) traceString() string {
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintf(&buf, "%s at %s has problem subpkg(s):\n", e.goal.id.ProjectRoot, e.goal.v)
|
|
for pkg, errdep := range e.failpkg {
|
|
if errdep.err == nil {
|
|
fmt.Fprintf(&buf, "\t%s is missing; ", pkg)
|
|
} else {
|
|
fmt.Fprintf(&buf, "\t%s has err (%T); ", pkg, errdep.err)
|
|
}
|
|
|
|
if len(errdep.deppers) == 1 {
|
|
fmt.Fprintf(&buf, "required by %s.", a2vs(errdep.deppers[0]))
|
|
} else {
|
|
fmt.Fprintf(&buf, " required by:")
|
|
for _, pa := range errdep.deppers {
|
|
fmt.Fprintf(&buf, "\n\t\t%s at %s", pa.id.errString(), pa.v)
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// depHasProblemPackagesFailure indicates that the goal dependency was rejected
|
|
// because there were problems with one or more of the packages the dependency
|
|
// requires in the atom currently selected for that dependency. (This failure
|
|
// can only occur if the target dependency is already selected.)
|
|
//
|
|
// "errors" includes package nonexistence, which is indicated by a nil err as
|
|
// the corresponding prob map value.
|
|
//
|
|
// depHasProblemPackagesFailure complements checkeeHasProblemPackagesFailure;
|
|
// one or the other could appear to describe the same fundamental issue,
|
|
// depending on the order in which dependencies were visited.
|
|
type depHasProblemPackagesFailure struct {
|
|
// goal is the dependency that was rejected due to the atom currently
|
|
// selected for the dependency's target id having errors (including, and
|
|
// probably most commonly,
|
|
// nonexistence) in one or more packages named by the dependency.
|
|
goal dependency
|
|
// v is the version of the currently selected atom targeted by the goal
|
|
// dependency.
|
|
v Version
|
|
// prob is a map of problem packages to their specific error. It does not
|
|
// include missing packages.
|
|
prob map[string]error
|
|
}
|
|
|
|
func (e *depHasProblemPackagesFailure) Error() string {
|
|
fcause := func(pkg string) string {
|
|
if err := e.prob[pkg]; err != nil {
|
|
return fmt.Sprintf("does not contain usable Go code (%T).", err)
|
|
}
|
|
return "is missing."
|
|
}
|
|
|
|
if len(e.prob) == 1 {
|
|
var pkg string
|
|
for pkg = range e.prob {
|
|
}
|
|
|
|
return fmt.Sprintf(
|
|
"Could not introduce %s, as it requires package %s from %s, but in version %s that package %s",
|
|
a2vs(e.goal.depender),
|
|
pkg,
|
|
e.goal.dep.Ident.errString(),
|
|
e.v,
|
|
fcause(pkg),
|
|
)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(
|
|
&buf, "Could not introduce %s, as it requires problematic packages from %s (current version %s):",
|
|
a2vs(e.goal.depender),
|
|
e.goal.dep.Ident.errString(),
|
|
e.v,
|
|
)
|
|
|
|
pkgs := make([]string, len(e.prob))
|
|
k := 0
|
|
for pkg := range e.prob {
|
|
pkgs[k] = pkg
|
|
k++
|
|
}
|
|
sort.Strings(pkgs)
|
|
for _, pkg := range pkgs {
|
|
fmt.Fprintf(&buf, "\t%s %s", pkg, fcause(pkg))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func (e *depHasProblemPackagesFailure) traceString() string {
|
|
var buf bytes.Buffer
|
|
fcause := func(pkg string) string {
|
|
if err := e.prob[pkg]; err != nil {
|
|
return fmt.Sprintf("has parsing err (%T).", err)
|
|
}
|
|
return "is missing"
|
|
}
|
|
|
|
fmt.Fprintf(
|
|
&buf, "%s depping on %s at %s has problem subpkg(s):",
|
|
a2vs(e.goal.depender),
|
|
e.goal.dep.Ident.errString(),
|
|
e.v,
|
|
)
|
|
|
|
pkgs := make([]string, len(e.prob))
|
|
k := 0
|
|
for pkg := range e.prob {
|
|
pkgs[k] = pkg
|
|
k++
|
|
}
|
|
sort.Strings(pkgs)
|
|
for _, pkg := range pkgs {
|
|
fmt.Fprintf(&buf, "\t%s %s", pkg, fcause(pkg))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// nonexistentRevisionFailure indicates that a revision constraint was specified
|
|
// for a given project, but that that revision does not exist in the source
|
|
// repository.
|
|
type nonexistentRevisionFailure struct {
|
|
goal dependency
|
|
r Revision
|
|
}
|
|
|
|
func (e *nonexistentRevisionFailure) Error() string {
|
|
return fmt.Sprintf(
|
|
"Could not introduce %s, as it requires %s at revision %s, but that revision does not exist",
|
|
a2vs(e.goal.depender),
|
|
e.goal.dep.Ident.errString(),
|
|
e.r,
|
|
)
|
|
}
|
|
|
|
func (e *nonexistentRevisionFailure) traceString() string {
|
|
return fmt.Sprintf(
|
|
"%s wants missing rev %s of %s",
|
|
a2vs(e.goal.depender),
|
|
e.r,
|
|
e.goal.dep.Ident.errString(),
|
|
)
|
|
}
|