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

View as plain text