This repository has been archived by the owner on Sep 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathroot_analyzer.go
208 lines (182 loc) · 5.58 KB
/
root_analyzer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"context"
"io/ioutil"
"log"
"golang.org/x/sync/errgroup"
"github.com/golang/dep"
"github.com/golang/dep/gps"
fb "github.com/golang/dep/internal/feedback"
"github.com/golang/dep/internal/importers"
)
// rootAnalyzer supplies manifest/lock data from both dep and external tool's
// configuration files.
// * When used on the root project, it imports only from external tools.
// * When used by the solver for dependencies, it first looks for dep config,
// then external tools.
type rootAnalyzer struct {
skipTools bool
ctx *dep.Ctx
sm gps.SourceManager
directDeps map[string]bool
}
func newRootAnalyzer(skipTools bool, ctx *dep.Ctx, directDeps map[string]bool, sm gps.SourceManager) *rootAnalyzer {
return &rootAnalyzer{
skipTools: skipTools,
ctx: ctx,
sm: sm,
directDeps: directDeps,
}
}
func (a *rootAnalyzer) InitializeRootManifestAndLock(dir string, pr gps.ProjectRoot) (rootM *dep.Manifest, rootL *dep.Lock, err error) {
if !a.skipTools {
rootM, rootL, err = a.importManifestAndLock(dir, pr, false)
if err != nil {
return
}
}
if rootM == nil {
rootM = dep.NewManifest()
// Since we didn't find anything to import, dep's cache is empty.
// We are prefetching dependencies and logging so that the subsequent solve step
// doesn't spend a long time retrieving dependencies without feedback for the user.
if err := a.cacheDeps(pr); err != nil {
return nil, nil, err
}
}
if rootL == nil {
rootL = &dep.Lock{}
}
return
}
func (a *rootAnalyzer) cacheDeps(pr gps.ProjectRoot) error {
logger := a.ctx.Err
g, _ := errgroup.WithContext(context.TODO())
concurrency := 4
syncDep := func(pr gps.ProjectRoot, sm gps.SourceManager) error {
if err := sm.SyncSourceFor(gps.ProjectIdentifier{ProjectRoot: pr}); err != nil {
logger.Printf("Unable to cache %s - %s", pr, err)
return err
}
return nil
}
deps := make(chan string)
for i := 0; i < concurrency; i++ {
g.Go(func() error {
for d := range deps {
err := syncDep(gps.ProjectRoot(d), a.sm)
if err != nil {
return err
}
}
return nil
})
}
g.Go(func() error {
defer close(deps)
for pr := range a.directDeps {
logger.Printf("Caching package %q", pr)
deps <- pr
}
return nil
})
if err := g.Wait(); err != nil {
return err
}
logger.Printf("Successfully cached all deps.")
return nil
}
func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, suppressLogs bool) (*dep.Manifest, *dep.Lock, error) {
logger := a.ctx.Err
if suppressLogs {
logger = log.New(ioutil.Discard, "", 0)
}
for _, i := range importers.BuildAll(logger, a.ctx.Verbose, a.sm) {
if i.HasDepMetadata(dir) {
a.ctx.Err.Printf("Importing configuration from %s. These are only initial constraints, and are further refined during the solve process.", i.Name())
m, l, err := i.Import(dir, pr)
if err != nil {
return nil, nil, err
}
a.removeTransitiveDependencies(m)
return m, l, err
}
}
var emptyManifest = dep.NewManifest()
return emptyManifest, nil, nil
}
func (a *rootAnalyzer) removeTransitiveDependencies(m *dep.Manifest) {
for pr := range m.Constraints {
if _, isDirect := a.directDeps[string(pr)]; !isDirect {
delete(m.Constraints, pr)
}
}
}
// DeriveManifestAndLock evaluates a dependency for existing dependency manager
// configuration (ours or external) and passes any configuration found back
// to the solver.
func (a *rootAnalyzer) DeriveManifestAndLock(dir string, pr gps.ProjectRoot) (gps.Manifest, gps.Lock, error) {
// Ignore other tools if we find dep configuration
var depAnalyzer dep.Analyzer
if depAnalyzer.HasDepMetadata(dir) {
return depAnalyzer.DeriveManifestAndLock(dir, pr)
}
if !a.skipTools {
// The assignment back to an interface prevents interface-based nil checks from failing later
var manifest gps.Manifest = gps.SimpleManifest{}
var lock gps.Lock
im, il, err := a.importManifestAndLock(dir, pr, true)
if im != nil {
manifest = im
}
if il != nil {
lock = il
}
return manifest, lock, err
}
return gps.SimpleManifest{}, nil, nil
}
func (a *rootAnalyzer) FinalizeRootManifestAndLock(m *dep.Manifest, l *dep.Lock, ol dep.Lock) {
// Iterate through the new projects in solved lock and add them to manifest
// if they are direct deps and log feedback for all the new projects.
for _, y := range l.Projects() {
var f *fb.ConstraintFeedback
pr := y.Ident().ProjectRoot
// New constraints: in new lock and dir dep but not in manifest
if _, ok := a.directDeps[string(pr)]; ok {
if _, ok := m.Constraints[pr]; !ok {
pp := getProjectPropertiesFromVersion(y.Version())
if pp.Constraint != nil {
m.Constraints[pr] = pp
pc := gps.ProjectConstraint{Ident: y.Ident(), Constraint: pp.Constraint}
f = fb.NewConstraintFeedback(pc, fb.DepTypeDirect)
f.LogFeedback(a.ctx.Err)
}
f = fb.NewLockedProjectFeedback(y, fb.DepTypeDirect)
f.LogFeedback(a.ctx.Err)
}
} else {
// New locked projects: in new lock but not in old lock
newProject := true
for _, opl := range ol.Projects() {
if pr == opl.Ident().ProjectRoot {
newProject = false
}
}
if newProject {
f = fb.NewLockedProjectFeedback(y, fb.DepTypeTransitive)
f.LogFeedback(a.ctx.Err)
}
}
}
}
// Info provides metadata on the analyzer algorithm used during solve.
func (a *rootAnalyzer) Info() gps.ProjectAnalyzerInfo {
return gps.ProjectAnalyzerInfo{
Name: "dep",
Version: 1,
}
}