Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ac40bf2

Browse files
committedJul 1, 2018
dep: Tell the user why we're solving
Add output to all of the information we assemble when checking if the Lock satisfies the current input set. Also some refactoring of the ctx.LoadProject() process to have fewer partial states.
1 parent b50a572 commit ac40bf2

11 files changed

+249
-149
lines changed
 

‎Gopkg.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎cmd/dep/ensure.go

+30-33
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,6 @@ func (cmd *ensureCommand) Run(ctx *dep.Ctx, args []string) error {
211211
statchan <- status
212212
}(filepath.Join(p.AbsRoot, "vendor"), lps)
213213

214-
params.RootPackageTree, err = p.ParseRootPackageTree()
215-
if err != nil {
216-
return err
217-
}
218-
219214
if fatal, err := checkErrors(params.RootPackageTree.Packages, p.Manifest.IgnoredPackages()); err != nil {
220215
if fatal {
221216
return err
@@ -283,20 +278,32 @@ func (cmd *ensureCommand) runDefault(ctx *dep.Ctx, args []string, p *dep.Project
283278
return err
284279
}
285280

286-
lock := p.Lock
281+
lock := p.ChangedLock
287282
if lock != nil {
288-
lsat := verify.LockSatisfiesInputs(p.Lock, p.Lock.SolveMeta.InputImports, p.Manifest, params.RootPackageTree)
283+
lsat := verify.LockSatisfiesInputs(p.Lock, p.Manifest, params.RootPackageTree)
289284
if !lsat.Passed() {
290-
// TODO(sdboyer) print out what bits are unsatisfied here
285+
if ctx.Verbose {
286+
ctx.Out.Println("Gopkg.lock is out of sync with Gopkg.toml and project code:")
287+
for _, missing := range lsat.MissingImports() {
288+
ctx.Out.Printf("\t%s is missing from input-imports\n", missing)
289+
}
290+
for _, excess := range lsat.ExcessImports() {
291+
ctx.Out.Printf("\t%s is in input-imports, but isn't imported\n", excess)
292+
}
293+
for pr, unmatched := range lsat.UnmatchedOverrides() {
294+
ctx.Out.Printf("\t%s is at %s, which is not allowed by override %s\n", pr, unmatched.V, unmatched.C)
295+
}
296+
for pr, unmatched := range lsat.UnmatchedConstraints() {
297+
ctx.Out.Printf("\t%s is at %s, which is not allowed by constraint %s\n", pr, unmatched.V, unmatched.C)
298+
}
299+
ctx.Out.Println()
300+
}
301+
291302
solver, err := gps.Prepare(params, sm)
292303
if err != nil {
293304
return errors.Wrap(err, "prepare solver")
294305
}
295306

296-
if cmd.noVendor && cmd.dryRun {
297-
return errors.New("Gopkg.lock was not up to date")
298-
}
299-
300307
solution, err := solver.Solve(context.TODO())
301308
if err != nil {
302309
return handleAllTheFailuresOfTheWorld(err)
@@ -306,23 +313,22 @@ func (cmd *ensureCommand) runDefault(ctx *dep.Ctx, args []string, p *dep.Project
306313
// The user said not to touch vendor/, so definitely nothing to do.
307314
return nil
308315
}
309-
310316
}
311317

312-
sw, err := dep.NewDeltaWriter(p.Lock, lock, <-statchan, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"))
318+
dw, err := dep.NewDeltaWriter(p.Lock, lock, <-statchan, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"))
313319
if err != nil {
314320
return err
315321
}
316322

317323
if cmd.dryRun {
318-
return sw.PrintPreparedActions(ctx.Out, ctx.Verbose)
324+
return dw.PrintPreparedActions(ctx.Out, ctx.Verbose)
319325
}
320326

321327
var logger *log.Logger
322328
if ctx.Verbose {
323329
logger = ctx.Err
324330
}
325-
return errors.WithMessage(sw.Write(p.AbsRoot, sm, true, logger), "grouped write of manifest, lock and vendor")
331+
return errors.WithMessage(dw.Write(p.AbsRoot, sm, true, logger), "grouped write of manifest, lock and vendor")
326332
}
327333

328334
func (cmd *ensureCommand) runVendorOnly(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.SourceManager, params gps.SolveParameters) error {
@@ -333,9 +339,10 @@ func (cmd *ensureCommand) runVendorOnly(ctx *dep.Ctx, args []string, p *dep.Proj
333339
if p.Lock == nil {
334340
return errors.Errorf("no %s exists from which to populate vendor/", dep.LockName)
335341
}
342+
336343
// Pass the same lock as old and new so that the writer will observe no
337344
// difference and choose not to write it out.
338-
sw, err := dep.NewSafeWriter(nil, p.Lock, p.Lock, dep.VendorAlways, p.Manifest.PruneOptions)
345+
sw, err := dep.NewSafeWriter(nil, p.Lock, p.ChangedLock, dep.VendorAlways, p.Manifest.PruneOptions)
339346
if err != nil {
340347
return err
341348
}
@@ -383,19 +390,19 @@ func (cmd *ensureCommand) runUpdate(ctx *dep.Ctx, args []string, p *dep.Project,
383390
return handleAllTheFailuresOfTheWorld(err)
384391
}
385392

386-
sw, err := dep.NewSafeWriter(nil, p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), cmd.vendorBehavior(), p.Manifest.PruneOptions)
393+
dw, err := dep.NewDeltaWriter(p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), <-statchan, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"))
387394
if err != nil {
388395
return err
389396
}
390397
if cmd.dryRun {
391-
return sw.PrintPreparedActions(ctx.Out, ctx.Verbose)
398+
return dw.PrintPreparedActions(ctx.Out, ctx.Verbose)
392399
}
393400

394401
var logger *log.Logger
395402
if ctx.Verbose {
396403
logger = ctx.Err
397404
}
398-
return errors.Wrap(sw.Write(p.AbsRoot, sm, false, logger), "grouped write of manifest, lock and vendor")
405+
return errors.Wrap(dw.Write(p.AbsRoot, sm, false, logger), "grouped write of manifest, lock and vendor")
399406
}
400407

401408
func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm gps.SourceManager, params gps.SolveParameters, statchan chan map[string]verify.VendorStatus) error {
@@ -417,16 +424,6 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
417424

418425
rm, _ := params.RootPackageTree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages())
419426

420-
// TODO(sdboyer) re-enable this once we ToReachMap() intelligently filters out normally-excluded (_*, .*), dirs from errmap
421-
//rm, errmap := params.RootPackageTree.ToReachMap(true, true, false, p.Manifest.IgnoredPackages())
422-
// Having some problematic internal packages isn't cause for termination,
423-
// but the user needs to be warned.
424-
//for fail, err := range errmap {
425-
//if _, is := err.Err.(*build.NoGoError); !is {
426-
//ctx.Err.Printf("Warning: %s, %s", fail, err)
427-
//}
428-
//}
429-
430427
// Compile unique sets of 1) all external packages imported or required, and
431428
// 2) the project roots under which they fall.
432429
exmap := make(map[string]bool)
@@ -673,20 +670,20 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
673670
}
674671
sort.Strings(reqlist)
675672

676-
sw, err := dep.NewSafeWriter(nil, p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), dep.VendorOnChanged, p.Manifest.PruneOptions)
673+
dw, err := dep.NewDeltaWriter(p.Lock, dep.LockFromSolution(solution, p.Manifest.PruneOptions), <-statchan, p.Manifest.PruneOptions, filepath.Join(p.AbsRoot, "vendor"))
677674
if err != nil {
678675
return err
679676
}
680677

681678
if cmd.dryRun {
682-
return sw.PrintPreparedActions(ctx.Out, ctx.Verbose)
679+
return dw.PrintPreparedActions(ctx.Out, ctx.Verbose)
683680
}
684681

685682
var logger *log.Logger
686683
if ctx.Verbose {
687684
logger = ctx.Err
688685
}
689-
if err := errors.Wrap(sw.Write(p.AbsRoot, sm, true, logger), "grouped write of manifest, lock and vendor"); err != nil {
686+
if err := errors.Wrap(dw.Write(p.AbsRoot, sm, true, logger), "grouped write of manifest, lock and vendor"); err != nil {
690687
return err
691688
}
692689

‎cmd/dep/init.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
102102
ctx.Out.Println("Getting direct dependencies...")
103103
}
104104

105-
ptree, directDeps, err := p.GetDirectDependencyNames(sm)
105+
directDeps, err := p.GetDirectDependencyNames(sm)
106106
if err != nil {
107107
return errors.Wrap(err, "init failed: unable to determine direct dependencies")
108108
}
109109
if ctx.Verbose {
110-
ctx.Out.Printf("Checked %d directories for packages.\nFound %d direct dependencies.\n", len(ptree.Packages), len(directDeps))
110+
ctx.Out.Printf("Checked %d directories for packages.\nFound %d direct dependencies.\n", len(p.RootPackageTree.Packages), len(directDeps))
111111
}
112112

113113
// Initialize with imported data, then fill in the gaps using the GOPATH
@@ -133,7 +133,7 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
133133

134134
params := gps.SolveParameters{
135135
RootDir: root,
136-
RootPackageTree: ptree,
136+
RootPackageTree: p.RootPackageTree,
137137
Manifest: p.Manifest,
138138
Lock: p.Lock,
139139
ProjectAnalyzer: rootAnalyzer,

‎cmd/dep/status.go

+6-12
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,13 @@ type dotOutput struct {
250250
func (out *dotOutput) BasicHeader() error {
251251
out.g = new(graphviz).New()
252252

253-
ptree, err := out.p.ParseRootPackageTree()
253+
ptree := out.p.RootPackageTree
254254
// TODO(sdboyer) should be true, true, false, out.p.Manifest.IgnoredPackages()
255255
prm, _ := ptree.ToReachMap(true, false, false, nil)
256256

257257
out.g.createNode(string(out.p.ImportRoot), "", prm.FlattenFn(paths.IsStandardImportPath))
258258

259-
return err
259+
return nil
260260
}
261261

262262
func (out *dotOutput) BasicFooter() error {
@@ -491,10 +491,7 @@ func (os OldStatus) marshalJSON() *rawOldStatus {
491491
func (cmd *statusCommand) runOld(ctx *dep.Ctx, out oldOutputter, p *dep.Project, sm gps.SourceManager) error {
492492
// While the network churns on ListVersions() requests, statically analyze
493493
// code from the current project.
494-
ptree, err := p.ParseRootPackageTree()
495-
if err != nil {
496-
return err
497-
}
494+
ptree := p.RootPackageTree
498495

499496
// Set up a solver in order to check the InputHash.
500497
params := gps.SolveParameters{
@@ -662,10 +659,7 @@ type MissingStatus struct {
662659
func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (hasMissingPkgs bool, errCount int, err error) {
663660
// While the network churns on ListVersions() requests, statically analyze
664661
// code from the current project.
665-
ptree, err := p.ParseRootPackageTree()
666-
if err != nil {
667-
return false, 0, err
668-
}
662+
ptree := p.RootPackageTree
669663

670664
// Set up a solver in order to check the InputHash.
671665
params := gps.SolveParameters{
@@ -702,7 +696,7 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana
702696
return slp[i].Ident().Less(slp[j].Ident())
703697
})
704698

705-
lsat := verify.LockSatisfiesInputs(p.Lock, p.Lock.SolveMeta.InputImports, p.Manifest, params.RootPackageTree)
699+
lsat := verify.LockSatisfiesInputs(p.Lock, p.Manifest, params.RootPackageTree)
706700
if lsat.Passed() {
707701
// If these are equal, we're guaranteed that the lock is a transitively
708702
// complete picture of all deps. That eliminates the need for at least
@@ -997,7 +991,7 @@ func collectConstraints(ctx *dep.Ctx, p *dep.Project, sm gps.SourceManager) (con
997991

998992
// Collect the complete set of direct project dependencies, incorporating
999993
// requireds and ignores appropriately.
1000-
_, directDeps, err := p.GetDirectDependencyNames(sm)
994+
directDeps, err := p.GetDirectDependencyNames(sm)
1001995
if err != nil {
1002996
// Return empty collection, not nil, if we fail here.
1003997
return constraintCollection, []error{errors.Wrap(err, "failed to get direct dependencies")}

‎context.go

+53
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import (
99
"os"
1010
"path/filepath"
1111
"runtime"
12+
"sort"
1213
"time"
1314

1415
"github.com/golang/dep/gps"
16+
"github.com/golang/dep/gps/paths"
17+
"github.com/golang/dep/gps/pkgtree"
18+
"github.com/golang/dep/gps/verify"
1519
"github.com/golang/dep/internal/fs"
1620
"github.com/pkg/errors"
1721
)
@@ -181,9 +185,58 @@ func (c *Ctx) LoadProject() (*Project, error) {
181185
return nil, errors.Wrapf(err, "error while parsing %s", lp)
182186
}
183187

188+
// Parse in the root package tree.
189+
ptree, err := p.parseRootPackageTree()
190+
if err != nil {
191+
return nil, err
192+
}
193+
194+
// If there's a current Lock, apply the input and pruneopt changes that we
195+
// can know without solving.
196+
if p.Lock != nil {
197+
p.ChangedLock = p.Lock.dup()
198+
p.ChangedLock.SolveMeta.InputImports = externalImportList(ptree, p.Manifest)
199+
200+
for k, lp := range p.ChangedLock.Projects() {
201+
vp := lp.(verify.VerifiableProject)
202+
vp.PruneOpts = p.Manifest.PruneOptions.PruneOptionsFor(lp.Ident().ProjectRoot)
203+
p.ChangedLock.P[k] = vp
204+
}
205+
}
206+
184207
return p, nil
185208
}
186209

210+
func externalImportList(rpt pkgtree.PackageTree, m gps.RootManifest) []string {
211+
if m == nil {
212+
m = &Manifest{}
213+
}
214+
rm, _ := rpt.ToReachMap(true, true, false, m.IgnoredPackages())
215+
reach := rm.FlattenFn(paths.IsStandardImportPath)
216+
req := m.RequiredPackages()
217+
218+
// If there are any requires, slide them into the reach list, as well.
219+
if len(req) > 0 {
220+
// Make a map of imports that are both in the import path list and the
221+
// required list to avoid duplication.
222+
skip := make(map[string]bool, len(req))
223+
for _, r := range reach {
224+
if req[r] {
225+
skip[r] = true
226+
}
227+
}
228+
229+
for r := range req {
230+
if !skip[r] {
231+
reach = append(reach, r)
232+
}
233+
}
234+
}
235+
236+
sort.Strings(reach)
237+
return reach
238+
}
239+
187240
// DetectProjectGOPATH attempt to find the GOPATH containing the project.
188241
//
189242
// If p.AbsRoot is not a symlink and is within a GOPATH, the GOPATH containing p.AbsRoot is returned.

‎gps/verify/digest.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,9 @@ func VerifyDepTree(osDirname string, wantDigests map[string]VersionedDigest) (ma
461461
if expectedSum, ok := wantDigests[slashPathname]; ok {
462462
ls := EmptyDigestInLock
463463
if expectedSum.HashVersion != HashVersion {
464-
ls = HashVersionMismatch
464+
if !expectedSum.IsEmpty() {
465+
ls = HashVersionMismatch
466+
}
465467
} else if len(expectedSum.Digest) > 0 {
466468
projectSum, err := DigestFromDirectory(osPathname)
467469
if err != nil {

‎gps/verify/lock.go

+56-33
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,24 @@ type VerifiableProject struct {
2020
Digest VersionedDigest
2121
}
2222

23-
type lockUnsatisfy uint8
24-
25-
const (
26-
missingFromLock lockUnsatisfy = iota
27-
inAdditionToLock
28-
)
29-
30-
type constraintMismatch struct {
31-
c gps.Constraint
32-
v gps.Version
23+
// ConstraintMismatch is a two-tuple of a gps.Version, and a gps.Constraint that
24+
// does not allow that version.
25+
type ConstraintMismatch struct {
26+
C gps.Constraint
27+
V gps.Version
3328
}
3429

35-
type constraintMismatches map[gps.ProjectRoot]constraintMismatch
36-
30+
// LockSatisfaction holds the compound result of LockSatisfiesInputs, allowing
31+
// the caller to inspect each of several orthogonal possible types of failure.
3732
type LockSatisfaction struct {
3833
nolock bool
3934
missingPkgs, excessPkgs []string
40-
badovr, badconstraint constraintMismatches
35+
badovr, badconstraint map[gps.ProjectRoot]ConstraintMismatch
4136
}
4237

43-
// Passed is a shortcut method to check if any problems with the evaluted lock
44-
// were identified.
38+
// Passed is a shortcut method that indicates whether there were any ways in
39+
// which the Lock did not satisfy the inputs. It will return true only if no
40+
// problems were found.
4541
func (ls LockSatisfaction) Passed() bool {
4642
if ls.nolock {
4743
return false
@@ -66,19 +62,27 @@ func (ls LockSatisfaction) Passed() bool {
6662
return true
6763
}
6864

69-
func (ls LockSatisfaction) MissingPackages() []string {
65+
// MissingImports reports the set of import paths that were present in the
66+
// inputs but missing in the Lock.
67+
func (ls LockSatisfaction) MissingImports() []string {
7068
return ls.missingPkgs
7169
}
7270

73-
func (ls LockSatisfaction) ExcessPackages() []string {
71+
// ExcessImports reports the set of import paths that were present in the Lock
72+
// but absent from the inputs.
73+
func (ls LockSatisfaction) ExcessImports() []string {
7474
return ls.excessPkgs
7575
}
7676

77-
func (ls LockSatisfaction) UnmatchedOverrides() map[gps.ProjectRoot]constraintMismatch {
77+
// UnmatchedOverrides reports any override rules that were not satisfied by the
78+
// corresponding LockedProject in the Lock.
79+
func (ls LockSatisfaction) UnmatchedOverrides() map[gps.ProjectRoot]ConstraintMismatch {
7880
return ls.badovr
7981
}
8082

81-
func (ls LockSatisfaction) UnmatchedConstraints() map[gps.ProjectRoot]constraintMismatch {
83+
// UnmatchedOverrides reports any normal, non-override constraint rules that
84+
// were not satisfied by the corresponding LockedProject in the Lock.
85+
func (ls LockSatisfaction) UnmatchedConstraints() map[gps.ProjectRoot]ConstraintMismatch {
8286
return ls.badconstraint
8387
}
8488

@@ -87,6 +91,8 @@ func findEffectualConstraints(m gps.Manifest, imports map[string]bool) map[strin
8791
xt := radix.New()
8892

8993
for pr, _ := range m.DependencyConstraints() {
94+
// FIXME(sdboyer) this has the trailing slash ambiguity problem; adapt
95+
// code from the solver
9096
xt.Insert(string(pr), nil)
9197
}
9298

@@ -107,7 +113,7 @@ func findEffectualConstraints(m gps.Manifest, imports map[string]bool) map[strin
107113
// compute package imports that may have been removed. Figuring out that
108114
// negative space would require exploring the entire graph to ensure there are
109115
// no in-edges for particular imports.
110-
func LockSatisfiesInputs(l gps.Lock, oldimports []string, m gps.RootManifest, rpt pkgtree.PackageTree) LockSatisfaction {
116+
func LockSatisfiesInputs(l gps.LockWithImports, m gps.RootManifest, rpt pkgtree.PackageTree) LockSatisfaction {
111117
if l == nil {
112118
return LockSatisfaction{nolock: true}
113119
}
@@ -122,8 +128,15 @@ func LockSatisfiesInputs(l gps.Lock, oldimports []string, m gps.RootManifest, rp
122128
rm, _ := rpt.ToReachMap(true, true, false, ig)
123129
reach := rm.FlattenFn(paths.IsStandardImportPath)
124130

125-
inlock := make(map[string]bool, len(oldimports))
131+
inlock := make(map[string]bool, len(l.InputImports()))
126132
ininputs := make(map[string]bool, len(reach)+len(req))
133+
134+
type lockUnsatisfy uint8
135+
const (
136+
missingFromLock lockUnsatisfy = iota
137+
inAdditionToLock
138+
)
139+
127140
pkgDiff := make(map[string]lockUnsatisfy)
128141

129142
for _, imp := range reach {
@@ -134,13 +147,13 @@ func LockSatisfiesInputs(l gps.Lock, oldimports []string, m gps.RootManifest, rp
134147
ininputs[imp] = true
135148
}
136149

137-
for _, imp := range oldimports {
150+
for _, imp := range l.InputImports() {
138151
inlock[imp] = true
139152
}
140153

141154
lsat := LockSatisfaction{
142-
badovr: make(constraintMismatches),
143-
badconstraint: make(constraintMismatches),
155+
badovr: make(map[gps.ProjectRoot]ConstraintMismatch),
156+
badconstraint: make(map[gps.ProjectRoot]ConstraintMismatch),
144157
}
145158

146159
for ip := range ininputs {
@@ -152,6 +165,12 @@ func LockSatisfiesInputs(l gps.Lock, oldimports []string, m gps.RootManifest, rp
152165
}
153166
}
154167

168+
// Something in the missing list might already be in the packages list,
169+
// because another package in the depgraph imports it. We could make a
170+
// special case for that, but it would break the simplicity of the model and
171+
// complicate the notion of LockSatisfaction.Passed(), so let's see if we
172+
// can get away without it.
173+
155174
for ip := range inlock {
156175
if !ininputs[ip] {
157176
pkgDiff[ip] = inAdditionToLock
@@ -167,23 +186,27 @@ func LockSatisfiesInputs(l gps.Lock, oldimports []string, m gps.RootManifest, rp
167186
}
168187

169188
eff := findEffectualConstraints(m, ininputs)
170-
ovr := m.Overrides()
171-
constraints := m.DependencyConstraints()
189+
ovr, constraints := m.Overrides(), m.DependencyConstraints()
172190

173191
for _, lp := range l.Projects() {
174192
pr := lp.Ident().ProjectRoot
175193

176-
if pp, has := ovr[pr]; has && !pp.Constraint.Matches(lp.Version()) {
177-
lsat.badovr[pr] = constraintMismatch{
178-
c: pp.Constraint,
179-
v: lp.Version(),
194+
if pp, has := ovr[pr]; has {
195+
if !pp.Constraint.Matches(lp.Version()) {
196+
lsat.badovr[pr] = ConstraintMismatch{
197+
C: pp.Constraint,
198+
V: lp.Version(),
199+
}
180200
}
201+
// The constraint isn't considered if we have an override,
202+
// independent of whether the override is satisfied.
203+
continue
181204
}
182205

183206
if pp, has := constraints[pr]; has && eff[string(pr)] && !pp.Constraint.Matches(lp.Version()) {
184-
lsat.badconstraint[pr] = constraintMismatch{
185-
c: pp.Constraint,
186-
v: lp.Version(),
207+
lsat.badconstraint[pr] = ConstraintMismatch{
208+
C: pp.Constraint,
209+
V: lp.Version(),
187210
}
188211
}
189212
}

‎gps/verify/lockdiff.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ func DiffProjects2(lp1, lp2 gps.LockedProject) LockedProjectPartsDelta {
211211
SourceAfter: lp2.Ident().Source,
212212
}
213213

214-
ld.PackagesRemoved, ld.PackagesAdded = findAddedAndRemoved(lp1.Packages(), lp2.Packages())
214+
ld.PackagesAdded, ld.PackagesRemoved = findAddedAndRemoved(lp1.Packages(), lp2.Packages())
215215

216216
switch v := lp1.Version().(type) {
217217
case gps.PairedVersion:

‎lock.go

+13
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ func (l *Lock) HasProjectWithRoot(root gps.ProjectRoot) bool {
154154
return false
155155
}
156156

157+
func (l *Lock) dup() *Lock {
158+
l2 := &Lock{
159+
SolveMeta: l.SolveMeta,
160+
P: make([]gps.LockedProject, len(l.P)),
161+
}
162+
163+
l2.SolveMeta.InputImports = make([]string, len(l.SolveMeta.InputImports))
164+
copy(l2.SolveMeta.InputImports, l.SolveMeta.InputImports)
165+
copy(l2.P, l.P)
166+
167+
return l2
168+
}
169+
157170
// toRaw converts the manifest into a representation suitable to write to the lock file
158171
func (l *Lock) toRaw() rawLock {
159172
raw := rawLock{

‎project.go

+31-44
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"sort"
1212

1313
"github.com/golang/dep/gps"
14-
"github.com/golang/dep/gps/paths"
1514
"github.com/golang/dep/gps/pkgtree"
1615
"github.com/golang/dep/internal/fs"
1716
"github.com/pkg/errors"
@@ -101,13 +100,19 @@ type Project struct {
101100
// If AbsRoot is not a symlink, then ResolvedAbsRoot should equal AbsRoot.
102101
ResolvedAbsRoot string
103102
// ImportRoot is the import path of the project's root directory.
104-
ImportRoot gps.ProjectRoot
105-
Manifest *Manifest
106-
Lock *Lock // Optional
103+
ImportRoot gps.ProjectRoot
104+
// The Manifest, as read from Gopkg.toml on disk.
105+
Manifest *Manifest
106+
// The Lock, as read from Gopkg.lock on disk.
107+
Lock *Lock // Optional
108+
// The above Lock, with changes applied to it. There are two possible classes of
109+
// changes:
110+
// 1. Changes to InputImports
111+
// 2. Changes to per-project prune options
112+
ChangedLock *Lock
113+
// The PackageTree representing the project, with hidden and ignored
114+
// packages already trimmed.
107115
RootPackageTree pkgtree.PackageTree
108-
// If populated, contains the results of comparing the Lock against the
109-
// current vendor tree, per verify.VerifyDepTree().
110-
//VendorStatus map[string]verify.VendorStatus
111116
}
112117

113118
// SetRoot sets the project AbsRoot and ResolvedAbsRoot. If root is not a symlink, ResolvedAbsRoot will be set to root.
@@ -127,25 +132,28 @@ func (p *Project) MakeParams() gps.SolveParameters {
127132
params := gps.SolveParameters{
128133
RootDir: p.AbsRoot,
129134
ProjectAnalyzer: Analyzer{},
135+
RootPackageTree: p.RootPackageTree,
130136
}
131137

132138
if p.Manifest != nil {
133139
params.Manifest = p.Manifest
134140
}
135141

136-
if p.Lock != nil {
137-
params.Lock = p.Lock
142+
// It should be impossible for p.ChangedLock to be nil if p.Lock is non-nil;
143+
// we always want to use the former for solving.
144+
if p.ChangedLock != nil {
145+
params.Lock = p.ChangedLock
138146
}
139147

140148
return params
141149
}
142150

143-
// ParseRootPackageTree analyzes the root project's disk contents to create a
151+
// parseRootPackageTree analyzes the root project's disk contents to create a
144152
// PackageTree, trimming out packages that are not relevant for root projects
145153
// along the way.
146154
//
147155
// The resulting tree is cached internally at p.RootPackageTree.
148-
func (p *Project) ParseRootPackageTree() (pkgtree.PackageTree, error) {
156+
func (p *Project) parseRootPackageTree() (pkgtree.PackageTree, error) {
149157
if p.RootPackageTree.Packages == nil {
150158
ptree, err := pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot))
151159
if err != nil {
@@ -177,49 +185,28 @@ func (p *Project) ParseRootPackageTree() (pkgtree.PackageTree, error) {
177185
// This function will correctly utilize ignores and requireds from an existing
178186
// manifest, if one is present, but will also do the right thing without a
179187
// manifest.
180-
func (p *Project) GetDirectDependencyNames(sm gps.SourceManager) (pkgtree.PackageTree, map[gps.ProjectRoot]bool, error) {
181-
ptree, err := p.ParseRootPackageTree()
182-
if err != nil {
183-
return pkgtree.PackageTree{}, nil, err
184-
}
185-
186-
var ig *pkgtree.IgnoredRuleset
187-
var req map[string]bool
188-
if p.Manifest != nil {
189-
ig = p.Manifest.IgnoredPackages()
190-
req = p.Manifest.RequiredPackages()
191-
}
192-
193-
rm, _ := ptree.ToReachMap(true, true, false, ig)
194-
reach := rm.FlattenFn(paths.IsStandardImportPath)
195-
196-
if len(req) > 0 {
197-
// Make a map of imports that are both in the import path list and the
198-
// required list to avoid duplication.
199-
skip := make(map[string]bool, len(req))
200-
for _, r := range reach {
201-
if req[r] {
202-
skip[r] = true
203-
}
204-
}
205-
206-
for r := range req {
207-
if !skip[r] {
208-
reach = append(reach, r)
209-
}
188+
func (p *Project) GetDirectDependencyNames(sm gps.SourceManager) (map[gps.ProjectRoot]bool, error) {
189+
var reach []string
190+
if p.ChangedLock != nil {
191+
reach = p.ChangedLock.InputImports()
192+
} else {
193+
ptree, err := p.parseRootPackageTree()
194+
if err != nil {
195+
return nil, err
210196
}
197+
reach = externalImportList(ptree, p.Manifest)
211198
}
212199

213200
directDeps := map[gps.ProjectRoot]bool{}
214201
for _, ip := range reach {
215202
pr, err := sm.DeduceProjectRoot(ip)
216203
if err != nil {
217-
return pkgtree.PackageTree{}, nil, err
204+
return nil, err
218205
}
219206
directDeps[pr] = true
220207
}
221208

222-
return ptree, directDeps, nil
209+
return directDeps, nil
223210
}
224211

225212
// FindIneffectualConstraints looks for constraint rules expressed in the
@@ -233,7 +220,7 @@ func (p *Project) FindIneffectualConstraints(sm gps.SourceManager) []gps.Project
233220
return nil
234221
}
235222

236-
_, dd, err := p.GetDirectDependencyNames(sm)
223+
dd, err := p.GetDirectDependencyNames(sm)
237224
if err != nil {
238225
return nil
239226
}

‎txn_writer.go

+52-21
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package dep
66

77
import (
8+
"bytes"
89
"context"
910
"fmt"
1011
"io/ioutil"
@@ -466,6 +467,7 @@ const (
466467
solveChanged
467468
hashMismatch
468469
hashVersionMismatch
470+
hashAbsent
469471
missingFromTree
470472
projectAdded
471473
projectRemoved
@@ -518,10 +520,12 @@ func NewDeltaWriter(oldLock, newLock *Lock, status map[string]verify.VendorStatu
518520
switch stat {
519521
case verify.NotInTree:
520522
sw.changed[pr] = missingFromTree
521-
case verify.EmptyDigestInLock, verify.DigestMismatchInLock:
523+
case verify.DigestMismatchInLock:
522524
sw.changed[pr] = hashMismatch
523525
case verify.HashVersionMismatch:
524526
sw.changed[pr] = hashVersionMismatch
527+
case verify.EmptyDigestInLock:
528+
sw.changed[pr] = hashAbsent
525529
}
526530
}
527531
}
@@ -564,53 +568,72 @@ func (dw *DeltaWriter) Write(path string, sm gps.SourceManager, examples bool, l
564568

565569
dropped := []gps.ProjectRoot{}
566570
// TODO(sdboyer) add a txn/rollback layer, like the safewriter?
571+
i := 0
572+
tot := len(dw.changed)
567573
for pr, reason := range dw.changed {
574+
if reason == projectRemoved {
575+
dropped = append(dropped, pr)
576+
continue
577+
}
578+
568579
to := filepath.FromSlash(filepath.Join(vnewpath, string(pr)))
569580
po := dw.pruneOptions.PruneOptionsFor(pr)
581+
if err := sm.ExportPrunedProject(context.TODO(), projs[pr], po, to); err != nil {
582+
return errors.Wrapf(err, "failed to export %s", pr)
583+
}
584+
585+
i++
570586
lpd := dw.lockDiff.ProjectDeltas[pr]
587+
v, id := projs[pr].Version(), projs[pr].Ident()
571588

589+
var buf bytes.Buffer
590+
fmt.Fprintf(&buf, "(%d/%d) Wrote %s@%s: ", i, tot, id, v)
572591
switch reason {
573592
case noChange:
574593
panic(fmt.Sprintf("wtf, no change for %s", pr))
575594
case solveChanged:
576595
if lpd.SourceChanged() {
577-
logger.Printf("Writing %s: source changed (%s -> %s)", pr, lpd.SourceBefore, lpd.SourceAfter)
596+
fmt.Fprintf(&buf, "source changed (%s -> %s)", lpd.SourceBefore, lpd.SourceAfter)
578597
} else if lpd.VersionChanged() {
579-
logger.Printf("Writing %s: version changed (%s -> %s)", pr, lpd.VersionBefore, lpd.VersionAfter)
598+
bv, av := "(none)", "(none)"
599+
if lpd.VersionBefore != nil {
600+
bv = lpd.VersionBefore.String()
601+
}
602+
if lpd.VersionAfter != nil {
603+
av = lpd.VersionAfter.String()
604+
}
605+
fmt.Fprintf(&buf, "version changed (%s -> %s)", bv, av)
580606
} else if lpd.RevisionChanged() {
581-
logger.Printf("Writing %s: revision changed (%s -> %s)", pr, lpd.RevisionBefore, lpd.RevisionAfter)
607+
fmt.Fprintf(&buf, "revision changed (%s -> %s)", lpd.RevisionBefore, lpd.RevisionAfter)
582608
} else if lpd.PackagesChanged() {
583609
la, lr := len(lpd.PackagesAdded), len(lpd.PackagesRemoved)
584610
if la > 0 && lr > 0 {
585-
logger.Printf("Writing %s: packages changed (%v added, %v removed)", pr, la, lr)
611+
fmt.Fprintf(&buf, "packages changed (%v added, %v removed)", la, lr)
586612
} else if la > 0 {
587-
logger.Printf("Writing %s: packages changed (%v added)", pr, la)
613+
fmt.Fprintf(&buf, "packages changed (%v added)", la)
588614
} else {
589-
logger.Printf("Writing %s: packages changed (%v removed)", pr, lr)
615+
fmt.Fprintf(&buf, "packages changed (%v removed)", lr)
590616
}
591617
} else if lpd.PruneOptsChanged() {
592618
// Override what's on the lockdiff with the extra info we have;
593619
// this lets us excise PruneNestedVendorDirs and get the real
594620
// value from the input param in place.
595621
old := lpd.PruneOptsBefore & ^gps.PruneNestedVendorDirs
596622
new := lpd.PruneOptsAfter & ^gps.PruneNestedVendorDirs
597-
logger.Printf("Writing %s: prune options changed (%s -> %s)", pr, old, new)
623+
fmt.Fprintf(&buf, "prune options changed (%s -> %s)", old, new)
598624
}
599625
case hashMismatch:
600-
logger.Printf("Writing %s: hash mismatch between Gopkg.lock and vendor contents", pr)
626+
fmt.Fprintf(&buf, "hash mismatch between Gopkg.lock and vendor contents")
601627
case hashVersionMismatch:
602-
logger.Printf("Writing %s: hashing algorithm mismatch", pr)
628+
fmt.Fprintf(&buf, "hashing algorithm mismatch")
629+
case hashAbsent:
630+
fmt.Fprintf(&buf, "hash digest absent from lock")
603631
case projectAdded:
604-
logger.Printf("Writing new project %s", pr)
605-
case projectRemoved:
606-
dropped = append(dropped, pr)
607-
continue
632+
fmt.Fprintf(&buf, "new project")
608633
case missingFromTree:
609-
logger.Printf("Writing %s: missing from vendor", pr)
610-
}
611-
if err := sm.ExportPrunedProject(context.TODO(), projs[pr], po, to); err != nil {
612-
return errors.Wrapf(err, "failed to export %s", pr)
634+
fmt.Fprintf(&buf, "missing from vendor", pr)
613635
}
636+
logger.Print(buf.String())
614637

615638
digest, err := verify.DigestFromDirectory(to)
616639
if err != nil {
@@ -649,9 +672,17 @@ func (dw *DeltaWriter) Write(path string, sm gps.SourceManager, examples bool, l
649672
}
650673
}
651674

652-
for _, pr := range dropped {
653-
// Kind of a lie to print this here. ¯\_(ツ)_/¯
654-
logger.Printf("Discarding unused project %s", pr)
675+
for i, pr := range dropped {
676+
// Kind of a lie to print this. ¯\_(ツ)_/¯
677+
logger.Printf("(%d/%d) Removed unused project %s", tot-(len(dropped)-i-1), tot, pr)
678+
}
679+
680+
// Ensure vendor/.git is preserved if present
681+
if hasDotGit(vpath) {
682+
err = fs.RenameWithFallback(filepath.Join(vpath, ".git"), filepath.Join(vnewpath, "vendor/.git"))
683+
if _, ok := err.(*os.LinkError); ok {
684+
return errors.Wrap(err, "failed to preserve vendor/.git")
685+
}
655686
}
656687

657688
err = os.RemoveAll(vpath)

0 commit comments

Comments
 (0)
This repository has been archived.