1
2
3
4
5 package modload
6
7 import (
8 "context"
9 "fmt"
10 "io/fs"
11 "os"
12 "path/filepath"
13 "strings"
14
15 "cmd/go/internal/cfg"
16 "cmd/go/internal/fsys"
17 "cmd/go/internal/imports"
18 "cmd/go/internal/search"
19
20 "golang.org/x/mod/module"
21 )
22
23 type stdFilter int8
24
25 const (
26 omitStd = stdFilter(iota)
27 includeStd
28 )
29
30
31
32
33 func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, filter stdFilter, modules []module.Version) {
34 m.Pkgs = []string{}
35
36 isMatch := func(string) bool { return true }
37 treeCanMatch := func(string) bool { return true }
38 if !m.IsMeta() {
39 isMatch = search.MatchPattern(m.Pattern())
40 treeCanMatch = search.TreeCanMatchPattern(m.Pattern())
41 }
42
43 have := map[string]bool{
44 "builtin": true,
45 }
46 if !cfg.BuildContext.CgoEnabled {
47 have["runtime/cgo"] = true
48 }
49
50 type pruning int8
51 const (
52 pruneVendor = pruning(1 << iota)
53 pruneGoMod
54 )
55
56 walkPkgs := func(root, importPathRoot string, prune pruning) {
57 root = filepath.Clean(root)
58 err := fsys.Walk(root, func(path string, fi fs.FileInfo, err error) error {
59 if err != nil {
60 m.AddError(err)
61 return nil
62 }
63
64 want := true
65 elem := ""
66
67
68 if path == root {
69 if importPathRoot == "" {
70 return nil
71 }
72 } else {
73
74 _, elem = filepath.Split(path)
75 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
76 want = false
77 }
78 }
79
80 name := importPathRoot + filepath.ToSlash(path[len(root):])
81 if importPathRoot == "" {
82 name = name[1:]
83 }
84 if !treeCanMatch(name) {
85 want = false
86 }
87
88 if !fi.IsDir() {
89 if fi.Mode()&fs.ModeSymlink != 0 && want && strings.Contains(m.Pattern(), "...") {
90 if target, err := fsys.Stat(path); err == nil && target.IsDir() {
91 fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
92 }
93 }
94 return nil
95 }
96
97 if !want {
98 return filepath.SkipDir
99 }
100
101 if (prune&pruneGoMod != 0) && path != root {
102 if fi, err := os.Stat(filepath.Join(path, "go.mod")); err == nil && !fi.IsDir() {
103 return filepath.SkipDir
104 }
105 }
106
107 if !have[name] {
108 have[name] = true
109 if isMatch(name) {
110 if _, _, err := scanDir(path, tags); err != imports.ErrNoGo {
111 m.Pkgs = append(m.Pkgs, name)
112 }
113 }
114 }
115
116 if elem == "vendor" && (prune&pruneVendor != 0) {
117 return filepath.SkipDir
118 }
119 return nil
120 })
121 if err != nil {
122 m.AddError(err)
123 }
124 }
125
126 if filter == includeStd {
127 walkPkgs(cfg.GOROOTsrc, "", pruneGoMod)
128 if treeCanMatch("cmd") {
129 walkPkgs(filepath.Join(cfg.GOROOTsrc, "cmd"), "cmd", pruneGoMod)
130 }
131 }
132
133 if cfg.BuildMod == "vendor" {
134 if HasModRoot() {
135 walkPkgs(ModRoot(), targetPrefix, pruneGoMod|pruneVendor)
136 walkPkgs(filepath.Join(ModRoot(), "vendor"), "", pruneVendor)
137 }
138 return
139 }
140
141 for _, mod := range modules {
142 if !treeCanMatch(mod.Path) {
143 continue
144 }
145
146 var (
147 root, modPrefix string
148 isLocal bool
149 )
150 if mod == Target {
151 if !HasModRoot() {
152 continue
153 }
154 root = ModRoot()
155 modPrefix = targetPrefix
156 isLocal = true
157 } else {
158 var err error
159 const needSum = true
160 root, isLocal, err = fetch(ctx, mod, needSum)
161 if err != nil {
162 m.AddError(err)
163 continue
164 }
165 modPrefix = mod.Path
166 }
167
168 prune := pruneVendor
169 if isLocal {
170 prune |= pruneGoMod
171 }
172 walkPkgs(root, modPrefix, prune)
173 }
174
175 return
176 }
177
178
179
180
181
182
183
184 func MatchInModule(ctx context.Context, pattern string, m module.Version, tags map[string]bool) *search.Match {
185 match := search.NewMatch(pattern)
186 if m == (module.Version{}) {
187 matchPackages(ctx, match, tags, includeStd, nil)
188 }
189
190 LoadModFile(ctx)
191
192 if !match.IsLiteral() {
193 matchPackages(ctx, match, tags, omitStd, []module.Version{m})
194 return match
195 }
196
197 const needSum = true
198 root, isLocal, err := fetch(ctx, m, needSum)
199 if err != nil {
200 match.Errs = []error{err}
201 return match
202 }
203
204 dir, haveGoFiles, err := dirInModule(pattern, m.Path, root, isLocal)
205 if err != nil {
206 match.Errs = []error{err}
207 return match
208 }
209 if haveGoFiles {
210 if _, _, err := scanDir(dir, tags); err != imports.ErrNoGo {
211
212
213
214
215 match.Pkgs = []string{pattern}
216 }
217 }
218 return match
219 }
220
View as plain text