Source file src/pkg/cmd/go/internal/modload/init.go
1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "go/build"
12 "internal/lazyregexp"
13 "io/ioutil"
14 "os"
15 "path"
16 "path/filepath"
17 "runtime/debug"
18 "strconv"
19 "strings"
20
21 "cmd/go/internal/base"
22 "cmd/go/internal/cache"
23 "cmd/go/internal/cfg"
24 "cmd/go/internal/load"
25 "cmd/go/internal/modconv"
26 "cmd/go/internal/modfetch"
27 "cmd/go/internal/modfetch/codehost"
28 "cmd/go/internal/modfile"
29 "cmd/go/internal/module"
30 "cmd/go/internal/mvs"
31 "cmd/go/internal/renameio"
32 "cmd/go/internal/search"
33 )
34
35 var (
36 cwd string
37 mustUseModules = false
38 initialized bool
39
40 modRoot string
41 modFile *modfile.File
42 modFileData []byte
43 excluded map[module.Version]bool
44 Target module.Version
45
46
47
48
49 targetPrefix string
50
51
52
53 targetInGorootSrc bool
54
55 gopath string
56
57 CmdModInit bool
58 CmdModModule string
59 )
60
61
62
63
64
65
66
67
68
69 func ModFile() *modfile.File {
70 Init()
71 if modFile == nil {
72 die()
73 }
74 return modFile
75 }
76
77 func BinDir() string {
78 Init()
79 return filepath.Join(gopath, "bin")
80 }
81
82
83
84
85
86 func Init() {
87 if initialized {
88 return
89 }
90 initialized = true
91
92 env := cfg.Getenv("GO111MODULE")
93 switch env {
94 default:
95 base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
96 case "auto", "":
97 mustUseModules = false
98 case "on":
99 mustUseModules = true
100 case "off":
101 mustUseModules = false
102 return
103 }
104
105
106
107
108
109
110
111 if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
112 os.Setenv("GIT_TERMINAL_PROMPT", "0")
113 }
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
129 os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
130 }
131
132 var err error
133 cwd, err = os.Getwd()
134 if err != nil {
135 base.Fatalf("go: %v", err)
136 }
137
138 if CmdModInit {
139
140 modRoot = cwd
141 } else {
142 modRoot = findModuleRoot(cwd)
143 if modRoot == "" {
144 if !mustUseModules {
145
146
147 return
148 }
149 } else if search.InDir(modRoot, os.TempDir()) == "." {
150
151
152
153
154
155 modRoot = ""
156 fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
157 }
158 }
159
160
161
162 if c := cache.Default(); c == nil {
163
164
165 base.Fatalf("go: cannot use modules with build cache disabled")
166 }
167
168 list := filepath.SplitList(cfg.BuildContext.GOPATH)
169 if len(list) == 0 || list[0] == "" {
170 base.Fatalf("missing $GOPATH")
171 }
172 gopath = list[0]
173 if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
174 base.Fatalf("$GOPATH/go.mod exists but should not")
175 }
176
177 oldSrcMod := filepath.Join(list[0], "src/mod")
178 pkgMod := filepath.Join(list[0], "pkg/mod")
179 infoOld, errOld := os.Stat(oldSrcMod)
180 _, errMod := os.Stat(pkgMod)
181 if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
182 os.Rename(oldSrcMod, pkgMod)
183 }
184
185 modfetch.PkgMod = pkgMod
186 codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
187
188 cfg.ModulesEnabled = true
189 load.ModBinDir = BinDir
190 load.ModLookup = Lookup
191 load.ModPackageModuleInfo = PackageModuleInfo
192 load.ModImportPaths = ImportPaths
193 load.ModPackageBuildInfo = PackageBuildInfo
194 load.ModInfoProg = ModInfoProg
195 load.ModImportFromFiles = ImportFromFiles
196 load.ModDirImportPath = DirImportPath
197
198 if modRoot == "" {
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223 } else {
224 modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
225 search.SetModRoot(modRoot)
226 }
227 }
228
229 func init() {
230 load.ModInit = Init
231
232
233
234 if list := filepath.SplitList(cfg.BuildContext.GOPATH); len(list) > 0 && list[0] != "" {
235 modfetch.PkgMod = filepath.Join(list[0], "pkg/mod")
236 codehost.WorkRoot = filepath.Join(list[0], "pkg/mod/cache/vcs")
237 }
238 }
239
240
241
242
243
244 func Enabled() bool {
245 Init()
246 return modRoot != "" || mustUseModules
247 }
248
249
250
251 func ModRoot() string {
252 if !HasModRoot() {
253 die()
254 }
255 return modRoot
256 }
257
258
259
260
261 func HasModRoot() bool {
262 Init()
263 return modRoot != ""
264 }
265
266
267
268
269
270 var printStackInDie = false
271
272 func die() {
273 if printStackInDie {
274 debug.PrintStack()
275 }
276 if cfg.Getenv("GO111MODULE") == "off" {
277 base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
278 }
279 if cwd != "" {
280 if dir, name := findAltConfig(cwd); dir != "" {
281 rel, err := filepath.Rel(cwd, dir)
282 if err != nil {
283 rel = dir
284 }
285 cdCmd := ""
286 if rel != "." {
287 cdCmd = fmt.Sprintf("cd %s && ", rel)
288 }
289 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)
290 }
291 }
292 base.Fatalf("go: cannot find main module; see 'go help modules'")
293 }
294
295
296
297 func InitMod() {
298 if len(buildList) > 0 {
299 return
300 }
301
302 Init()
303 if modRoot == "" {
304 Target = module.Version{Path: "command-line-arguments"}
305 targetPrefix = "command-line-arguments"
306 buildList = []module.Version{Target}
307 return
308 }
309
310 if CmdModInit {
311
312 legacyModInit()
313 modFileToBuildList()
314 WriteGoMod()
315 return
316 }
317
318 gomod := filepath.Join(modRoot, "go.mod")
319 data, err := renameio.ReadFile(gomod)
320 if err != nil {
321 base.Fatalf("go: %v", err)
322 }
323
324 f, err := modfile.Parse(gomod, data, fixVersion)
325 if err != nil {
326
327 base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
328 }
329 modFile = f
330 modFileData = data
331
332 if len(f.Syntax.Stmt) == 0 || f.Module == nil {
333
334 path, err := findModulePath(modRoot)
335 if err != nil {
336 base.Fatalf("go: %v", err)
337 }
338 f.AddModuleStmt(path)
339 }
340
341 if len(f.Syntax.Stmt) == 1 && f.Module != nil {
342
343
344 legacyModInit()
345 }
346
347 excluded = make(map[module.Version]bool)
348 for _, x := range f.Exclude {
349 excluded[x.Mod] = true
350 }
351 modFileToBuildList()
352 stdVendorMode()
353 WriteGoMod()
354 }
355
356
357 func modFileToBuildList() {
358 Target = modFile.Module.Mod
359 targetPrefix = Target.Path
360 if rel := search.InDir(cwd, cfg.GOROOTsrc); rel != "" {
361 targetInGorootSrc = true
362 if Target.Path == "std" {
363 targetPrefix = ""
364 }
365 }
366
367 list := []module.Version{Target}
368 for _, r := range modFile.Require {
369 list = append(list, r.Mod)
370 }
371 buildList = list
372 }
373
374
375
376
377
378 func stdVendorMode() {
379 if !targetInGorootSrc {
380 return
381 }
382 if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
383 return
384 }
385
386 readVendorList()
387 BuildList:
388 for _, m := range buildList {
389 if m.Path == "cmd" || m.Path == "std" {
390 continue
391 }
392 for _, v := range vendorList {
393 if m.Path == v.Path {
394 if m.Version != v.Version {
395 base.Fatalf("go: inconsistent vendoring in %s:\n"+
396 "\tgo.mod requires %s %s but vendor/modules.txt has %s.\n"+
397 "\trun 'go mod tidy; go mod vendor' to sync",
398 modRoot, m.Path, m.Version, v.Version)
399 }
400 continue BuildList
401 }
402 }
403 base.Fatalf("go: inconsistent vendoring in %s:\n"+
404 "\tgo.mod requires %s %s but vendor/modules.txt does not include it.\n"+
405 "\trun 'go mod tidy; go mod vendor' to sync", modRoot, m.Path, m.Version)
406 }
407 cfg.BuildMod = "vendor"
408 }
409
410
411 func Allowed(m module.Version) bool {
412 return !excluded[m]
413 }
414
415 func legacyModInit() {
416 if modFile == nil {
417 path, err := findModulePath(modRoot)
418 if err != nil {
419 base.Fatalf("go: %v", err)
420 }
421 fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path)
422 modFile = new(modfile.File)
423 modFile.AddModuleStmt(path)
424 addGoStmt()
425 }
426
427 for _, name := range altConfigs {
428 cfg := filepath.Join(modRoot, name)
429 data, err := ioutil.ReadFile(cfg)
430 if err == nil {
431 convert := modconv.Converters[name]
432 if convert == nil {
433 return
434 }
435 fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg))
436 cfg = filepath.ToSlash(cfg)
437 if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil {
438 base.Fatalf("go: %v", err)
439 }
440 if len(modFile.Syntax.Stmt) == 1 {
441
442 modFile.AddComment("// go: no requirements found in " + name)
443 }
444 return
445 }
446 }
447 }
448
449
450
451 func addGoStmt() {
452 if modFile.Go != nil && modFile.Go.Version != "" {
453 return
454 }
455 tags := build.Default.ReleaseTags
456 version := tags[len(tags)-1]
457 if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
458 base.Fatalf("go: unrecognized default version %q", version)
459 }
460 if err := modFile.AddGoStmt(version[2:]); err != nil {
461 base.Fatalf("go: internal error: %v", err)
462 }
463 }
464
465 var altConfigs = []string{
466 "Gopkg.lock",
467
468 "GLOCKFILE",
469 "Godeps/Godeps.json",
470 "dependencies.tsv",
471 "glide.lock",
472 "vendor.conf",
473 "vendor.yml",
474 "vendor/manifest",
475 "vendor/vendor.json",
476
477 ".git/config",
478 }
479
480 func findModuleRoot(dir string) (root string) {
481 dir = filepath.Clean(dir)
482
483
484 for {
485 if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
486 return dir
487 }
488 d := filepath.Dir(dir)
489 if d == dir {
490 break
491 }
492 dir = d
493 }
494 return ""
495 }
496
497 func findAltConfig(dir string) (root, name string) {
498 dir = filepath.Clean(dir)
499 for {
500 for _, name := range altConfigs {
501 if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
502 if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel == "." {
503
504 return "", ""
505 }
506 return dir, name
507 }
508 }
509 d := filepath.Dir(dir)
510 if d == dir {
511 break
512 }
513 dir = d
514 }
515 return "", ""
516 }
517
518 func findModulePath(dir string) (string, error) {
519 if CmdModModule != "" {
520
521 if err := module.CheckImportPath(CmdModModule); err != nil {
522 return "", err
523 }
524 return CmdModModule, nil
525 }
526
527
528
529
530
531
532
533
534
535 list, _ := ioutil.ReadDir(dir)
536 for _, info := range list {
537 if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
538 if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
539 return com, nil
540 }
541 }
542 }
543 for _, info1 := range list {
544 if info1.IsDir() {
545 files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name()))
546 for _, info2 := range files {
547 if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
548 if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
549 return path.Dir(com), nil
550 }
551 }
552 }
553 }
554 }
555
556
557 data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
558 var cfg1 struct{ ImportPath string }
559 json.Unmarshal(data, &cfg1)
560 if cfg1.ImportPath != "" {
561 return cfg1.ImportPath, nil
562 }
563
564
565 data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
566 var cfg2 struct{ RootPath string }
567 json.Unmarshal(data, &cfg2)
568 if cfg2.RootPath != "" {
569 return cfg2.RootPath, nil
570 }
571
572
573 for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
574 if gpdir == "" {
575 continue
576 }
577 if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
578 return filepath.ToSlash(rel), nil
579 }
580 }
581
582 msg := `cannot determine module path for source directory %s (outside GOPATH, module path must be specified)
583
584 Example usage:
585 'go mod init example.com/m' to initialize a v0 or v1 module
586 'go mod init example.com/m/v2' to initialize a v2 module
587
588 Run 'go help mod init' for more information.
589 `
590 return "", fmt.Errorf(msg, dir)
591 }
592
593 var (
594 importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
595 )
596
597 func findImportComment(file string) string {
598 data, err := ioutil.ReadFile(file)
599 if err != nil {
600 return ""
601 }
602 m := importCommentRE.FindSubmatch(data)
603 if m == nil {
604 return ""
605 }
606 path, err := strconv.Unquote(string(m[1]))
607 if err != nil {
608 return ""
609 }
610 return path
611 }
612
613 var allowWriteGoMod = true
614
615
616 func DisallowWriteGoMod() {
617 allowWriteGoMod = false
618 }
619
620
621
622
623
624 func AllowWriteGoMod() {
625 allowWriteGoMod = true
626 }
627
628
629
630 func MinReqs() mvs.Reqs {
631 var direct []string
632 for _, m := range buildList[1:] {
633 if loaded.direct[m.Path] {
634 direct = append(direct, m.Path)
635 }
636 }
637 min, err := mvs.Req(Target, buildList, direct, Reqs())
638 if err != nil {
639 base.Fatalf("go: %v", err)
640 }
641 return &mvsReqs{buildList: append([]module.Version{Target}, min...)}
642 }
643
644
645 func WriteGoMod() {
646
647
648
649 if !allowWriteGoMod || cfg.BuildMod == "vendor" {
650 return
651 }
652
653
654 if modRoot == "" {
655 return
656 }
657
658 addGoStmt()
659
660 if loaded != nil {
661 reqs := MinReqs()
662 min, err := reqs.Required(Target)
663 if err != nil {
664 base.Fatalf("go: %v", err)
665 }
666 var list []*modfile.Require
667 for _, m := range min {
668 list = append(list, &modfile.Require{
669 Mod: m,
670 Indirect: !loaded.direct[m.Path],
671 })
672 }
673 modFile.SetRequire(list)
674 }
675
676 modFile.Cleanup()
677 new, err := modFile.Format()
678 if err != nil {
679 base.Fatalf("go: %v", err)
680 }
681
682 dirty := !bytes.Equal(new, modFileData)
683 if dirty && cfg.BuildMod == "readonly" {
684
685
686 base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
687 }
688
689
690 modfetch.WriteGoSum()
691
692 if !dirty {
693
694
695 return
696 }
697
698 unlock := modfetch.SideLock()
699 defer unlock()
700
701 file := filepath.Join(modRoot, "go.mod")
702 old, err := renameio.ReadFile(file)
703 if !bytes.Equal(old, modFileData) {
704 if bytes.Equal(old, new) {
705
706 modFileData = new
707 return
708 }
709 if err != nil {
710 base.Fatalf("go: can't determine whether go.mod has changed: %v", err)
711 }
712
713
714
715
716
717
718 base.Fatalf("go: updates to go.mod needed, but contents have changed")
719
720 }
721
722 if err := renameio.WriteFile(file, new, 0666); err != nil {
723 base.Fatalf("error writing go.mod: %v", err)
724 }
725 modFileData = new
726 }
727
728 func fixVersion(path, vers string) (string, error) {
729
730 if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
731 vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
732 }
733
734
735
736
737 _, pathMajor, ok := module.SplitPathVersion(path)
738 if !ok {
739 return "", &module.ModuleError{
740 Path: path,
741 Err: &module.InvalidVersionError{
742 Version: vers,
743 Err: fmt.Errorf("malformed module path %q", path),
744 },
745 }
746 }
747 if vers != "" && module.CanonicalVersion(vers) == vers {
748 if err := module.MatchPathMajor(vers, pathMajor); err == nil {
749 return vers, nil
750 }
751 }
752
753 info, err := Query(path, vers, "", nil)
754 if err != nil {
755 return "", err
756 }
757 return info.Version, nil
758 }
759
View as plain text