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