1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "go/build"
14 "internal/lazyregexp"
15 "os"
16 "path"
17 "path/filepath"
18 "strconv"
19 "strings"
20
21 "cmd/go/internal/base"
22 "cmd/go/internal/cfg"
23 "cmd/go/internal/fsys"
24 "cmd/go/internal/lockedfile"
25 "cmd/go/internal/modconv"
26 "cmd/go/internal/modfetch"
27 "cmd/go/internal/search"
28
29 "golang.org/x/mod/modfile"
30 "golang.org/x/mod/module"
31 "golang.org/x/mod/semver"
32 )
33
34
35
36
37 var (
38
39 RootMode Root
40
41
42
43 ForceUseModules bool
44
45 allowMissingModuleImports bool
46 )
47
48
49 var (
50 initialized bool
51 modRoot string
52 gopath string
53 )
54
55
56 var (
57 Target module.Version
58
59
60
61
62 targetPrefix string
63
64
65
66 targetInGorootSrc bool
67 )
68
69 type Root int
70
71 const (
72
73
74
75
76 AutoRoot Root = iota
77
78
79
80 NoRoot
81
82
83
84 NeedRoot
85 )
86
87
88
89
90
91
92
93
94
95 func ModFile() *modfile.File {
96 Init()
97 if modFile == nil {
98 die()
99 }
100 return modFile
101 }
102
103 func BinDir() string {
104 Init()
105 return filepath.Join(gopath, "bin")
106 }
107
108
109
110
111
112 func Init() {
113 if initialized {
114 return
115 }
116 initialized = true
117
118
119
120
121 var mustUseModules bool
122 env := cfg.Getenv("GO111MODULE")
123 switch env {
124 default:
125 base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
126 case "auto":
127 mustUseModules = ForceUseModules
128 case "on", "":
129 mustUseModules = true
130 case "off":
131 if ForceUseModules {
132 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
133 }
134 mustUseModules = false
135 return
136 }
137
138 if err := fsys.Init(base.Cwd()); err != nil {
139 base.Fatalf("go: %v", err)
140 }
141
142
143
144
145
146
147
148 if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
149 os.Setenv("GIT_TERMINAL_PROMPT", "0")
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165 if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
166 os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes")
167 }
168
169 if os.Getenv("GCM_INTERACTIVE") == "" {
170 os.Setenv("GCM_INTERACTIVE", "never")
171 }
172
173 if modRoot != "" {
174
175
176 } else if RootMode == NoRoot {
177 if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
178 base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
179 }
180 modRoot = ""
181 } else {
182 modRoot = findModuleRoot(base.Cwd())
183 if modRoot == "" {
184 if cfg.ModFile != "" {
185 base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
186 }
187 if RootMode == NeedRoot {
188 base.Fatalf("go: %v", ErrNoModRoot)
189 }
190 if !mustUseModules {
191
192
193 return
194 }
195 } else if search.InDir(modRoot, os.TempDir()) == "." {
196
197
198
199
200
201 modRoot = ""
202 fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
203 if !mustUseModules {
204 return
205 }
206 }
207 }
208 if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
209 base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
210 }
211
212
213 cfg.ModulesEnabled = true
214 setDefaultBuildMod()
215 list := filepath.SplitList(cfg.BuildContext.GOPATH)
216 if len(list) == 0 || list[0] == "" {
217 base.Fatalf("missing $GOPATH")
218 }
219 gopath = list[0]
220 if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
221 base.Fatalf("$GOPATH/go.mod exists but should not")
222 }
223
224 if modRoot == "" {
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 } else {
243 modfetch.GoSumFile = strings.TrimSuffix(ModFilePath(), ".mod") + ".sum"
244 search.SetModRoot(modRoot)
245 }
246 }
247
248
249
250
251
252
253
254
255
256
257 func WillBeEnabled() bool {
258 if modRoot != "" || cfg.ModulesEnabled {
259
260 return true
261 }
262 if initialized {
263
264 return false
265 }
266
267
268
269 env := cfg.Getenv("GO111MODULE")
270 switch env {
271 case "on", "":
272 return true
273 case "auto":
274 break
275 default:
276 return false
277 }
278
279 if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
280
281
282 return false
283 } else if search.InDir(modRoot, os.TempDir()) == "." {
284
285
286
287
288
289 return false
290 }
291 return true
292 }
293
294
295
296
297
298 func Enabled() bool {
299 Init()
300 return modRoot != "" || cfg.ModulesEnabled
301 }
302
303
304
305 func ModRoot() string {
306 if !HasModRoot() {
307 die()
308 }
309 return modRoot
310 }
311
312
313
314
315 func HasModRoot() bool {
316 Init()
317 return modRoot != ""
318 }
319
320
321
322
323
324 func ModFilePath() string {
325 if !HasModRoot() {
326 die()
327 }
328 if cfg.ModFile != "" {
329 return cfg.ModFile
330 }
331 return filepath.Join(modRoot, "go.mod")
332 }
333
334 func die() {
335 if cfg.Getenv("GO111MODULE") == "off" {
336 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
337 }
338 if dir, name := findAltConfig(base.Cwd()); dir != "" {
339 rel, err := filepath.Rel(base.Cwd(), dir)
340 if err != nil {
341 rel = dir
342 }
343 cdCmd := ""
344 if rel != "." {
345 cdCmd = fmt.Sprintf("cd %s && ", rel)
346 }
347 base.Fatalf("go: cannot find main module, but found %s in %s\n\tto create a module there, run:\n\t%sgo mod init", name, dir, cdCmd)
348 }
349 base.Fatalf("go: %v", ErrNoModRoot)
350 }
351
352 var ErrNoModRoot = errors.New("go.mod file not found in current directory or any parent directory; see 'go help modules'")
353
354 type goModDirtyError struct{}
355
356 func (goModDirtyError) Error() string {
357 if cfg.BuildModExplicit {
358 return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%v; to update it:\n\tgo mod tidy", cfg.BuildMod)
359 }
360 if cfg.BuildModReason != "" {
361 return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%s\n\t(%s)\n\tto update it:\n\tgo mod tidy", cfg.BuildMod, cfg.BuildModReason)
362 }
363 return "updates to go.mod needed; to update it:\n\tgo mod tidy"
364 }
365
366 var errGoModDirty error = goModDirtyError{}
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385 func LoadModFile(ctx context.Context) *Requirements {
386 rs, needCommit := loadModFile(ctx)
387 if needCommit {
388 commitRequirements(ctx, modFileGoVersion(), rs)
389 }
390 return rs
391 }
392
393
394
395
396
397
398
399 func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
400 if requirements != nil {
401 return requirements, false
402 }
403
404 Init()
405 if modRoot == "" {
406 Target = module.Version{Path: "command-line-arguments"}
407 targetPrefix = "command-line-arguments"
408 goVersion := LatestGoVersion()
409 rawGoVersion.Store(Target, goVersion)
410 requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
411 return requirements, false
412 }
413
414 gomod := ModFilePath()
415 var data []byte
416 var err error
417 if gomodActual, ok := fsys.OverlayPath(gomod); ok {
418
419
420
421 data, err = os.ReadFile(gomodActual)
422 } else {
423 data, err = lockedfile.Read(gomodActual)
424 }
425 if err != nil {
426 base.Fatalf("go: %v", err)
427 }
428
429 var fixed bool
430 f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed))
431 if err != nil {
432
433 base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
434 }
435 if f.Module == nil {
436
437 base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
438 }
439
440 modFile = f
441 initTarget(f.Module.Mod)
442 index = indexModFile(data, f, fixed)
443
444 if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
445 if pathErr, ok := err.(*module.InvalidPathError); ok {
446 pathErr.Kind = "module"
447 }
448 base.Fatalf("go: %v", err)
449 }
450
451 setDefaultBuildMod()
452 rs = requirementsFromModFile()
453 if cfg.BuildMod == "vendor" {
454 readVendorList()
455 checkVendorConsistency()
456 rs.initVendor(vendorList)
457 }
458 if rs.hasRedundantRoot() {
459
460
461
462 rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
463 if err != nil {
464 base.Fatalf("go: %v", err)
465 }
466 }
467
468 if index.goVersionV == "" {
469
470
471 if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
472 addGoStmt(LatestGoVersion())
473 if go117EnableLazyLoading {
474
475
476
477
478
479 rs, err = convertDepth(ctx, rs, lazy)
480 if err != nil {
481 base.Fatalf("go: %v", err)
482 }
483 }
484 } else {
485 rawGoVersion.Store(Target, modFileGoVersion())
486 }
487 }
488
489 requirements = rs
490 return requirements, true
491 }
492
493
494
495
496
497
498
499
500
501
502 func CreateModFile(ctx context.Context, modPath string) {
503 modRoot = base.Cwd()
504 Init()
505 modFilePath := ModFilePath()
506 if _, err := fsys.Stat(modFilePath); err == nil {
507 base.Fatalf("go: %s already exists", modFilePath)
508 }
509
510 if modPath == "" {
511 var err error
512 modPath, err = findModulePath(modRoot)
513 if err != nil {
514 base.Fatalf("go: %v", err)
515 }
516 } else if err := module.CheckImportPath(modPath); err != nil {
517 if pathErr, ok := err.(*module.InvalidPathError); ok {
518 pathErr.Kind = "module"
519
520 if pathErr.Path == "." || pathErr.Path == ".." ||
521 strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
522 pathErr.Err = errors.New("is a local import path")
523 }
524 }
525 base.Fatalf("go: %v", err)
526 }
527
528 fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
529 modFile = new(modfile.File)
530 modFile.AddModuleStmt(modPath)
531 initTarget(modFile.Module.Mod)
532 addGoStmt(LatestGoVersion())
533
534 convertedFrom, err := convertLegacyConfig(modPath)
535 if convertedFrom != "" {
536 fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom))
537 }
538 if err != nil {
539 base.Fatalf("go: %v", err)
540 }
541
542 rs := requirementsFromModFile()
543 rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
544 if err != nil {
545 base.Fatalf("go: %v", err)
546 }
547 commitRequirements(ctx, modFileGoVersion(), rs)
548
549
550
551
552
553
554
555
556 empty := true
557 files, _ := os.ReadDir(modRoot)
558 for _, f := range files {
559 name := f.Name()
560 if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {
561 continue
562 }
563 if strings.HasSuffix(name, ".go") || f.IsDir() {
564 empty = false
565 break
566 }
567 }
568 if !empty {
569 fmt.Fprintf(os.Stderr, "go: to add module requirements and sums:\n\tgo mod tidy\n")
570 }
571 }
572
573
574
575
576
577
578
579
580 func fixVersion(ctx context.Context, fixed *bool) modfile.VersionFixer {
581 return func(path, vers string) (resolved string, err error) {
582 defer func() {
583 if err == nil && resolved != vers {
584 *fixed = true
585 }
586 }()
587
588
589 if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
590 vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
591 }
592
593
594
595
596 _, pathMajor, ok := module.SplitPathVersion(path)
597 if !ok {
598 return "", &module.ModuleError{
599 Path: path,
600 Err: &module.InvalidVersionError{
601 Version: vers,
602 Err: fmt.Errorf("malformed module path %q", path),
603 },
604 }
605 }
606 if vers != "" && module.CanonicalVersion(vers) == vers {
607 if err := module.CheckPathMajor(vers, pathMajor); err != nil {
608 return "", module.VersionError(module.Version{Path: path, Version: vers}, err)
609 }
610 return vers, nil
611 }
612
613 info, err := Query(ctx, path, vers, "", nil)
614 if err != nil {
615 return "", err
616 }
617 return info.Version, nil
618 }
619 }
620
621
622
623
624
625
626
627
628 func AllowMissingModuleImports() {
629 if initialized {
630 panic("AllowMissingModuleImports after Init")
631 }
632 allowMissingModuleImports = true
633 }
634
635
636 func initTarget(m module.Version) {
637 Target = m
638 targetPrefix = m.Path
639
640 if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" {
641 targetInGorootSrc = true
642 if m.Path == "std" {
643
644
645
646
647
648
649
650
651 targetPrefix = ""
652 }
653 }
654 }
655
656
657
658 func requirementsFromModFile() *Requirements {
659 roots := make([]module.Version, 0, len(modFile.Require))
660 direct := map[string]bool{}
661 for _, r := range modFile.Require {
662 if index != nil && index.exclude[r.Mod] {
663 if cfg.BuildMod == "mod" {
664 fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
665 } else {
666 fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
667 }
668 continue
669 }
670
671 roots = append(roots, r.Mod)
672 if !r.Indirect {
673 direct[r.Mod.Path] = true
674 }
675 }
676 module.Sort(roots)
677 rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
678 return rs
679 }
680
681
682
683 func setDefaultBuildMod() {
684 if cfg.BuildModExplicit {
685
686 return
687 }
688
689 if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
690
691
692
693 cfg.BuildMod = "mod"
694 return
695 }
696 if modRoot == "" {
697 if allowMissingModuleImports {
698 cfg.BuildMod = "mod"
699 } else {
700 cfg.BuildMod = "readonly"
701 }
702 return
703 }
704
705 if fi, err := fsys.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() {
706 modGo := "unspecified"
707 if index != nil && index.goVersionV != "" {
708 if semver.Compare(index.goVersionV, "v1.14") >= 0 {
709
710
711 cfg.BuildMod = "vendor"
712 cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
713 return
714 } else {
715 modGo = index.goVersionV[1:]
716 }
717 }
718
719
720
721 cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
722 }
723
724 cfg.BuildMod = "readonly"
725 }
726
727
728
729 func convertLegacyConfig(modPath string) (from string, err error) {
730 noneSelected := func(path string) (version string) { return "none" }
731 queryPackage := func(path, rev string) (module.Version, error) {
732 pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil)
733 if err != nil {
734 return module.Version{}, err
735 }
736 if len(pkgMods) > 0 {
737 return pkgMods[0].Mod, nil
738 }
739 return modOnly.Mod, nil
740 }
741 for _, name := range altConfigs {
742 cfg := filepath.Join(modRoot, name)
743 data, err := os.ReadFile(cfg)
744 if err == nil {
745 convert := modconv.Converters[name]
746 if convert == nil {
747 return "", nil
748 }
749 cfg = filepath.ToSlash(cfg)
750 err := modconv.ConvertLegacyConfig(modFile, cfg, data, queryPackage)
751 return name, err
752 }
753 }
754 return "", nil
755 }
756
757
758
759
760 func addGoStmt(v string) {
761 if modFile.Go != nil && modFile.Go.Version != "" {
762 return
763 }
764 if err := modFile.AddGoStmt(v); err != nil {
765 base.Fatalf("go: internal error: %v", err)
766 }
767 rawGoVersion.Store(Target, v)
768 }
769
770
771
772 func LatestGoVersion() string {
773 tags := build.Default.ReleaseTags
774 version := tags[len(tags)-1]
775 if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
776 base.Fatalf("go: internal error: unrecognized default version %q", version)
777 }
778 return version[2:]
779 }
780
781
782
783
784 func priorGoVersion(v string) string {
785 vTag := "go" + v
786 tags := build.Default.ReleaseTags
787 for i, tag := range tags {
788 if tag == vTag {
789 if i == 0 {
790 return v
791 }
792
793 version := tags[i-1]
794 if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
795 base.Fatalf("go: internal error: unrecognized version %q", version)
796 }
797 return version[2:]
798 }
799 }
800 return v
801 }
802
803 var altConfigs = []string{
804 "Gopkg.lock",
805
806 "GLOCKFILE",
807 "Godeps/Godeps.json",
808 "dependencies.tsv",
809 "glide.lock",
810 "vendor.conf",
811 "vendor.yml",
812 "vendor/manifest",
813 "vendor/vendor.json",
814
815 ".git/config",
816 }
817
818 func findModuleRoot(dir string) (root string) {
819 if dir == "" {
820 panic("dir not set")
821 }
822 dir = filepath.Clean(dir)
823
824
825 for {
826 if fi, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
827 return dir
828 }
829 d := filepath.Dir(dir)
830 if d == dir {
831 break
832 }
833 dir = d
834 }
835 return ""
836 }
837
838 func findAltConfig(dir string) (root, name string) {
839 if dir == "" {
840 panic("dir not set")
841 }
842 dir = filepath.Clean(dir)
843 if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {
844
845
846 return "", ""
847 }
848 for {
849 for _, name := range altConfigs {
850 if fi, err := fsys.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
851 return dir, name
852 }
853 }
854 d := filepath.Dir(dir)
855 if d == dir {
856 break
857 }
858 dir = d
859 }
860 return "", ""
861 }
862
863 func findModulePath(dir string) (string, error) {
864
865
866
867
868
869
870
871
872 list, _ := os.ReadDir(dir)
873 for _, info := range list {
874 if info.Type().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
875 if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
876 return com, nil
877 }
878 }
879 }
880 for _, info1 := range list {
881 if info1.IsDir() {
882 files, _ := os.ReadDir(filepath.Join(dir, info1.Name()))
883 for _, info2 := range files {
884 if info2.Type().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
885 if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
886 return path.Dir(com), nil
887 }
888 }
889 }
890 }
891 }
892
893
894 data, _ := os.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
895 var cfg1 struct{ ImportPath string }
896 json.Unmarshal(data, &cfg1)
897 if cfg1.ImportPath != "" {
898 return cfg1.ImportPath, nil
899 }
900
901
902 data, _ = os.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
903 var cfg2 struct{ RootPath string }
904 json.Unmarshal(data, &cfg2)
905 if cfg2.RootPath != "" {
906 return cfg2.RootPath, nil
907 }
908
909
910 var badPathErr error
911 for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
912 if gpdir == "" {
913 continue
914 }
915 if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
916 path := filepath.ToSlash(rel)
917
918 if err := module.CheckImportPath(path); err != nil {
919 badPathErr = err
920 break
921 }
922 return path, nil
923 }
924 }
925
926 reason := "outside GOPATH, module path must be specified"
927 if badPathErr != nil {
928
929
930 reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
931 }
932 msg := `cannot determine module path for source directory %s (%s)
933
934 Example usage:
935 'go mod init example.com/m' to initialize a v0 or v1 module
936 'go mod init example.com/m/v2' to initialize a v2 module
937
938 Run 'go help mod init' for more information.
939 `
940 return "", fmt.Errorf(msg, dir, reason)
941 }
942
943 var (
944 importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
945 )
946
947 func findImportComment(file string) string {
948 data, err := os.ReadFile(file)
949 if err != nil {
950 return ""
951 }
952 m := importCommentRE.FindSubmatch(data)
953 if m == nil {
954 return ""
955 }
956 path, err := strconv.Unquote(string(m[1]))
957 if err != nil {
958 return ""
959 }
960 return path
961 }
962
963 var allowWriteGoMod = true
964
965
966 func DisallowWriteGoMod() {
967 allowWriteGoMod = false
968 }
969
970
971
972
973
974 func AllowWriteGoMod() {
975 allowWriteGoMod = true
976 }
977
978
979 func WriteGoMod(ctx context.Context) {
980 if !allowWriteGoMod {
981 panic("WriteGoMod called while disallowed")
982 }
983 commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx))
984 }
985
986
987
988 func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) {
989 requirements = rs
990
991 if !allowWriteGoMod {
992
993 return
994 }
995
996 if modRoot == "" {
997
998 return
999 }
1000
1001 var list []*modfile.Require
1002 for _, m := range rs.rootModules {
1003 list = append(list, &modfile.Require{
1004 Mod: m,
1005 Indirect: !rs.direct[m.Path],
1006 })
1007 }
1008 if goVersion != "" {
1009 modFile.AddGoStmt(goVersion)
1010 }
1011 if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 {
1012 modFile.SetRequire(list)
1013 } else {
1014 modFile.SetRequireSeparateIndirect(list)
1015 }
1016 modFile.Cleanup()
1017
1018 dirty := index.modFileIsDirty(modFile)
1019 if dirty && cfg.BuildMod != "mod" {
1020
1021
1022 base.Fatalf("go: %v", errGoModDirty)
1023 }
1024
1025 if !dirty && cfg.CmdName != "mod tidy" {
1026
1027
1028
1029
1030 if cfg.CmdName != "mod init" {
1031 modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
1032 }
1033 return
1034 }
1035 gomod := ModFilePath()
1036 if _, ok := fsys.OverlayPath(gomod); ok {
1037 if dirty {
1038 base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
1039 }
1040 return
1041 }
1042
1043 new, err := modFile.Format()
1044 if err != nil {
1045 base.Fatalf("go: %v", err)
1046 }
1047 defer func() {
1048
1049 index = indexModFile(new, modFile, false)
1050
1051
1052
1053 if cfg.CmdName != "mod init" {
1054 modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
1055 }
1056 }()
1057
1058
1059
1060 if unlock, err := modfetch.SideLock(); err == nil {
1061 defer unlock()
1062 }
1063
1064 errNoChange := errors.New("no update needed")
1065
1066 err = lockedfile.Transform(ModFilePath(), func(old []byte) ([]byte, error) {
1067 if bytes.Equal(old, new) {
1068
1069
1070 return nil, errNoChange
1071 }
1072
1073 if index != nil && !bytes.Equal(old, index.data) {
1074
1075
1076
1077
1078
1079
1080 return nil, fmt.Errorf("existing contents have changed since last read")
1081 }
1082
1083 return new, nil
1084 })
1085
1086 if err != nil && err != errNoChange {
1087 base.Fatalf("go: updating go.mod: %v", err)
1088 }
1089 }
1090
1091
1092
1093
1094
1095
1096 func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums) map[module.Version]bool {
1097
1098
1099
1100
1101 keep := make(map[module.Version]bool)
1102
1103
1104
1105
1106
1107 if ld != nil {
1108 for _, pkg := range ld.pkgs {
1109
1110
1111
1112 if pkg.testOf != nil || (pkg.mod.Path == "" && pkg.err == nil) || module.CheckImportPath(pkg.path) != nil {
1113 continue
1114 }
1115
1116 if rs.depth == lazy && pkg.mod.Path != "" {
1117 if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
1118
1119
1120
1121
1122 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
1123 if v, ok := rs.rootSelected(prefix); ok && v != "none" {
1124 m := module.Version{Path: prefix, Version: v}
1125 keep[resolveReplacement(m)] = true
1126 }
1127 }
1128 continue
1129 }
1130 }
1131
1132 mg, _ := rs.Graph(ctx)
1133 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
1134 if v := mg.Selected(prefix); v != "none" {
1135 m := module.Version{Path: prefix, Version: v}
1136 keep[resolveReplacement(m)] = true
1137 }
1138 }
1139 }
1140 }
1141
1142 if rs.graph.Load() == nil {
1143
1144
1145
1146
1147 for _, m := range rs.rootModules {
1148 r := resolveReplacement(m)
1149 keep[modkey(r)] = true
1150 if which == addBuildListZipSums {
1151 keep[r] = true
1152 }
1153 }
1154 } else {
1155 mg, _ := rs.Graph(ctx)
1156 mg.WalkBreadthFirst(func(m module.Version) {
1157 if _, ok := mg.RequiredBy(m); ok {
1158
1159
1160
1161 keep[modkey(resolveReplacement(m))] = true
1162 }
1163 })
1164
1165 if which == addBuildListZipSums {
1166 for _, m := range mg.BuildList() {
1167 keep[resolveReplacement(m)] = true
1168 }
1169 }
1170 }
1171
1172 return keep
1173 }
1174
1175 type whichSums int8
1176
1177 const (
1178 loadedZipSumsOnly = whichSums(iota)
1179 addBuildListZipSums
1180 )
1181
1182
1183
1184 func modkey(m module.Version) module.Version {
1185 return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
1186 }
1187
View as plain text