]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modload/search.go
[dev.typeparams] Merge branch 'master' into dev.typeparams
[gostls13.git] / src / cmd / go / internal / modload / search.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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 // matchPackages is like m.MatchPackages, but uses a local variable (rather than
31 // a global) for tags, can include or exclude packages in the standard library,
32 // and is restricted to the given list of modules.
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, // ignore pseudo-package that exists only for documentation
45         }
46         if !cfg.BuildContext.CgoEnabled {
47                 have["runtime/cgo"] = true // ignore during walk
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                         // Don't use GOROOT/src but do walk down into it.
68                         if path == root {
69                                 if importPathRoot == "" {
70                                         return nil
71                                 }
72                         } else {
73                                 // Avoid .foo, _foo, and testdata subdirectory trees.
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:] // cut leading slash
83                         }
84                         if !treeCanMatch(name) {
85                                 want = false
86                         }
87
88                         if !fi.IsDir() {
89                                 if fi.Mode()&fs.ModeSymlink != 0 && want {
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                         // Stop at module boundaries.
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 // If there is no main module, we can't search in it.
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 // MatchInModule identifies the packages matching the given pattern within the
179 // given module version, which does not need to be in the build list or module
180 // requirement graph.
181 //
182 // If m is the zero module.Version, MatchInModule matches the pattern
183 // against the standard library (std and cmd) in GOROOT/src.
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                         // ErrNoGo indicates that the directory is not actually a Go package,
212                         // perhaps due to the tags in use. Any other non-nil error indicates a
213                         // problem with one or more of the Go source files, but such an error does
214                         // not stop the package from existing, so it has no impact on matching.
215                         match.Pkgs = []string{pattern}
216                 }
217         }
218         return match
219 }