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