@@ -22,6 +22,7 @@ import (
22
22
"github.com/golang/dep/internal/gps/paths"
23
23
"github.com/golang/dep/internal/gps/pkgtree"
24
24
"github.com/pkg/errors"
25
+ "golang.org/x/sync/syncmap"
25
26
)
26
27
27
28
const ensureShortHelp = `Ensure a dependency is safely vendored in the project`
@@ -112,7 +113,10 @@ dep ensure -update -no-vendor
112
113
113
114
`
114
115
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
+ )
116
120
117
121
func (cmd * ensureCommand ) Name () string { return "ensure" }
118
122
func (cmd * ensureCommand ) Args () string {
@@ -466,93 +470,136 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
466
470
constraint gps.Constraint
467
471
typ addType
468
472
}
469
- addInstructions := make (map [gps.ProjectRoot ]addInstruction )
470
473
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 )
479
476
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 ))
484
479
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
490
481
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 )
494
489
}
495
490
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
+ }
497
519
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
506
535
}
536
+ } else {
537
+ instr .ephReq = make (map [string ]bool )
507
538
instr .constraint = pc .Constraint
508
539
instr .id = pc .Ident
509
540
}
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
- }
520
541
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
527
546
}
528
547
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
531
548
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
541
559
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
542
572
}
543
- instr .typ |= isInImportsWithConstraint
573
+ } else {
574
+ instr .typ |= isInNeither
575
+ instr .ephReq [path ] = true
544
576
}
545
- } else {
546
- instr .typ |= isInNeither
547
- instr .ephReq [path ] = true
548
- }
549
577
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
551
596
}
552
597
553
598
// We're now sure all of our add instructions are individually and mutually
554
599
// 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 )
556
603
// The arg processing logic above only adds to the ephReq list if
557
604
// that package definitely needs to be on that list, so we don't
558
605
// 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
569
616
Constraint : instr .constraint ,
570
617
}
571
618
}
572
- }
619
+
620
+ return true
621
+ })
573
622
574
623
// Re-prepare a solver now that our params are complete.
575
624
solver , err = gps .Prepare (params , sm )
@@ -587,7 +636,10 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
587
636
var reqlist []string
588
637
appender := dep .NewManifest ()
589
638
590
- for pr , instr := range addInstructions {
639
+ addInstructions .Range (func (ki , vi interface {}) bool {
640
+ pr := ki .(gps.ProjectRoot )
641
+ instr := vi .(addInstruction )
642
+
591
643
for path := range instr .ephReq {
592
644
reqlist = append (reqlist , path )
593
645
}
@@ -615,7 +667,9 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
615
667
}
616
668
appender .Constraints [pr ] = pp
617
669
}
618
- }
670
+
671
+ return true
672
+ })
619
673
620
674
extra , err := appender .MarshalTOML ()
621
675
if err != nil {
0 commit comments