1
2
3
4
5 package modload
6
7 import (
8 "errors"
9 "fmt"
10 "io/fs"
11 "os"
12 "path/filepath"
13 "strings"
14 "sync"
15
16 "cmd/go/internal/base"
17
18 "golang.org/x/mod/module"
19 "golang.org/x/mod/semver"
20 )
21
22 var (
23 vendorOnce sync.Once
24 vendorList []module.Version
25 vendorReplaced []module.Version
26 vendorVersion map[string]string
27 vendorPkgModule map[string]module.Version
28 vendorMeta map[module.Version]vendorMetadata
29 )
30
31 type vendorMetadata struct {
32 Explicit bool
33 Replacement module.Version
34 GoVersion string
35 }
36
37
38 func readVendorList() {
39 vendorOnce.Do(func() {
40 vendorList = nil
41 vendorPkgModule = make(map[string]module.Version)
42 vendorVersion = make(map[string]string)
43 vendorMeta = make(map[module.Version]vendorMetadata)
44 data, err := os.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
45 if err != nil {
46 if !errors.Is(err, fs.ErrNotExist) {
47 base.Fatalf("go: %s", err)
48 }
49 return
50 }
51
52 var mod module.Version
53 for _, line := range strings.Split(string(data), "\n") {
54 if strings.HasPrefix(line, "# ") {
55 f := strings.Fields(line)
56
57 if len(f) < 3 {
58 continue
59 }
60 if semver.IsValid(f[2]) {
61
62
63 mod = module.Version{Path: f[1], Version: f[2]}
64 f = f[3:]
65 } else if f[2] == "=>" {
66
67 mod = module.Version{Path: f[1]}
68 f = f[2:]
69 } else {
70
71
72 mod = module.Version{}
73 continue
74 }
75
76 if len(f) >= 2 && f[0] == "=>" {
77 meta := vendorMeta[mod]
78 if len(f) == 2 {
79
80 meta.Replacement = module.Version{Path: f[1]}
81 vendorReplaced = append(vendorReplaced, mod)
82 } else if len(f) == 3 && semver.IsValid(f[2]) {
83
84 meta.Replacement = module.Version{Path: f[1], Version: f[2]}
85 vendorReplaced = append(vendorReplaced, mod)
86 } else {
87
88 }
89 vendorMeta[mod] = meta
90 }
91 continue
92 }
93
94
95
96 if mod.Path == "" {
97 continue
98 }
99
100 if strings.HasPrefix(line, "## ") {
101
102 meta := vendorMeta[mod]
103 for _, entry := range strings.Split(strings.TrimPrefix(line, "## "), ";") {
104 entry = strings.TrimSpace(entry)
105 if entry == "explicit" {
106 meta.Explicit = true
107 }
108 if strings.HasPrefix(entry, "go ") {
109 meta.GoVersion = strings.TrimPrefix(entry, "go ")
110 rawGoVersion.Store(mod, meta.GoVersion)
111 }
112
113 }
114 vendorMeta[mod] = meta
115 continue
116 }
117
118 if f := strings.Fields(line); len(f) == 1 && module.CheckImportPath(f[0]) == nil {
119
120 vendorPkgModule[f[0]] = mod
121
122
123
124
125 if v, ok := vendorVersion[mod.Path]; !ok || semver.Compare(v, mod.Version) < 0 {
126 vendorList = append(vendorList, mod)
127 vendorVersion[mod.Path] = mod.Version
128 }
129 }
130 }
131 })
132 }
133
134
135
136
137 func checkVendorConsistency() {
138 readVendorList()
139
140 pre114 := false
141 if semver.Compare(index.goVersionV, "v1.14") < 0 {
142
143
144
145 pre114 = true
146 }
147
148 vendErrors := new(strings.Builder)
149 vendErrorf := func(mod module.Version, format string, args ...interface{}) {
150 detail := fmt.Sprintf(format, args...)
151 if mod.Version == "" {
152 fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
153 } else {
154 fmt.Fprintf(vendErrors, "\n\t%s@%s: %s", mod.Path, mod.Version, detail)
155 }
156 }
157
158
159
160 for _, r := range modFile.Require {
161 if !vendorMeta[r.Mod].Explicit {
162 if pre114 {
163
164
165
166
167 if vv, ok := vendorVersion[r.Mod.Path]; ok && vv != r.Mod.Version {
168 vendErrorf(r.Mod, fmt.Sprintf("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s", r.Mod.Path, vv))
169 }
170 } else {
171 vendErrorf(r.Mod, "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt")
172 }
173 }
174 }
175
176 describe := func(m module.Version) string {
177 if m.Version == "" {
178 return m.Path
179 }
180 return m.Path + "@" + m.Version
181 }
182
183
184
185
186
187 for _, r := range modFile.Replace {
188 vr := vendorMeta[r.Old].Replacement
189 if vr == (module.Version{}) {
190 if pre114 && (r.Old.Version == "" || vendorVersion[r.Old.Path] != r.Old.Version) {
191
192
193 } else {
194 vendErrorf(r.Old, "is replaced in go.mod, but not marked as replaced in vendor/modules.txt")
195 }
196 } else if vr != r.New {
197 vendErrorf(r.Old, "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt", describe(r.New), describe(vr))
198 }
199 }
200
201 for _, mod := range vendorList {
202 meta := vendorMeta[mod]
203 if meta.Explicit {
204 if _, inGoMod := index.require[mod]; !inGoMod {
205 vendErrorf(mod, "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod")
206 }
207 }
208 }
209
210 for _, mod := range vendorReplaced {
211 r := Replacement(mod)
212 if r == (module.Version{}) {
213 vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
214 continue
215 }
216 if meta := vendorMeta[mod]; r != meta.Replacement {
217 vendErrorf(mod, "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod", describe(meta.Replacement), describe(r))
218 }
219 }
220
221 if vendErrors.Len() > 0 {
222 base.Fatalf("go: inconsistent vendoring in %s:%s\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor", modRoot, vendErrors)
223 }
224 }
225
View as plain text