1 // Copyright 2020 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.
17 "golang.org/x/mod/semver"
20 // ModuleJSON holds information about a module.
21 type ModuleJSON struct {
22 Path string // module path
23 Version string // module version
24 Versions []string // available module versions (with -versions)
25 Replace *ModuleJSON // replaced by this module
26 Time *time.Time // time version was created
27 Update *ModuleJSON // available update, if any (with -u)
28 Main bool // is this the main module?
29 Indirect bool // is this module only an indirect dependency of main module?
30 Dir string // directory holding files for this module, if any
31 GoMod string // path to go.mod file used when loading this module, if any
32 GoVersion string // go version used in module
35 var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
37 // VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
38 // with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
39 // of which only Verb and Args are modified to run the appropriate Go command.
40 // Inspired by setDefaultBuildMod in modload/init.go
41 func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
42 mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
44 return nil, false, err
47 // We check the GOFLAGS to see if there is anything overridden or not.
49 inv.Args = []string{"GOFLAGS"}
50 stdout, err := r.Run(ctx, inv)
52 return nil, false, err
54 goflags := string(bytes.TrimSpace(stdout.Bytes()))
55 matches := modFlagRegexp.FindStringSubmatch(goflags)
57 if len(matches) != 0 {
61 // Don't override an explicit '-mod=' argument.
62 return mainMod, modFlag == "vendor", nil
64 if mainMod == nil || !go114 {
65 return mainMod, false, nil
67 // Check 1.14's automatic vendor mode.
68 if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
69 if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
70 // The Go version is at least 1.14, and a vendor directory exists.
71 // Set -mod=vendor by default.
72 return mainMod, true, nil
75 return mainMod, false, nil
78 // getMainModuleAnd114 gets the main module's information and whether the
79 // go command in use is 1.14+. This is the information needed to figure out
80 // if vendoring should be enabled.
81 func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
82 const format = `{{.Path}}
86 {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
89 inv.Args = []string{"-m", "-f", format}
90 stdout, err := r.Run(ctx, inv)
92 return nil, false, err
95 lines := strings.Split(stdout.String(), "\n")
97 return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
106 return mod, lines[4] == "go1.14", nil