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  func ListModules(ctx context.Context, args []string, listU, listVersions, listRetracted bool) []*modinfo.ModulePublic {
    24  	mods := listModules(ctx, args, listVersions, listRetracted)
    25  
    26  	type token struct{}
    27  	sem := make(chan token, runtime.GOMAXPROCS(0))
    28  	if listU || listVersions || listRetracted {
    29  		for _, m := range mods {
    30  			add := func(m *modinfo.ModulePublic) {
    31  				sem <- token{}
    32  				go func() {
    33  					if listU {
    34  						addUpdate(ctx, m)
    35  					}
    36  					if listVersions {
    37  						addVersions(ctx, m, listRetracted)
    38  					}
    39  					if listRetracted || listU {
    40  						addRetraction(ctx, m)
    41  					}
    42  					<-sem
    43  				}()
    44  			}
    45  
    46  			add(m)
    47  			if m.Replace != nil {
    48  				add(m.Replace)
    49  			}
    50  		}
    51  	}
    52  	// Fill semaphore channel to wait for all tasks to finish.
    53  	for n := cap(sem); n > 0; n-- {
    54  		sem <- token{}
    55  	}
    56  
    57  	return mods
    58  }
    59  
    60  func listModules(ctx context.Context, args []string, listVersions, listRetracted bool) []*modinfo.ModulePublic {
    61  	LoadAllModules(ctx)
    62  	if len(args) == 0 {
    63  		return []*modinfo.ModulePublic{moduleInfo(ctx, buildList[0], true, listRetracted)}
    64  	}
    65  
    66  	var mods []*modinfo.ModulePublic
    67  	matchedBuildList := make([]bool, len(buildList))
    68  	for _, arg := range args {
    69  		if strings.Contains(arg, `\`) {
    70  			base.Fatalf("go: module paths never use backslash")
    71  		}
    72  		if search.IsRelativePath(arg) {
    73  			base.Fatalf("go: cannot use relative path %s to specify module", arg)
    74  		}
    75  		if !HasModRoot() && (arg == "all" || strings.Contains(arg, "...")) {
    76  			base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
    77  		}
    78  		if i := strings.Index(arg, "@"); i >= 0 {
    79  			path := arg[:i]
    80  			vers := arg[i+1:]
    81  			var current string
    82  			for _, m := range buildList {
    83  				if m.Path == path {
    84  					current = m.Version
    85  					break
    86  				}
    87  			}
    88  
    89  			allowed := CheckAllowed
    90  			if IsRevisionQuery(vers) || listRetracted {
    91  				// Allow excluded and retracted versions if the user asked for a
    92  				// specific revision or used 'go list -retracted'.
    93  				allowed = nil
    94  			}
    95  			info, err := Query(ctx, path, vers, current, allowed)
    96  			if err != nil {
    97  				mods = append(mods, &modinfo.ModulePublic{
    98  					Path:    path,
    99  					Version: vers,
   100  					Error:   modinfoError(path, vers, err),
   101  				})
   102  				continue
   103  			}
   104  			mod := moduleInfo(ctx, module.Version{Path: path, Version: info.Version}, false, listRetracted)
   105  			mods = append(mods, mod)
   106  			continue
   107  		}
   108  
   109  		// Module path or pattern.
   110  		var match func(string) bool
   111  		var literal bool
   112  		if arg == "all" {
   113  			match = func(string) bool { return true }
   114  		} else if strings.Contains(arg, "...") {
   115  			match = search.MatchPattern(arg)
   116  		} else {
   117  			match = func(p string) bool { return arg == p }
   118  			literal = true
   119  		}
   120  		matched := false
   121  		for i, m := range buildList {
   122  			if i == 0 && !HasModRoot() {
   123  				// The root module doesn't actually exist: omit it.
   124  				continue
   125  			}
   126  			if match(m.Path) {
   127  				matched = true
   128  				if !matchedBuildList[i] {
   129  					matchedBuildList[i] = true
   130  					mods = append(mods, moduleInfo(ctx, m, true, listRetracted))
   131  				}
   132  			}
   133  		}
   134  		if !matched {
   135  			if literal {
   136  				if listVersions {
   137  					// Don't make the user provide an explicit '@latest' when they're
   138  					// explicitly asking what the available versions are.
   139  					// Instead, resolve the module, even if it isn't an existing dependency.
   140  					info, err := Query(ctx, arg, "latest", "", nil)
   141  					if err == nil {
   142  						mod := moduleInfo(ctx, module.Version{Path: arg, Version: info.Version}, false, listRetracted)
   143  						mods = append(mods, mod)
   144  					} else {
   145  						mods = append(mods, &modinfo.ModulePublic{
   146  							Path:  arg,
   147  							Error: modinfoError(arg, "", err),
   148  						})
   149  					}
   150  					continue
   151  				}
   152  				if cfg.BuildMod == "vendor" {
   153  					// In vendor mode, we can't determine whether a missing module is “a
   154  					// known dependency” because the module graph is incomplete.
   155  					// Give a more explicit error message.
   156  					mods = append(mods, &modinfo.ModulePublic{
   157  						Path:  arg,
   158  						Error: modinfoError(arg, "", errors.New("can't resolve module using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)")),
   159  					})
   160  				} else {
   161  					mods = append(mods, &modinfo.ModulePublic{
   162  						Path:  arg,
   163  						Error: modinfoError(arg, "", errors.New("not a known dependency")),
   164  					})
   165  				}
   166  			} else {
   167  				fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
   168  			}
   169  		}
   170  	}
   171  
   172  	return mods
   173  }
   174  
   175  // modinfoError wraps an error to create an error message in
   176  // modinfo.ModuleError with minimal redundancy.
   177  func modinfoError(path, vers string, err error) *modinfo.ModuleError {
   178  	var nerr *NoMatchingVersionError
   179  	var merr *module.ModuleError
   180  	if errors.As(err, &nerr) {
   181  		// NoMatchingVersionError contains the query, so we don't mention the
   182  		// query again in ModuleError.
   183  		err = &module.ModuleError{Path: path, Err: err}
   184  	} else if !errors.As(err, &merr) {
   185  		// If the error does not contain path and version, wrap it in a
   186  		// module.ModuleError.
   187  		err = &module.ModuleError{Path: path, Version: vers, Err: err}
   188  	}
   189  
   190  	return &modinfo.ModuleError{Err: err.Error()}
   191  }
   192  

View as plain text