Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/modload/list.go

Documentation: cmd/go/internal/modload

     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  		commitRequirements(ctx, modFileGoVersion(), rs)
    76  	}
    77  	return mods, err
    78  }
    79  
    80  func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) {
    81  	if len(args) == 0 {
    82  		return rs, []*modinfo.ModulePublic{moduleInfo(ctx, rs, Target, mode)}, nil
    83  	}
    84  
    85  	needFullGraph := false
    86  	for _, arg := range args {
    87  		if strings.Contains(arg, `\`) {
    88  			base.Fatalf("go: module paths never use backslash")
    89  		}
    90  		if search.IsRelativePath(arg) {
    91  			base.Fatalf("go: cannot use relative path %s to specify module", arg)
    92  		}
    93  		if arg == "all" || strings.Contains(arg, "...") {
    94  			needFullGraph = true
    95  			if !HasModRoot() {
    96  				base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
    97  			}
    98  			continue
    99  		}
   100  		if i := strings.Index(arg, "@"); i >= 0 {
   101  			path := arg[:i]
   102  			vers := arg[i+1:]
   103  			if vers == "upgrade" || vers == "patch" {
   104  				if _, ok := rs.rootSelected(path); !ok || rs.depth == eager {
   105  					needFullGraph = true
   106  					if !HasModRoot() {
   107  						base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
   108  					}
   109  				}
   110  			}
   111  			continue
   112  		}
   113  		if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager {
   114  			needFullGraph = true
   115  			if mode&ListVersions == 0 && !HasModRoot() {
   116  				base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)
   117  			}
   118  		}
   119  	}
   120  
   121  	var mg *ModuleGraph
   122  	if needFullGraph {
   123  		rs, mg, mgErr = expandGraph(ctx, rs)
   124  	}
   125  
   126  	matchedModule := map[module.Version]bool{}
   127  	for _, arg := range args {
   128  		if i := strings.Index(arg, "@"); i >= 0 {
   129  			path := arg[:i]
   130  			vers := arg[i+1:]
   131  
   132  			var current string
   133  			if mg == nil {
   134  				current, _ = rs.rootSelected(path)
   135  			} else {
   136  				current = mg.Selected(path)
   137  			}
   138  			if current == "none" && mgErr != nil {
   139  				if vers == "upgrade" || vers == "patch" {
   140  					// The module graph is incomplete, so we don't know what version we're
   141  					// actually upgrading from.
   142  					// mgErr is already set, so just skip this module.
   143  					continue
   144  				}
   145  			}
   146  
   147  			allowed := CheckAllowed
   148  			if IsRevisionQuery(vers) || mode&ListRetracted != 0 {
   149  				// Allow excluded and retracted versions if the user asked for a
   150  				// specific revision or used 'go list -retracted'.
   151  				allowed = nil
   152  			}
   153  			info, err := Query(ctx, path, vers, current, allowed)
   154  			if err != nil {
   155  				mods = append(mods, &modinfo.ModulePublic{
   156  					Path:    path,
   157  					Version: vers,
   158  					Error:   modinfoError(path, vers, err),
   159  				})
   160  				continue
   161  			}
   162  
   163  			// Indicate that m was resolved from outside of rs by passing a nil
   164  			// *Requirements instead.
   165  			var noRS *Requirements
   166  
   167  			mod := moduleInfo(ctx, noRS, module.Version{Path: path, Version: info.Version}, mode)
   168  			mods = append(mods, mod)
   169  			continue
   170  		}
   171  
   172  		// Module path or pattern.
   173  		var match func(string) bool
   174  		if arg == "all" {
   175  			match = func(string) bool { return true }
   176  		} else if strings.Contains(arg, "...") {
   177  			match = search.MatchPattern(arg)
   178  		} else {
   179  			var v string
   180  			if mg == nil {
   181  				var ok bool
   182  				v, ok = rs.rootSelected(arg)
   183  				if !ok {
   184  					// We checked rootSelected(arg) in the earlier args loop, so if there
   185  					// is no such root we should have loaded a non-nil mg.
   186  					panic(fmt.Sprintf("internal error: root requirement expected but not found for %v", arg))
   187  				}
   188  			} else {
   189  				v = mg.Selected(arg)
   190  			}
   191  			if v == "none" && mgErr != nil {
   192  				// mgErr is already set, so just skip this module.
   193  				continue
   194  			}
   195  			if v != "none" {
   196  				mods = append(mods, moduleInfo(ctx, rs, module.Version{Path: arg, Version: v}, mode))
   197  			} else if cfg.BuildMod == "vendor" {
   198  				// In vendor mode, we can't determine whether a missing module is “a
   199  				// known dependency” because the module graph is incomplete.
   200  				// Give a more explicit error message.
   201  				mods = append(mods, &modinfo.ModulePublic{
   202  					Path:  arg,
   203  					Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
   204  				})
   205  			} else if mode&ListVersions != 0 {
   206  				// Don't make the user provide an explicit '@latest' when they're
   207  				// explicitly asking what the available versions are. Instead, return a
   208  				// module with version "none", to which we can add the requested list.
   209  				mods = append(mods, &modinfo.ModulePublic{Path: arg})
   210  			} else {
   211  				mods = append(mods, &modinfo.ModulePublic{
   212  					Path:  arg,
   213  					Error: modinfoError(arg, "", errors.New("not a known dependency")),
   214  				})
   215  			}
   216  			continue
   217  		}
   218  
   219  		matched := false
   220  		for _, m := range mg.BuildList() {
   221  			if match(m.Path) {
   222  				matched = true
   223  				if !matchedModule[m] {
   224  					matchedModule[m] = true
   225  					mods = append(mods, moduleInfo(ctx, rs, m, mode))
   226  				}
   227  			}
   228  		}
   229  		if !matched {
   230  			fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
   231  		}
   232  	}
   233  
   234  	return rs, mods, mgErr
   235  }
   236  
   237  // modinfoError wraps an error to create an error message in
   238  // modinfo.ModuleError with minimal redundancy.
   239  func modinfoError(path, vers string, err error) *modinfo.ModuleError {
   240  	var nerr *NoMatchingVersionError
   241  	var merr *module.ModuleError
   242  	if errors.As(err, &nerr) {
   243  		// NoMatchingVersionError contains the query, so we don't mention the
   244  		// query again in ModuleError.
   245  		err = &module.ModuleError{Path: path, Err: err}
   246  	} else if !errors.As(err, &merr) {
   247  		// If the error does not contain path and version, wrap it in a
   248  		// module.ModuleError.
   249  		err = &module.ModuleError{Path: path, Version: vers, Err: err}
   250  	}
   251  
   252  	return &modinfo.ModuleError{Err: err.Error()}
   253  }
   254  

View as plain text