@@ -24,26 +24,39 @@ const ManifestName = "Gopkg.toml"
24
24
25
25
// Errors
26
26
var (
27
- errInvalidConstraint = errors .New ("\" constraint\" must be a TOML array of tables" )
28
- errInvalidOverride = errors .New ("\" override\" must be a TOML array of tables" )
29
- errInvalidRequired = errors .New ("\" required\" must be a TOML list of strings" )
30
- errInvalidIgnored = errors .New ("\" ignored\" must be a TOML list of strings" )
31
- errInvalidProjectRoot = errors .New ("ProjectRoot name validation failed" )
27
+ errInvalidConstraint = errors .Errorf ("%q must be a TOML array of tables" , "constraint" )
28
+ errInvalidOverride = errors .Errorf ("%q must be a TOML array of tables" , "override" )
29
+ errInvalidRequired = errors .Errorf ("%q must be a TOML list of strings" , "required" )
30
+ errInvalidIgnored = errors .Errorf ("%q must be a TOML list of strings" , "ignored" )
31
+ errInvalidPrune = errors .Errorf ("%q must be a TOML table of booleans" , "prune" )
32
+
33
+ errInvalidProjectRoot = errors .New ("ProjectRoot name validation failed" )
34
+ errInvalidPruneValue = errors .New ("prune options values must be booleans" )
35
+ errInvalidPruneProject = errors .Errorf ("%q must be a TOML array of tables" , "prune.project" )
36
+ errPruneSubProject = errors .New ("prune projects should not contain sub projects" )
37
+
38
+ errInvalidRootPruneValue = errors .New ("root prune options must be omitted instead of being set to false" )
39
+ errInvalidPruneProjectName = errors .Errorf ("%q in %q must be a string" , "name" , "prune.project" )
32
40
)
33
41
34
42
// Manifest holds manifest file data and implements gps.RootManifest.
35
43
type Manifest struct {
36
44
Constraints gps.ProjectConstraints
37
45
Ovr gps.ProjectConstraints
38
- Ignored []string
39
- Required []string
46
+
47
+ Ignored []string
48
+ Required []string
49
+
50
+ PruneOptions gps.PruneOptions
51
+ PruneProjectOptions gps.PruneProjectOptions
40
52
}
41
53
42
54
type rawManifest struct {
43
- Constraints []rawProject `toml:"constraint,omitempty"`
44
- Overrides []rawProject `toml:"override,omitempty"`
45
- Ignored []string `toml:"ignored,omitempty"`
46
- Required []string `toml:"required,omitempty"`
55
+ Constraints []rawProject `toml:"constraint,omitempty"`
56
+ Overrides []rawProject `toml:"override,omitempty"`
57
+ Ignored []string `toml:"ignored,omitempty"`
58
+ Required []string `toml:"required,omitempty"`
59
+ PruneOptions rawRootPruneOptions `toml:"prune,omitempty"`
47
60
}
48
61
49
62
type rawProject struct {
@@ -54,11 +67,33 @@ type rawProject struct {
54
67
Source string `toml:"source,omitempty"`
55
68
}
56
69
57
- // NewManifest instantiates a new manifest.
70
+ type rawRootPruneOptions struct {
71
+ UnusedPackages bool `toml:"unused-packages,omitempty"`
72
+ NonGoFiles bool `toml:"non-go,omitempty"`
73
+ GoTests bool `toml:"go-tests,omitempty"`
74
+
75
+ Projects []rawPruneProjectOptions `toml:"project,omitempty"`
76
+ }
77
+
78
+ type rawPruneProjectOptions struct {
79
+ Name string `toml:"name"`
80
+ UnusedPackages bool `toml:"unused-packages,omitempty"`
81
+ NonGoFiles bool `toml:"non-go,omitempty"`
82
+ GoTests bool `toml:"go-tests,omitempty"`
83
+ }
84
+
85
+ const (
86
+ pruneOptionUnusedPackages = "unused-packages"
87
+ pruneOptionGoTests = "go-tests"
88
+ pruneOptionNonGo = "non-go"
89
+ )
90
+
91
+ // NewManifest instantites a new manifest.
58
92
func NewManifest () * Manifest {
59
93
return & Manifest {
60
- Constraints : make (gps.ProjectConstraints ),
61
- Ovr : make (gps.ProjectConstraints ),
94
+ Constraints : make (gps.ProjectConstraints ),
95
+ Ovr : make (gps.ProjectConstraints ),
96
+ PruneOptions : gps .PruneNestedVendorDirs ,
62
97
}
63
98
}
64
99
@@ -151,6 +186,12 @@ func validateManifest(s string) ([]error, error) {
151
186
return warns , errInvalidRequired
152
187
}
153
188
}
189
+ case "prune" :
190
+ pruneWarns , err := validatePruneOptions (val , true )
191
+ warns = append (warns , pruneWarns ... )
192
+ if err != nil {
193
+ return warns , err
194
+ }
154
195
default :
155
196
warns = append (warns , fmt .Errorf ("unknown field in manifest: %v" , prop ))
156
197
}
@@ -159,6 +200,70 @@ func validateManifest(s string) ([]error, error) {
159
200
return warns , nil
160
201
}
161
202
203
+ func validatePruneOptions (val interface {}, root bool ) (warns []error , err error ) {
204
+ if reflect .TypeOf (val ).Kind () != reflect .Map {
205
+ return warns , errInvalidPrune
206
+ }
207
+
208
+ for key , value := range val .(map [string ]interface {}) {
209
+ switch key {
210
+ case pruneOptionNonGo , pruneOptionGoTests , pruneOptionUnusedPackages :
211
+ if option , ok := value .(bool ); ! ok {
212
+ return warns , errInvalidPruneValue
213
+ } else if root && ! option {
214
+ return warns , errInvalidRootPruneValue
215
+ }
216
+ case "name" :
217
+ if root {
218
+ warns = append (warns , errors .Errorf ("%q should not include a name" , "prune" ))
219
+ } else if _ , ok := value .(string ); ! ok {
220
+ return warns , errInvalidPruneProjectName
221
+ }
222
+ case "project" :
223
+ if ! root {
224
+ return warns , errPruneSubProject
225
+ }
226
+ if reflect .TypeOf (value ).Kind () != reflect .Slice {
227
+ return warns , errInvalidPruneProject
228
+ }
229
+ for _ , project := range value .([]interface {}) {
230
+ projectWarns , err := validatePruneOptions (project , false )
231
+ warns = append (warns , projectWarns ... )
232
+ if err != nil {
233
+ return nil , err
234
+ }
235
+ }
236
+
237
+ default :
238
+ if root {
239
+ warns = append (warns , errors .Errorf ("unknown field %q in %q" , key , "prune" ))
240
+ } else {
241
+ warns = append (warns , errors .Errorf ("unknown field %q in %q" , key , "prune.project" ))
242
+ }
243
+ }
244
+ }
245
+
246
+ return warns , err
247
+ }
248
+
249
+ func checkRedundantPruneOptions (raw rawManifest ) (warns []error ) {
250
+ rootOptions := raw .PruneOptions
251
+
252
+ for _ , project := range raw .PruneOptions .Projects {
253
+ if rootOptions .GoTests && project .GoTests {
254
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionGoTests , project .Name ))
255
+ }
256
+ if rootOptions .NonGoFiles && project .NonGoFiles {
257
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionNonGo , project .Name ))
258
+ }
259
+ if rootOptions .UnusedPackages && project .UnusedPackages {
260
+ warns = append (warns , errors .Errorf ("redundant prune option %q set for %q" , pruneOptionUnusedPackages , project .Name ))
261
+ }
262
+ }
263
+
264
+ return warns
265
+ }
266
+
162
267
// ValidateProjectRoots validates the project roots present in manifest.
163
268
func ValidateProjectRoots (c * Ctx , m * Manifest , sm gps.SourceManager ) error {
164
269
// Channel to receive all the errors
@@ -184,6 +289,10 @@ func ValidateProjectRoots(c *Ctx, m *Manifest, sm gps.SourceManager) error {
184
289
wg .Add (1 )
185
290
go validate (pr )
186
291
}
292
+ for pr := range m .PruneProjectOptions {
293
+ wg .Add (1 )
294
+ go validate (pr )
295
+ }
187
296
188
297
wg .Wait ()
189
298
close (errorCh )
@@ -254,9 +363,43 @@ func fromRawManifest(raw rawManifest) (*Manifest, error) {
254
363
m .Ovr [name ] = prj
255
364
}
256
365
366
+ m .PruneOptions , m .PruneProjectOptions = fromRawRootPruneOptions (raw .PruneOptions )
367
+
257
368
return m , nil
258
369
}
259
370
371
+ func fromRawRootPruneOptions (raw rawRootPruneOptions ) (gps.PruneOptions , gps.PruneProjectOptions ) {
372
+ rootOptions := gps .PruneNestedVendorDirs
373
+ pruneProjects := make (gps.PruneProjectOptions )
374
+
375
+ if raw .UnusedPackages {
376
+ rootOptions |= gps .PruneUnusedPackages
377
+ }
378
+ if raw .GoTests {
379
+ rootOptions |= gps .PruneGoTestFiles
380
+ }
381
+ if raw .NonGoFiles {
382
+ rootOptions |= gps .PruneNonGoFiles
383
+ }
384
+
385
+ for _ , p := range raw .Projects {
386
+ pr := gps .ProjectRoot (p .Name )
387
+ pruneProjects [pr ] = gps .PruneNestedVendorDirs
388
+
389
+ if raw .UnusedPackages {
390
+ pruneProjects [pr ] |= gps .PruneUnusedPackages
391
+ }
392
+ if raw .GoTests {
393
+ pruneProjects [pr ] |= gps .PruneGoTestFiles
394
+ }
395
+ if raw .NonGoFiles {
396
+ pruneProjects [pr ] |= gps .PruneNonGoFiles
397
+ }
398
+ }
399
+
400
+ return rootOptions , pruneProjects
401
+ }
402
+
260
403
// toProject interprets the string representations of project information held in
261
404
// a rawProject, converting them into a proper gps.ProjectProperties. An
262
405
// error is returned if the rawProject contains some invalid combination -
@@ -288,17 +431,27 @@ func toProject(raw rawProject) (n gps.ProjectRoot, pp gps.ProjectProperties, err
288
431
}
289
432
290
433
pp .Source = raw .Source
434
+
291
435
return n , pp , nil
292
436
}
293
437
438
+ // MarshalTOML serializes this manifest into TOML via an intermediate raw form.
439
+ func (m * Manifest ) MarshalTOML () ([]byte , error ) {
440
+ raw := m .toRaw ()
441
+ result , err := toml .Marshal (raw )
442
+ return result , errors .Wrap (err , "unable to marshal the lock to a TOML string" )
443
+ }
444
+
294
445
// toRaw converts the manifest into a representation suitable to write to the manifest file
295
446
func (m * Manifest ) toRaw () rawManifest {
296
447
raw := rawManifest {
297
- Constraints : make ([]rawProject , 0 , len (m .Constraints )),
298
- Overrides : make ([]rawProject , 0 , len (m .Ovr )),
299
- Ignored : m .Ignored ,
300
- Required : m .Required ,
448
+ Constraints : make ([]rawProject , 0 , len (m .Constraints )),
449
+ Overrides : make ([]rawProject , 0 , len (m .Ovr )),
450
+ Ignored : m .Ignored ,
451
+ Required : m .Required ,
452
+ PruneOptions : rawRootPruneOptions {},
301
453
}
454
+
302
455
for n , prj := range m .Constraints {
303
456
raw .Constraints = append (raw .Constraints , toRawProject (n , prj ))
304
457
}
@@ -309,6 +462,8 @@ func (m *Manifest) toRaw() rawManifest {
309
462
}
310
463
sort .Sort (sortedRawProjects (raw .Overrides ))
311
464
465
+ // TODO(ibrasho): write out prune options.
466
+
312
467
return raw
313
468
}
314
469
@@ -329,13 +484,6 @@ func (s sortedRawProjects) Less(i, j int) bool {
329
484
return l .Source < r .Source
330
485
}
331
486
332
- // MarshalTOML serializes this manifest into TOML via an intermediate raw form.
333
- func (m * Manifest ) MarshalTOML () ([]byte , error ) {
334
- raw := m .toRaw ()
335
- result , err := toml .Marshal (raw )
336
- return result , errors .Wrap (err , "Unable to marshal the lock to a TOML string" )
337
- }
338
-
339
487
func toRawProject (name gps.ProjectRoot , project gps.ProjectProperties ) rawProject {
340
488
raw := rawProject {
341
489
Name : string (name ),
@@ -363,6 +511,7 @@ func toRawProject(name gps.ProjectRoot, project gps.ProjectProperties) rawProjec
363
511
// Has to be a semver range.
364
512
raw .Version = project .Constraint .ImpliedCaretString ()
365
513
}
514
+
366
515
return raw
367
516
}
368
517
@@ -407,3 +556,11 @@ func (m *Manifest) RequiredPackages() map[string]bool {
407
556
408
557
return mp
409
558
}
559
+
560
+ func (m * Manifest ) PruneOptionsFor (pr gps.ProjectRoot ) gps.PruneOptions {
561
+ if po , ok := m .PruneProjectOptions [pr ]; ok {
562
+ return po
563
+ }
564
+
565
+ return m .PruneOptions
566
+ }
0 commit comments