github.com/golang/dep@v0.5.4/gps/solution.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gps 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "path/filepath" 12 "sync" 13 14 "github.com/pkg/errors" 15 "golang.org/x/sync/errgroup" 16 ) 17 18 // A Solution is returned by a solver run. It is mostly just a Lock, with some 19 // additional methods that report information about the solve run. 20 type Solution interface { 21 Lock 22 // The name of the ProjectAnalyzer used in generating this solution. 23 AnalyzerName() string 24 // The version of the ProjectAnalyzer used in generating this solution. 25 AnalyzerVersion() int 26 // The name of the Solver used in generating this solution. 27 SolverName() string 28 // The version of the Solver used in generating this solution. 29 SolverVersion() int 30 Attempts() int 31 } 32 33 type solution struct { 34 // The projects selected by the solver. 35 p []LockedProject 36 37 // The import inputs that created this solution (including requires). 38 i []string 39 40 // The number of solutions that were attempted 41 att int 42 43 // The analyzer info 44 analyzerInfo ProjectAnalyzerInfo 45 46 // The solver used in producing this solution 47 solv Solver 48 } 49 50 // WriteProgress informs about the progress of WriteDepTree. 51 type WriteProgress struct { 52 Count int 53 Total int 54 LP LockedProject 55 Failure bool 56 } 57 58 func (p WriteProgress) String() string { 59 msg := "Wrote" 60 if p.Failure { 61 msg = "Failed to write" 62 } 63 return fmt.Sprintf("(%d/%d) %s %s@%s", p.Count, p.Total, msg, p.LP.Ident(), p.LP.Version()) 64 } 65 66 const concurrentWriters = 16 67 68 // WriteDepTree takes a basedir, a Lock and a RootPruneOptions and exports all 69 // the projects listed in the lock to the appropriate target location within basedir. 70 // 71 // If the goal is to populate a vendor directory, basedir should be the absolute 72 // path to that vendor directory, not its parent (a project root, typically). 73 // 74 // It requires a SourceManager to do the work. Prune options are read from the 75 // passed manifest. 76 // 77 // If onWrite is not nil, it will be called after each project write. Calls are ordered and atomic. 78 func WriteDepTree(basedir string, l Lock, sm SourceManager, co CascadingPruneOptions, onWrite func(WriteProgress)) error { 79 if l == nil { 80 return fmt.Errorf("must provide non-nil Lock to WriteDepTree") 81 } 82 83 if err := os.MkdirAll(basedir, 0777); err != nil { 84 return err 85 } 86 87 g, ctx := errgroup.WithContext(context.TODO()) 88 lps := l.Projects() 89 sem := make(chan struct{}, concurrentWriters) 90 var cnt struct { 91 sync.Mutex 92 i int 93 } 94 95 for i := range lps { 96 p := lps[i] // per-iteration copy 97 98 g.Go(func() error { 99 err := func() error { 100 select { 101 case sem <- struct{}{}: 102 defer func() { <-sem }() 103 case <-ctx.Done(): 104 return ctx.Err() 105 } 106 107 ident := p.Ident() 108 projectRoot := string(ident.ProjectRoot) 109 to := filepath.FromSlash(filepath.Join(basedir, projectRoot)) 110 111 if err := sm.ExportProject(ctx, ident, p.Version(), to); err != nil { 112 return errors.Wrapf(err, "failed to export %s", projectRoot) 113 } 114 115 err := PruneProject(to, p, co.PruneOptionsFor(ident.ProjectRoot)) 116 if err != nil { 117 return errors.Wrapf(err, "failed to prune %s", projectRoot) 118 } 119 120 return ctx.Err() 121 }() 122 123 switch err { 124 case context.Canceled, context.DeadlineExceeded: 125 // Don't report "secondary" errors. 126 default: 127 if onWrite != nil { 128 // Increment and call atomically to prevent re-ordering. 129 cnt.Lock() 130 cnt.i++ 131 onWrite(WriteProgress{ 132 Count: cnt.i, 133 Total: len(lps), 134 LP: p, 135 Failure: err != nil, 136 }) 137 cnt.Unlock() 138 } 139 } 140 141 return err 142 }) 143 } 144 145 err := g.Wait() 146 if err != nil { 147 os.RemoveAll(basedir) 148 } 149 return errors.Wrap(err, "failed to write dep tree") 150 } 151 152 func (r solution) Projects() []LockedProject { 153 return r.p 154 } 155 156 func (r solution) InputImports() []string { 157 return r.i 158 } 159 160 func (r solution) Attempts() int { 161 return r.att 162 } 163 164 func (r solution) AnalyzerName() string { 165 return r.analyzerInfo.Name 166 } 167 168 func (r solution) AnalyzerVersion() int { 169 return r.analyzerInfo.Version 170 } 171 172 func (r solution) SolverName() string { 173 return r.solv.Name() 174 } 175 176 func (r solution) SolverVersion() int { 177 return r.solv.Version() 178 }