github.com/jd-ly/tools@v0.5.7/internal/gocommand/vendor.go (about) 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. 4 5 package gocommand 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "os" 12 "path/filepath" 13 "regexp" 14 "strings" 15 16 "golang.org/x/mod/semver" 17 ) 18 19 // ModuleJSON holds information about a module. 20 type ModuleJSON struct { 21 Path string // module path 22 Replace *ModuleJSON // replaced by this module 23 Main bool // is this the main module? 24 Indirect bool // is this module only an indirect dependency of main module? 25 Dir string // directory holding files for this module, if any 26 GoMod string // path to go.mod file for this module, if any 27 GoVersion string // go version used in module 28 } 29 30 var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) 31 32 // VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands 33 // with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields, 34 // of which only Verb and Args are modified to run the appropriate Go command. 35 // Inspired by setDefaultBuildMod in modload/init.go 36 func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { 37 mainMod, go114, err := getMainModuleAnd114(ctx, inv, r) 38 if err != nil { 39 return nil, false, err 40 } 41 42 // We check the GOFLAGS to see if there is anything overridden or not. 43 inv.Verb = "env" 44 inv.Args = []string{"GOFLAGS"} 45 stdout, err := r.Run(ctx, inv) 46 if err != nil { 47 return nil, false, err 48 } 49 goflags := string(bytes.TrimSpace(stdout.Bytes())) 50 matches := modFlagRegexp.FindStringSubmatch(goflags) 51 var modFlag string 52 if len(matches) != 0 { 53 modFlag = matches[1] 54 } 55 if modFlag != "" { 56 // Don't override an explicit '-mod=' argument. 57 return mainMod, modFlag == "vendor", nil 58 } 59 if mainMod == nil || !go114 { 60 return mainMod, false, nil 61 } 62 // Check 1.14's automatic vendor mode. 63 if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { 64 if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { 65 // The Go version is at least 1.14, and a vendor directory exists. 66 // Set -mod=vendor by default. 67 return mainMod, true, nil 68 } 69 } 70 return mainMod, false, nil 71 } 72 73 // getMainModuleAnd114 gets the main module's information and whether the 74 // go command in use is 1.14+. This is the information needed to figure out 75 // if vendoring should be enabled. 76 func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { 77 const format = `{{.Path}} 78 {{.Dir}} 79 {{.GoMod}} 80 {{.GoVersion}} 81 {{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} 82 ` 83 inv.Verb = "list" 84 inv.Args = []string{"-m", "-f", format} 85 stdout, err := r.Run(ctx, inv) 86 if err != nil { 87 return nil, false, err 88 } 89 90 lines := strings.Split(stdout.String(), "\n") 91 if len(lines) < 5 { 92 return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String()) 93 } 94 mod := &ModuleJSON{ 95 Path: lines[0], 96 Dir: lines[1], 97 GoMod: lines[2], 98 GoVersion: lines[3], 99 Main: true, 100 } 101 return mod, lines[4] == "go1.14", nil 102 }