]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modload/search.go
cmd/go: changes to use modindex
[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 && 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                         // 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(root, 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                 mod := MainModules.mustGetSingleMainModule()
135                 if modRoot := MainModules.ModRoot(mod); modRoot != "" {
136                         walkPkgs(modRoot, MainModules.PathPrefix(mod), pruneGoMod|pruneVendor)
137                         walkPkgs(filepath.Join(modRoot, "vendor"), "", pruneVendor)
138                 }
139                 return
140         }
141
142         for _, mod := range modules {
143                 if !treeCanMatch(mod.Path) {
144                         continue
145                 }
146
147                 var (
148                         root, modPrefix string
149                         isLocal         bool
150                 )
151                 if MainModules.Contains(mod.Path) {
152                         if MainModules.ModRoot(mod) == "" {
153                                 continue // If there is no main module, we can't search in it.
154                         }
155                         root = MainModules.ModRoot(mod)
156                         modPrefix = MainModules.PathPrefix(mod)
157                         isLocal = true
158                 } else {
159                         var err error
160                         const needSum = true
161                         root, isLocal, err = fetch(ctx, mod, needSum)
162                         if err != nil {
163                                 m.AddError(err)
164                                 continue
165                         }
166                         modPrefix = mod.Path
167                 }
168
169                 prune := pruneVendor
170                 if isLocal {
171                         prune |= pruneGoMod
172                 }
173                 walkPkgs(root, modPrefix, prune)
174         }
175
176         return
177 }
178
179 // MatchInModule identifies the packages matching the given pattern within the
180 // given module version, which does not need to be in the build list or module
181 // requirement graph.
182 //
183 // If m is the zero module.Version, MatchInModule matches the pattern
184 // against the standard library (std and cmd) in GOROOT/src.
185 func MatchInModule(ctx context.Context, pattern string, m module.Version, tags map[string]bool) *search.Match {
186         match := search.NewMatch(pattern)
187         if m == (module.Version{}) {
188                 matchPackages(ctx, match, tags, includeStd, nil)
189         }
190
191         LoadModFile(ctx) // Sets Target, needed by fetch and matchPackages.
192
193         if !match.IsLiteral() {
194                 matchPackages(ctx, match, tags, omitStd, []module.Version{m})
195                 return match
196         }
197
198         const needSum = true
199         root, isLocal, err := fetch(ctx, m, needSum)
200         if err != nil {
201                 match.Errs = []error{err}
202                 return match
203         }
204
205         dir, haveGoFiles, err := dirInModule(pattern, m.Path, root, isLocal)
206         if err != nil {
207                 match.Errs = []error{err}
208                 return match
209         }
210         if haveGoFiles {
211                 if _, _, err := scanDir(root, dir, tags); err != imports.ErrNoGo {
212                         // ErrNoGo indicates that the directory is not actually a Go package,
213                         // perhaps due to the tags in use. Any other non-nil error indicates a
214                         // problem with one or more of the Go source files, but such an error does
215                         // not stop the package from existing, so it has no impact on matching.
216                         match.Pkgs = []string{pattern}
217                 }
218         }
219         return match
220 }