1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "internal/godebugs"
14 "internal/lazyregexp"
15 "io"
16 "os"
17 "path"
18 "path/filepath"
19 "slices"
20 "strconv"
21 "strings"
22 "sync"
23
24 "cmd/go/internal/base"
25 "cmd/go/internal/cfg"
26 "cmd/go/internal/fsys"
27 "cmd/go/internal/gover"
28 "cmd/go/internal/lockedfile"
29 "cmd/go/internal/modfetch"
30 "cmd/go/internal/search"
31
32 "golang.org/x/mod/modfile"
33 "golang.org/x/mod/module"
34 )
35
36
37
38
39 var (
40
41 RootMode Root
42
43
44
45 ForceUseModules bool
46
47 allowMissingModuleImports bool
48
49
50
51
52
53
54
55
56
57 ExplicitWriteGoMod bool
58 )
59
60
61 var (
62 initialized bool
63
64
65
66
67
68
69 modRoots []string
70 gopath string
71 )
72
73
74 func EnterModule(ctx context.Context, enterModroot string) {
75 MainModules = nil
76 requirements = nil
77 workFilePath = ""
78 modfetch.Reset()
79
80 modRoots = []string{enterModroot}
81 LoadModFile(ctx)
82 }
83
84
85 var (
86
87 workFilePath string
88 )
89
90 type MainModuleSet struct {
91
92
93
94
95 versions []module.Version
96
97
98 modRoot map[module.Version]string
99
100
101
102
103 pathPrefix map[module.Version]string
104
105
106
107 inGorootSrc map[module.Version]bool
108
109 modFiles map[module.Version]*modfile.File
110
111 modContainingCWD module.Version
112
113 workFile *modfile.WorkFile
114
115 workFileReplaceMap map[module.Version]module.Version
116
117 highestReplaced map[string]string
118
119 indexMu sync.Mutex
120 indices map[module.Version]*modFileIndex
121 }
122
123 func (mms *MainModuleSet) PathPrefix(m module.Version) string {
124 return mms.pathPrefix[m]
125 }
126
127
128
129
130
131 func (mms *MainModuleSet) Versions() []module.Version {
132 if mms == nil {
133 return nil
134 }
135 return mms.versions
136 }
137
138 func (mms *MainModuleSet) Contains(path string) bool {
139 if mms == nil {
140 return false
141 }
142 for _, v := range mms.versions {
143 if v.Path == path {
144 return true
145 }
146 }
147 return false
148 }
149
150 func (mms *MainModuleSet) ModRoot(m module.Version) string {
151 if mms == nil {
152 return ""
153 }
154 return mms.modRoot[m]
155 }
156
157 func (mms *MainModuleSet) InGorootSrc(m module.Version) bool {
158 if mms == nil {
159 return false
160 }
161 return mms.inGorootSrc[m]
162 }
163
164 func (mms *MainModuleSet) mustGetSingleMainModule() module.Version {
165 if mms == nil || len(mms.versions) == 0 {
166 panic("internal error: mustGetSingleMainModule called in context with no main modules")
167 }
168 if len(mms.versions) != 1 {
169 if inWorkspaceMode() {
170 panic("internal error: mustGetSingleMainModule called in workspace mode")
171 } else {
172 panic("internal error: multiple main modules present outside of workspace mode")
173 }
174 }
175 return mms.versions[0]
176 }
177
178 func (mms *MainModuleSet) GetSingleIndexOrNil() *modFileIndex {
179 if mms == nil {
180 return nil
181 }
182 if len(mms.versions) == 0 {
183 return nil
184 }
185 return mms.indices[mms.mustGetSingleMainModule()]
186 }
187
188 func (mms *MainModuleSet) Index(m module.Version) *modFileIndex {
189 mms.indexMu.Lock()
190 defer mms.indexMu.Unlock()
191 return mms.indices[m]
192 }
193
194 func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) {
195 mms.indexMu.Lock()
196 defer mms.indexMu.Unlock()
197 mms.indices[m] = index
198 }
199
200 func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File {
201 return mms.modFiles[m]
202 }
203
204 func (mms *MainModuleSet) WorkFile() *modfile.WorkFile {
205 return mms.workFile
206 }
207
208 func (mms *MainModuleSet) Len() int {
209 if mms == nil {
210 return 0
211 }
212 return len(mms.versions)
213 }
214
215
216
217
218 func (mms *MainModuleSet) ModContainingCWD() module.Version {
219 return mms.modContainingCWD
220 }
221
222 func (mms *MainModuleSet) HighestReplaced() map[string]string {
223 return mms.highestReplaced
224 }
225
226
227
228 func (mms *MainModuleSet) GoVersion() string {
229 if inWorkspaceMode() {
230 return gover.FromGoWork(mms.workFile)
231 }
232 if mms != nil && len(mms.versions) == 1 {
233 f := mms.ModFile(mms.mustGetSingleMainModule())
234 if f == nil {
235
236
237
238 return gover.Local()
239 }
240 return gover.FromGoMod(f)
241 }
242 return gover.DefaultGoModVersion
243 }
244
245
246
247
248 func (mms *MainModuleSet) Godebugs() []*modfile.Godebug {
249 if inWorkspaceMode() {
250 if mms.workFile != nil {
251 return mms.workFile.Godebug
252 }
253 return nil
254 }
255 if mms != nil && len(mms.versions) == 1 {
256 f := mms.ModFile(mms.mustGetSingleMainModule())
257 if f == nil {
258
259 return nil
260 }
261 return f.Godebug
262 }
263 return nil
264 }
265
266
267
268 func (mms *MainModuleSet) Toolchain() string {
269 if inWorkspaceMode() {
270 if mms.workFile != nil && mms.workFile.Toolchain != nil {
271 return mms.workFile.Toolchain.Name
272 }
273 return "go" + mms.GoVersion()
274 }
275 if mms != nil && len(mms.versions) == 1 {
276 f := mms.ModFile(mms.mustGetSingleMainModule())
277 if f == nil {
278
279
280
281 return gover.LocalToolchain()
282 }
283 if f.Toolchain != nil {
284 return f.Toolchain.Name
285 }
286 }
287 return "go" + mms.GoVersion()
288 }
289
290 func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {
291 return mms.workFileReplaceMap
292 }
293
294 var MainModules *MainModuleSet
295
296 type Root int
297
298 const (
299
300
301
302
303 AutoRoot Root = iota
304
305
306
307 NoRoot
308
309
310
311 NeedRoot
312 )
313
314
315
316
317
318
319
320
321
322 func ModFile() *modfile.File {
323 Init()
324 modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule())
325 if modFile == nil {
326 die()
327 }
328 return modFile
329 }
330
331 func BinDir() string {
332 Init()
333 if cfg.GOBIN != "" {
334 return cfg.GOBIN
335 }
336 if gopath == "" {
337 return ""
338 }
339 return filepath.Join(gopath, "bin")
340 }
341
342
343
344
345 func InitWorkfile() {
346
347 if err := fsys.Init(base.Cwd()); err != nil {
348 base.Fatal(err)
349 }
350 workFilePath = FindGoWork(base.Cwd())
351 }
352
353
354
355
356
357
358 func FindGoWork(wd string) string {
359 if RootMode == NoRoot {
360 return ""
361 }
362
363 switch gowork := cfg.Getenv("GOWORK"); gowork {
364 case "off":
365 return ""
366 case "", "auto":
367 return findWorkspaceFile(wd)
368 default:
369 if !filepath.IsAbs(gowork) {
370 base.Fatalf("go: invalid GOWORK: not an absolute path")
371 }
372 return gowork
373 }
374 }
375
376
377
378 func WorkFilePath() string {
379 return workFilePath
380 }
381
382
383
384 func Reset() {
385 initialized = false
386 ForceUseModules = false
387 RootMode = 0
388 modRoots = nil
389 cfg.ModulesEnabled = false
390 MainModules = nil
391 requirements = nil
392 workFilePath = ""
393 modfetch.Reset()
394 }
395
396
397
398
399
400 func Init() {
401 if initialized {
402 return
403 }
404 initialized = true
405
406
407
408
409 var mustUseModules bool
410 env := cfg.Getenv("GO111MODULE")
411 switch env {
412 default:
413 base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
414 case "auto":
415 mustUseModules = ForceUseModules
416 case "on", "":
417 mustUseModules = true
418 case "off":
419 if ForceUseModules {
420 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
421 }
422 mustUseModules = false
423 return
424 }
425
426 if err := fsys.Init(base.Cwd()); err != nil {
427 base.Fatal(err)
428 }
429
430
431
432
433
434
435
436 if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
437 os.Setenv("GIT_TERMINAL_PROMPT", "0")
438 }
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
454 os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes")
455 }
456
457 if os.Getenv("GCM_INTERACTIVE") == "" {
458 os.Setenv("GCM_INTERACTIVE", "never")
459 }
460 if modRoots != nil {
461
462
463 } else if RootMode == NoRoot {
464 if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
465 base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
466 }
467 modRoots = nil
468 } else if workFilePath != "" {
469
470 if cfg.ModFile != "" {
471 base.Fatalf("go: -modfile cannot be used in workspace mode")
472 }
473 } else {
474 if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
475 if cfg.ModFile != "" {
476 base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
477 }
478 if RootMode == NeedRoot {
479 base.Fatal(ErrNoModRoot)
480 }
481 if !mustUseModules {
482
483
484 return
485 }
486 } else if search.InDir(modRoot, os.TempDir()) == "." {
487
488
489
490
491
492 fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
493 if RootMode == NeedRoot {
494 base.Fatal(ErrNoModRoot)
495 }
496 if !mustUseModules {
497 return
498 }
499 } else {
500 modRoots = []string{modRoot}
501 }
502 }
503 if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
504 base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
505 }
506
507
508 cfg.ModulesEnabled = true
509 setDefaultBuildMod()
510 list := filepath.SplitList(cfg.BuildContext.GOPATH)
511 if len(list) > 0 && list[0] != "" {
512 gopath = list[0]
513 if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
514 fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in $GOPATH %v\n", gopath)
515 if RootMode == NeedRoot {
516 base.Fatal(ErrNoModRoot)
517 }
518 if !mustUseModules {
519 return
520 }
521 }
522 }
523 }
524
525
526
527
528
529
530
531
532
533
534 func WillBeEnabled() bool {
535 if modRoots != nil || cfg.ModulesEnabled {
536
537 return true
538 }
539 if initialized {
540
541 return false
542 }
543
544
545
546 env := cfg.Getenv("GO111MODULE")
547 switch env {
548 case "on", "":
549 return true
550 case "auto":
551 break
552 default:
553 return false
554 }
555
556 return FindGoMod(base.Cwd()) != ""
557 }
558
559
560
561
562
563
564 func FindGoMod(wd string) string {
565 modRoot := findModuleRoot(wd)
566 if modRoot == "" {
567
568
569 return ""
570 }
571 if search.InDir(modRoot, os.TempDir()) == "." {
572
573
574
575
576
577 return ""
578 }
579 return filepath.Join(modRoot, "go.mod")
580 }
581
582
583
584
585
586 func Enabled() bool {
587 Init()
588 return modRoots != nil || cfg.ModulesEnabled
589 }
590
591 func VendorDir() string {
592 if inWorkspaceMode() {
593 return filepath.Join(filepath.Dir(WorkFilePath()), "vendor")
594 }
595
596
597
598 modRoot := MainModules.ModRoot(MainModules.mustGetSingleMainModule())
599 if modRoot == "" {
600 panic("vendor directory does not exist when in single module mode outside of a module")
601 }
602 return filepath.Join(modRoot, "vendor")
603 }
604
605 func inWorkspaceMode() bool {
606 if !initialized {
607 panic("inWorkspaceMode called before modload.Init called")
608 }
609 if !Enabled() {
610 return false
611 }
612 return workFilePath != ""
613 }
614
615
616
617
618 func HasModRoot() bool {
619 Init()
620 return modRoots != nil
621 }
622
623
624
625 func MustHaveModRoot() {
626 Init()
627 if !HasModRoot() {
628 die()
629 }
630 }
631
632
633
634
635 func ModFilePath() string {
636 MustHaveModRoot()
637 return modFilePath(findModuleRoot(base.Cwd()))
638 }
639
640 func modFilePath(modRoot string) string {
641 if cfg.ModFile != "" {
642 return cfg.ModFile
643 }
644 return filepath.Join(modRoot, "go.mod")
645 }
646
647 func die() {
648 if cfg.Getenv("GO111MODULE") == "off" {
649 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
650 }
651 if inWorkspaceMode() {
652 base.Fatalf("go: no modules were found in the current workspace; see 'go help work'")
653 }
654 if dir, name := findAltConfig(base.Cwd()); dir != "" {
655 rel, err := filepath.Rel(base.Cwd(), dir)
656 if err != nil {
657 rel = dir
658 }
659 cdCmd := ""
660 if rel != "." {
661 cdCmd = fmt.Sprintf("cd %s && ", rel)
662 }
663 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)
664 }
665 base.Fatal(ErrNoModRoot)
666 }
667
668 var ErrNoModRoot = errors.New("go.mod file not found in current directory or any parent directory; see 'go help modules'")
669
670 type goModDirtyError struct{}
671
672 func (goModDirtyError) Error() string {
673 if cfg.BuildModExplicit {
674 return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%v; to update it:\n\tgo mod tidy", cfg.BuildMod)
675 }
676 if cfg.BuildModReason != "" {
677 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)
678 }
679 return "updates to go.mod needed; to update it:\n\tgo mod tidy"
680 }
681
682 var errGoModDirty error = goModDirtyError{}
683
684 func loadWorkFile(path string) (workFile *modfile.WorkFile, modRoots []string, err error) {
685 workDir := filepath.Dir(path)
686 wf, err := ReadWorkFile(path)
687 if err != nil {
688 return nil, nil, err
689 }
690 seen := map[string]bool{}
691 for _, d := range wf.Use {
692 modRoot := d.Path
693 if !filepath.IsAbs(modRoot) {
694 modRoot = filepath.Join(workDir, modRoot)
695 }
696
697 if seen[modRoot] {
698 return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: path %s appears multiple times in workspace", base.ShortPath(path), d.Syntax.Start.Line, modRoot)
699 }
700 seen[modRoot] = true
701 modRoots = append(modRoots, modRoot)
702 }
703
704 for _, g := range wf.Godebug {
705 if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {
706 return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: %w", base.ShortPath(path), g.Syntax.Start.Line, err)
707 }
708 }
709
710 return wf, modRoots, nil
711 }
712
713
714 func ReadWorkFile(path string) (*modfile.WorkFile, error) {
715 path = base.ShortPath(path)
716 workData, err := fsys.ReadFile(path)
717 if err != nil {
718 return nil, fmt.Errorf("reading go.work: %w", err)
719 }
720
721 f, err := modfile.ParseWork(path, workData, nil)
722 if err != nil {
723 return nil, fmt.Errorf("errors parsing go.work:\n%w", err)
724 }
725 if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 && cfg.CmdName != "work edit" {
726 base.Fatal(&gover.TooNewError{What: base.ShortPath(path), GoVersion: f.Go.Version})
727 }
728 return f, nil
729 }
730
731
732 func WriteWorkFile(path string, wf *modfile.WorkFile) error {
733 wf.SortBlocks()
734 wf.Cleanup()
735 out := modfile.Format(wf.Syntax)
736
737 return os.WriteFile(path, out, 0666)
738 }
739
740
741
742 func UpdateWorkGoVersion(wf *modfile.WorkFile, goVers string) (changed bool) {
743 old := gover.FromGoWork(wf)
744 if gover.Compare(old, goVers) >= 0 {
745 return false
746 }
747
748 wf.AddGoStmt(goVers)
749
750
751
752
753
754
755
756
757
758
759
760 toolchain := "go" + old
761 if wf.Toolchain != nil {
762 toolchain = wf.Toolchain.Name
763 }
764 if gover.IsLang(gover.Local()) {
765 toolchain = gover.ToolchainMax(toolchain, "go"+goVers)
766 } else {
767 toolchain = gover.ToolchainMax(toolchain, "go"+gover.Local())
768 }
769
770
771
772
773 if toolchain == "go"+goVers || gover.Compare(gover.FromToolchain(toolchain), gover.GoStrictVersion) < 0 {
774 wf.DropToolchainStmt()
775 } else {
776 wf.AddToolchainStmt(toolchain)
777 }
778 return true
779 }
780
781
782
783 func UpdateWorkFile(wf *modfile.WorkFile) {
784 missingModulePaths := map[string]string{}
785
786 for _, d := range wf.Use {
787 if d.Path == "" {
788 continue
789 }
790 modRoot := d.Path
791 if d.ModulePath == "" {
792 missingModulePaths[d.Path] = modRoot
793 }
794 }
795
796
797
798 for moddir, absmodroot := range missingModulePaths {
799 _, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil)
800 if err != nil {
801 continue
802 }
803 wf.AddUse(moddir, f.Module.Mod.Path)
804 }
805 }
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825 func LoadModFile(ctx context.Context) *Requirements {
826 rs, err := loadModFile(ctx, nil)
827 if err != nil {
828 base.Fatal(err)
829 }
830 return rs
831 }
832
833 func loadModFile(ctx context.Context, opts *PackageOpts) (*Requirements, error) {
834 if requirements != nil {
835 return requirements, nil
836 }
837
838 Init()
839 var workFile *modfile.WorkFile
840 if inWorkspaceMode() {
841 var err error
842 workFile, modRoots, err = loadWorkFile(workFilePath)
843 if err != nil {
844 return nil, err
845 }
846 for _, modRoot := range modRoots {
847 sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum"
848 modfetch.WorkspaceGoSumFiles = append(modfetch.WorkspaceGoSumFiles, sumFile)
849 }
850 modfetch.GoSumFile = workFilePath + ".sum"
851 } else if len(modRoots) == 0 {
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869 } else {
870 modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum"
871 }
872 if len(modRoots) == 0 {
873
874
875
876 mainModule := module.Version{Path: "command-line-arguments"}
877 MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, nil)
878 var (
879 goVersion string
880 pruning modPruning
881 roots []module.Version
882 direct = map[string]bool{"go": true}
883 )
884 if inWorkspaceMode() {
885
886
887
888 goVersion = MainModules.GoVersion()
889 pruning = workspace
890 roots = []module.Version{
891 mainModule,
892 {Path: "go", Version: goVersion},
893 {Path: "toolchain", Version: gover.LocalToolchain()},
894 }
895 } else {
896 goVersion = gover.Local()
897 pruning = pruningForGoVersion(goVersion)
898 roots = []module.Version{
899 {Path: "go", Version: goVersion},
900 {Path: "toolchain", Version: gover.LocalToolchain()},
901 }
902 }
903 rawGoVersion.Store(mainModule, goVersion)
904 requirements = newRequirements(pruning, roots, direct)
905 if cfg.BuildMod == "vendor" {
906
907
908
909 requirements.initVendor(nil)
910 }
911 return requirements, nil
912 }
913
914 var modFiles []*modfile.File
915 var mainModules []module.Version
916 var indices []*modFileIndex
917 var errs []error
918 for _, modroot := range modRoots {
919 gomod := modFilePath(modroot)
920 var fixed bool
921 data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
922 if err != nil {
923 if inWorkspaceMode() {
924 if tooNew, ok := err.(*gover.TooNewError); ok && !strings.HasPrefix(cfg.CmdName, "work ") {
925
926
927
928
929 err = errWorkTooOld(gomod, workFile, tooNew.GoVersion)
930 } else {
931 err = fmt.Errorf("cannot load module %s listed in go.work file: %w",
932 base.ShortPath(filepath.Dir(gomod)), err)
933 }
934 }
935 errs = append(errs, err)
936 continue
937 }
938 if inWorkspaceMode() && !strings.HasPrefix(cfg.CmdName, "work ") {
939
940
941
942 mv := gover.FromGoMod(f)
943 wv := gover.FromGoWork(workFile)
944 if gover.Compare(mv, wv) > 0 && gover.Compare(mv, gover.GoStrictVersion) >= 0 {
945 errs = append(errs, errWorkTooOld(gomod, workFile, mv))
946 continue
947 }
948 }
949
950 if !inWorkspaceMode() {
951 ok := true
952 for _, g := range f.Godebug {
953 if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {
954 errs = append(errs, fmt.Errorf("error loading go.mod:\n%s:%d: %v", base.ShortPath(gomod), g.Syntax.Start.Line, err))
955 ok = false
956 }
957 }
958 if !ok {
959 continue
960 }
961 }
962
963 modFiles = append(modFiles, f)
964 mainModule := f.Module.Mod
965 mainModules = append(mainModules, mainModule)
966 indices = append(indices, indexModFile(data, f, mainModule, fixed))
967
968 if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
969 if pathErr, ok := err.(*module.InvalidPathError); ok {
970 pathErr.Kind = "module"
971 }
972 errs = append(errs, err)
973 }
974 }
975 if len(errs) > 0 {
976 return nil, errors.Join(errs...)
977 }
978
979 MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFile)
980 setDefaultBuildMod()
981 rs := requirementsFromModFiles(ctx, workFile, modFiles, opts)
982
983 if cfg.BuildMod == "vendor" {
984 readVendorList(VendorDir())
985 var indexes []*modFileIndex
986 var modFiles []*modfile.File
987 var modRoots []string
988 for _, m := range MainModules.Versions() {
989 indexes = append(indexes, MainModules.Index(m))
990 modFiles = append(modFiles, MainModules.ModFile(m))
991 modRoots = append(modRoots, MainModules.ModRoot(m))
992 }
993 checkVendorConsistency(indexes, modFiles, modRoots)
994 rs.initVendor(vendorList)
995 }
996
997 if inWorkspaceMode() {
998
999 requirements = rs
1000 return rs, nil
1001 }
1002
1003 mainModule := MainModules.mustGetSingleMainModule()
1004
1005 if rs.hasRedundantRoot() {
1006
1007
1008
1009 var err error
1010 rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
1011 if err != nil {
1012 return nil, err
1013 }
1014 }
1015
1016 if MainModules.Index(mainModule).goVersion == "" && rs.pruning != workspace {
1017
1018
1019 if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
1020
1021 v := gover.Local()
1022 if opts != nil && opts.TidyGoVersion != "" {
1023 v = opts.TidyGoVersion
1024 }
1025 addGoStmt(MainModules.ModFile(mainModule), mainModule, v)
1026 rs = overrideRoots(ctx, rs, []module.Version{{Path: "go", Version: v}})
1027
1028
1029
1030
1031
1032
1033 if gover.Compare(v, gover.ExplicitIndirectVersion) >= 0 {
1034 var err error
1035 rs, err = convertPruning(ctx, rs, pruned)
1036 if err != nil {
1037 return nil, err
1038 }
1039 }
1040 } else {
1041 rawGoVersion.Store(mainModule, gover.DefaultGoModVersion)
1042 }
1043 }
1044
1045 requirements = rs
1046 return requirements, nil
1047 }
1048
1049 func errWorkTooOld(gomod string, wf *modfile.WorkFile, goVers string) error {
1050 verb := "lists"
1051 if wf == nil || wf.Go == nil {
1052
1053
1054 verb = "implicitly requires"
1055 }
1056 return fmt.Errorf("module %s listed in go.work file requires go >= %s, but go.work %s go %s; to update it:\n\tgo work use",
1057 base.ShortPath(filepath.Dir(gomod)), goVers, verb, gover.FromGoWork(wf))
1058 }
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069 func CreateModFile(ctx context.Context, modPath string) {
1070 modRoot := base.Cwd()
1071 modRoots = []string{modRoot}
1072 Init()
1073 modFilePath := modFilePath(modRoot)
1074 if _, err := fsys.Stat(modFilePath); err == nil {
1075 base.Fatalf("go: %s already exists", modFilePath)
1076 }
1077
1078 if modPath == "" {
1079 var err error
1080 modPath, err = findModulePath(modRoot)
1081 if err != nil {
1082 base.Fatal(err)
1083 }
1084 } else if err := module.CheckImportPath(modPath); err != nil {
1085 if pathErr, ok := err.(*module.InvalidPathError); ok {
1086 pathErr.Kind = "module"
1087
1088 if pathErr.Path == "." || pathErr.Path == ".." ||
1089 strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
1090 pathErr.Err = errors.New("is a local import path")
1091 }
1092 }
1093 base.Fatal(err)
1094 } else if _, _, ok := module.SplitPathVersion(modPath); !ok {
1095 if strings.HasPrefix(modPath, "gopkg.in/") {
1096 invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))
1097 base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
1098 }
1099 invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))
1100 base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
1101 }
1102
1103 fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
1104 modFile := new(modfile.File)
1105 modFile.AddModuleStmt(modPath)
1106 MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, nil)
1107 addGoStmt(modFile, modFile.Module.Mod, gover.Local())
1108
1109 rs := requirementsFromModFiles(ctx, nil, []*modfile.File{modFile}, nil)
1110 rs, err := updateRoots(ctx, rs.direct, rs, nil, nil, false)
1111 if err != nil {
1112 base.Fatal(err)
1113 }
1114 requirements = rs
1115 if err := commitRequirements(ctx, WriteOpts{}); err != nil {
1116 base.Fatal(err)
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126 empty := true
1127 files, _ := os.ReadDir(modRoot)
1128 for _, f := range files {
1129 name := f.Name()
1130 if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {
1131 continue
1132 }
1133 if strings.HasSuffix(name, ".go") || f.IsDir() {
1134 empty = false
1135 break
1136 }
1137 }
1138 if !empty {
1139 fmt.Fprintf(os.Stderr, "go: to add module requirements and sums:\n\tgo mod tidy\n")
1140 }
1141 }
1142
1143
1144
1145
1146
1147
1148
1149
1150 func fixVersion(ctx context.Context, fixed *bool) modfile.VersionFixer {
1151 return func(path, vers string) (resolved string, err error) {
1152 defer func() {
1153 if err == nil && resolved != vers {
1154 *fixed = true
1155 }
1156 }()
1157
1158
1159 if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
1160 vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
1161 }
1162
1163
1164
1165
1166 _, pathMajor, ok := module.SplitPathVersion(path)
1167 if !ok {
1168 return "", &module.ModuleError{
1169 Path: path,
1170 Err: &module.InvalidVersionError{
1171 Version: vers,
1172 Err: fmt.Errorf("malformed module path %q", path),
1173 },
1174 }
1175 }
1176 if vers != "" && module.CanonicalVersion(vers) == vers {
1177 if err := module.CheckPathMajor(vers, pathMajor); err != nil {
1178 return "", module.VersionError(module.Version{Path: path, Version: vers}, err)
1179 }
1180 return vers, nil
1181 }
1182
1183 info, err := Query(ctx, path, vers, "", nil)
1184 if err != nil {
1185 return "", err
1186 }
1187 return info.Version, nil
1188 }
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198 func AllowMissingModuleImports() {
1199 if initialized {
1200 panic("AllowMissingModuleImports after Init")
1201 }
1202 allowMissingModuleImports = true
1203 }
1204
1205
1206
1207 func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFile *modfile.WorkFile) *MainModuleSet {
1208 for _, m := range ms {
1209 if m.Version != "" {
1210 panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m))
1211 }
1212 }
1213 modRootContainingCWD := findModuleRoot(base.Cwd())
1214 mainModules := &MainModuleSet{
1215 versions: slices.Clip(ms),
1216 inGorootSrc: map[module.Version]bool{},
1217 pathPrefix: map[module.Version]string{},
1218 modRoot: map[module.Version]string{},
1219 modFiles: map[module.Version]*modfile.File{},
1220 indices: map[module.Version]*modFileIndex{},
1221 highestReplaced: map[string]string{},
1222 workFile: workFile,
1223 }
1224 var workFileReplaces []*modfile.Replace
1225 if workFile != nil {
1226 workFileReplaces = workFile.Replace
1227 mainModules.workFileReplaceMap = toReplaceMap(workFile.Replace)
1228 }
1229 mainModulePaths := make(map[string]bool)
1230 for _, m := range ms {
1231 if mainModulePaths[m.Path] {
1232 base.Errorf("go: module %s appears multiple times in workspace", m.Path)
1233 }
1234 mainModulePaths[m.Path] = true
1235 }
1236 replacedByWorkFile := make(map[string]bool)
1237 replacements := make(map[module.Version]module.Version)
1238 for _, r := range workFileReplaces {
1239 if mainModulePaths[r.Old.Path] && r.Old.Version == "" {
1240 base.Errorf("go: workspace module %v is replaced at all versions in the go.work file. To fix, remove the replacement from the go.work file or specify the version at which to replace the module.", r.Old.Path)
1241 }
1242 replacedByWorkFile[r.Old.Path] = true
1243 v, ok := mainModules.highestReplaced[r.Old.Path]
1244 if !ok || gover.ModCompare(r.Old.Path, r.Old.Version, v) > 0 {
1245 mainModules.highestReplaced[r.Old.Path] = r.Old.Version
1246 }
1247 replacements[r.Old] = r.New
1248 }
1249 for i, m := range ms {
1250 mainModules.pathPrefix[m] = m.Path
1251 mainModules.modRoot[m] = rootDirs[i]
1252 mainModules.modFiles[m] = modFiles[i]
1253 mainModules.indices[m] = indices[i]
1254
1255 if mainModules.modRoot[m] == modRootContainingCWD {
1256 mainModules.modContainingCWD = m
1257 }
1258
1259 if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" {
1260 mainModules.inGorootSrc[m] = true
1261 if m.Path == "std" {
1262
1263
1264
1265
1266
1267
1268
1269
1270 mainModules.pathPrefix[m] = ""
1271 }
1272 }
1273
1274 if modFiles[i] != nil {
1275 curModuleReplaces := make(map[module.Version]bool)
1276 for _, r := range modFiles[i].Replace {
1277 if replacedByWorkFile[r.Old.Path] {
1278 continue
1279 }
1280 var newV module.Version = r.New
1281 if WorkFilePath() != "" && newV.Version == "" && !filepath.IsAbs(newV.Path) {
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291 newV.Path = filepath.Join(rootDirs[i], newV.Path)
1292 }
1293 if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != newV {
1294 base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -replace %v=[override]\" to resolve", r.Old, prev, newV, r.Old)
1295 }
1296 curModuleReplaces[r.Old] = true
1297 replacements[r.Old] = newV
1298
1299 v, ok := mainModules.highestReplaced[r.Old.Path]
1300 if !ok || gover.ModCompare(r.Old.Path, r.Old.Version, v) > 0 {
1301 mainModules.highestReplaced[r.Old.Path] = r.Old.Version
1302 }
1303 }
1304 }
1305 }
1306
1307 return mainModules
1308 }
1309
1310
1311
1312 func requirementsFromModFiles(ctx context.Context, workFile *modfile.WorkFile, modFiles []*modfile.File, opts *PackageOpts) *Requirements {
1313 var roots []module.Version
1314 direct := map[string]bool{}
1315 var pruning modPruning
1316 if inWorkspaceMode() {
1317 pruning = workspace
1318 roots = make([]module.Version, len(MainModules.Versions()), 2+len(MainModules.Versions()))
1319 copy(roots, MainModules.Versions())
1320 goVersion := gover.FromGoWork(workFile)
1321 var toolchain string
1322 if workFile.Toolchain != nil {
1323 toolchain = workFile.Toolchain.Name
1324 }
1325 roots = appendGoAndToolchainRoots(roots, goVersion, toolchain, direct)
1326 direct = directRequirements(modFiles)
1327 } else {
1328 pruning = pruningForGoVersion(MainModules.GoVersion())
1329 if len(modFiles) != 1 {
1330 panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles)))
1331 }
1332 modFile := modFiles[0]
1333 roots, direct = rootsFromModFile(MainModules.mustGetSingleMainModule(), modFile, withToolchainRoot)
1334 }
1335
1336 gover.ModSort(roots)
1337 rs := newRequirements(pruning, roots, direct)
1338 return rs
1339 }
1340
1341 type addToolchainRoot bool
1342
1343 const (
1344 omitToolchainRoot addToolchainRoot = false
1345 withToolchainRoot = true
1346 )
1347
1348 func directRequirements(modFiles []*modfile.File) map[string]bool {
1349 direct := make(map[string]bool)
1350 for _, modFile := range modFiles {
1351 for _, r := range modFile.Require {
1352 if !r.Indirect {
1353 direct[r.Mod.Path] = true
1354 }
1355 }
1356 }
1357 return direct
1358 }
1359
1360 func rootsFromModFile(m module.Version, modFile *modfile.File, addToolchainRoot addToolchainRoot) (roots []module.Version, direct map[string]bool) {
1361 direct = make(map[string]bool)
1362 padding := 2
1363 if !addToolchainRoot {
1364 padding = 1
1365 }
1366 roots = make([]module.Version, 0, padding+len(modFile.Require))
1367 for _, r := range modFile.Require {
1368 if index := MainModules.Index(m); index != nil && index.exclude[r.Mod] {
1369 if cfg.BuildMod == "mod" {
1370 fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
1371 } else {
1372 fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
1373 }
1374 continue
1375 }
1376
1377 roots = append(roots, r.Mod)
1378 if !r.Indirect {
1379 direct[r.Mod.Path] = true
1380 }
1381 }
1382 goVersion := gover.FromGoMod(modFile)
1383 var toolchain string
1384 if addToolchainRoot && modFile.Toolchain != nil {
1385 toolchain = modFile.Toolchain.Name
1386 }
1387 roots = appendGoAndToolchainRoots(roots, goVersion, toolchain, direct)
1388 return roots, direct
1389 }
1390
1391 func appendGoAndToolchainRoots(roots []module.Version, goVersion, toolchain string, direct map[string]bool) []module.Version {
1392
1393 roots = append(roots, module.Version{Path: "go", Version: goVersion})
1394 direct["go"] = true
1395
1396 if toolchain != "" {
1397 roots = append(roots, module.Version{Path: "toolchain", Version: toolchain})
1398
1399
1400
1401
1402
1403 }
1404 return roots
1405 }
1406
1407
1408
1409 func setDefaultBuildMod() {
1410 if cfg.BuildModExplicit {
1411 if inWorkspaceMode() && cfg.BuildMod != "readonly" && cfg.BuildMod != "vendor" {
1412 switch cfg.CmdName {
1413 case "work sync", "mod graph", "mod verify", "mod why":
1414
1415
1416 panic("in workspace mode and -mod was set explicitly, but command doesn't support setting -mod")
1417 default:
1418 base.Fatalf("go: -mod may only be set to readonly or vendor when in workspace mode, but it is set to %q"+
1419 "\n\tRemove the -mod flag to use the default readonly value, "+
1420 "\n\tor set GOWORK=off to disable workspace mode.", cfg.BuildMod)
1421 }
1422 }
1423
1424 return
1425 }
1426
1427
1428
1429
1430 switch cfg.CmdName {
1431 case "get", "mod download", "mod init", "mod tidy", "work sync":
1432
1433 cfg.BuildMod = "mod"
1434 return
1435 case "mod graph", "mod verify", "mod why":
1436
1437
1438
1439
1440 cfg.BuildMod = "mod"
1441 return
1442 case "mod vendor", "work vendor":
1443 cfg.BuildMod = "readonly"
1444 return
1445 }
1446 if modRoots == nil {
1447 if allowMissingModuleImports {
1448 cfg.BuildMod = "mod"
1449 } else {
1450 cfg.BuildMod = "readonly"
1451 }
1452 return
1453 }
1454
1455 if len(modRoots) >= 1 {
1456 var goVersion string
1457 var versionSource string
1458 if inWorkspaceMode() {
1459 versionSource = "go.work"
1460 if wfg := MainModules.WorkFile().Go; wfg != nil {
1461 goVersion = wfg.Version
1462 }
1463 } else {
1464 versionSource = "go.mod"
1465 index := MainModules.GetSingleIndexOrNil()
1466 if index != nil {
1467 goVersion = index.goVersion
1468 }
1469 }
1470 vendorDir := ""
1471 if workFilePath != "" {
1472 vendorDir = filepath.Join(filepath.Dir(workFilePath), "vendor")
1473 } else {
1474 if len(modRoots) != 1 {
1475 panic(fmt.Errorf("outside workspace mode, but have %v modRoots", modRoots))
1476 }
1477 vendorDir = filepath.Join(modRoots[0], "vendor")
1478 }
1479 if fi, err := fsys.Stat(vendorDir); err == nil && fi.IsDir() {
1480 if goVersion != "" {
1481 if gover.Compare(goVersion, "1.14") < 0 {
1482
1483
1484
1485 cfg.BuildModReason = fmt.Sprintf("Go version in "+versionSource+" is %s, so vendor directory was not used.", goVersion)
1486 } else {
1487 vendoredWorkspace, err := modulesTextIsForWorkspace(vendorDir)
1488 if err != nil {
1489 base.Fatalf("go: reading modules.txt for vendor directory: %v", err)
1490 }
1491 if vendoredWorkspace != (versionSource == "go.work") {
1492 if vendoredWorkspace {
1493 cfg.BuildModReason = "Outside workspace mode, but vendor directory is for a workspace."
1494 } else {
1495 cfg.BuildModReason = "In workspace mode, but vendor directory is not for a workspace"
1496 }
1497 } else {
1498
1499
1500
1501 cfg.BuildMod = "vendor"
1502 cfg.BuildModReason = "Go version in " + versionSource + " is at least 1.14 and vendor directory exists."
1503 return
1504 }
1505 }
1506 } else {
1507 cfg.BuildModReason = fmt.Sprintf("Go version in " + versionSource + " is unspecified, so vendor directory was not used.")
1508 }
1509 }
1510 }
1511
1512 cfg.BuildMod = "readonly"
1513 }
1514
1515 func modulesTextIsForWorkspace(vendorDir string) (bool, error) {
1516 f, err := fsys.Open(filepath.Join(vendorDir, "modules.txt"))
1517 if errors.Is(err, os.ErrNotExist) {
1518
1519
1520
1521
1522 return false, nil
1523 }
1524 if err != nil {
1525 return false, err
1526 }
1527 defer f.Close()
1528 var buf [512]byte
1529 n, err := f.Read(buf[:])
1530 if err != nil && err != io.EOF {
1531 return false, err
1532 }
1533 line, _, _ := strings.Cut(string(buf[:n]), "\n")
1534 if annotations, ok := strings.CutPrefix(line, "## "); ok {
1535 for _, entry := range strings.Split(annotations, ";") {
1536 entry = strings.TrimSpace(entry)
1537 if entry == "workspace" {
1538 return true, nil
1539 }
1540 }
1541 }
1542 return false, nil
1543 }
1544
1545 func mustHaveCompleteRequirements() bool {
1546 return cfg.BuildMod != "mod" && !inWorkspaceMode()
1547 }
1548
1549
1550
1551
1552 func addGoStmt(modFile *modfile.File, mod module.Version, v string) {
1553 if modFile.Go != nil && modFile.Go.Version != "" {
1554 return
1555 }
1556 forceGoStmt(modFile, mod, v)
1557 }
1558
1559 func forceGoStmt(modFile *modfile.File, mod module.Version, v string) {
1560 if err := modFile.AddGoStmt(v); err != nil {
1561 base.Fatalf("go: internal error: %v", err)
1562 }
1563 rawGoVersion.Store(mod, v)
1564 }
1565
1566 var altConfigs = []string{
1567 ".git/config",
1568 }
1569
1570 func findModuleRoot(dir string) (roots string) {
1571 if dir == "" {
1572 panic("dir not set")
1573 }
1574 dir = filepath.Clean(dir)
1575
1576
1577 for {
1578 if fi, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
1579 return dir
1580 }
1581 d := filepath.Dir(dir)
1582 if d == dir {
1583 break
1584 }
1585 dir = d
1586 }
1587 return ""
1588 }
1589
1590 func findWorkspaceFile(dir string) (root string) {
1591 if dir == "" {
1592 panic("dir not set")
1593 }
1594 dir = filepath.Clean(dir)
1595
1596
1597 for {
1598 f := filepath.Join(dir, "go.work")
1599 if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() {
1600 return f
1601 }
1602 d := filepath.Dir(dir)
1603 if d == dir {
1604 break
1605 }
1606 if d == cfg.GOROOT {
1607
1608
1609
1610 return ""
1611 }
1612 dir = d
1613 }
1614 return ""
1615 }
1616
1617 func findAltConfig(dir string) (root, name string) {
1618 if dir == "" {
1619 panic("dir not set")
1620 }
1621 dir = filepath.Clean(dir)
1622 if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {
1623
1624
1625 return "", ""
1626 }
1627 for {
1628 for _, name := range altConfigs {
1629 if fi, err := fsys.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
1630 return dir, name
1631 }
1632 }
1633 d := filepath.Dir(dir)
1634 if d == dir {
1635 break
1636 }
1637 dir = d
1638 }
1639 return "", ""
1640 }
1641
1642 func findModulePath(dir string) (string, error) {
1643
1644
1645
1646
1647
1648
1649
1650
1651 list, _ := os.ReadDir(dir)
1652 for _, info := range list {
1653 if info.Type().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
1654 if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
1655 return com, nil
1656 }
1657 }
1658 }
1659 for _, info1 := range list {
1660 if info1.IsDir() {
1661 files, _ := os.ReadDir(filepath.Join(dir, info1.Name()))
1662 for _, info2 := range files {
1663 if info2.Type().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
1664 if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
1665 return path.Dir(com), nil
1666 }
1667 }
1668 }
1669 }
1670 }
1671
1672
1673 data, _ := os.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
1674 var cfg1 struct{ ImportPath string }
1675 json.Unmarshal(data, &cfg1)
1676 if cfg1.ImportPath != "" {
1677 return cfg1.ImportPath, nil
1678 }
1679
1680
1681 data, _ = os.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
1682 var cfg2 struct{ RootPath string }
1683 json.Unmarshal(data, &cfg2)
1684 if cfg2.RootPath != "" {
1685 return cfg2.RootPath, nil
1686 }
1687
1688
1689 var badPathErr error
1690 for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
1691 if gpdir == "" {
1692 continue
1693 }
1694 if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
1695 path := filepath.ToSlash(rel)
1696
1697 if err := module.CheckImportPath(path); err != nil {
1698 badPathErr = err
1699 break
1700 }
1701 return path, nil
1702 }
1703 }
1704
1705 reason := "outside GOPATH, module path must be specified"
1706 if badPathErr != nil {
1707
1708
1709 reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
1710 }
1711 msg := `cannot determine module path for source directory %s (%s)
1712
1713 Example usage:
1714 'go mod init example.com/m' to initialize a v0 or v1 module
1715 'go mod init example.com/m/v2' to initialize a v2 module
1716
1717 Run 'go help mod init' for more information.
1718 `
1719 return "", fmt.Errorf(msg, dir, reason)
1720 }
1721
1722 var (
1723 importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
1724 )
1725
1726 func findImportComment(file string) string {
1727 data, err := os.ReadFile(file)
1728 if err != nil {
1729 return ""
1730 }
1731 m := importCommentRE.FindSubmatch(data)
1732 if m == nil {
1733 return ""
1734 }
1735 path, err := strconv.Unquote(string(m[1]))
1736 if err != nil {
1737 return ""
1738 }
1739 return path
1740 }
1741
1742
1743 type WriteOpts struct {
1744 DropToolchain bool
1745 ExplicitToolchain bool
1746
1747
1748
1749 TidyWroteGo bool
1750 }
1751
1752
1753 func WriteGoMod(ctx context.Context, opts WriteOpts) error {
1754 requirements = LoadModFile(ctx)
1755 return commitRequirements(ctx, opts)
1756 }
1757
1758 var errNoChange = errors.New("no update needed")
1759
1760
1761
1762 func UpdateGoModFromReqs(ctx context.Context, opts WriteOpts) (before, after []byte, modFile *modfile.File, err error) {
1763 if MainModules.Len() != 1 || MainModules.ModRoot(MainModules.Versions()[0]) == "" {
1764
1765 return nil, nil, nil, errNoChange
1766 }
1767 mainModule := MainModules.mustGetSingleMainModule()
1768 modFile = MainModules.ModFile(mainModule)
1769 if modFile == nil {
1770
1771 return nil, nil, nil, errNoChange
1772 }
1773 before, err = modFile.Format()
1774 if err != nil {
1775 return nil, nil, nil, err
1776 }
1777
1778 var list []*modfile.Require
1779 toolchain := ""
1780 goVersion := ""
1781 for _, m := range requirements.rootModules {
1782 if m.Path == "go" {
1783 goVersion = m.Version
1784 continue
1785 }
1786 if m.Path == "toolchain" {
1787 toolchain = m.Version
1788 continue
1789 }
1790 list = append(list, &modfile.Require{
1791 Mod: m,
1792 Indirect: !requirements.direct[m.Path],
1793 })
1794 }
1795
1796
1797
1798
1799 if goVersion == "" {
1800 base.Fatalf("go: internal error: missing go root module in WriteGoMod")
1801 }
1802 if gover.Compare(goVersion, gover.Local()) > 0 {
1803
1804 return nil, nil, nil, &gover.TooNewError{What: "updating go.mod", GoVersion: goVersion}
1805 }
1806 wroteGo := opts.TidyWroteGo
1807 if !wroteGo && modFile.Go == nil || modFile.Go.Version != goVersion {
1808 alwaysUpdate := cfg.BuildMod == "mod" || cfg.CmdName == "mod tidy" || cfg.CmdName == "get"
1809 if modFile.Go == nil && goVersion == gover.DefaultGoModVersion && !alwaysUpdate {
1810
1811
1812
1813 } else {
1814 wroteGo = true
1815 forceGoStmt(modFile, mainModule, goVersion)
1816 }
1817 }
1818 if toolchain == "" {
1819 toolchain = "go" + goVersion
1820 }
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830 toolVers := gover.FromToolchain(toolchain)
1831 if wroteGo && !opts.DropToolchain && !opts.ExplicitToolchain &&
1832 gover.Compare(goVersion, gover.GoStrictVersion) >= 0 &&
1833 (gover.Compare(gover.Local(), toolVers) > 0 && !gover.IsLang(gover.Local())) {
1834 toolchain = "go" + gover.Local()
1835 toolVers = gover.FromToolchain(toolchain)
1836 }
1837
1838 if opts.DropToolchain || toolchain == "go"+goVersion || (gover.Compare(toolVers, gover.GoStrictVersion) < 0 && !opts.ExplicitToolchain) {
1839
1840
1841 modFile.DropToolchainStmt()
1842 } else {
1843 modFile.AddToolchainStmt(toolchain)
1844 }
1845
1846
1847 if gover.Compare(goVersion, gover.SeparateIndirectVersion) < 0 {
1848 modFile.SetRequire(list)
1849 } else {
1850 modFile.SetRequireSeparateIndirect(list)
1851 }
1852 modFile.Cleanup()
1853 after, err = modFile.Format()
1854 if err != nil {
1855 return nil, nil, nil, err
1856 }
1857 return before, after, modFile, nil
1858 }
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869 func commitRequirements(ctx context.Context, opts WriteOpts) (err error) {
1870 if inWorkspaceMode() {
1871
1872
1873 return modfetch.WriteGoSum(ctx, keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
1874 }
1875 _, updatedGoMod, modFile, err := UpdateGoModFromReqs(ctx, opts)
1876 if err != nil {
1877 if errors.Is(err, errNoChange) {
1878 return nil
1879 }
1880 return err
1881 }
1882
1883 index := MainModules.GetSingleIndexOrNil()
1884 dirty := index.modFileIsDirty(modFile)
1885 if dirty && cfg.BuildMod != "mod" {
1886
1887
1888 return errGoModDirty
1889 }
1890
1891 if !dirty && cfg.CmdName != "mod tidy" {
1892
1893
1894
1895
1896 if cfg.CmdName != "mod init" {
1897 if err := modfetch.WriteGoSum(ctx, keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()); err != nil {
1898 return err
1899 }
1900 }
1901 return nil
1902 }
1903
1904 mainModule := MainModules.mustGetSingleMainModule()
1905 modFilePath := modFilePath(MainModules.ModRoot(mainModule))
1906 if _, ok := fsys.OverlayPath(modFilePath); ok {
1907 if dirty {
1908 return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
1909 }
1910 return nil
1911 }
1912 defer func() {
1913
1914 MainModules.SetIndex(mainModule, indexModFile(updatedGoMod, modFile, mainModule, false))
1915
1916
1917
1918 if cfg.CmdName != "mod init" {
1919 if err == nil {
1920 err = modfetch.WriteGoSum(ctx, keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
1921 }
1922 }
1923 }()
1924
1925
1926
1927 if unlock, err := modfetch.SideLock(ctx); err == nil {
1928 defer unlock()
1929 }
1930
1931 err = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) {
1932 if bytes.Equal(old, updatedGoMod) {
1933
1934
1935 return nil, errNoChange
1936 }
1937
1938 if index != nil && !bytes.Equal(old, index.data) {
1939
1940
1941
1942
1943
1944
1945 return nil, fmt.Errorf("existing contents have changed since last read")
1946 }
1947
1948 return updatedGoMod, nil
1949 })
1950
1951 if err != nil && err != errNoChange {
1952 return fmt.Errorf("updating go.mod: %w", err)
1953 }
1954 return nil
1955 }
1956
1957
1958
1959
1960
1961
1962
1963 func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums) map[module.Version]bool {
1964
1965
1966
1967
1968 keep := make(map[module.Version]bool)
1969
1970
1971
1972
1973
1974 keepModSumsForZipSums := true
1975 if ld == nil {
1976 if gover.Compare(MainModules.GoVersion(), gover.TidyGoModSumVersion) < 0 && cfg.BuildMod != "mod" {
1977 keepModSumsForZipSums = false
1978 }
1979 } else {
1980 keepPkgGoModSums := true
1981 if gover.Compare(ld.requirements.GoVersion(), gover.TidyGoModSumVersion) < 0 && (ld.Tidy || cfg.BuildMod != "mod") {
1982 keepPkgGoModSums = false
1983 keepModSumsForZipSums = false
1984 }
1985 for _, pkg := range ld.pkgs {
1986
1987
1988
1989 if pkg.testOf != nil || (pkg.mod.Path == "" && pkg.err == nil) || module.CheckImportPath(pkg.path) != nil {
1990 continue
1991 }
1992
1993
1994
1995
1996
1997
1998 if keepPkgGoModSums {
1999 r := resolveReplacement(pkg.mod)
2000 keep[modkey(r)] = true
2001 }
2002
2003 if rs.pruning == pruned && pkg.mod.Path != "" {
2004 if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
2005
2006
2007
2008
2009
2010 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
2011 if v, ok := rs.rootSelected(prefix); ok && v != "none" {
2012 m := module.Version{Path: prefix, Version: v}
2013 r := resolveReplacement(m)
2014 keep[r] = true
2015 }
2016 }
2017 continue
2018 }
2019 }
2020
2021 mg, _ := rs.Graph(ctx)
2022 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
2023 if v := mg.Selected(prefix); v != "none" {
2024 m := module.Version{Path: prefix, Version: v}
2025 r := resolveReplacement(m)
2026 keep[r] = true
2027 }
2028 }
2029 }
2030 }
2031
2032 if rs.graph.Load() == nil {
2033
2034
2035
2036 for _, m := range rs.rootModules {
2037 r := resolveReplacement(m)
2038 keep[modkey(r)] = true
2039 if which == addBuildListZipSums {
2040 keep[r] = true
2041 }
2042 }
2043 } else {
2044 mg, _ := rs.Graph(ctx)
2045 mg.WalkBreadthFirst(func(m module.Version) {
2046 if _, ok := mg.RequiredBy(m); ok {
2047
2048
2049
2050 r := resolveReplacement(m)
2051 keep[modkey(r)] = true
2052 }
2053 })
2054
2055 if which == addBuildListZipSums {
2056 for _, m := range mg.BuildList() {
2057 r := resolveReplacement(m)
2058 if keepModSumsForZipSums {
2059 keep[modkey(r)] = true
2060 }
2061 keep[r] = true
2062 }
2063 }
2064 }
2065
2066 return keep
2067 }
2068
2069 type whichSums int8
2070
2071 const (
2072 loadedZipSumsOnly = whichSums(iota)
2073 addBuildListZipSums
2074 )
2075
2076
2077
2078 func modkey(m module.Version) module.Version {
2079 return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
2080 }
2081
2082 func suggestModulePath(path string) string {
2083 var m string
2084
2085 i := len(path)
2086 for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
2087 i--
2088 }
2089 url := path[:i]
2090 url = strings.TrimSuffix(url, "/v")
2091 url = strings.TrimSuffix(url, "/")
2092
2093 f := func(c rune) bool {
2094 return c > '9' || c < '0'
2095 }
2096 s := strings.FieldsFunc(path[i:], f)
2097 if len(s) > 0 {
2098 m = s[0]
2099 }
2100 m = strings.TrimLeft(m, "0")
2101 if m == "" || m == "1" {
2102 return url + "/v2"
2103 }
2104
2105 return url + "/v" + m
2106 }
2107
2108 func suggestGopkgIn(path string) string {
2109 var m string
2110 i := len(path)
2111 for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) {
2112 i--
2113 }
2114 url := path[:i]
2115 url = strings.TrimSuffix(url, ".v")
2116 url = strings.TrimSuffix(url, "/v")
2117 url = strings.TrimSuffix(url, "/")
2118
2119 f := func(c rune) bool {
2120 return c > '9' || c < '0'
2121 }
2122 s := strings.FieldsFunc(path, f)
2123 if len(s) > 0 {
2124 m = s[0]
2125 }
2126
2127 m = strings.TrimLeft(m, "0")
2128
2129 if m == "" {
2130 return url + ".v1"
2131 }
2132 return url + ".v" + m
2133 }
2134
2135 func CheckGodebug(verb, k, v string) error {
2136 if strings.ContainsAny(k, " \t") {
2137 return fmt.Errorf("key contains space")
2138 }
2139 if strings.ContainsAny(v, " \t") {
2140 return fmt.Errorf("value contains space")
2141 }
2142 if strings.ContainsAny(k, ",") {
2143 return fmt.Errorf("key contains comma")
2144 }
2145 if strings.ContainsAny(v, ",") {
2146 return fmt.Errorf("value contains comma")
2147 }
2148 if k == "default" {
2149 if !strings.HasPrefix(v, "go") || !gover.IsValid(v[len("go"):]) {
2150 return fmt.Errorf("value for default= must be goVERSION")
2151 }
2152 if gover.Compare(v[len("go"):], gover.Local()) > 0 {
2153 return fmt.Errorf("default=%s too new (toolchain is go%s)", v, gover.Local())
2154 }
2155 return nil
2156 }
2157 for _, info := range godebugs.All {
2158 if k == info.Name {
2159 return nil
2160 }
2161 }
2162 return fmt.Errorf("unknown %s %q", verb, k)
2163 }
2164
View as plain text