github.com/golang/dep@v0.5.4/gps/solve_failures.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gps
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"sort"
    11  	"strings"
    12  )
    13  
    14  func a2vs(a atom) string {
    15  	if a.v == rootRev || a.v == nil {
    16  		return "(root)"
    17  	}
    18  
    19  	return fmt.Sprintf("%s@%s", a.id, a.v)
    20  }
    21  
    22  type traceError interface {
    23  	traceString() string
    24  }
    25  
    26  type noVersionError struct {
    27  	pn    ProjectIdentifier
    28  	fails []failedVersion
    29  }
    30  
    31  func (e *noVersionError) Error() string {
    32  	if len(e.fails) == 0 {
    33  		return fmt.Sprintf("No versions found for project %q.", e.pn.ProjectRoot)
    34  	}
    35  
    36  	var buf bytes.Buffer
    37  	fmt.Fprintf(&buf, "No versions of %s met constraints:", e.pn.ProjectRoot)
    38  	for _, f := range e.fails {
    39  		fmt.Fprintf(&buf, "\n\t%s: %s", f.v, f.f.Error())
    40  	}
    41  
    42  	return buf.String()
    43  }
    44  
    45  func (e *noVersionError) traceString() string {
    46  	if len(e.fails) == 0 {
    47  		return fmt.Sprintf("No versions found")
    48  	}
    49  
    50  	var buf bytes.Buffer
    51  	fmt.Fprintf(&buf, "No versions of %s met constraints:", e.pn.ProjectRoot)
    52  	for _, f := range e.fails {
    53  		if te, ok := f.f.(traceError); ok {
    54  			fmt.Fprintf(&buf, "\n  %s: %s", f.v, te.traceString())
    55  		} else {
    56  			fmt.Fprintf(&buf, "\n  %s: %s", f.v, f.f.Error())
    57  		}
    58  	}
    59  
    60  	return buf.String()
    61  }
    62  
    63  // caseMismatchFailure occurs when there are import paths that differ only by
    64  // case. The compiler disallows this case.
    65  type caseMismatchFailure struct {
    66  	// goal is the depender atom that tried to introduce the case-varying name,
    67  	// along with the case-varying name.
    68  	goal dependency
    69  	// current is the specific casing of a ProjectRoot that is presently
    70  	// selected for all possible case variations of its contained unicode code
    71  	// points.
    72  	current ProjectRoot
    73  	// failsib is the list of active dependencies that have determined the
    74  	// specific casing for the target project.
    75  	failsib []dependency
    76  }
    77  
    78  func (e *caseMismatchFailure) Error() string {
    79  	if len(e.failsib) == 1 {
    80  		str := "Could not introduce %s due to a case-only variation: it depends on %q, but %q was already established as the case variant for that project root by depender %s"
    81  		return fmt.Sprintf(str, a2vs(e.goal.depender), e.goal.dep.Ident.ProjectRoot, e.current, a2vs(e.failsib[0].depender))
    82  	}
    83  
    84  	var buf bytes.Buffer
    85  
    86  	str := "Could not introduce %s due to a case-only variation: it depends on %q, but %q was already established as the case variant for that project root by the following other dependers:\n"
    87  	fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.goal.dep.Ident.ProjectRoot, e.current)
    88  
    89  	for _, c := range e.failsib {
    90  		fmt.Fprintf(&buf, "\t%s\n", a2vs(c.depender))
    91  	}
    92  
    93  	return buf.String()
    94  }
    95  
    96  func (e *caseMismatchFailure) traceString() string {
    97  	var buf bytes.Buffer
    98  	fmt.Fprintf(&buf, "case-only variation in dependency on %q; %q already established by:\n", e.goal.dep.Ident.ProjectRoot, e.current)
    99  	for _, f := range e.failsib {
   100  		fmt.Fprintf(&buf, "%s\n", a2vs(f.depender))
   101  	}
   102  
   103  	return buf.String()
   104  }
   105  
   106  // wrongCaseFailure occurs when one or more projects - A, B, ... - depend on
   107  // another project - Z - with an incorrect case variant, as indicated by the
   108  // case variant used internally by Z to reference its own packages.
   109  //
   110  // For example, github.com/sirupsen/logrus/hooks/syslog references itself via
   111  // github.com/sirupsen/logrus, establishing that as the canonical case variant.
   112  type wrongCaseFailure struct {
   113  	// correct is the canonical representation of the ProjectRoot
   114  	correct ProjectRoot
   115  	// goal is the incorrectly-referenced target project
   116  	goal dependency
   117  	// badcase is the list of active dependencies that have specified an
   118  	// incorrect ProjectRoot casing for the project in question.
   119  	badcase []dependency
   120  }
   121  
   122  func (e *wrongCaseFailure) Error() string {
   123  	if len(e.badcase) == 1 {
   124  		str := "Could not introduce %s; imports amongst its packages establish %q as the canonical casing for root, but %s tried to import it as %q"
   125  		return fmt.Sprintf(str, a2vs(e.goal.depender), e.correct, a2vs(e.badcase[0].depender), e.badcase[0].dep.Ident.ProjectRoot)
   126  	}
   127  
   128  	var buf bytes.Buffer
   129  
   130  	str := "Could not introduce %s; imports amongst its packages establish %q as the canonical casing for root, but the following projects tried to import it as %q"
   131  	fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.correct, e.badcase[0].dep.Ident.ProjectRoot)
   132  
   133  	for _, c := range e.badcase {
   134  		fmt.Fprintf(&buf, "\t%s\n", a2vs(c.depender))
   135  	}
   136  
   137  	return buf.String()
   138  }
   139  
   140  func (e *wrongCaseFailure) traceString() string {
   141  	var buf bytes.Buffer
   142  	fmt.Fprintf(&buf, "internal imports establish %q as correct casing; %q was used by:\n", e.correct, e.goal.dep.Ident.ProjectRoot)
   143  	for _, f := range e.badcase {
   144  		fmt.Fprintf(&buf, "%s\n", a2vs(f.depender))
   145  	}
   146  
   147  	return buf.String()
   148  }
   149  
   150  // disjointConstraintFailure occurs when attempting to introduce an atom that
   151  // itself has an acceptable version, but one of its dependency constraints is
   152  // disjoint with one or more dependency constraints already active for that
   153  // identifier.
   154  type disjointConstraintFailure struct {
   155  	// goal is the dependency with the problematic constraint, forcing us to
   156  	// reject the atom that introduces it.
   157  	goal dependency
   158  	// failsib is the list of active dependencies that are disjoint with the
   159  	// goal dependency. This will be at least one, but may not be all of the
   160  	// active dependencies.
   161  	failsib []dependency
   162  	// nofailsib is the list of active dependencies that are NOT disjoint with
   163  	// the goal dependency. The total of nofailsib and failsib will always be
   164  	// the total number of active dependencies on target identifier.
   165  	nofailsib []dependency
   166  	// c is the current constraint on the target identifier. It is intersection
   167  	// of all the active dependencies' constraints.
   168  	c Constraint
   169  }
   170  
   171  func (e *disjointConstraintFailure) Error() string {
   172  	if len(e.failsib) == 1 {
   173  		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"
   174  		return fmt.Sprintf(str, a2vs(e.goal.depender), e.goal.dep.Ident, e.goal.dep.Constraint.String(), e.failsib[0].dep.Constraint.String(), a2vs(e.failsib[0].depender))
   175  	}
   176  
   177  	var buf bytes.Buffer
   178  
   179  	var sibs []dependency
   180  	if len(e.failsib) > 1 {
   181  		sibs = e.failsib
   182  
   183  		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"
   184  		fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.goal.dep.Ident, e.goal.dep.Constraint.String())
   185  	} else {
   186  		sibs = e.nofailsib
   187  
   188  		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"
   189  		fmt.Fprintf(&buf, str, a2vs(e.goal.depender), e.goal.dep.Ident, e.goal.dep.Constraint.String())
   190  	}
   191  
   192  	for _, c := range sibs {
   193  		fmt.Fprintf(&buf, "\t%s from %s\n", c.dep.Constraint.String(), a2vs(c.depender))
   194  	}
   195  
   196  	return buf.String()
   197  }
   198  
   199  func (e *disjointConstraintFailure) traceString() string {
   200  	var buf bytes.Buffer
   201  	fmt.Fprintf(&buf, "constraint %s on %s disjoint with other dependers:\n", e.goal.dep.Constraint.String(), e.goal.dep.Ident)
   202  	for _, f := range e.failsib {
   203  		fmt.Fprintf(
   204  			&buf,
   205  			"%s from %s (no overlap)\n",
   206  			f.dep.Constraint.String(),
   207  			a2vs(f.depender),
   208  		)
   209  	}
   210  	for _, f := range e.nofailsib {
   211  		fmt.Fprintf(
   212  			&buf,
   213  			"%s from %s (some overlap)\n",
   214  			f.dep.Constraint.String(),
   215  			a2vs(f.depender),
   216  		)
   217  	}
   218  
   219  	return buf.String()
   220  }
   221  
   222  // Indicates that an atom could not be introduced because one of its dep
   223  // constraints does not admit the currently-selected version of the target
   224  // project.
   225  type constraintNotAllowedFailure struct {
   226  	// The dependency with the problematic constraint that could not be
   227  	// introduced.
   228  	goal dependency
   229  	// The (currently selected) version of the target project that was not
   230  	// admissible by the goal dependency.
   231  	v Version
   232  }
   233  
   234  func (e *constraintNotAllowedFailure) Error() string {
   235  	return fmt.Sprintf(
   236  		"Could not introduce %s, as it has a dependency on %s with constraint %s, which does not allow the currently selected version of %s",
   237  		a2vs(e.goal.depender),
   238  		e.goal.dep.Ident,
   239  		e.goal.dep.Constraint,
   240  		e.v,
   241  	)
   242  }
   243  
   244  func (e *constraintNotAllowedFailure) traceString() string {
   245  	return fmt.Sprintf(
   246  		"%s depends on %s with %s, but that's already selected at %s",
   247  		a2vs(e.goal.depender),
   248  		e.goal.dep.Ident.ProjectRoot,
   249  		e.goal.dep.Constraint,
   250  		e.v,
   251  	)
   252  }
   253  
   254  // versionNotAllowedFailure describes a failure where an atom is rejected
   255  // because its version is not allowed by current constraints.
   256  //
   257  // (This is one of the more straightforward types of failures)
   258  type versionNotAllowedFailure struct {
   259  	// goal is the atom that was rejected by current constraints.
   260  	goal atom
   261  	// failparent is the list of active dependencies that caused the atom to be
   262  	// rejected. Note that this only includes dependencies that actually
   263  	// rejected the atom, which will be at least one, but may not be all the
   264  	// active dependencies on the atom's identifier.
   265  	failparent []dependency
   266  	// c is the current constraint on the atom's identifier. This is the intersection
   267  	// of all active dependencies' constraints.
   268  	c Constraint
   269  }
   270  
   271  func (e *versionNotAllowedFailure) Error() string {
   272  	if len(e.failparent) == 1 {
   273  		return fmt.Sprintf(
   274  			"Could not introduce %s, as it is not allowed by constraint %s from project %s.",
   275  			a2vs(e.goal),
   276  			e.failparent[0].dep.Constraint.String(),
   277  			e.failparent[0].depender.id,
   278  		)
   279  	}
   280  
   281  	var buf bytes.Buffer
   282  
   283  	fmt.Fprintf(&buf, "Could not introduce %s, as it is not allowed by constraints from the following projects:\n", a2vs(e.goal))
   284  
   285  	for _, f := range e.failparent {
   286  		fmt.Fprintf(&buf, "\t%s from %s\n", f.dep.Constraint.String(), a2vs(f.depender))
   287  	}
   288  
   289  	return buf.String()
   290  }
   291  
   292  func (e *versionNotAllowedFailure) traceString() string {
   293  	var buf bytes.Buffer
   294  
   295  	fmt.Fprintf(&buf, "%s not allowed by constraint %s:\n", a2vs(e.goal), e.c.String())
   296  	for _, f := range e.failparent {
   297  		fmt.Fprintf(&buf, "  %s from %s\n", f.dep.Constraint.String(), a2vs(f.depender))
   298  	}
   299  
   300  	return buf.String()
   301  }
   302  
   303  type missingSourceFailure struct {
   304  	goal ProjectIdentifier
   305  	prob string
   306  }
   307  
   308  func (e *missingSourceFailure) Error() string {
   309  	return fmt.Sprintf(e.prob, e.goal)
   310  }
   311  
   312  type badOptsFailure string
   313  
   314  func (e badOptsFailure) Error() string {
   315  	return string(e)
   316  }
   317  
   318  type sourceMismatchFailure struct {
   319  	// The ProjectRoot over which there is disagreement about where it should be
   320  	// sourced from
   321  	shared ProjectRoot
   322  	// The current value for the network source
   323  	current string
   324  	// The mismatched value for the network source
   325  	mismatch string
   326  	// The currently selected dependencies which have agreed upon/established
   327  	// the given network source
   328  	sel []dependency
   329  	// The atom with the constraint that has the new, incompatible network source
   330  	prob atom
   331  }
   332  
   333  func (e *sourceMismatchFailure) Error() string {
   334  	var cur []string
   335  	for _, c := range e.sel {
   336  		cur = append(cur, string(c.depender.id.ProjectRoot))
   337  	}
   338  
   339  	str := "Could not introduce %s, as it depends on %s from %s, but %s is already marked as coming from %s by %s"
   340  	return fmt.Sprintf(str, a2vs(e.prob), e.shared, e.mismatch, e.shared, e.current, strings.Join(cur, ", "))
   341  }
   342  
   343  func (e *sourceMismatchFailure) traceString() string {
   344  	var buf bytes.Buffer
   345  	fmt.Fprintf(&buf, "disagreement on network addr for %s:\n", e.shared)
   346  
   347  	fmt.Fprintf(&buf, "  %s from %s\n", e.mismatch, e.prob.id)
   348  	for _, dep := range e.sel {
   349  		fmt.Fprintf(&buf, "  %s from %s\n", e.current, dep.depender.id)
   350  	}
   351  
   352  	return buf.String()
   353  }
   354  
   355  type errDeppers struct {
   356  	err     error
   357  	deppers []atom
   358  }
   359  
   360  // checkeeHasProblemPackagesFailure indicates that the goal atom was rejected
   361  // because one or more of the packages required by its deppers had errors.
   362  //
   363  // "errors" includes package nonexistence, which is indicated by a nil err in
   364  // the corresponding errDeppers failpkg map value.
   365  //
   366  // checkeeHasProblemPackagesFailure complements depHasProblemPackagesFailure;
   367  // one or the other could appear to describe the same fundamental issue,
   368  // depending on the order in which dependencies were visited.
   369  type checkeeHasProblemPackagesFailure struct {
   370  	// goal is the atom that was rejected due to problematic packages.
   371  	goal atom
   372  	// failpkg is a map of package names to the error describing the problem
   373  	// with them, plus a list of the selected atoms that require that package.
   374  	failpkg map[string]errDeppers
   375  }
   376  
   377  func (e *checkeeHasProblemPackagesFailure) Error() string {
   378  	var buf bytes.Buffer
   379  	indent := ""
   380  
   381  	if len(e.failpkg) > 1 {
   382  		indent = "\t"
   383  		fmt.Fprintf(
   384  			&buf, "Could not introduce %s due to multiple problematic subpackages:\n",
   385  			a2vs(e.goal),
   386  		)
   387  	}
   388  
   389  	for pkg, errdep := range e.failpkg {
   390  		var cause string
   391  		if errdep.err == nil {
   392  			cause = "is missing"
   393  		} else {
   394  			cause = fmt.Sprintf("does not contain usable Go code (%T).", errdep.err)
   395  		}
   396  
   397  		if len(e.failpkg) == 1 {
   398  			fmt.Fprintf(
   399  				&buf, "Could not introduce %s, as its subpackage %s %s.",
   400  				a2vs(e.goal),
   401  				pkg,
   402  				cause,
   403  			)
   404  		} else {
   405  			fmt.Fprintf(&buf, "\tSubpackage %s %s.", pkg, cause)
   406  		}
   407  
   408  		if len(errdep.deppers) == 1 {
   409  			fmt.Fprintf(
   410  				&buf, " (Package is required by %s.)",
   411  				a2vs(errdep.deppers[0]),
   412  			)
   413  		} else {
   414  			fmt.Fprintf(&buf, " Package is required by:")
   415  			for _, pa := range errdep.deppers {
   416  				fmt.Fprintf(&buf, "\n%s\t%s", indent, a2vs(pa))
   417  			}
   418  		}
   419  	}
   420  
   421  	return buf.String()
   422  }
   423  
   424  func (e *checkeeHasProblemPackagesFailure) traceString() string {
   425  	var buf bytes.Buffer
   426  
   427  	fmt.Fprintf(&buf, "%s at %s has problem subpkg(s):\n", e.goal.id.ProjectRoot, e.goal.v)
   428  	for pkg, errdep := range e.failpkg {
   429  		if errdep.err == nil {
   430  			fmt.Fprintf(&buf, "\t%s is missing; ", pkg)
   431  		} else {
   432  			fmt.Fprintf(&buf, "\t%s has err (%T); ", pkg, errdep.err)
   433  		}
   434  
   435  		if len(errdep.deppers) == 1 {
   436  			fmt.Fprintf(&buf, "required by %s.", a2vs(errdep.deppers[0]))
   437  		} else {
   438  			fmt.Fprintf(&buf, " required by:")
   439  			for _, pa := range errdep.deppers {
   440  				fmt.Fprintf(&buf, "\n\t\t%s at %s", pa.id, pa.v)
   441  			}
   442  		}
   443  	}
   444  
   445  	return buf.String()
   446  }
   447  
   448  // depHasProblemPackagesFailure indicates that the goal dependency was rejected
   449  // because there were problems with one or more of the packages the dependency
   450  // requires in the atom currently selected for that dependency. (This failure
   451  // can only occur if the target dependency is already selected.)
   452  //
   453  // "errors" includes package nonexistence, which is indicated by a nil err as
   454  // the corresponding prob map value.
   455  //
   456  // depHasProblemPackagesFailure complements checkeeHasProblemPackagesFailure;
   457  // one or the other could appear to describe the same fundamental issue,
   458  // depending on the order in which dependencies were visited.
   459  type depHasProblemPackagesFailure struct {
   460  	// goal is the dependency that was rejected due to the atom currently
   461  	// selected for the dependency's target id having errors (including, and
   462  	// probably most commonly,
   463  	// nonexistence) in one or more packages named by the dependency.
   464  	goal dependency
   465  	// v is the version of the currently selected atom targeted by the goal
   466  	// dependency.
   467  	v Version
   468  	// prob is a map of problem packages to their specific error. It does not
   469  	// include missing packages.
   470  	prob map[string]error
   471  }
   472  
   473  func (e *depHasProblemPackagesFailure) Error() string {
   474  	fcause := func(pkg string) string {
   475  		if err := e.prob[pkg]; err != nil {
   476  			return fmt.Sprintf("does not contain usable Go code (%T).", err)
   477  		}
   478  		return "is missing."
   479  	}
   480  
   481  	if len(e.prob) == 1 {
   482  		var pkg string
   483  		for pkg = range e.prob {
   484  		}
   485  
   486  		return fmt.Sprintf(
   487  			"Could not introduce %s, as it requires package %s from %s, but in version %s that package %s",
   488  			a2vs(e.goal.depender),
   489  			pkg,
   490  			e.goal.dep.Ident,
   491  			e.v,
   492  			fcause(pkg),
   493  		)
   494  	}
   495  
   496  	var buf bytes.Buffer
   497  	fmt.Fprintf(
   498  		&buf, "Could not introduce %s, as it requires problematic packages from %s (current version %s):",
   499  		a2vs(e.goal.depender),
   500  		e.goal.dep.Ident,
   501  		e.v,
   502  	)
   503  
   504  	pkgs := make([]string, len(e.prob))
   505  	k := 0
   506  	for pkg := range e.prob {
   507  		pkgs[k] = pkg
   508  		k++
   509  	}
   510  	sort.Strings(pkgs)
   511  	for _, pkg := range pkgs {
   512  		fmt.Fprintf(&buf, "\t%s %s", pkg, fcause(pkg))
   513  	}
   514  
   515  	return buf.String()
   516  }
   517  
   518  func (e *depHasProblemPackagesFailure) traceString() string {
   519  	var buf bytes.Buffer
   520  	fcause := func(pkg string) string {
   521  		if err := e.prob[pkg]; err != nil {
   522  			return fmt.Sprintf("has parsing err (%T).", err)
   523  		}
   524  		return "is missing"
   525  	}
   526  
   527  	fmt.Fprintf(
   528  		&buf, "%s depping on %s at %s has problem subpkg(s):",
   529  		a2vs(e.goal.depender),
   530  		e.goal.dep.Ident,
   531  		e.v,
   532  	)
   533  
   534  	pkgs := make([]string, len(e.prob))
   535  	k := 0
   536  	for pkg := range e.prob {
   537  		pkgs[k] = pkg
   538  		k++
   539  	}
   540  	sort.Strings(pkgs)
   541  	for _, pkg := range pkgs {
   542  		fmt.Fprintf(&buf, "\t%s %s", pkg, fcause(pkg))
   543  	}
   544  
   545  	return buf.String()
   546  }
   547  
   548  // nonexistentRevisionFailure indicates that a revision constraint was specified
   549  // for a given project, but that that revision does not exist in the source
   550  // repository.
   551  type nonexistentRevisionFailure struct {
   552  	goal dependency
   553  	r    Revision
   554  }
   555  
   556  func (e *nonexistentRevisionFailure) Error() string {
   557  	return fmt.Sprintf(
   558  		"Could not introduce %s, as it requires %s at revision %s, but that revision does not exist",
   559  		a2vs(e.goal.depender),
   560  		e.goal.dep.Ident,
   561  		e.r,
   562  	)
   563  }
   564  
   565  func (e *nonexistentRevisionFailure) traceString() string {
   566  	return fmt.Sprintf(
   567  		"%s wants missing rev %s of %s",
   568  		a2vs(e.goal.depender),
   569  		e.r,
   570  		e.goal.dep.Ident,
   571  	)
   572  }