@@ -112,7 +112,10 @@ dep ensure -update -no-vendor
112
112
113
113
`
114
114
115
- var errUpdateArgsValidation = errors .New ("update arguments validation failed" )
115
+ var (
116
+ errUpdateArgsValidation = errors .New ("update arguments validation failed" )
117
+ errAddDepsFailed = errors .New ("adding dependencies failed" )
118
+ )
116
119
117
120
func (cmd * ensureCommand ) Name () string { return "ensure" }
118
121
func (cmd * ensureCommand ) Args () string {
@@ -468,86 +471,130 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm
468
471
}
469
472
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
+ // A mutex for limited access to addInstructions by goroutines.
475
+ var mutex sync.Mutex
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
+ }
497
513
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 )
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
+ }
519
+
520
+ someConstraint := ! gps .IsAny (pc .Constraint ) || pc .Ident .Source != ""
521
+
522
+ // Obtain a lock for addInstructions
523
+ mutex .Lock ()
524
+ defer mutex .Unlock ()
525
+ instr , has := addInstructions [pc .Ident .ProjectRoot ]
526
+ if has {
527
+ // Multiple packages from the same project were specified as
528
+ // arguments; make sure they agree on declared constraints.
529
+ // TODO(sdboyer) until we have a general method for checking constraint equality, only allow one to declare
530
+ if someConstraint {
531
+ if ! gps .IsAny (instr .constraint ) || instr .id .Source != "" {
532
+ errCh <- errors .Errorf ("can only specify rules once per project being added; rules were given at least twice for %s" , pc .Ident .ProjectRoot )
533
+ return
534
+ }
535
+ instr .constraint = pc .Constraint
536
+ instr .id = pc .Ident
506
537
}
538
+ } else {
539
+ instr .ephReq = make (map [string ]bool )
507
540
instr .constraint = pc .Constraint
508
541
instr .id = pc .Ident
509
542
}
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
543
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 )
544
+ if inManifest {
545
+ if someConstraint {
546
+ errCh <- errors .Errorf ("%s already contains rules for %s, cannot specify a version constraint or alternate source" , dep .ManifestName , path )
547
+ return
527
548
}
528
549
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
550
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 {
551
+ instr .typ |= isInManifest
552
+ } else if inImports {
553
+ if ! someConstraint {
554
+ if exmap [path ] {
555
+ errCh <- errors .Errorf ("%s is already imported or required, so -add is only valid with a constraint" , path )
556
+ return
557
+ }
558
+
559
+ // No constraints, but the package isn't imported; require it.
560
+ // TODO(sdboyer) this case seems like it's getting overly specific and risks muddying the water more than it helps
541
561
instr .ephReq [path ] = true
562
+ instr .typ |= isInImportsNoConstraint
563
+ } else {
564
+ // Don't require on this branch if the path was a ProjectRoot;
565
+ // most common here will be the user adding constraints to
566
+ // something they already imported, and if they specify the
567
+ // root, there's a good chance they don't actually want to
568
+ // require the project's root package, but are just trying to
569
+ // indicate which project should receive the constraints.
570
+ if ! exmap [path ] && string (pc .Ident .ProjectRoot ) != path {
571
+ instr .ephReq [path ] = true
572
+ }
573
+ instr .typ |= isInImportsWithConstraint
542
574
}
543
- instr .typ |= isInImportsWithConstraint
575
+ } else {
576
+ instr .typ |= isInNeither
577
+ instr .ephReq [path ] = true
544
578
}
545
- } else {
546
- instr .typ |= isInNeither
547
- instr .ephReq [path ] = true
548
- }
549
579
550
- addInstructions [pc .Ident .ProjectRoot ] = instr
580
+ addInstructions [pc .Ident .ProjectRoot ] = instr
581
+ }(arg )
582
+ }
583
+
584
+ wg .Wait ()
585
+ close (errCh )
586
+
587
+ // Newline after printing the fetching source output.
588
+ ctx .Err .Println ()
589
+
590
+ // Log all the errors.
591
+ if len (errCh ) > 0 {
592
+ ctx .Err .Printf ("Failed to add the dependencies:\n \n " )
593
+ for err := range errCh {
594
+ ctx .Err .Println (" ✗" , err .Error ())
595
+ }
596
+ ctx .Err .Println ()
597
+ return errAddDepsFailed
551
598
}
552
599
553
600
// We're now sure all of our add instructions are individually and mutually
0 commit comments