summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/tools/internal/gocommand/vendor.go
blob: 2d3d408c0bed3f2ce5fd6fe52ec280b3e3848e4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocommand

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"golang.org/x/mod/semver"
)

// ModuleJSON holds information about a module.
type ModuleJSON struct {
	Path      string      // module path
	Version   string      // module version
	Versions  []string    // available module versions (with -versions)
	Replace   *ModuleJSON // replaced by this module
	Time      *time.Time  // time version was created
	Update    *ModuleJSON // available update, if any (with -u)
	Main      bool        // is this the main module?
	Indirect  bool        // is this module only an indirect dependency of main module?
	Dir       string      // directory holding files for this module, if any
	GoMod     string      // path to go.mod file used when loading this module, if any
	GoVersion string      // go version used in module
}

var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)

// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
// of which only Verb and Args are modified to run the appropriate Go command.
// Inspired by setDefaultBuildMod in modload/init.go
func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, *ModuleJSON, error) {
	mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
	if err != nil {
		return false, nil, err
	}

	// We check the GOFLAGS to see if there is anything overridden or not.
	inv.Verb = "env"
	inv.Args = []string{"GOFLAGS"}
	stdout, err := r.Run(ctx, inv)
	if err != nil {
		return false, nil, err
	}
	goflags := string(bytes.TrimSpace(stdout.Bytes()))
	matches := modFlagRegexp.FindStringSubmatch(goflags)
	var modFlag string
	if len(matches) != 0 {
		modFlag = matches[1]
	}
	// Don't override an explicit '-mod=' argument.
	if modFlag == "vendor" {
		return true, mainMod, nil
	} else if modFlag != "" {
		return false, nil, nil
	}
	if mainMod == nil || !go114 {
		return false, nil, nil
	}
	// Check 1.14's automatic vendor mode.
	if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
		if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
			// The Go version is at least 1.14, and a vendor directory exists.
			// Set -mod=vendor by default.
			return true, mainMod, nil
		}
	}
	return false, nil, nil
}

// getMainModuleAnd114 gets one of the main modules' information and whether the
// go command in use is 1.14+. This is the information needed to figure out
// if vendoring should be enabled.
func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
	const format = `{{.Path}}
{{.Dir}}
{{.GoMod}}
{{.GoVersion}}
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
`
	inv.Verb = "list"
	inv.Args = []string{"-m", "-f", format}
	stdout, err := r.Run(ctx, inv)
	if err != nil {
		return nil, false, err
	}

	lines := strings.Split(stdout.String(), "\n")
	if len(lines) < 5 {
		return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
	}
	mod := &ModuleJSON{
		Path:      lines[0],
		Dir:       lines[1],
		GoMod:     lines[2],
		GoVersion: lines[3],
		Main:      true,
	}
	return mod, lines[4] == "go1.14", nil
}