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  }