1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "go/build"
14 "internal/lazyregexp"
15 "io/ioutil"
16 "os"
17 "path"
18 "path/filepath"
19 "strconv"
20 "strings"
21 "sync"
22
23 "cmd/go/internal/base"
24 "cmd/go/internal/cfg"
25 "cmd/go/internal/fsys"
26 "cmd/go/internal/lockedfile"
27 "cmd/go/internal/modconv"
28 "cmd/go/internal/modfetch"
29 "cmd/go/internal/search"
30
31 "golang.org/x/mod/modfile"
32 "golang.org/x/mod/module"
33 "golang.org/x/mod/semver"
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 versions []module.Version
95
96
97 modRoot map[module.Version]string
98
99
100
101
102 pathPrefix map[module.Version]string
103
104
105
106 inGorootSrc map[module.Version]bool
107
108 modFiles map[module.Version]*modfile.File
109
110 modContainingCWD module.Version
111
112 workFileGoVersion string
113
114 workFileReplaceMap map[module.Version]module.Version
115
116 highestReplaced map[string]string
117
118 indexMu sync.Mutex
119 indices map[module.Version]*modFileIndex
120 }
121
122 func (mms *MainModuleSet) PathPrefix(m module.Version) string {
123 return mms.pathPrefix[m]
124 }
125
126
127
128
129
130 func (mms *MainModuleSet) Versions() []module.Version {
131 if mms == nil {
132 return nil
133 }
134 return mms.versions
135 }
136
137 func (mms *MainModuleSet) Contains(path string) bool {
138 if mms == nil {
139 return false
140 }
141 for _, v := range mms.versions {
142 if v.Path == path {
143 return true
144 }
145 }
146 return false
147 }
148
149 func (mms *MainModuleSet) ModRoot(m module.Version) string {
150 if mms == nil {
151 return ""
152 }
153 return mms.modRoot[m]
154 }
155
156 func (mms *MainModuleSet) InGorootSrc(m module.Version) bool {
157 if mms == nil {
158 return false
159 }
160 return mms.inGorootSrc[m]
161 }
162
163 func (mms *MainModuleSet) mustGetSingleMainModule() module.Version {
164 if mms == nil || len(mms.versions) == 0 {
165 panic("internal error: mustGetSingleMainModule called in context with no main modules")
166 }
167 if len(mms.versions) != 1 {
168 if inWorkspaceMode() {
169 panic("internal error: mustGetSingleMainModule called in workspace mode")
170 } else {
171 panic("internal error: multiple main modules present outside of workspace mode")
172 }
173 }
174 return mms.versions[0]
175 }
176
177 func (mms *MainModuleSet) GetSingleIndexOrNil() *modFileIndex {
178 if mms == nil {
179 return nil
180 }
181 if len(mms.versions) == 0 {
182 return nil
183 }
184 return mms.indices[mms.mustGetSingleMainModule()]
185 }
186
187 func (mms *MainModuleSet) Index(m module.Version) *modFileIndex {
188 mms.indexMu.Lock()
189 defer mms.indexMu.Unlock()
190 return mms.indices[m]
191 }
192
193 func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) {
194 mms.indexMu.Lock()
195 defer mms.indexMu.Unlock()
196 mms.indices[m] = index
197 }
198
199 func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File {
200 return mms.modFiles[m]
201 }
202
203 func (mms *MainModuleSet) Len() int {
204 if mms == nil {
205 return 0
206 }
207 return len(mms.versions)
208 }
209
210
211
212
213 func (mms *MainModuleSet) ModContainingCWD() module.Version {
214 return mms.modContainingCWD
215 }
216
217 func (mms *MainModuleSet) HighestReplaced() map[string]string {
218 return mms.highestReplaced
219 }
220
221
222
223 func (mms *MainModuleSet) GoVersion() string {
224 if !inWorkspaceMode() {
225 return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule()))
226 }
227 v := mms.workFileGoVersion
228 if v == "" {
229
230 v = "1.18"
231 }
232 return v
233 }
234
235 func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {
236 return mms.workFileReplaceMap
237 }
238
239 var MainModules *MainModuleSet
240
241 type Root int
242
243 const (
244
245
246
247
248 AutoRoot Root = iota
249
250
251
252 NoRoot
253
254
255
256 NeedRoot
257 )
258
259
260
261
262
263
264
265
266
267 func ModFile() *modfile.File {
268 Init()
269 modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule())
270 if modFile == nil {
271 die()
272 }
273 return modFile
274 }
275
276 func BinDir() string {
277 Init()
278 if cfg.GOBIN != "" {
279 return cfg.GOBIN
280 }
281 if gopath == "" {
282 return ""
283 }
284 return filepath.Join(gopath, "bin")
285 }
286
287
288
289
290 func InitWorkfile() {
291 if RootMode == NoRoot {
292 workFilePath = ""
293 return
294 }
295
296 switch gowork := cfg.Getenv("GOWORK"); gowork {
297 case "off":
298 workFilePath = ""
299 case "", "auto":
300 workFilePath = findWorkspaceFile(base.Cwd())
301 default:
302 if !filepath.IsAbs(gowork) {
303 base.Fatalf("the path provided to GOWORK must be an absolute path")
304 }
305 workFilePath = gowork
306 }
307 }
308
309
310
311 func WorkFilePath() string {
312 return workFilePath
313 }
314
315
316
317
318
319 func Init() {
320 if initialized {
321 return
322 }
323 initialized = true
324
325
326
327
328 var mustUseModules bool
329 env := cfg.Getenv("GO111MODULE")
330 switch env {
331 default:
332 base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
333 case "auto":
334 mustUseModules = ForceUseModules
335 case "on", "":
336 mustUseModules = true
337 case "off":
338 if ForceUseModules {
339 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
340 }
341 mustUseModules = false
342 return
343 }
344
345 if err := fsys.Init(base.Cwd()); err != nil {
346 base.Fatalf("go: %v", err)
347 }
348
349
350
351
352
353
354
355 if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
356 os.Setenv("GIT_TERMINAL_PROMPT", "0")
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
373 os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes")
374 }
375
376 if os.Getenv("GCM_INTERACTIVE") == "" {
377 os.Setenv("GCM_INTERACTIVE", "never")
378 }
379 if modRoots != nil {
380
381
382 } else if RootMode == NoRoot {
383 if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
384 base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
385 }
386 modRoots = nil
387 } else if inWorkspaceMode() {
388
389 } else {
390 if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
391 if cfg.ModFile != "" {
392 base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
393 }
394 if RootMode == NeedRoot {
395 base.Fatalf("go: %v", ErrNoModRoot)
396 }
397 if !mustUseModules {
398
399
400 return
401 }
402 } else if search.InDir(modRoot, os.TempDir()) == "." {
403
404
405
406
407
408 fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
409 if !mustUseModules {
410 return
411 }
412 } else {
413 modRoots = []string{modRoot}
414 }
415 }
416 if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
417 base.Fatalf("go: -modfile=%s: file does not have .mod extension", cfg.ModFile)
418 }
419
420
421 cfg.ModulesEnabled = true
422 setDefaultBuildMod()
423 list := filepath.SplitList(cfg.BuildContext.GOPATH)
424 if len(list) > 0 && list[0] != "" {
425 gopath = list[0]
426 if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
427 base.Fatalf("$GOPATH/go.mod exists but should not")
428 }
429 }
430 }
431
432
433
434
435
436
437
438
439
440
441 func WillBeEnabled() bool {
442 if modRoots != nil || cfg.ModulesEnabled {
443
444 return true
445 }
446 if initialized {
447
448 return false
449 }
450
451
452
453 env := cfg.Getenv("GO111MODULE")
454 switch env {
455 case "on", "":
456 return true
457 case "auto":
458 break
459 default:
460 return false
461 }
462
463 if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
464
465
466 return false
467 } else if search.InDir(modRoot, os.TempDir()) == "." {
468
469
470
471
472
473 return false
474 }
475 return true
476 }
477
478
479
480
481
482 func Enabled() bool {
483 Init()
484 return modRoots != nil || cfg.ModulesEnabled
485 }
486
487 func VendorDir() string {
488 return filepath.Join(MainModules.ModRoot(MainModules.mustGetSingleMainModule()), "vendor")
489 }
490
491 func inWorkspaceMode() bool {
492 if !initialized {
493 panic("inWorkspaceMode called before modload.Init called")
494 }
495 return workFilePath != ""
496 }
497
498
499
500
501 func HasModRoot() bool {
502 Init()
503 return modRoots != nil
504 }
505
506
507
508 func MustHaveModRoot() {
509 Init()
510 if !HasModRoot() {
511 die()
512 }
513 }
514
515
516
517
518 func ModFilePath() string {
519 MustHaveModRoot()
520 return modFilePath(findModuleRoot(base.Cwd()))
521 }
522
523 func modFilePath(modRoot string) string {
524 if cfg.ModFile != "" {
525 return cfg.ModFile
526 }
527 return filepath.Join(modRoot, "go.mod")
528 }
529
530 func die() {
531 if cfg.Getenv("GO111MODULE") == "off" {
532 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
533 }
534 if inWorkspaceMode() {
535 base.Fatalf("go: no modules were found in the current workspace; see 'go help work'")
536 }
537 if dir, name := findAltConfig(base.Cwd()); dir != "" {
538 rel, err := filepath.Rel(base.Cwd(), dir)
539 if err != nil {
540 rel = dir
541 }
542 cdCmd := ""
543 if rel != "." {
544 cdCmd = fmt.Sprintf("cd %s && ", rel)
545 }
546 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)
547 }
548 base.Fatalf("go: %v", ErrNoModRoot)
549 }
550
551 var ErrNoModRoot = errors.New("go.mod file not found in current directory or any parent directory; see 'go help modules'")
552
553 type goModDirtyError struct{}
554
555 func (goModDirtyError) Error() string {
556 if cfg.BuildModExplicit {
557 return fmt.Sprintf("updates to go.mod needed, disabled by -mod=%v; to update it:\n\tgo mod tidy", cfg.BuildMod)
558 }
559 if cfg.BuildModReason != "" {
560 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)
561 }
562 return "updates to go.mod needed; to update it:\n\tgo mod tidy"
563 }
564
565 var errGoModDirty error = goModDirtyError{}
566
567 func loadWorkFile(path string) (goVersion string, modRoots []string, replaces []*modfile.Replace, err error) {
568 workDir := filepath.Dir(path)
569 wf, err := ReadWorkFile(path)
570 if err != nil {
571 return "", nil, nil, err
572 }
573 if wf.Go != nil {
574 goVersion = wf.Go.Version
575 }
576 seen := map[string]bool{}
577 for _, d := range wf.Use {
578 modRoot := d.Path
579 if !filepath.IsAbs(modRoot) {
580 modRoot = filepath.Join(workDir, modRoot)
581 }
582
583 if seen[modRoot] {
584 return "", nil, nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot)
585 }
586 seen[modRoot] = true
587 modRoots = append(modRoots, modRoot)
588 }
589
590 return goVersion, modRoots, wf.Replace, nil
591 }
592
593
594 func ReadWorkFile(path string) (*modfile.WorkFile, error) {
595 workData, err := ioutil.ReadFile(path)
596 if err != nil {
597 return nil, err
598 }
599
600 return modfile.ParseWork(path, workData, nil)
601 }
602
603
604 func WriteWorkFile(path string, wf *modfile.WorkFile) error {
605 wf.SortBlocks()
606 wf.Cleanup()
607 out := modfile.Format(wf.Syntax)
608
609 return ioutil.WriteFile(path, out, 0666)
610 }
611
612
613
614 func UpdateWorkFile(wf *modfile.WorkFile) {
615 missingModulePaths := map[string]string{}
616
617 for _, d := range wf.Use {
618 if d.Path == "" {
619 continue
620 }
621 modRoot := d.Path
622 if d.ModulePath == "" {
623 missingModulePaths[d.Path] = modRoot
624 }
625 }
626
627
628
629 for moddir, absmodroot := range missingModulePaths {
630 _, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil)
631 if err != nil {
632 continue
633 }
634 wf.AddUse(moddir, f.Module.Mod.Path)
635 }
636 }
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656 func LoadModFile(ctx context.Context) *Requirements {
657 if requirements != nil {
658 return requirements
659 }
660
661 Init()
662 var (
663 workFileGoVersion string
664 workFileReplaces []*modfile.Replace
665 )
666 if inWorkspaceMode() {
667 var err error
668 workFileGoVersion, modRoots, workFileReplaces, err = loadWorkFile(workFilePath)
669 if err != nil {
670 base.Fatalf("reading go.work: %v", err)
671 }
672 for _, modRoot := range modRoots {
673 sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum"
674 modfetch.WorkspaceGoSumFiles = append(modfetch.WorkspaceGoSumFiles, sumFile)
675 }
676 modfetch.GoSumFile = workFilePath + ".sum"
677 } else if modRoots == nil {
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 } else {
696 modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum"
697 }
698 if len(modRoots) == 0 {
699
700
701
702 mainModule := module.Version{Path: "command-line-arguments"}
703 MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "", nil)
704 goVersion := LatestGoVersion()
705 rawGoVersion.Store(mainModule, goVersion)
706 pruning := pruningForGoVersion(goVersion)
707 if inWorkspaceMode() {
708 pruning = workspace
709 }
710 requirements = newRequirements(pruning, nil, nil)
711 return requirements
712 }
713
714 var modFiles []*modfile.File
715 var mainModules []module.Version
716 var indices []*modFileIndex
717 for _, modroot := range modRoots {
718 gomod := modFilePath(modroot)
719 var fixed bool
720 data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
721 if err != nil {
722 base.Fatalf("go: %v", err)
723 }
724
725 modFiles = append(modFiles, f)
726 mainModule := f.Module.Mod
727 mainModules = append(mainModules, mainModule)
728 indices = append(indices, indexModFile(data, f, mainModule, fixed))
729
730 if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
731 if pathErr, ok := err.(*module.InvalidPathError); ok {
732 pathErr.Kind = "module"
733 }
734 base.Fatalf("go: %v", err)
735 }
736 }
737
738 MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion, workFileReplaces)
739 setDefaultBuildMod()
740 rs := requirementsFromModFiles(ctx, modFiles)
741
742 if inWorkspaceMode() {
743
744
745 requirements = rs
746 return rs
747 }
748
749 mainModule := MainModules.mustGetSingleMainModule()
750
751 if cfg.BuildMod == "vendor" {
752 readVendorList(mainModule)
753 index := MainModules.Index(mainModule)
754 modFile := MainModules.ModFile(mainModule)
755 checkVendorConsistency(index, modFile)
756 rs.initVendor(vendorList)
757 }
758
759 if rs.hasRedundantRoot() {
760
761
762
763 var err error
764 rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
765 if err != nil {
766 base.Fatalf("go: %v", err)
767 }
768 }
769
770 if MainModules.Index(mainModule).goVersionV == "" && rs.pruning != workspace {
771
772
773 if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
774 addGoStmt(MainModules.ModFile(mainModule), mainModule, LatestGoVersion())
775
776
777
778
779
780
781 var err error
782 rs, err = convertPruning(ctx, rs, pruned)
783 if err != nil {
784 base.Fatalf("go: %v", err)
785 }
786 } else {
787 rawGoVersion.Store(mainModule, modFileGoVersion(MainModules.ModFile(mainModule)))
788 }
789 }
790
791 requirements = rs
792 return requirements
793 }
794
795
796
797
798
799
800
801
802
803
804 func CreateModFile(ctx context.Context, modPath string) {
805 modRoot := base.Cwd()
806 modRoots = []string{modRoot}
807 Init()
808 modFilePath := modFilePath(modRoot)
809 if _, err := fsys.Stat(modFilePath); err == nil {
810 base.Fatalf("go: %s already exists", modFilePath)
811 }
812
813 if modPath == "" {
814 var err error
815 modPath, err = findModulePath(modRoot)
816 if err != nil {
817 base.Fatalf("go: %v", err)
818 }
819 } else if err := module.CheckImportPath(modPath); err != nil {
820 if pathErr, ok := err.(*module.InvalidPathError); ok {
821 pathErr.Kind = "module"
822
823 if pathErr.Path == "." || pathErr.Path == ".." ||
824 strings.HasPrefix(pathErr.Path, "./") || strings.HasPrefix(pathErr.Path, "../") {
825 pathErr.Err = errors.New("is a local import path")
826 }
827 }
828 base.Fatalf("go: %v", err)
829 } else if _, _, ok := module.SplitPathVersion(modPath); !ok {
830 if strings.HasPrefix(modPath, "gopkg.in/") {
831 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))
832 base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
833 }
834 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))
835 base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
836 }
837
838 fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
839 modFile := new(modfile.File)
840 modFile.AddModuleStmt(modPath)
841 MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "", nil)
842 addGoStmt(modFile, modFile.Module.Mod, LatestGoVersion())
843
844 convertedFrom, err := convertLegacyConfig(modFile, modRoot)
845 if convertedFrom != "" {
846 fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom))
847 }
848 if err != nil {
849 base.Fatalf("go: %v", err)
850 }
851
852 rs := requirementsFromModFiles(ctx, []*modfile.File{modFile})
853 rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
854 if err != nil {
855 base.Fatalf("go: %v", err)
856 }
857 requirements = rs
858 if err := commitRequirements(ctx); err != nil {
859 base.Fatalf("go: %v", err)
860 }
861
862
863
864
865
866
867
868
869 empty := true
870 files, _ := os.ReadDir(modRoot)
871 for _, f := range files {
872 name := f.Name()
873 if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") {
874 continue
875 }
876 if strings.HasSuffix(name, ".go") || f.IsDir() {
877 empty = false
878 break
879 }
880 }
881 if !empty {
882 fmt.Fprintf(os.Stderr, "go: to add module requirements and sums:\n\tgo mod tidy\n")
883 }
884 }
885
886
887 func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) {
888 if _, err := fsys.Stat(workFile); err == nil {
889 base.Fatalf("go: %s already exists", workFile)
890 }
891
892 goV := LatestGoVersion()
893 workF := new(modfile.WorkFile)
894 workF.Syntax = new(modfile.FileSyntax)
895 workF.AddGoStmt(goV)
896
897 for _, dir := range modDirs {
898 _, f, err := ReadModFile(filepath.Join(dir, "go.mod"), nil)
899 if err != nil {
900 if os.IsNotExist(err) {
901 base.Fatalf("go: creating workspace file: no go.mod file exists in directory %v", dir)
902 }
903 base.Fatalf("go: error parsing go.mod in directory %s: %v", dir, err)
904 }
905 workF.AddUse(ToDirectoryPath(dir), f.Module.Mod.Path)
906 }
907
908 UpdateWorkFile(workF)
909 WriteWorkFile(workFile, workF)
910 }
911
912
913
914
915
916
917
918
919 func fixVersion(ctx context.Context, fixed *bool) modfile.VersionFixer {
920 return func(path, vers string) (resolved string, err error) {
921 defer func() {
922 if err == nil && resolved != vers {
923 *fixed = true
924 }
925 }()
926
927
928 if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
929 vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
930 }
931
932
933
934
935 _, pathMajor, ok := module.SplitPathVersion(path)
936 if !ok {
937 return "", &module.ModuleError{
938 Path: path,
939 Err: &module.InvalidVersionError{
940 Version: vers,
941 Err: fmt.Errorf("malformed module path %q", path),
942 },
943 }
944 }
945 if vers != "" && module.CanonicalVersion(vers) == vers {
946 if err := module.CheckPathMajor(vers, pathMajor); err != nil {
947 return "", module.VersionError(module.Version{Path: path, Version: vers}, err)
948 }
949 return vers, nil
950 }
951
952 info, err := Query(ctx, path, vers, "", nil)
953 if err != nil {
954 return "", err
955 }
956 return info.Version, nil
957 }
958 }
959
960
961
962
963
964
965
966
967 func AllowMissingModuleImports() {
968 if initialized {
969 panic("AllowMissingModuleImports after Init")
970 }
971 allowMissingModuleImports = true
972 }
973
974
975
976 func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string, workFileReplaces []*modfile.Replace) *MainModuleSet {
977 for _, m := range ms {
978 if m.Version != "" {
979 panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m))
980 }
981 }
982 modRootContainingCWD := findModuleRoot(base.Cwd())
983 mainModules := &MainModuleSet{
984 versions: ms[:len(ms):len(ms)],
985 inGorootSrc: map[module.Version]bool{},
986 pathPrefix: map[module.Version]string{},
987 modRoot: map[module.Version]string{},
988 modFiles: map[module.Version]*modfile.File{},
989 indices: map[module.Version]*modFileIndex{},
990 workFileGoVersion: workFileGoVersion,
991 workFileReplaceMap: toReplaceMap(workFileReplaces),
992 highestReplaced: map[string]string{},
993 }
994 mainModulePaths := make(map[string]bool)
995 for _, m := range ms {
996 mainModulePaths[m.Path] = true
997 }
998 replacedByWorkFile := make(map[string]bool)
999 replacements := make(map[module.Version]module.Version)
1000 for _, r := range workFileReplaces {
1001 if mainModulePaths[r.Old.Path] && r.Old.Version == "" {
1002 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)
1003 }
1004 replacedByWorkFile[r.Old.Path] = true
1005 v, ok := mainModules.highestReplaced[r.Old.Path]
1006 if !ok || semver.Compare(r.Old.Version, v) > 0 {
1007 mainModules.highestReplaced[r.Old.Path] = r.Old.Version
1008 }
1009 replacements[r.Old] = r.New
1010 }
1011 for i, m := range ms {
1012 mainModules.pathPrefix[m] = m.Path
1013 mainModules.modRoot[m] = rootDirs[i]
1014 mainModules.modFiles[m] = modFiles[i]
1015 mainModules.indices[m] = indices[i]
1016
1017 if mainModules.modRoot[m] == modRootContainingCWD {
1018 mainModules.modContainingCWD = m
1019 }
1020
1021 if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" {
1022 mainModules.inGorootSrc[m] = true
1023 if m.Path == "std" {
1024
1025
1026
1027
1028
1029
1030
1031
1032 mainModules.pathPrefix[m] = ""
1033 }
1034 }
1035
1036 if modFiles[i] != nil {
1037 curModuleReplaces := make(map[module.Version]bool)
1038 for _, r := range modFiles[i].Replace {
1039 if replacedByWorkFile[r.Old.Path] {
1040 continue
1041 }
1042 var newV module.Version = r.New
1043 if WorkFilePath() != "" && newV.Version == "" && !filepath.IsAbs(newV.Path) {
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053 newV.Path = filepath.Join(rootDirs[i], newV.Path)
1054 }
1055 if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] && prev != newV {
1056 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)
1057 }
1058 curModuleReplaces[r.Old] = true
1059 replacements[r.Old] = newV
1060
1061 v, ok := mainModules.highestReplaced[r.Old.Path]
1062 if !ok || semver.Compare(r.Old.Version, v) > 0 {
1063 mainModules.highestReplaced[r.Old.Path] = r.Old.Version
1064 }
1065 }
1066 }
1067 }
1068 return mainModules
1069 }
1070
1071
1072
1073 func requirementsFromModFiles(ctx context.Context, modFiles []*modfile.File) *Requirements {
1074 var roots []module.Version
1075 direct := map[string]bool{}
1076 var pruning modPruning
1077 if inWorkspaceMode() {
1078 pruning = workspace
1079 roots = make([]module.Version, len(MainModules.Versions()))
1080 copy(roots, MainModules.Versions())
1081 } else {
1082 pruning = pruningForGoVersion(MainModules.GoVersion())
1083 if len(modFiles) != 1 {
1084 panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles)))
1085 }
1086 modFile := modFiles[0]
1087 roots = make([]module.Version, 0, len(modFile.Require))
1088 mm := MainModules.mustGetSingleMainModule()
1089 for _, r := range modFile.Require {
1090 if index := MainModules.Index(mm); index != nil && index.exclude[r.Mod] {
1091 if cfg.BuildMod == "mod" {
1092 fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
1093 } else {
1094 fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
1095 }
1096 continue
1097 }
1098
1099 roots = append(roots, r.Mod)
1100 if !r.Indirect {
1101 direct[r.Mod.Path] = true
1102 }
1103 }
1104 }
1105 module.Sort(roots)
1106 rs := newRequirements(pruning, roots, direct)
1107 return rs
1108 }
1109
1110
1111
1112 func setDefaultBuildMod() {
1113 if cfg.BuildModExplicit {
1114 if inWorkspaceMode() && cfg.BuildMod != "readonly" {
1115 base.Fatalf("go: -mod may only be set to readonly when in workspace mode, but it is set to %q"+
1116 "\n\tRemove the -mod flag to use the default readonly value,"+
1117 "\n\tor set GOWORK=off to disable workspace mode.", cfg.BuildMod)
1118 }
1119
1120 return
1121 }
1122
1123
1124
1125
1126 switch cfg.CmdName {
1127 case "get", "mod download", "mod init", "mod tidy", "work sync":
1128
1129 cfg.BuildMod = "mod"
1130 return
1131 case "mod graph", "mod verify", "mod why":
1132
1133
1134
1135
1136 cfg.BuildMod = "mod"
1137 return
1138 case "mod vendor":
1139 cfg.BuildMod = "readonly"
1140 return
1141 }
1142 if modRoots == nil {
1143 if allowMissingModuleImports {
1144 cfg.BuildMod = "mod"
1145 } else {
1146 cfg.BuildMod = "readonly"
1147 }
1148 return
1149 }
1150
1151 if len(modRoots) == 1 {
1152 index := MainModules.GetSingleIndexOrNil()
1153 if fi, err := fsys.Stat(filepath.Join(modRoots[0], "vendor")); err == nil && fi.IsDir() {
1154 modGo := "unspecified"
1155 if index != nil && index.goVersionV != "" {
1156 if semver.Compare(index.goVersionV, "v1.14") >= 0 {
1157
1158
1159 cfg.BuildMod = "vendor"
1160 cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
1161 return
1162 } else {
1163 modGo = index.goVersionV[1:]
1164 }
1165 }
1166
1167
1168
1169 cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
1170 }
1171 }
1172
1173 cfg.BuildMod = "readonly"
1174 }
1175
1176 func mustHaveCompleteRequirements() bool {
1177 return cfg.BuildMod != "mod" && !inWorkspaceMode()
1178 }
1179
1180
1181
1182 func convertLegacyConfig(modFile *modfile.File, modRoot string) (from string, err error) {
1183 noneSelected := func(path string) (version string) { return "none" }
1184 queryPackage := func(path, rev string) (module.Version, error) {
1185 pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil)
1186 if err != nil {
1187 return module.Version{}, err
1188 }
1189 if len(pkgMods) > 0 {
1190 return pkgMods[0].Mod, nil
1191 }
1192 return modOnly.Mod, nil
1193 }
1194 for _, name := range altConfigs {
1195 cfg := filepath.Join(modRoot, name)
1196 data, err := os.ReadFile(cfg)
1197 if err == nil {
1198 convert := modconv.Converters[name]
1199 if convert == nil {
1200 return "", nil
1201 }
1202 cfg = filepath.ToSlash(cfg)
1203 err := modconv.ConvertLegacyConfig(modFile, cfg, data, queryPackage)
1204 return name, err
1205 }
1206 }
1207 return "", nil
1208 }
1209
1210
1211
1212
1213 func addGoStmt(modFile *modfile.File, mod module.Version, v string) {
1214 if modFile.Go != nil && modFile.Go.Version != "" {
1215 return
1216 }
1217 if err := modFile.AddGoStmt(v); err != nil {
1218 base.Fatalf("go: internal error: %v", err)
1219 }
1220 rawGoVersion.Store(mod, v)
1221 }
1222
1223
1224
1225 func LatestGoVersion() string {
1226 tags := build.Default.ReleaseTags
1227 version := tags[len(tags)-1]
1228 if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
1229 base.Fatalf("go: internal error: unrecognized default version %q", version)
1230 }
1231 return version[2:]
1232 }
1233
1234
1235
1236
1237 func priorGoVersion(v string) string {
1238 vTag := "go" + v
1239 tags := build.Default.ReleaseTags
1240 for i, tag := range tags {
1241 if tag == vTag {
1242 if i == 0 {
1243 return v
1244 }
1245
1246 version := tags[i-1]
1247 if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
1248 base.Fatalf("go: internal error: unrecognized version %q", version)
1249 }
1250 return version[2:]
1251 }
1252 }
1253 return v
1254 }
1255
1256 var altConfigs = []string{
1257 "Gopkg.lock",
1258
1259 "GLOCKFILE",
1260 "Godeps/Godeps.json",
1261 "dependencies.tsv",
1262 "glide.lock",
1263 "vendor.conf",
1264 "vendor.yml",
1265 "vendor/manifest",
1266 "vendor/vendor.json",
1267
1268 ".git/config",
1269 }
1270
1271 func findModuleRoot(dir string) (roots string) {
1272 if dir == "" {
1273 panic("dir not set")
1274 }
1275 dir = filepath.Clean(dir)
1276
1277
1278 for {
1279 if fi, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
1280 return dir
1281 }
1282 d := filepath.Dir(dir)
1283 if d == dir {
1284 break
1285 }
1286 dir = d
1287 }
1288 return ""
1289 }
1290
1291 func findWorkspaceFile(dir string) (root string) {
1292 if dir == "" {
1293 panic("dir not set")
1294 }
1295 dir = filepath.Clean(dir)
1296
1297
1298 for {
1299 f := filepath.Join(dir, "go.work")
1300 if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() {
1301 return f
1302 }
1303 d := filepath.Dir(dir)
1304 if d == dir {
1305 break
1306 }
1307 if d == cfg.GOROOT {
1308
1309
1310
1311 return ""
1312 }
1313 dir = d
1314 }
1315 return ""
1316 }
1317
1318 func findAltConfig(dir string) (root, name string) {
1319 if dir == "" {
1320 panic("dir not set")
1321 }
1322 dir = filepath.Clean(dir)
1323 if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {
1324
1325
1326 return "", ""
1327 }
1328 for {
1329 for _, name := range altConfigs {
1330 if fi, err := fsys.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
1331 return dir, name
1332 }
1333 }
1334 d := filepath.Dir(dir)
1335 if d == dir {
1336 break
1337 }
1338 dir = d
1339 }
1340 return "", ""
1341 }
1342
1343 func findModulePath(dir string) (string, error) {
1344
1345
1346
1347
1348
1349
1350
1351
1352 list, _ := os.ReadDir(dir)
1353 for _, info := range list {
1354 if info.Type().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
1355 if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
1356 return com, nil
1357 }
1358 }
1359 }
1360 for _, info1 := range list {
1361 if info1.IsDir() {
1362 files, _ := os.ReadDir(filepath.Join(dir, info1.Name()))
1363 for _, info2 := range files {
1364 if info2.Type().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
1365 if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
1366 return path.Dir(com), nil
1367 }
1368 }
1369 }
1370 }
1371 }
1372
1373
1374 data, _ := os.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
1375 var cfg1 struct{ ImportPath string }
1376 json.Unmarshal(data, &cfg1)
1377 if cfg1.ImportPath != "" {
1378 return cfg1.ImportPath, nil
1379 }
1380
1381
1382 data, _ = os.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
1383 var cfg2 struct{ RootPath string }
1384 json.Unmarshal(data, &cfg2)
1385 if cfg2.RootPath != "" {
1386 return cfg2.RootPath, nil
1387 }
1388
1389
1390 var badPathErr error
1391 for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
1392 if gpdir == "" {
1393 continue
1394 }
1395 if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
1396 path := filepath.ToSlash(rel)
1397
1398 if err := module.CheckImportPath(path); err != nil {
1399 badPathErr = err
1400 break
1401 }
1402 return path, nil
1403 }
1404 }
1405
1406 reason := "outside GOPATH, module path must be specified"
1407 if badPathErr != nil {
1408
1409
1410 reason = fmt.Sprintf("bad module path inferred from directory in GOPATH: %v", badPathErr)
1411 }
1412 msg := `cannot determine module path for source directory %s (%s)
1413
1414 Example usage:
1415 'go mod init example.com/m' to initialize a v0 or v1 module
1416 'go mod init example.com/m/v2' to initialize a v2 module
1417
1418 Run 'go help mod init' for more information.
1419 `
1420 return "", fmt.Errorf(msg, dir, reason)
1421 }
1422
1423 var (
1424 importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
1425 )
1426
1427 func findImportComment(file string) string {
1428 data, err := os.ReadFile(file)
1429 if err != nil {
1430 return ""
1431 }
1432 m := importCommentRE.FindSubmatch(data)
1433 if m == nil {
1434 return ""
1435 }
1436 path, err := strconv.Unquote(string(m[1]))
1437 if err != nil {
1438 return ""
1439 }
1440 return path
1441 }
1442
1443
1444 func WriteGoMod(ctx context.Context) error {
1445 requirements = LoadModFile(ctx)
1446 return commitRequirements(ctx)
1447 }
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458 func commitRequirements(ctx context.Context) (err error) {
1459 if inWorkspaceMode() {
1460
1461
1462 return modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
1463 }
1464 if MainModules.Len() != 1 || MainModules.ModRoot(MainModules.Versions()[0]) == "" {
1465
1466 return nil
1467 }
1468 mainModule := MainModules.mustGetSingleMainModule()
1469 modFile := MainModules.ModFile(mainModule)
1470 if modFile == nil {
1471
1472 return nil
1473 }
1474 modFilePath := modFilePath(MainModules.ModRoot(mainModule))
1475
1476 var list []*modfile.Require
1477 for _, m := range requirements.rootModules {
1478 list = append(list, &modfile.Require{
1479 Mod: m,
1480 Indirect: !requirements.direct[m.Path],
1481 })
1482 }
1483 if modFile.Go == nil || modFile.Go.Version == "" {
1484 modFile.AddGoStmt(modFileGoVersion(modFile))
1485 }
1486 if semver.Compare("v"+modFileGoVersion(modFile), separateIndirectVersionV) < 0 {
1487 modFile.SetRequire(list)
1488 } else {
1489 modFile.SetRequireSeparateIndirect(list)
1490 }
1491 modFile.Cleanup()
1492
1493 index := MainModules.GetSingleIndexOrNil()
1494 dirty := index.modFileIsDirty(modFile)
1495 if dirty && cfg.BuildMod != "mod" {
1496
1497
1498 return errGoModDirty
1499 }
1500
1501 if !dirty && cfg.CmdName != "mod tidy" {
1502
1503
1504
1505
1506 if cfg.CmdName != "mod init" {
1507 if err := modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()); err != nil {
1508 return err
1509 }
1510 }
1511 return nil
1512 }
1513 if _, ok := fsys.OverlayPath(modFilePath); ok {
1514 if dirty {
1515 return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
1516 }
1517 return nil
1518 }
1519
1520 new, err := modFile.Format()
1521 if err != nil {
1522 return err
1523 }
1524 defer func() {
1525
1526 MainModules.SetIndex(mainModule, indexModFile(new, modFile, mainModule, false))
1527
1528
1529
1530 if cfg.CmdName != "mod init" {
1531 if err == nil {
1532 err = modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
1533 }
1534 }
1535 }()
1536
1537
1538
1539 if unlock, err := modfetch.SideLock(); err == nil {
1540 defer unlock()
1541 }
1542
1543 errNoChange := errors.New("no update needed")
1544
1545 err = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) {
1546 if bytes.Equal(old, new) {
1547
1548
1549 return nil, errNoChange
1550 }
1551
1552 if index != nil && !bytes.Equal(old, index.data) {
1553
1554
1555
1556
1557
1558
1559 return nil, fmt.Errorf("existing contents have changed since last read")
1560 }
1561
1562 return new, nil
1563 })
1564
1565 if err != nil && err != errNoChange {
1566 return fmt.Errorf("updating go.mod: %w", err)
1567 }
1568 return nil
1569 }
1570
1571
1572
1573
1574
1575
1576 func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums) map[module.Version]bool {
1577
1578
1579
1580
1581 keep := make(map[module.Version]bool)
1582
1583
1584
1585
1586
1587 if ld != nil {
1588 for _, pkg := range ld.pkgs {
1589
1590
1591
1592 if pkg.testOf != nil || (pkg.mod.Path == "" && pkg.err == nil) || module.CheckImportPath(pkg.path) != nil {
1593 continue
1594 }
1595
1596 if rs.pruning == pruned && pkg.mod.Path != "" {
1597 if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
1598
1599
1600
1601
1602
1603 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
1604 if v, ok := rs.rootSelected(prefix); ok && v != "none" {
1605 m := module.Version{Path: prefix, Version: v}
1606 r := resolveReplacement(m)
1607 keep[r] = true
1608 }
1609 }
1610 continue
1611 }
1612 }
1613
1614 mg, _ := rs.Graph(ctx)
1615 for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
1616 if v := mg.Selected(prefix); v != "none" {
1617 m := module.Version{Path: prefix, Version: v}
1618 r := resolveReplacement(m)
1619 keep[r] = true
1620 }
1621 }
1622 }
1623 }
1624
1625 if rs.graph.Load() == nil {
1626
1627
1628
1629 for _, m := range rs.rootModules {
1630 r := resolveReplacement(m)
1631 keep[modkey(r)] = true
1632 if which == addBuildListZipSums {
1633 keep[r] = true
1634 }
1635 }
1636 } else {
1637 mg, _ := rs.Graph(ctx)
1638 mg.WalkBreadthFirst(func(m module.Version) {
1639 if _, ok := mg.RequiredBy(m); ok {
1640
1641
1642
1643 r := resolveReplacement(m)
1644 keep[modkey(r)] = true
1645 }
1646 })
1647
1648 if which == addBuildListZipSums {
1649 for _, m := range mg.BuildList() {
1650 r := resolveReplacement(m)
1651 keep[r] = true
1652 }
1653 }
1654 }
1655
1656 return keep
1657 }
1658
1659 type whichSums int8
1660
1661 const (
1662 loadedZipSumsOnly = whichSums(iota)
1663 addBuildListZipSums
1664 )
1665
1666
1667
1668 func modkey(m module.Version) module.Version {
1669 return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
1670 }
1671
1672 func suggestModulePath(path string) string {
1673 var m string
1674
1675 i := len(path)
1676 for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
1677 i--
1678 }
1679 url := path[:i]
1680 url = strings.TrimSuffix(url, "/v")
1681 url = strings.TrimSuffix(url, "/")
1682
1683 f := func(c rune) bool {
1684 return c > '9' || c < '0'
1685 }
1686 s := strings.FieldsFunc(path[i:], f)
1687 if len(s) > 0 {
1688 m = s[0]
1689 }
1690 m = strings.TrimLeft(m, "0")
1691 if m == "" || m == "1" {
1692 return url + "/v2"
1693 }
1694
1695 return url + "/v" + m
1696 }
1697
1698 func suggestGopkgIn(path string) string {
1699 var m string
1700 i := len(path)
1701 for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) {
1702 i--
1703 }
1704 url := path[:i]
1705 url = strings.TrimSuffix(url, ".v")
1706 url = strings.TrimSuffix(url, "/v")
1707 url = strings.TrimSuffix(url, "/")
1708
1709 f := func(c rune) bool {
1710 return c > '9' || c < '0'
1711 }
1712 s := strings.FieldsFunc(path, f)
1713 if len(s) > 0 {
1714 m = s[0]
1715 }
1716
1717 m = strings.TrimLeft(m, "0")
1718
1719 if m == "" {
1720 return url + ".v1"
1721 }
1722 return url + ".v" + m
1723 }
1724
View as plain text