]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/modload/search.go
[dev.typeparams] merge 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 := os.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                         root, isLocal, err = fetch(ctx, mod)
160                         if err != nil {
161                                 m.AddError(err)
162                                 continue
163                         }
164                         modPrefix = mod.Path
165                 }
166
167                 prune := pruneVendor
168                 if isLocal {
169                         prune |= pruneGoMod
170                 }
171                 walkPkgs(root, modPrefix, prune)
172         }
173
174         return
175 }