github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/go/internal/modload/init.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package modload
     6  
     7  import (
     8  	"bytes"
     9  	"cmd/go/internal/base"
    10  	"cmd/go/internal/cache"
    11  	"cmd/go/internal/cfg"
    12  	"cmd/go/internal/load"
    13  	"cmd/go/internal/modconv"
    14  	"cmd/go/internal/modfetch"
    15  	"cmd/go/internal/modfetch/codehost"
    16  	"cmd/go/internal/modfile"
    17  	"cmd/go/internal/module"
    18  	"cmd/go/internal/mvs"
    19  	"cmd/go/internal/search"
    20  	"encoding/json"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"regexp"
    27  	"runtime"
    28  	"strconv"
    29  	"strings"
    30  )
    31  
    32  var (
    33  	cwd            string
    34  	MustUseModules = mustUseModules()
    35  	initialized    bool
    36  
    37  	ModRoot  string
    38  	modFile  *modfile.File
    39  	excluded map[module.Version]bool
    40  	Target   module.Version
    41  
    42  	gopath string
    43  
    44  	CmdModInit   bool   // running 'go mod init'
    45  	CmdModModule string // module argument for 'go mod init'
    46  )
    47  
    48  // ModFile returns the parsed go.mod file.
    49  //
    50  // Note that after calling ImportPaths or LoadBuildList,
    51  // the require statements in the modfile.File are no longer
    52  // the source of truth and will be ignored: edits made directly
    53  // will be lost at the next call to WriteGoMod.
    54  // To make permanent changes to the require statements
    55  // in go.mod, edit it before calling ImportPaths or LoadBuildList.
    56  func ModFile() *modfile.File {
    57  	return modFile
    58  }
    59  
    60  func BinDir() string {
    61  	MustInit()
    62  	return filepath.Join(gopath, "bin")
    63  }
    64  
    65  // mustUseModules reports whether we are invoked as vgo
    66  // (as opposed to go).
    67  // If so, we only support builds with go.mod files.
    68  func mustUseModules() bool {
    69  	name := os.Args[0]
    70  	name = name[strings.LastIndex(name, "/")+1:]
    71  	name = name[strings.LastIndex(name, `\`)+1:]
    72  	return strings.HasPrefix(name, "vgo")
    73  }
    74  
    75  var inGOPATH bool // running in GOPATH/src
    76  
    77  func Init() {
    78  	if initialized {
    79  		return
    80  	}
    81  	initialized = true
    82  
    83  	env := os.Getenv("GO111MODULE")
    84  	switch env {
    85  	default:
    86  		base.Fatalf("go: unknown environment setting GO111MODULE=%s", env)
    87  	case "", "auto":
    88  		// leave MustUseModules alone
    89  	case "on":
    90  		MustUseModules = true
    91  	case "off":
    92  		if !MustUseModules {
    93  			return
    94  		}
    95  	}
    96  
    97  	// Disable any prompting for passwords by Git.
    98  	// Only has an effect for 2.3.0 or later, but avoiding
    99  	// the prompt in earlier versions is just too hard.
   100  	// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
   101  	// prompting.
   102  	// See golang.org/issue/9341 and golang.org/issue/12706.
   103  	if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
   104  		os.Setenv("GIT_TERMINAL_PROMPT", "0")
   105  	}
   106  
   107  	// Disable any ssh connection pooling by Git.
   108  	// If a Git subprocess forks a child into the background to cache a new connection,
   109  	// that child keeps stdout/stderr open. After the Git subprocess exits,
   110  	// os /exec expects to be able to read from the stdout/stderr pipe
   111  	// until EOF to get all the data that the Git subprocess wrote before exiting.
   112  	// The EOF doesn't come until the child exits too, because the child
   113  	// is holding the write end of the pipe.
   114  	// This is unfortunate, but it has come up at least twice
   115  	// (see golang.org/issue/13453 and golang.org/issue/16104)
   116  	// and confuses users when it does.
   117  	// If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND,
   118  	// assume they know what they are doing and don't step on it.
   119  	// But default to turning off ControlMaster.
   120  	if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" {
   121  		os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no")
   122  	}
   123  
   124  	var err error
   125  	cwd, err = os.Getwd()
   126  	if err != nil {
   127  		base.Fatalf("go: %v", err)
   128  	}
   129  
   130  	inGOPATH = false
   131  	for _, gopath := range filepath.SplitList(cfg.BuildContext.GOPATH) {
   132  		if gopath == "" {
   133  			continue
   134  		}
   135  		if search.InDir(cwd, filepath.Join(gopath, "src")) != "" {
   136  			inGOPATH = true
   137  			break
   138  		}
   139  	}
   140  
   141  	if inGOPATH && !MustUseModules {
   142  		// No automatic enabling in GOPATH.
   143  		if root, _ := FindModuleRoot(cwd, "", false); root != "" {
   144  			cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
   145  		}
   146  		return
   147  	}
   148  
   149  	if CmdModInit {
   150  		// Running 'go mod init': go.mod will be created in current directory.
   151  		ModRoot = cwd
   152  	} else {
   153  		ModRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
   154  		if !MustUseModules {
   155  			if ModRoot == "" {
   156  				return
   157  			}
   158  			if search.InDir(ModRoot, os.TempDir()) == "." {
   159  				// If you create /tmp/go.mod for experimenting,
   160  				// then any tests that create work directories under /tmp
   161  				// will find it and get modules when they're not expecting them.
   162  				// It's a bit of a peculiar thing to disallow but quite mysterious
   163  				// when it happens. See golang.org/issue/26708.
   164  				ModRoot = ""
   165  				fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
   166  				return
   167  			}
   168  		}
   169  	}
   170  
   171  	cfg.ModulesEnabled = true
   172  	load.ModBinDir = BinDir
   173  	load.ModLookup = Lookup
   174  	load.ModPackageModuleInfo = PackageModuleInfo
   175  	load.ModImportPaths = ImportPaths
   176  	load.ModPackageBuildInfo = PackageBuildInfo
   177  	load.ModInfoProg = ModInfoProg
   178  	load.ModImportFromFiles = ImportFromFiles
   179  	load.ModDirImportPath = DirImportPath
   180  
   181  	search.SetModRoot(ModRoot)
   182  }
   183  
   184  func init() {
   185  	load.ModInit = Init
   186  
   187  	// Set modfetch.PkgMod unconditionally, so that go clean -modcache can run even without modules enabled.
   188  	if list := filepath.SplitList(cfg.BuildContext.GOPATH); len(list) > 0 && list[0] != "" {
   189  		modfetch.PkgMod = filepath.Join(list[0], "pkg/mod")
   190  	}
   191  }
   192  
   193  // Enabled reports whether modules are (or must be) enabled.
   194  // If modules must be enabled but are not, Enabled returns true
   195  // and then the first use of module information will call die
   196  // (usually through InitMod and MustInit).
   197  func Enabled() bool {
   198  	if !initialized {
   199  		panic("go: Enabled called before Init")
   200  	}
   201  	return ModRoot != "" || MustUseModules
   202  }
   203  
   204  // MustInit calls Init if needed and checks that
   205  // modules are enabled and the main module has been found.
   206  // If not, MustInit calls base.Fatalf with an appropriate message.
   207  func MustInit() {
   208  	if Init(); ModRoot == "" {
   209  		die()
   210  	}
   211  	if c := cache.Default(); c == nil {
   212  		// With modules, there are no install locations for packages
   213  		// other than the build cache.
   214  		base.Fatalf("go: cannot use modules with build cache disabled")
   215  	}
   216  }
   217  
   218  // Failed reports whether module loading failed.
   219  // If Failed returns true, then any use of module information will call die.
   220  func Failed() bool {
   221  	Init()
   222  	return cfg.ModulesEnabled && ModRoot == ""
   223  }
   224  
   225  func die() {
   226  	if os.Getenv("GO111MODULE") == "off" {
   227  		base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
   228  	}
   229  	if inGOPATH && !MustUseModules {
   230  		base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
   231  	}
   232  	base.Fatalf("go: cannot find main module; see 'go help modules'")
   233  }
   234  
   235  func InitMod() {
   236  	MustInit()
   237  	if modFile != nil {
   238  		return
   239  	}
   240  
   241  	list := filepath.SplitList(cfg.BuildContext.GOPATH)
   242  	if len(list) == 0 || list[0] == "" {
   243  		base.Fatalf("missing $GOPATH")
   244  	}
   245  	gopath = list[0]
   246  	if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
   247  		base.Fatalf("$GOPATH/go.mod exists but should not")
   248  	}
   249  
   250  	oldSrcMod := filepath.Join(list[0], "src/mod")
   251  	pkgMod := filepath.Join(list[0], "pkg/mod")
   252  	infoOld, errOld := os.Stat(oldSrcMod)
   253  	_, errMod := os.Stat(pkgMod)
   254  	if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
   255  		os.Rename(oldSrcMod, pkgMod)
   256  	}
   257  
   258  	modfetch.PkgMod = pkgMod
   259  	modfetch.GoSumFile = filepath.Join(ModRoot, "go.sum")
   260  	codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
   261  
   262  	if CmdModInit {
   263  		// Running go mod init: do legacy module conversion
   264  		legacyModInit()
   265  		modFileToBuildList()
   266  		WriteGoMod()
   267  		return
   268  	}
   269  
   270  	gomod := filepath.Join(ModRoot, "go.mod")
   271  	data, err := ioutil.ReadFile(gomod)
   272  	if err != nil {
   273  		if os.IsNotExist(err) {
   274  			legacyModInit()
   275  			modFileToBuildList()
   276  			WriteGoMod()
   277  			return
   278  		}
   279  		base.Fatalf("go: %v", err)
   280  	}
   281  
   282  	f, err := modfile.Parse(gomod, data, fixVersion)
   283  	if err != nil {
   284  		// Errors returned by modfile.Parse begin with file:line.
   285  		base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
   286  	}
   287  	modFile = f
   288  
   289  	if len(f.Syntax.Stmt) == 0 || f.Module == nil {
   290  		// Empty mod file. Must add module path.
   291  		path, err := FindModulePath(ModRoot)
   292  		if err != nil {
   293  			base.Fatalf("go: %v", err)
   294  		}
   295  		f.AddModuleStmt(path)
   296  	}
   297  
   298  	if len(f.Syntax.Stmt) == 1 && f.Module != nil {
   299  		// Entire file is just a module statement.
   300  		// Populate require if possible.
   301  		legacyModInit()
   302  	}
   303  
   304  	excluded = make(map[module.Version]bool)
   305  	for _, x := range f.Exclude {
   306  		excluded[x.Mod] = true
   307  	}
   308  	modFileToBuildList()
   309  	WriteGoMod()
   310  }
   311  
   312  // modFileToBuildList initializes buildList from the modFile.
   313  func modFileToBuildList() {
   314  	Target = modFile.Module.Mod
   315  	list := []module.Version{Target}
   316  	for _, r := range modFile.Require {
   317  		list = append(list, r.Mod)
   318  	}
   319  	buildList = list
   320  }
   321  
   322  // Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
   323  func Allowed(m module.Version) bool {
   324  	return !excluded[m]
   325  }
   326  
   327  func legacyModInit() {
   328  	if modFile == nil {
   329  		path, err := FindModulePath(ModRoot)
   330  		if err != nil {
   331  			base.Fatalf("go: %v", err)
   332  		}
   333  		fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", path)
   334  		modFile = new(modfile.File)
   335  		modFile.AddModuleStmt(path)
   336  	}
   337  
   338  	for _, name := range altConfigs {
   339  		cfg := filepath.Join(ModRoot, name)
   340  		data, err := ioutil.ReadFile(cfg)
   341  		if err == nil {
   342  			convert := modconv.Converters[name]
   343  			if convert == nil {
   344  				return
   345  			}
   346  			fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(cfg))
   347  			cfg = filepath.ToSlash(cfg)
   348  			if err := modconv.ConvertLegacyConfig(modFile, cfg, data); err != nil {
   349  				base.Fatalf("go: %v", err)
   350  			}
   351  			if len(modFile.Syntax.Stmt) == 1 {
   352  				// Add comment to avoid re-converting every time it runs.
   353  				modFile.AddComment("// go: no requirements found in " + name)
   354  			}
   355  			return
   356  		}
   357  	}
   358  }
   359  
   360  var altConfigs = []string{
   361  	"Gopkg.lock",
   362  
   363  	"GLOCKFILE",
   364  	"Godeps/Godeps.json",
   365  	"dependencies.tsv",
   366  	"glide.lock",
   367  	"vendor.conf",
   368  	"vendor.yml",
   369  	"vendor/manifest",
   370  	"vendor/vendor.json",
   371  
   372  	".git/config",
   373  }
   374  
   375  // Exported only for testing.
   376  func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string) {
   377  	dir = filepath.Clean(dir)
   378  	dir1 := dir
   379  	limit = filepath.Clean(limit)
   380  
   381  	// Look for enclosing go.mod.
   382  	for {
   383  		if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !(runtime.GOOS == "plan9" && fi.IsDir()) {
   384  			return dir, "go.mod"
   385  		}
   386  		if dir == limit {
   387  			break
   388  		}
   389  		d := filepath.Dir(dir)
   390  		if d == dir {
   391  			break
   392  		}
   393  		dir = d
   394  	}
   395  
   396  	// Failing that, look for enclosing alternate version config.
   397  	if legacyConfigOK {
   398  		dir = dir1
   399  		for {
   400  			for _, name := range altConfigs {
   401  				if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !(runtime.GOOS == "plan9" && fi.IsDir()) {
   402  					return dir, name
   403  				}
   404  			}
   405  			if dir == limit {
   406  				break
   407  			}
   408  			d := filepath.Dir(dir)
   409  			if d == dir {
   410  				break
   411  			}
   412  			dir = d
   413  		}
   414  	}
   415  
   416  	return "", ""
   417  }
   418  
   419  // Exported only for testing.
   420  func FindModulePath(dir string) (string, error) {
   421  	if CmdModModule != "" {
   422  		// Running go mod init x/y/z; return x/y/z.
   423  		return CmdModModule, nil
   424  	}
   425  
   426  	// Cast about for import comments,
   427  	// first in top-level directory, then in subdirectories.
   428  	list, _ := ioutil.ReadDir(dir)
   429  	for _, info := range list {
   430  		if info.Mode().IsRegular() && strings.HasSuffix(info.Name(), ".go") {
   431  			if com := findImportComment(filepath.Join(dir, info.Name())); com != "" {
   432  				return com, nil
   433  			}
   434  		}
   435  	}
   436  	for _, info1 := range list {
   437  		if info1.IsDir() {
   438  			files, _ := ioutil.ReadDir(filepath.Join(dir, info1.Name()))
   439  			for _, info2 := range files {
   440  				if info2.Mode().IsRegular() && strings.HasSuffix(info2.Name(), ".go") {
   441  					if com := findImportComment(filepath.Join(dir, info1.Name(), info2.Name())); com != "" {
   442  						return path.Dir(com), nil
   443  					}
   444  				}
   445  			}
   446  		}
   447  	}
   448  
   449  	// Look for Godeps.json declaring import path.
   450  	data, _ := ioutil.ReadFile(filepath.Join(dir, "Godeps/Godeps.json"))
   451  	var cfg1 struct{ ImportPath string }
   452  	json.Unmarshal(data, &cfg1)
   453  	if cfg1.ImportPath != "" {
   454  		return cfg1.ImportPath, nil
   455  	}
   456  
   457  	// Look for vendor.json declaring import path.
   458  	data, _ = ioutil.ReadFile(filepath.Join(dir, "vendor/vendor.json"))
   459  	var cfg2 struct{ RootPath string }
   460  	json.Unmarshal(data, &cfg2)
   461  	if cfg2.RootPath != "" {
   462  		return cfg2.RootPath, nil
   463  	}
   464  
   465  	// Look for path in GOPATH.
   466  	for _, gpdir := range filepath.SplitList(cfg.BuildContext.GOPATH) {
   467  		if gpdir == "" {
   468  			continue
   469  		}
   470  		if rel := search.InDir(dir, filepath.Join(gpdir, "src")); rel != "" && rel != "." {
   471  			return filepath.ToSlash(rel), nil
   472  		}
   473  	}
   474  
   475  	// Look for .git/config with github origin as last resort.
   476  	data, _ = ioutil.ReadFile(filepath.Join(dir, ".git/config"))
   477  	if m := gitOriginRE.FindSubmatch(data); m != nil {
   478  		return "github.com/" + string(m[1]), nil
   479  	}
   480  
   481  	return "", fmt.Errorf("cannot determine module path for source directory %s (outside GOPATH, no import comments)", dir)
   482  }
   483  
   484  var (
   485  	gitOriginRE     = regexp.MustCompile(`(?m)^\[remote "origin"\]\r?\n\turl = (?:https://github.com/|git@github.com:|gh:)([^/]+/[^/]+?)(\.git)?\r?\n`)
   486  	importCommentRE = regexp.MustCompile(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
   487  )
   488  
   489  func findImportComment(file string) string {
   490  	data, err := ioutil.ReadFile(file)
   491  	if err != nil {
   492  		return ""
   493  	}
   494  	m := importCommentRE.FindSubmatch(data)
   495  	if m == nil {
   496  		return ""
   497  	}
   498  	path, err := strconv.Unquote(string(m[1]))
   499  	if err != nil {
   500  		return ""
   501  	}
   502  	return path
   503  }
   504  
   505  var allowWriteGoMod = true
   506  
   507  // DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all.
   508  func DisallowWriteGoMod() {
   509  	allowWriteGoMod = false
   510  }
   511  
   512  // AllowWriteGoMod undoes the effect of DisallowWriteGoMod:
   513  // future calls to WriteGoMod will update go.mod if needed.
   514  // Note that any past calls have been discarded, so typically
   515  // a call to AlowWriteGoMod should be followed by a call to WriteGoMod.
   516  func AllowWriteGoMod() {
   517  	allowWriteGoMod = true
   518  }
   519  
   520  // MinReqs returns a Reqs with minimal dependencies of Target,
   521  // as will be written to go.mod.
   522  func MinReqs() mvs.Reqs {
   523  	var direct []string
   524  	for _, m := range buildList[1:] {
   525  		if loaded.direct[m.Path] {
   526  			direct = append(direct, m.Path)
   527  		}
   528  	}
   529  	min, err := mvs.Req(Target, buildList, direct, Reqs())
   530  	if err != nil {
   531  		base.Fatalf("go: %v", err)
   532  	}
   533  	return &mvsReqs{buildList: append([]module.Version{Target}, min...)}
   534  }
   535  
   536  // WriteGoMod writes the current build list back to go.mod.
   537  func WriteGoMod() {
   538  	// If we're using -mod=vendor we basically ignored
   539  	// go.mod, so definitely don't try to write back our
   540  	// incomplete view of the world.
   541  	if !allowWriteGoMod || cfg.BuildMod == "vendor" {
   542  		return
   543  	}
   544  
   545  	if loaded != nil {
   546  		reqs := MinReqs()
   547  		min, err := reqs.Required(Target)
   548  		if err != nil {
   549  			base.Fatalf("go: %v", err)
   550  		}
   551  		var list []*modfile.Require
   552  		for _, m := range min {
   553  			list = append(list, &modfile.Require{
   554  				Mod:      m,
   555  				Indirect: !loaded.direct[m.Path],
   556  			})
   557  		}
   558  		modFile.SetRequire(list)
   559  	}
   560  
   561  	file := filepath.Join(ModRoot, "go.mod")
   562  	old, _ := ioutil.ReadFile(file)
   563  	modFile.Cleanup() // clean file after edits
   564  	new, err := modFile.Format()
   565  	if err != nil {
   566  		base.Fatalf("go: %v", err)
   567  	}
   568  	if !bytes.Equal(old, new) {
   569  		if cfg.BuildMod == "readonly" {
   570  			base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
   571  		}
   572  		if err := ioutil.WriteFile(file, new, 0666); err != nil {
   573  			base.Fatalf("go: %v", err)
   574  		}
   575  	}
   576  	modfetch.WriteGoSum()
   577  }
   578  
   579  func fixVersion(path, vers string) (string, error) {
   580  	// Special case: remove the old -gopkgin- hack.
   581  	if strings.HasPrefix(path, "gopkg.in/") && strings.Contains(vers, "-gopkgin-") {
   582  		vers = vers[strings.Index(vers, "-gopkgin-")+len("-gopkgin-"):]
   583  	}
   584  
   585  	// fixVersion is called speculatively on every
   586  	// module, version pair from every go.mod file.
   587  	// Avoid the query if it looks OK.
   588  	_, pathMajor, ok := module.SplitPathVersion(path)
   589  	if !ok {
   590  		return "", fmt.Errorf("malformed module path: %s", path)
   591  	}
   592  	if vers != "" && module.CanonicalVersion(vers) == vers && module.MatchPathMajor(vers, pathMajor) {
   593  		return vers, nil
   594  	}
   595  
   596  	info, err := Query(path, vers, nil)
   597  	if err != nil {
   598  		return "", err
   599  	}
   600  	return info.Version, nil
   601  }