github.com/bir3/gocompiler@v0.3.205/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/modfetch/codehost" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modinfo" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/search" 23 "github.com/bir3/gocompiler/src/cmd/internal/pkgpattern" 24 25 "github.com/bir3/gocompiler/src/xvendor/golang.org/x/mod/module" 26 ) 27 28 type ListMode int 29 30 const ( 31 ListU ListMode = 1 << iota 32 ListRetracted 33 ListDeprecated 34 ListVersions 35 ListRetractedVersions 36 ) 37 38 // ListModules returns a description of the modules matching args, if known, 39 // along with any error preventing additional matches from being identified. 40 // 41 // The returned slice can be nonempty even if the error is non-nil. 42 func ListModules(ctx context.Context, args []string, mode ListMode, reuseFile string) ([]*modinfo.ModulePublic, error) { 43 var reuse map[module.Version]*modinfo.ModulePublic 44 if reuseFile != "" { 45 data, err := os.ReadFile(reuseFile) 46 if err != nil { 47 return nil, err 48 } 49 dec := json.NewDecoder(bytes.NewReader(data)) 50 reuse = make(map[module.Version]*modinfo.ModulePublic) 51 for { 52 var m modinfo.ModulePublic 53 if err := dec.Decode(&m); err != nil { 54 if err == io.EOF { 55 break 56 } 57 return nil, fmt.Errorf("parsing %s: %v", reuseFile, err) 58 } 59 if m.Origin == nil || !m.Origin.Checkable() { 60 // Nothing to check to validate reuse. 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 if !ExplicitWriteGoMod { 113 err = commitRequirements(ctx) 114 } 115 } 116 return mods, err 117 } 118 119 func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode, reuse map[module.Version]*modinfo.ModulePublic) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) { 120 if len(args) == 0 { 121 var ms []*modinfo.ModulePublic 122 for _, m := range MainModules.Versions() { 123 ms = append(ms, moduleInfo(ctx, rs, m, mode, reuse)) 124 } 125 return rs, ms, nil 126 } 127 128 needFullGraph := false 129 for _, arg := range args { 130 if strings.Contains(arg, `\`) { 131 base.Fatalf("go: module paths never use backslash") 132 } 133 if search.IsRelativePath(arg) { 134 base.Fatalf("go: cannot use relative path %s to specify module", arg) 135 } 136 if arg == "all" || strings.Contains(arg, "...") { 137 needFullGraph = true 138 if !HasModRoot() { 139 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) 140 } 141 continue 142 } 143 if path, vers, found := strings.Cut(arg, "@"); found { 144 if vers == "upgrade" || vers == "patch" { 145 if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned { 146 needFullGraph = true 147 if !HasModRoot() { 148 base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot) 149 } 150 } 151 } 152 continue 153 } 154 if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned { 155 needFullGraph = true 156 if mode&ListVersions == 0 && !HasModRoot() { 157 base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot) 158 } 159 } 160 } 161 162 var mg *ModuleGraph 163 if needFullGraph { 164 rs, mg, mgErr = expandGraph(ctx, rs) 165 } 166 167 matchedModule := map[module.Version]bool{} 168 for _, arg := range args { 169 if path, vers, found := strings.Cut(arg, "@"); found { 170 var current string 171 if mg == nil { 172 current, _ = rs.rootSelected(path) 173 } else { 174 current = mg.Selected(path) 175 } 176 if current == "none" && mgErr != nil { 177 if vers == "upgrade" || vers == "patch" { 178 // The module graph is incomplete, so we don't know what version we're 179 // actually upgrading from. 180 // mgErr is already set, so just skip this module. 181 continue 182 } 183 } 184 185 allowed := CheckAllowed 186 if IsRevisionQuery(vers) || mode&ListRetracted != 0 { 187 // Allow excluded and retracted versions if the user asked for a 188 // specific revision or used 'go list -retracted'. 189 allowed = nil 190 } 191 info, err := queryReuse(ctx, path, vers, current, allowed, reuse) 192 if err != nil { 193 var origin *codehost.Origin 194 if info != nil { 195 origin = info.Origin 196 } 197 mods = append(mods, &modinfo.ModulePublic{ 198 Path: path, 199 Version: vers, 200 Error: modinfoError(path, vers, err), 201 Origin: origin, 202 }) 203 continue 204 } 205 206 // Indicate that m was resolved from outside of rs by passing a nil 207 // *Requirements instead. 208 var noRS *Requirements 209 210 mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode, reuse) 211 if vers != mod.Version { 212 mod.Query = vers 213 } 214 mod.Origin = info.Origin 215 mods = append(mods, mod) 216 continue 217 } 218 219 // Module path or pattern. 220 var match func(string) bool 221 if arg == "all" { 222 match = func(string) bool { return true } 223 } else if strings.Contains(arg, "...") { 224 match = pkgpattern.MatchPattern(arg) 225 } else { 226 var v string 227 if mg == nil { 228 var ok bool 229 v, ok = rs.rootSelected(arg) 230 if !ok { 231 // We checked rootSelected(arg) in the earlier args loop, so if there 232 // is no such root we should have loaded a non-nil mg. 233 panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg)) 234 } 235 } else { 236 v = mg.Selected(arg) 237 } 238 if v == "none" && mgErr != nil { 239 // mgErr is already set, so just skip this module. 240 continue 241 } 242 if v != "none" { 243 mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode, reuse)) 244 } else if cfg.BuildMod == "vendor" { 245 // In vendor mode, we can't determine whether a missing module is “a 246 // known dependency” because the module graph is incomplete. 247 // Give a more explicit error message. 248 mods = append(mods, &modinfo.ModulePublic{ 249 Path: arg, 250 Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")), 251 }) 252 } else if mode&ListVersions != 0 { 253 // Don't make the user provide an explicit '@latest' when they're 254 // explicitly asking what the available versions are. Instead, return a 255 // module with version "none", to which we can add the requested list. 256 mods = append(mods, &modinfo.ModulePublic{Path: arg}) 257 } else { 258 mods = append(mods, &modinfo.ModulePublic{ 259 Path: arg, 260 Error: modinfoError(arg, "", errors.New("not a known dependency")), 261 }) 262 } 263 continue 264 } 265 266 matched := false 267 for _, m := range mg.BuildList() { 268 if match(m.Path) { 269 matched = true 270 if !matchedModule[m] { 271 matchedModule[m] = true 272 mods = append(mods, moduleInfo(ctx, rs, m, mode, reuse)) 273 } 274 } 275 } 276 if !matched { 277 fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg) 278 } 279 } 280 281 return rs, mods, mgErr 282 } 283 284 // modinfoError wraps an error to create an error message in 285 // modinfo.ModuleError with minimal redundancy. 286 func modinfoError(path, vers string, err error) *modinfo.ModuleError { 287 var nerr *NoMatchingVersionError 288 var merr *module.ModuleError 289 if errors.As(err, &nerr) { 290 // NoMatchingVersionError contains the query, so we don't mention the 291 // query again in ModuleError. 292 err = &module.ModuleError{Path: path, Err: err} 293 } else if !errors.As(err, &merr) { 294 // If the error does not contain path and version, wrap it in a 295 // module.ModuleError. 296 err = &module.ModuleError{Path: path, Version: vers, Err: err} 297 } 298 299 return &modinfo.ModuleError{Err: err.Error()} 300 }