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