This repository has been archived by the owner on Feb 3, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathsolve_failures.go
492 lines (420 loc) · 13.8 KB
/
solve_failures.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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(),
)
}