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

Commit bc88218

Browse files
committedSep 24, 2017
ref(ensure): make ensure -add concurrent
This change makes `ensure -add` concurrently add the dependencies and uses golang.org/x/sync/syncmap for a concurrent map to replace addInstructions map. Also, logs a message when the sources are being fetched.
1 parent 203c059 commit bc88218

File tree

2 files changed

+124
-68
lines changed

2 files changed

+124
-68
lines changed
 

‎CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
IMPROVEMENTS:
44

5+
* `dep ensure -add` now concurrently fetches the source and adds the projects.
6+
(#1218)
57
* File name case check is now performed on `Gopkg.toml` and `Gopkg.lock`.
68
(#1114)
79
* gps: gps now supports pruning. (#1020)

‎cmd/dep/ensure.go

+122-68
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/golang/dep/internal/gps/paths"
2323
"github.com/golang/dep/internal/gps/pkgtree"
2424
"github.com/pkg/errors"
25+
"golang.org/x/sync/syncmap"
2526
)
2627

2728
const ensureShortHelp = `Ensure a dependency is safely vendored in the project`
@@ -112,7 +113,10 @@ dep ensure -update -no-vendor
112113
113114
`
114115

115-
var errUpdateArgsValidation = errors.New("update arguments validation failed")
116+
var (
117+
errUpdateArgsValidation = errors.New("update arguments validation failed")
118+
errAddDepsFailed = errors.New("adding dependencies failed")
119+
)
116120

117121
func (cmd *ensureCommand) Name() string { return "ensure" }
118122
func (cmd *ensureCommand) Args() string {
@@ -466,93 +470,136 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
466470
constraint gps.Constraint
467471
typ addType
468472
}
469-
addInstructions := make(map[gps.ProjectRoot]addInstruction)
470473

471-
for _, arg := range args {
472-
// TODO(sdboyer) return all errors, not just the first one we encounter
473-
// TODO(sdboyer) do these concurrently?
474-
pc, path, err := getProjectConstraint(arg, sm)
475-
if err != nil {
476-
// TODO(sdboyer) ensure these errors are contextualized in a sensible way for -add
477-
return err
478-
}
474+
// Create a syncmap to store all the addInstruction(s) concurrently.
475+
addInstructions := new(syncmap.Map)
479476

480-
// check if the the parsed path is the current root path
481-
if strings.EqualFold(string(p.ImportRoot), string(pc.Ident.ProjectRoot)) {
482-
return errors.New("cannot add current project to itself")
483-
}
477+
// Channel for receiving all the errors.
478+
errCh := make(chan error, len(args))
484479

485-
inManifest := p.Manifest.HasConstraintsOn(pc.Ident.ProjectRoot)
486-
inImports := exrmap[pc.Ident.ProjectRoot]
487-
if inManifest && inImports {
488-
return errors.Errorf("nothing to -add, %s is already in %s and the project's direct imports or required list", pc.Ident.ProjectRoot, dep.ManifestName)
489-
}
480+
var wg sync.WaitGroup
490481

491-
err = sm.SyncSourceFor(pc.Ident)
492-
if err != nil {
493-
return errors.Wrapf(err, "failed to fetch source for %s", pc.Ident.ProjectRoot)
482+
fmt.Println("Fetching sources...")
483+
484+
for i, arg := range args {
485+
wg.Add(1)
486+
487+
if ctx.Verbose {
488+
ctx.Err.Printf("(%d/%d) %s\n", i+1, len(args), arg)
494489
}
495490

496-
someConstraint := !gps.IsAny(pc.Constraint) || pc.Ident.Source != ""
491+
go func(arg string) {
492+
defer wg.Done()
493+
494+
pc, path, err := getProjectConstraint(arg, sm)
495+
if err != nil {
496+
// TODO(sdboyer) ensure these errors are contextualized in a sensible way for -add
497+
errCh <- err
498+
return
499+
}
500+
501+
// check if the the parsed path is the current root path
502+
if strings.EqualFold(string(p.ImportRoot), string(pc.Ident.ProjectRoot)) {
503+
errCh <- errors.New("cannot add current project to itself")
504+
return
505+
}
506+
507+
inManifest := p.Manifest.HasConstraintsOn(pc.Ident.ProjectRoot)
508+
inImports := exrmap[pc.Ident.ProjectRoot]
509+
if inManifest && inImports {
510+
errCh <- errors.Errorf("nothing to -add, %s is already in %s and the project's direct imports or required list", pc.Ident.ProjectRoot, dep.ManifestName)
511+
return
512+
}
513+
514+
err = sm.SyncSourceFor(pc.Ident)
515+
if err != nil {
516+
errCh <- errors.Wrapf(err, "failed to fetch source for %s", pc.Ident.ProjectRoot)
517+
return
518+
}
497519

498-
instr, has := addInstructions[pc.Ident.ProjectRoot]
499-
if has {
500-
// Multiple packages from the same project were specified as
501-
// arguments; make sure they agree on declared constraints.
502-
// TODO(sdboyer) until we have a general method for checking constraint equality, only allow one to declare
503-
if someConstraint {
504-
if !gps.IsAny(instr.constraint) || instr.id.Source != "" {
505-
return errors.Errorf("can only specify rules once per project being added; rules were given at least twice for %s", pc.Ident.ProjectRoot)
520+
someConstraint := !gps.IsAny(pc.Constraint) || pc.Ident.Source != ""
521+
522+
instrInterface, has := addInstructions.LoadOrStore(pc.Ident.ProjectRoot, addInstruction{})
523+
instr := instrInterface.(addInstruction)
524+
if has {
525+
// Multiple packages from the same project were specified as
526+
// arguments; make sure they agree on declared constraints.
527+
// TODO(sdboyer) until we have a general method for checking constraint equality, only allow one to declare
528+
if someConstraint {
529+
if !gps.IsAny(instr.constraint) || instr.id.Source != "" {
530+
errCh <- errors.Errorf("can only specify rules once per project being added; rules were given at least twice for %s", pc.Ident.ProjectRoot)
531+
return
532+
}
533+
instr.constraint = pc.Constraint
534+
instr.id = pc.Ident
506535
}
536+
} else {
537+
instr.ephReq = make(map[string]bool)
507538
instr.constraint = pc.Constraint
508539
instr.id = pc.Ident
509540
}
510-
} else {
511-
instr.ephReq = make(map[string]bool)
512-
instr.constraint = pc.Constraint
513-
instr.id = pc.Ident
514-
}
515-
516-
if inManifest {
517-
if someConstraint {
518-
return errors.Errorf("%s already contains rules for %s, cannot specify a version constraint or alternate source", dep.ManifestName, path)
519-
}
520541

521-
instr.ephReq[path] = true
522-
instr.typ |= isInManifest
523-
} else if inImports {
524-
if !someConstraint {
525-
if exmap[path] {
526-
return errors.Errorf("%s is already imported or required, so -add is only valid with a constraint", path)
542+
if inManifest {
543+
if someConstraint {
544+
errCh <- errors.Errorf("%s already contains rules for %s, cannot specify a version constraint or alternate source", dep.ManifestName, path)
545+
return
527546
}
528547

529-
// No constraints, but the package isn't imported; require it.
530-
// TODO(sdboyer) this case seems like it's getting overly specific and risks muddying the water more than it helps
531548
instr.ephReq[path] = true
532-
instr.typ |= isInImportsNoConstraint
533-
} else {
534-
// Don't require on this branch if the path was a ProjectRoot;
535-
// most common here will be the user adding constraints to
536-
// something they already imported, and if they specify the
537-
// root, there's a good chance they don't actually want to
538-
// require the project's root package, but are just trying to
539-
// indicate which project should receive the constraints.
540-
if !exmap[path] && string(pc.Ident.ProjectRoot) != path {
549+
instr.typ |= isInManifest
550+
} else if inImports {
551+
if !someConstraint {
552+
if exmap[path] {
553+
errCh <- errors.Errorf("%s is already imported or required, so -add is only valid with a constraint", path)
554+
return
555+
}
556+
557+
// No constraints, but the package isn't imported; require it.
558+
// TODO(sdboyer) this case seems like it's getting overly specific and risks muddying the water more than it helps
541559
instr.ephReq[path] = true
560+
instr.typ |= isInImportsNoConstraint
561+
} else {
562+
// Don't require on this branch if the path was a ProjectRoot;
563+
// most common here will be the user adding constraints to
564+
// something they already imported, and if they specify the
565+
// root, there's a good chance they don't actually want to
566+
// require the project's root package, but are just trying to
567+
// indicate which project should receive the constraints.
568+
if !exmap[path] && string(pc.Ident.ProjectRoot) != path {
569+
instr.ephReq[path] = true
570+
}
571+
instr.typ |= isInImportsWithConstraint
542572
}
543-
instr.typ |= isInImportsWithConstraint
573+
} else {
574+
instr.typ |= isInNeither
575+
instr.ephReq[path] = true
544576
}
545-
} else {
546-
instr.typ |= isInNeither
547-
instr.ephReq[path] = true
548-
}
549577

550-
addInstructions[pc.Ident.ProjectRoot] = instr
578+
addInstructions.Store(pc.Ident.ProjectRoot, instr)
579+
}(arg)
580+
}
581+
582+
wg.Wait()
583+
close(errCh)
584+
585+
// Newline after printing the fetching source output.
586+
ctx.Err.Println()
587+
588+
// Log all the errors.
589+
if len(errCh) > 0 {
590+
ctx.Err.Printf("Failed to add the dependencies:\n\n")
591+
for err := range errCh {
592+
ctx.Err.Println(" ✗", err.Error())
593+
}
594+
ctx.Err.Println()
595+
return errAddDepsFailed
551596
}
552597

553598
// We're now sure all of our add instructions are individually and mutually
554599
// valid, so it's safe to begin modifying the input parameters.
555-
for pr, instr := range addInstructions {
600+
addInstructions.Range(func(ki, vi interface{}) bool {
601+
pr := ki.(gps.ProjectRoot)
602+
instr := vi.(addInstruction)
556603
// The arg processing logic above only adds to the ephReq list if
557604
// that package definitely needs to be on that list, so we don't
558605
// need to check instr.typ here - if it's in instr.ephReq, it
@@ -569,7 +616,9 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
569616
Constraint: instr.constraint,
570617
}
571618
}
572-
}
619+
620+
return true
621+
})
573622

574623
// Re-prepare a solver now that our params are complete.
575624
solver, err = gps.Prepare(params, sm)
@@ -587,7 +636,10 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
587636
var reqlist []string
588637
appender := dep.NewManifest()
589638

590-
for pr, instr := range addInstructions {
639+
addInstructions.Range(func(ki, vi interface{}) bool {
640+
pr := ki.(gps.ProjectRoot)
641+
instr := vi.(addInstruction)
642+
591643
for path := range instr.ephReq {
592644
reqlist = append(reqlist, path)
593645
}
@@ -615,7 +667,9 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
615667
}
616668
appender.Constraints[pr] = pp
617669
}
618-
}
670+
671+
return true
672+
})
619673

620674
extra, err := appender.MarshalTOML()
621675
if err != nil {

0 commit comments

Comments
 (0)
This repository has been archived.