Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 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 ( |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 8 | "context" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 9 | "fmt" |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 10 | "io/fs" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 11 | "os" |
| 12 | "path/filepath" |
| 13 | "strings" |
| 14 | |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 15 | "cmd/go/internal/cfg" |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 16 | "cmd/go/internal/fsys" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 17 | "cmd/go/internal/imports" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 18 | "cmd/go/internal/search" |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 19 | |
| 20 | "golang.org/x/mod/module" |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 21 | ) |
| 22 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 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. |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 33 | func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, filter stdFilter, modules []module.Version) { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 34 | m.Pkgs = []string{} |
| 35 | |
| 36 | isMatch := func(string) bool { return true } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 37 | treeCanMatch := func(string) bool { return true } |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 38 | if !m.IsMeta() { |
| 39 | isMatch = search.MatchPattern(m.Pattern()) |
| 40 | treeCanMatch = search.TreeCanMatchPattern(m.Pattern()) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 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 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 49 | |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 50 | type pruning int8 |
| 51 | const ( |
| 52 | pruneVendor = pruning(1 << iota) |
| 53 | pruneGoMod |
| 54 | ) |
| 55 | |
| 56 | walkPkgs := func(root, importPathRoot string, prune pruning) { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 57 | root = filepath.Clean(root) |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 58 | err := fsys.Walk(root, func(path string, fi fs.FileInfo, err error) error { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 59 | if err != nil { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 60 | m.AddError(err) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 61 | return nil |
| 62 | } |
| 63 | |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 64 | want := true |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 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 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 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() { |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 89 | if fi.Mode()&fs.ModeSymlink != 0 && want && strings.Contains(m.Pattern(), "...") { |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 90 | if target, err := fsys.Stat(path); err == nil && target.IsDir() { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 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 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 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() { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 103 | return filepath.SkipDir |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | if !have[name] { |
| 108 | have[name] = true |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 109 | if isMatch(name) { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 110 | if _, _, err := scanDir(path, tags); err != imports.ErrNoGo { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 111 | m.Pkgs = append(m.Pkgs, name) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 116 | if elem == "vendor" && (prune&pruneVendor != 0) { |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 117 | return filepath.SkipDir |
| 118 | } |
| 119 | return nil |
| 120 | }) |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 121 | if err != nil { |
| 122 | m.AddError(err) |
| 123 | } |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 124 | } |
| 125 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 126 | if filter == includeStd { |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 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" { |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 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) |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 138 | } |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 139 | return |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | for _, mod := range modules { |
| 143 | if !treeCanMatch(mod.Path) { |
| 144 | continue |
| 145 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 146 | |
| 147 | var ( |
| 148 | root, modPrefix string |
| 149 | isLocal bool |
| 150 | ) |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 151 | if MainModules.Contains(mod.Path) { |
| 152 | if MainModules.ModRoot(mod) == "" { |
Colin Cross | d9c6b80 | 2019-03-19 21:10:31 -0700 | [diff] [blame] | 153 | continue // If there is no main module, we can't search in it. |
| 154 | } |
Dan Willemsen | bc60c3c | 2021-12-15 01:09:00 -0800 | [diff] [blame] | 155 | root = MainModules.ModRoot(mod) |
| 156 | modPrefix = MainModules.PathPrefix(mod) |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 157 | isLocal = true |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 158 | } else { |
| 159 | var err error |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 160 | const needSum = true |
| 161 | root, isLocal, err = fetch(ctx, mod, needSum) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 162 | if err != nil { |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 163 | m.AddError(err) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 164 | continue |
| 165 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 166 | modPrefix = mod.Path |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 167 | } |
Colin Cross | 430342c | 2019-09-07 08:36:04 -0700 | [diff] [blame] | 168 | |
| 169 | prune := pruneVendor |
| 170 | if isLocal { |
| 171 | prune |= pruneGoMod |
| 172 | } |
| 173 | walkPkgs(root, modPrefix, prune) |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 174 | } |
| 175 | |
Patrice Arruda | 748609c | 2020-06-25 12:12:21 -0700 | [diff] [blame] | 176 | return |
Dan Willemsen | c741332 | 2018-08-27 23:21:26 -0700 | [diff] [blame] | 177 | } |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 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 | |
Dan Willemsen | cc753b7 | 2021-08-31 13:25:42 -0700 | [diff] [blame] | 191 | LoadModFile(ctx) // Sets Target, needed by fetch and matchPackages. |
Colin Cross | 1f80552 | 2021-05-14 11:10:59 -0700 | [diff] [blame] | 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(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 | } |