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