github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/internal/modload/list.go (about) 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 "bytes" 9 "context" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "io" 14 "os" 15 "runtime" 16 "strings" 17 18 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/gover" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch/codehost" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modinfo" 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/search" 24 "github.com/bir3/gocompiler/src/cmd/internal/pkgpattern" 25 26 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 27 ) 28 29 type ListMode int 30 31 const ( 32 ListU ListMode = 1 << iota 33 ListRetracted 34 ListDeprecated 35 ListVersions 36 ListRetractedVersions 37 ) 38 39 // ListModules returns a description of the modules matching args, if known, 40 // along with any error preventing additional matches from being identified. 41 // 42 // The returned slice can be nonempty even if the error is non-nil. 43 func ListModules(ctx context.Context, args []string, mode ListMode, reuseFile string) ([]*modinfo.ModulePublic, error) { 44 var reuse map[module.Version]*modinfo.ModulePublic 45 if reuseFile != "" { 46 data, err := os.ReadFile(reuseFile) 47 if err != nil { 48 return nil, err 49 } 50 dec := json.NewDecoder(bytes.NewReader(data)) 51 reuse = make(map[module.Version]*modinfo.ModulePublic) 52 for { 53 var m modinfo.ModulePublic 54 if err := dec.Decode(&m); err != nil { 55 if err == io.EOF { 56 break 57 } 58 return nil, fmt.Errorf("parsing %s: %v", reuseFile, err) 59 } 60 if m.Origin == nil { 61 continue 62 } 63 m.Reuse = true 64 reuse[module.Version{Path: m.Path, Version: m.Version}] = &m 65 if m.Query != "" { 66 reuse[module.Version{Path: m.Path, Version: m.Query}] = &m 67 } 68 } 69 } 70 71 rs, mods, err := listModules(ctx, LoadModFile(ctx), args, mode, reuse) 72 73 type token struct{} 74 sem := make(chan token, runtime.GOMAXPROCS(0)) 75 if mode != 0 { 76 for _, m := range mods { 77 if m.Reuse { 78 continue 79 } 80 add := func(m *modinfo.ModulePublic) { 81 sem <- token{} 82 go func() { 83 if mode&ListU != 0 { 84 addUpdate(ctx, m) 85 } 86 if mode&ListVersions != 0 { 87 addVersions(ctx, m, mode&ListRetractedVersions != 0) 88 } 89 if mode&ListRetracted != 0 { 90 addRetraction(ctx, m) 91 } 92 if mode&ListDeprecated != 0 { 93 addDeprecation(ctx, m) 94 } 95 <-sem 96 }() 97 } 98 99 add(m) 100 if m.Replace != nil { 101 add(m.Replace) 102 } 103 } 104 } 105 // Fill semaphore channel to wait for all tasks to finish. 106 for n := cap(sem); n > 0; n-- { 107 sem <- token{} 108 } 109 110 if err == nil { 111 requirements = rs 112 // TODO(#61605): The extra ListU clause fixes a problem with Go 1.21rc3 113 // where "go mod tidy" and "go list -m -u all" fight over whether the go.sum 114 // should be considered up-to-date. The fix for now is to always treat the 115 // go.sum as up-to-date during list -m -u. Probably the right fix is more targeted, 116 // but in general list -u is looking up other checksums in the checksum database 117 // that won't be necessary later, so it makes sense not to write the go.sum back out. 118 if !ExplicitWriteGoMod && mode&ListU == 0 { 119 err = commitRequirements(ctx, WriteOpts{}) 120 } 121 } 122 return mods, err 123 } 124 125 func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) { 126 if len(args) == 0 { 127 var ms []*modinfo.ModulePublic 128 for _, m := range MainModules.Versions() { 129 if gover.IsToolchain(m.Path) { 130 continue 131 } 132 ms = append(ms, moduleInfo(ctx, rs, m, mode, reuse)) 133 } 134 return rs, ms, nil 135 } 136 137 needFullGraph := false 138 for _, arg := range args { 139 if strings.Contains(arg, `\`) { 140 base.Fatalf("go: module paths never use backslash") 141 } 142 if search.IsRelativePath(arg) { 143 base.Fatalf("go: cannot use relative path %s to specify module", arg) 144 } 145 if arg == "all" || strings.Contains(arg, "...") { 146 needFullGraph = true 147 if !HasModRoot() { 148 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) 149 } 150 continue 151 } 152 if path, vers, found := strings.Cut(arg, "@"); found { 153 if vers == "upgrade" || vers == "patch" { 154 if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned { 155 needFullGraph = true 156 if !HasModRoot() { 157 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) 158 } 159 } 160 } 161 continue 162 } 163 if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned { 164 needFullGraph = true 165 if mode&ListVersions == 0 && !HasModRoot() { 166 base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot) 167 } 168 } 169 } 170 171 var mg *ModuleGraph 172 if needFullGraph { 173 rs, mg, mgErr = expandGraph(ctx, rs) 174 } 175 176 matchedModule := map[module.Version]bool{} 177 for _, arg := range args { 178 if path, vers, found := strings.Cut(arg, "@"); found { 179 var current string 180 if mg == nil { 181 current, _ = rs.rootSelected(path) 182 } else { 183 current = mg.Selected(path) 184 } 185 if current == "none" && mgErr != nil { 186 if vers == "upgrade" || vers == "patch" { 187 // The module graph is incomplete, so we don't know what version we're 188 // actually upgrading from. 189 // mgErr is already set, so just skip this module. 190 continue 191 } 192 } 193 194 allowed := CheckAllowed 195 if IsRevisionQuery(path, vers) || mode&ListRetracted != 0 { 196 // Allow excluded and retracted versions if the user asked for a 197 // specific revision or used 'go list -retracted'. 198 allowed = nil 199 } 200 info, err := queryReuse(ctx, path, vers, current, allowed, reuse) 201 if err != nil { 202 var origin *codehost.Origin 203 if info != nil { 204 origin = info.Origin 205 } 206 mods = append(mods, &modinfo.ModulePublic{ 207 Path: path, 208 Version: vers, 209 Error: modinfoError(path, vers, err), 210 Origin: origin, 211 }) 212 continue 213 } 214 215 // Indicate that m was resolved from outside of rs by passing a nil 216 // *Requirements instead. 217 var noRS *Requirements 218 219 mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode, reuse) 220 if vers != mod.Version { 221 mod.Query = vers 222 } 223 mod.Origin = info.Origin 224 mods = append(mods, mod) 225 continue 226 } 227 228 // Module path or pattern. 229 var match func(string) bool 230 if arg == "all" { 231 match = func(p string) bool { return !gover.IsToolchain(p) } 232 } else if strings.Contains(arg, "...") { 233 mp := pkgpattern.MatchPattern(arg) 234 match = func(p string) bool { return mp(p) && !gover.IsToolchain(p) } 235 } else { 236 var v string 237 if mg == nil { 238 var ok bool 239 v, ok = rs.rootSelected(arg) 240 if !ok { 241 // We checked rootSelected(arg) in the earlier args loop, so if there 242 // is no such root we should have loaded a non-nil mg. 243 panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg)) 244 } 245 } else { 246 v = mg.Selected(arg) 247 } 248 if v == "none" && mgErr != nil { 249 // mgErr is already set, so just skip this module. 250 continue 251 } 252 if v != "none" { 253 mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode, reuse)) 254 } else if cfg.BuildMod == "vendor" { 255 // In vendor mode, we can't determine whether a missing module is “a 256 // known dependency” because the module graph is incomplete. 257 // Give a more explicit error message. 258 mods = append(mods, &modinfo.ModulePublic{ 259 Path: arg, 260 Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")), 261 }) 262 } else if mode&ListVersions != 0 { 263 // Don't make the user provide an explicit '@latest' when they're 264 // explicitly asking what the available versions are. Instead, return a 265 // module with version "none", to which we can add the requested list. 266 mods = append(mods, &modinfo.ModulePublic{Path: arg}) 267 } else { 268 mods = append(mods, &modinfo.ModulePublic{ 269 Path: arg, 270 Error: modinfoError(arg, "", errors.New("not a known dependency")), 271 }) 272 } 273 continue 274 } 275 276 matched := false 277 for _, m := range mg.BuildList() { 278 if match(m.Path) { 279 matched = true 280 if !matchedModule[m] { 281 matchedModule[m] = true 282 mods = append(mods, moduleInfo(ctx, rs, m, mode, reuse)) 283 } 284 } 285 } 286 if !matched { 287 fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg) 288 } 289 } 290 291 return rs, mods, mgErr 292 } 293 294 // modinfoError wraps an error to create an error message in 295 // modinfo.ModuleError with minimal redundancy. 296 func modinfoError(path, vers string, err error) *modinfo.ModuleError { 297 var nerr *NoMatchingVersionError 298 var merr *module.ModuleError 299 if errors.As(err, &nerr) { 300 // NoMatchingVersionError contains the query, so we don't mention the 301 // query again in ModuleError. 302 err = &module.ModuleError{Path: path, Err: err} 303 } else if !errors.As(err, &merr) { 304 // If the error does not contain path and version, wrap it in a 305 // module.ModuleError. 306 err = &module.ModuleError{Path: path, Version: vers, Err: err} 307 } 308 309 return &modinfo.ModuleError{Err: err.Error()} 310 }