Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 2e56bed

Browse files
authored
Merge pull request #697 from ibrasho-forks/ensure-packages-are-deducible-before-solving
internal/gps: ensure packages are deducible before attempting to solve
2 parents 4455eff + 92df3dc commit 2e56bed

File tree

5 files changed

+273
-155
lines changed

5 files changed

+273
-155
lines changed

cmd/dep/ensure.go

+12
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ func (cmd *ensureCommand) runDefault(ctx *dep.Ctx, args []string, p *dep.Project
209209
return errors.New("dep ensure only takes spec arguments with -add or -update")
210210
}
211211

212+
err = gps.ValidateParams(params, sm)
213+
if err != nil {
214+
if deduceErrs, ok := err.(gps.DeductionErrs); ok {
215+
ctx.Err.Println("The following errors occurred while deducing packages:")
216+
for ip, dErr := range deduceErrs {
217+
ctx.Err.Printf(" * \"%s\": %s", ip, dErr)
218+
}
219+
ctx.Err.Println()
220+
}
221+
return errors.Wrap(err, "validateParams")
222+
}
223+
212224
solver, err := gps.Prepare(params, sm)
213225
if err != nil {
214226
return errors.Wrap(err, "prepare solver")

internal/gps/hash_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,8 @@ func TestHashInputsOverrides(t *testing.T) {
528528

529529
s, err := Prepare(params, newdepspecSM(basefix.ds, nil))
530530
if err != nil {
531-
t.Fatalf("(fix: %q) Unexpected error while prepping solver: %s", fix.name, err)
531+
t.Errorf("(fix: %q) Unexpected error while prepping solver: %s", fix.name, err)
532+
continue
532533
}
533534

534535
h := sha256.New()

internal/gps/solve_test.go

-152
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@ package gps
77
import (
88
"bytes"
99
"fmt"
10-
"io/ioutil"
1110
"log"
12-
"math/rand"
1311
"reflect"
1412
"sort"
15-
"strconv"
1613
"strings"
1714
"testing"
1815
"unicode"
19-
20-
"github.com/golang/dep/internal/gps/pkgtree"
2116
)
2217

2318
// overrideMkBridge overrides the base bridge with the depspecBridge that skips
@@ -274,150 +269,3 @@ func TestRootLockNoVersionPairMatching(t *testing.T) {
274269

275270
fixtureSolveSimpleChecks(fix, res, err, t)
276271
}
277-
278-
// TestBadSolveOpts exercises the different possible inputs to a solver that can
279-
// be determined as invalid in Prepare(), without any further work
280-
func TestBadSolveOpts(t *testing.T) {
281-
pn := strconv.FormatInt(rand.Int63(), 36)
282-
fix := basicFixtures["no dependencies"]
283-
fix.ds[0].n = ProjectRoot(pn)
284-
285-
sm := newdepspecSM(fix.ds, nil)
286-
params := SolveParameters{
287-
mkBridgeFn: overrideMkBridge,
288-
}
289-
290-
_, err := Prepare(params, nil)
291-
if err == nil {
292-
t.Errorf("Prepare should have errored on nil SourceManager")
293-
} else if !strings.Contains(err.Error(), "non-nil SourceManager") {
294-
t.Error("Prepare should have given error on nil SourceManager, but gave:", err)
295-
}
296-
297-
_, err = Prepare(params, sm)
298-
if err == nil {
299-
t.Errorf("Prepare should have errored without ProjectAnalyzer")
300-
} else if !strings.Contains(err.Error(), "must provide a ProjectAnalyzer") {
301-
t.Error("Prepare should have given error without ProjectAnalyzer, but gave:", err)
302-
}
303-
304-
params.ProjectAnalyzer = naiveAnalyzer{}
305-
_, err = Prepare(params, sm)
306-
if err == nil {
307-
t.Errorf("Prepare should have errored on empty root")
308-
} else if !strings.Contains(err.Error(), "non-empty root directory") {
309-
t.Error("Prepare should have given error on empty root, but gave:", err)
310-
}
311-
312-
params.RootDir = pn
313-
_, err = Prepare(params, sm)
314-
if err == nil {
315-
t.Errorf("Prepare should have errored on empty name")
316-
} else if !strings.Contains(err.Error(), "non-empty import root") {
317-
t.Error("Prepare should have given error on empty import root, but gave:", err)
318-
}
319-
320-
params.RootPackageTree = pkgtree.PackageTree{
321-
ImportRoot: pn,
322-
}
323-
_, err = Prepare(params, sm)
324-
if err == nil {
325-
t.Errorf("Prepare should have errored on empty name")
326-
} else if !strings.Contains(err.Error(), "at least one package") {
327-
t.Error("Prepare should have given error on empty import root, but gave:", err)
328-
}
329-
330-
params.RootPackageTree = pkgtree.PackageTree{
331-
ImportRoot: pn,
332-
Packages: map[string]pkgtree.PackageOrErr{
333-
pn: {
334-
P: pkgtree.Package{
335-
ImportPath: pn,
336-
Name: pn,
337-
},
338-
},
339-
},
340-
}
341-
params.TraceLogger = log.New(ioutil.Discard, "", 0)
342-
343-
params.Manifest = simpleRootManifest{
344-
ovr: ProjectConstraints{
345-
ProjectRoot("foo"): ProjectProperties{},
346-
},
347-
}
348-
_, err = Prepare(params, sm)
349-
if err == nil {
350-
t.Errorf("Should have errored on override with empty ProjectProperties")
351-
} else if !strings.Contains(err.Error(), "foo, but without any non-zero properties") {
352-
t.Error("Prepare should have given error override with empty ProjectProperties, but gave:", err)
353-
}
354-
355-
params.Manifest = simpleRootManifest{
356-
ig: map[string]bool{"foo": true},
357-
req: map[string]bool{"foo": true},
358-
}
359-
_, err = Prepare(params, sm)
360-
if err == nil {
361-
t.Errorf("Should have errored on pkg both ignored and required")
362-
} else if !strings.Contains(err.Error(), "was given as both a required and ignored package") {
363-
t.Error("Prepare should have given error with single ignore/require conflict error, but gave:", err)
364-
}
365-
366-
params.Manifest = simpleRootManifest{
367-
ig: map[string]bool{"foo": true, "bar": true},
368-
req: map[string]bool{"foo": true, "bar": true},
369-
}
370-
_, err = Prepare(params, sm)
371-
if err == nil {
372-
t.Errorf("Should have errored on pkg both ignored and required")
373-
} else if !strings.Contains(err.Error(), "multiple packages given as both required and ignored:") {
374-
t.Error("Prepare should have given error with multiple ignore/require conflict error, but gave:", err)
375-
}
376-
params.Manifest = nil
377-
378-
params.ToChange = []ProjectRoot{"foo"}
379-
_, err = Prepare(params, sm)
380-
if err == nil {
381-
t.Errorf("Should have errored on non-empty ToChange without a lock provided")
382-
} else if !strings.Contains(err.Error(), "update specifically requested for") {
383-
t.Error("Prepare should have given error on ToChange without Lock, but gave:", err)
384-
}
385-
386-
params.Lock = safeLock{
387-
p: []LockedProject{
388-
NewLockedProject(mkPI("bar"), Revision("makebelieve"), nil),
389-
},
390-
}
391-
_, err = Prepare(params, sm)
392-
if err == nil {
393-
t.Errorf("Should have errored on ToChange containing project not in lock")
394-
} else if !strings.Contains(err.Error(), "cannot update foo as it is not in the lock") {
395-
t.Error("Prepare should have given error on ToChange with item not present in Lock, but gave:", err)
396-
}
397-
398-
params.Lock, params.ToChange = nil, nil
399-
_, err = Prepare(params, sm)
400-
if err != nil {
401-
t.Error("Basic conditions satisfied, prepare should have completed successfully, err as:", err)
402-
}
403-
404-
// swap out the test mkBridge override temporarily, just to make sure we get
405-
// the right error
406-
params.mkBridgeFn = nil
407-
408-
_, err = Prepare(params, sm)
409-
if err == nil {
410-
t.Errorf("Should have errored on nonexistent root")
411-
} else if !strings.Contains(err.Error(), "could not read project root") {
412-
t.Error("Prepare should have given error nonexistent project root dir, but gave:", err)
413-
}
414-
415-
// Pointing it at a file should also be an err
416-
params.RootDir = "solve_test.go"
417-
_, err = Prepare(params, sm)
418-
if err == nil {
419-
t.Errorf("Should have errored on file for RootDir")
420-
} else if !strings.Contains(err.Error(), "is a file, not a directory") {
421-
t.Error("Prepare should have given error on file as RootDir, but gave:", err)
422-
}
423-
}

internal/gps/solver.go

+45-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"log"
1111
"sort"
1212
"strings"
13+
"sync"
1314

1415
"github.com/armon/go-radix"
1516
"github.com/golang/dep/internal/gps/paths"
@@ -381,6 +382,49 @@ func (s *solver) Version() int {
381382
return 1
382383
}
383384

385+
// DeductionErrs maps package import path to errors occurring during deduction.
386+
type DeductionErrs map[string]error
387+
388+
func (e DeductionErrs) Error() string {
389+
return "could not deduce external imports' project roots"
390+
}
391+
392+
// ValidateParams validates the solver parameters to ensure solving can be completed.
393+
func ValidateParams(params SolveParameters, sm SourceManager) error {
394+
// Ensure that all packages are deducible without issues.
395+
var deducePkgsGroup sync.WaitGroup
396+
deductionErrs := make(DeductionErrs)
397+
var errsMut sync.Mutex
398+
399+
rd, err := params.toRootdata()
400+
if err != nil {
401+
return err
402+
}
403+
404+
deducePkg := func(ip string, sm SourceManager) {
405+
_, err := sm.DeduceProjectRoot(ip)
406+
if err != nil {
407+
errsMut.Lock()
408+
deductionErrs[ip] = err
409+
errsMut.Unlock()
410+
}
411+
deducePkgsGroup.Done()
412+
}
413+
414+
for _, ip := range rd.externalImportList(paths.IsStandardImportPath) {
415+
deducePkgsGroup.Add(1)
416+
go deducePkg(ip, sm)
417+
}
418+
419+
deducePkgsGroup.Wait()
420+
421+
if len(deductionErrs) > 0 {
422+
return deductionErrs
423+
}
424+
425+
return nil
426+
}
427+
384428
// Solve attempts to find a dependency solution for the given project, as
385429
// represented by the SolveParameters with which this Solver was created.
386430
//
@@ -391,8 +435,7 @@ func (s *solver) Solve() (Solution, error) {
391435
s.vUnify.mtr = s.mtr
392436

393437
// Prime the queues with the root project
394-
err := s.selectRoot()
395-
if err != nil {
438+
if err := s.selectRoot(); err != nil {
396439
return nil, err
397440
}
398441

0 commit comments

Comments
 (0)