@@ -30,6 +30,7 @@ import (
30
30
"cmd/go/internal/mvs"
31
31
"cmd/go/internal/renameio"
32
32
"cmd/go/internal/search"
33
+ "cmd/go/internal/semver"
33
34
)
34
35
35
36
var (
@@ -349,8 +350,14 @@ func InitMod() {
349
350
excluded [x .Mod ] = true
350
351
}
351
352
modFileToBuildList ()
352
- stdVendorMode ()
353
- WriteGoMod ()
353
+ setDefaultBuildMod ()
354
+ if cfg .BuildMod == "vendor" {
355
+ readVendorList ()
356
+ checkVendorConsistency ()
357
+ } else {
358
+ // TODO(golang.org/issue/33326): if cfg.BuildMod != "readonly"?
359
+ WriteGoMod ()
360
+ }
354
361
}
355
362
356
363
// modFileToBuildList initializes buildList from the modFile.
@@ -371,40 +378,133 @@ func modFileToBuildList() {
371
378
buildList = list
372
379
}
373
380
374
- // stdVendorMode applies inside $GOROOT/src.
375
- // It checks that the go.mod matches vendor/modules.txt
376
- // and then sets -mod=vendor unless this is a command
377
- // that has to do explicitly with modules.
378
- func stdVendorMode () {
379
- if ! targetInGorootSrc {
381
+ // setDefaultBuildMod sets a default value for cfg.BuildMod
382
+ // if it is currently empty.
383
+ func setDefaultBuildMod () {
384
+ if cfg .BuildMod != "" {
385
+ // Don't override an explicit '-mod=' argument.
380
386
return
381
387
}
388
+ cfg .BuildMod = "mod"
382
389
if cfg .CmdName == "get" || strings .HasPrefix (cfg .CmdName , "mod " ) {
390
+ // Don't set -mod implicitly for commands whose purpose is to
391
+ // manipulate the build list.
383
392
return
384
393
}
394
+ if modRoot != "" {
395
+ if fi , err := os .Stat (filepath .Join (modRoot , "vendor" )); err == nil && fi .IsDir () {
396
+ modGo := "unspecified"
397
+ if modFile .Go != nil {
398
+ if semver .Compare ("v" + modFile .Go .Version , "v1.14" ) >= 0 {
399
+ // The Go version is at least 1.14, and a vendor directory exists.
400
+ // Set -mod=vendor by default.
401
+ cfg .BuildMod = "vendor"
402
+ return
403
+ } else {
404
+ modGo = modFile .Go .Version
405
+ }
406
+ }
407
+ fmt .Fprintf (os .Stderr , "go: not defaulting to -mod=vendor because go.mod 'go' version is %s\n " , modGo )
408
+ }
409
+ }
385
410
411
+ // TODO(golang.org/issue/33326): set -mod=readonly implicitly if the go.mod
412
+ // file is itself read-only?
413
+ }
414
+
415
+ // checkVendorConsistency verifies that the vendor/modules.txt file matches (if
416
+ // go 1.14) or at least does not contradict (go 1.13 or earlier) the
417
+ // requirements and replacements listed in the main module's go.mod file.
418
+ func checkVendorConsistency () {
386
419
readVendorList ()
387
- BuildList:
388
- for _ , m := range buildList {
389
- if m .Path == "cmd" || m .Path == "std" {
390
- continue
420
+
421
+ pre114 := false
422
+ if modFile .Go == nil || semver .Compare ("v" + modFile .Go .Version , "v1.14" ) < 0 {
423
+ // Go versions before 1.14 did not include enough information in
424
+ // vendor/modules.txt to check for consistency.
425
+ // If we know that we're on an earlier version, relax the consistency check.
426
+ pre114 = true
427
+ }
428
+
429
+ vendErrors := new (strings.Builder )
430
+ vendErrorf := func (mod module.Version , format string , args ... interface {}) {
431
+ detail := fmt .Sprintf (format , args ... )
432
+ if mod .Version == "" {
433
+ fmt .Fprintf (vendErrors , "\n \t %s: %s" , mod .Path , detail )
434
+ } else {
435
+ fmt .Fprintf (vendErrors , "\n \t %s@%s: %s" , mod .Path , mod .Version , detail )
391
436
}
392
- for _ , v := range vendorList {
393
- if m .Path == v .Path {
394
- if m .Version != v .Version {
395
- base .Fatalf ("go: inconsistent vendoring in %s:\n " +
396
- "\t go.mod requires %s %s but vendor/modules.txt has %s.\n " +
397
- "\t run 'go mod tidy; go mod vendor' to sync" ,
398
- modRoot , m .Path , m .Version , v .Version )
437
+ }
438
+
439
+ explicitInGoMod := make (map [module.Version ]bool , len (modFile .Require ))
440
+ for _ , r := range modFile .Require {
441
+ explicitInGoMod [r .Mod ] = true
442
+ if ! vendorMeta [r .Mod ].Explicit {
443
+ if pre114 {
444
+ // Before 1.14, modules.txt did not indicate whether modules were listed
445
+ // explicitly in the main module's go.mod file.
446
+ // However, we can at least detect a version mismatch if packages were
447
+ // vendored from a non-matching version.
448
+ if vv , ok := vendorVersion [r .Mod .Path ]; ok && vv != r .Mod .Version {
449
+ vendErrorf (r .Mod , fmt .Sprintf ("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s" , r .Mod .Path , vv ))
399
450
}
400
- continue BuildList
451
+ } else {
452
+ vendErrorf (r .Mod , "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt" )
453
+ }
454
+ }
455
+ }
456
+
457
+ describe := func (m module.Version ) string {
458
+ if m .Version == "" {
459
+ return m .Path
460
+ }
461
+ return m .Path + "@" + m .Version
462
+ }
463
+
464
+ // We need to verify *all* replacements that occur in modfile: even if they
465
+ // don't directly apply to any module in the vendor list, the replacement
466
+ // go.mod file can affect the selected versions of other (transitive)
467
+ // dependencies
468
+ goModReplacement := make (map [module.Version ]module.Version , len (modFile .Replace ))
469
+ for _ , r := range modFile .Replace {
470
+ goModReplacement [r .Old ] = r .New
471
+ vr := vendorMeta [r .Old ].Replacement
472
+ if vr == (module.Version {}) {
473
+ if pre114 && (r .Old .Version == "" || vendorVersion [r .Old .Path ] != r .Old .Version ) {
474
+ // Before 1.14, modules.txt omitted wildcard replacements and
475
+ // replacements for modules that did not have any packages to vendor.
476
+ } else {
477
+ vendErrorf (r .Old , "is replaced in go.mod, but not marked as replaced in vendor/modules.txt" )
401
478
}
479
+ } else if vr != r .New {
480
+ vendErrorf (r .Old , "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt" , describe (r .New ), describe (vr ))
402
481
}
403
- base .Fatalf ("go: inconsistent vendoring in %s:\n " +
404
- "\t go.mod requires %s %s but vendor/modules.txt does not include it.\n " +
405
- "\t run 'go mod tidy; go mod vendor' to sync" , modRoot , m .Path , m .Version )
406
482
}
407
- cfg .BuildMod = "vendor"
483
+
484
+ for _ , mod := range vendorList {
485
+ meta := vendorMeta [mod ]
486
+ if meta .Explicit && ! explicitInGoMod [mod ] {
487
+ vendErrorf (mod , "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod" )
488
+ }
489
+ }
490
+
491
+ for _ , mod := range vendorReplaced {
492
+ r , ok := goModReplacement [mod ]
493
+ if ! ok {
494
+ r , ok = goModReplacement [module.Version {Path : mod .Path }]
495
+ }
496
+ if ! ok {
497
+ vendErrorf (mod , "is marked as replaced in vendor/modules.txt, but not replaced in go.mod" )
498
+ continue
499
+ }
500
+ if meta := vendorMeta [mod ]; r != meta .Replacement {
501
+ vendErrorf (mod , "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod" , describe (meta .Replacement ), describe (r ))
502
+ }
503
+ }
504
+
505
+ if vendErrors .Len () > 0 {
506
+ base .Fatalf ("go: inconsistent vendoring in %s:%s\n \n run 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory" , modRoot , vendErrors )
507
+ }
408
508
}
409
509
410
510
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
0 commit comments