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

     1  // Copyright 2020 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  	"os"
    11  	"sort"
    12  
    13  	"cmd/go/internal/gover"
    14  	"cmd/go/internal/modfetch"
    15  	"cmd/go/internal/modfetch/codehost"
    16  
    17  	"golang.org/x/mod/module"
    18  )
    19  
    20  // cmpVersion implements the comparison for versions in the module loader.
    21  //
    22  // It is consistent with gover.ModCompare except that as a special case,
    23  // the version "" is considered higher than all other versions.
    24  // The main module (also known as the target) has no version and must be chosen
    25  // over other versions of the same module in the module dependency graph.
    26  func cmpVersion(p string, v1, v2 string) int {
    27  	if v2 == "" {
    28  		if v1 == "" {
    29  			return 0
    30  		}
    31  		return -1
    32  	}
    33  	if v1 == "" {
    34  		return 1
    35  	}
    36  	return gover.ModCompare(p, v1, v2)
    37  }
    38  
    39  // mvsReqs implements mvs.Reqs for module semantic versions,
    40  // with any exclusions or replacements applied internally.
    41  type mvsReqs struct {
    42  	loaderstate *State // TODO(jitsu): Is there a way we can not depend on the entire loader state?
    43  	roots       []module.Version
    44  }
    45  
    46  func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
    47  	if mod.Version == "" && r.loaderstate.MainModules.Contains(mod.Path) {
    48  		// Use the build list as it existed when r was constructed, not the current
    49  		// global build list.
    50  		return r.roots, nil
    51  	}
    52  
    53  	if mod.Version == "none" {
    54  		return nil, nil
    55  	}
    56  
    57  	summary, err := goModSummary(r.loaderstate, mod)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	return summary.require, nil
    62  }
    63  
    64  // Max returns the maximum of v1 and v2 according to gover.ModCompare.
    65  //
    66  // As a special case, the version "" is considered higher than all other
    67  // versions. The main module (also known as the target) has no version and must
    68  // be chosen over other versions of the same module in the module dependency
    69  // graph.
    70  func (*mvsReqs) Max(p, v1, v2 string) string {
    71  	if cmpVersion(p, v1, v2) < 0 {
    72  		return v2
    73  	}
    74  	return v1
    75  }
    76  
    77  // Upgrade is a no-op, here to implement mvs.Reqs.
    78  // The upgrade logic for go get -u is in ../modget/get.go.
    79  func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
    80  	return m, nil
    81  }
    82  
    83  func versions(loaderstate *State, ctx context.Context, path string, allowed AllowedFunc) (versions []string, origin *codehost.Origin, err error) {
    84  	// Note: modfetch.Lookup and repo.Versions are cached,
    85  	// so there's no need for us to add extra caching here.
    86  	err = modfetch.TryProxies(func(proxy string) error {
    87  		repo, err := lookupRepo(loaderstate, ctx, proxy, path)
    88  		if err != nil {
    89  			return err
    90  		}
    91  		allVersions, err := repo.Versions(ctx, "")
    92  		if err != nil {
    93  			return err
    94  		}
    95  		allowedVersions := make([]string, 0, len(allVersions.List))
    96  		for _, v := range allVersions.List {
    97  			if err := allowed(ctx, module.Version{Path: path, Version: v}); err == nil {
    98  				allowedVersions = append(allowedVersions, v)
    99  			} else if !errors.Is(err, ErrDisallowed) {
   100  				return err
   101  			}
   102  		}
   103  		versions = allowedVersions
   104  		origin = allVersions.Origin
   105  		return nil
   106  	})
   107  	return versions, origin, err
   108  }
   109  
   110  // previousVersion returns the tagged version of m.Path immediately prior to
   111  // m.Version, or version "none" if no prior version is tagged.
   112  //
   113  // Since the version of a main module is not found in the version list,
   114  // it has no previous version.
   115  func previousVersion(loaderstate *State, ctx context.Context, m module.Version) (module.Version, error) {
   116  	if m.Version == "" && loaderstate.MainModules.Contains(m.Path) {
   117  		return module.Version{Path: m.Path, Version: "none"}, nil
   118  	}
   119  
   120  	list, _, err := versions(loaderstate, ctx, m.Path, loaderstate.CheckAllowed)
   121  	if err != nil {
   122  		if errors.Is(err, os.ErrNotExist) {
   123  			return module.Version{Path: m.Path, Version: "none"}, nil
   124  		}
   125  		return module.Version{}, err
   126  	}
   127  	i := sort.Search(len(list), func(i int) bool { return gover.ModCompare(m.Path, list[i], m.Version) >= 0 })
   128  	if i > 0 {
   129  		return module.Version{Path: m.Path, Version: list[i-1]}, nil
   130  	}
   131  	return module.Version{Path: m.Path, Version: "none"}, nil
   132  }
   133  
   134  func (r *mvsReqs) Previous(m module.Version) (module.Version, error) {
   135  	// TODO(golang.org/issue/38714): thread tracing context through MVS.
   136  	return previousVersion(r.loaderstate, context.TODO(), m)
   137  }
   138  

View as plain text