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