cuelang.org/go@v0.10.1/internal/mod/mvs/graph.go (about)

     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 mvs
     6  
     7  import (
     8  	"fmt"
     9  	"slices"
    10  	"sort"
    11  )
    12  
    13  // Versions is an interface that should be provided by implementations
    14  // to define the mvs algorithm in terms of their own version type V, where
    15  // a version type holds a (module path, module version) pair.
    16  type Versions[V any] interface {
    17  	// New creates a new instance of V holding the
    18  	// given module path and version.
    19  	New(path, version string) (V, error)
    20  	// Path returns the path part of V.
    21  	Path(v V) string
    22  	// Version returns the version part of V.
    23  	Version(v V) string
    24  }
    25  
    26  // Graph implements an incremental version of the MVS algorithm, with the
    27  // requirements pushed by the caller instead of pulled by the MVS traversal.
    28  type Graph[V comparable] struct {
    29  	v     Versions[V]
    30  	cmp   func(v1, v2 string) int
    31  	roots []V
    32  
    33  	required map[V][]V
    34  
    35  	isRoot   map[V]bool        // contains true for roots and false for reachable non-roots
    36  	selected map[string]string // path → version
    37  }
    38  
    39  // NewGraph returns an incremental MVS graph containing only a set of root
    40  // dependencies and using the given max function for version strings.
    41  //
    42  // The caller must ensure that the root slice is not modified while the Graph
    43  // may be in use.
    44  func NewGraph[V comparable](v Versions[V], cmp func(string, string) int, roots []V) *Graph[V] {
    45  	g := &Graph[V]{
    46  		v:        v,
    47  		cmp:      cmp,
    48  		roots:    slices.Clip(roots),
    49  		required: make(map[V][]V),
    50  		isRoot:   make(map[V]bool),
    51  		selected: make(map[string]string),
    52  	}
    53  
    54  	for _, m := range roots {
    55  		g.isRoot[m] = true
    56  		if g.cmp(g.Selected(g.v.Path(m)), g.v.Version(m)) < 0 {
    57  			g.selected[g.v.Path(m)] = g.v.Version(m)
    58  		}
    59  	}
    60  
    61  	return g
    62  }
    63  
    64  // Require adds the information that module m requires all modules in reqs.
    65  // The reqs slice must not be modified after it is passed to Require.
    66  //
    67  // m must be reachable by some existing chain of requirements from g's target,
    68  // and Require must not have been called for it already.
    69  //
    70  // If any of the modules in reqs has the same path as g's target,
    71  // the target must have higher precedence than the version in req.
    72  func (g *Graph[V]) Require(m V, reqs []V) {
    73  	// To help catch disconnected-graph bugs, enforce that all required versions
    74  	// are actually reachable from the roots (and therefore should affect the
    75  	// selected versions of the modules they name).
    76  	if _, reachable := g.isRoot[m]; !reachable {
    77  		panic(fmt.Sprintf("%v is not reachable from any root", m))
    78  	}
    79  
    80  	// Truncate reqs to its capacity to avoid aliasing bugs if it is later
    81  	// returned from RequiredBy and appended to.
    82  	reqs = slices.Clip(reqs)
    83  
    84  	if _, dup := g.required[m]; dup {
    85  		panic(fmt.Sprintf("requirements of %v have already been set", m))
    86  	}
    87  	g.required[m] = reqs
    88  
    89  	for _, dep := range reqs {
    90  		// Mark dep reachable, regardless of whether it is selected.
    91  		if _, ok := g.isRoot[dep]; !ok {
    92  			g.isRoot[dep] = false
    93  		}
    94  
    95  		if g.cmp(g.Selected(g.v.Path(dep)), g.v.Version(dep)) < 0 {
    96  			g.selected[g.v.Path(dep)] = g.v.Version(dep)
    97  		}
    98  	}
    99  }
   100  
   101  // RequiredBy returns the slice of requirements passed to Require for m, if any,
   102  // with its capacity reduced to its length.
   103  // If Require has not been called for m, RequiredBy(m) returns ok=false.
   104  //
   105  // The caller must not modify the returned slice, but may safely append to it
   106  // and may rely on it not to be modified.
   107  func (g *Graph[V]) RequiredBy(m V) (reqs []V, ok bool) {
   108  	reqs, ok = g.required[m]
   109  	return reqs, ok
   110  }
   111  
   112  // Selected returns the selected version of the given module path.
   113  //
   114  // If no version is selected, Selected returns version "none".
   115  func (g *Graph[V]) Selected(path string) (version string) {
   116  	v, ok := g.selected[path]
   117  	if !ok {
   118  		return "none"
   119  	}
   120  	return v
   121  }
   122  
   123  // BuildList returns the selected versions of all modules present in the Graph,
   124  // beginning with the selected versions of each module path in the roots of g.
   125  //
   126  // The order of the remaining elements in the list is deterministic
   127  // but arbitrary.
   128  func (g *Graph[V]) BuildList() []V {
   129  	seenRoot := make(map[string]bool, len(g.roots))
   130  
   131  	var list []V
   132  	for _, r := range g.roots {
   133  		if seenRoot[g.v.Path(r)] {
   134  			// Multiple copies of the same root, with the same or different versions,
   135  			// are a bit of a degenerate case: we will take the transitive
   136  			// requirements of both roots into account, but only the higher one can
   137  			// possibly be selected. However — especially given that we need the
   138  			// seenRoot map for later anyway — it is simpler to support this
   139  			// degenerate case than to forbid it.
   140  			continue
   141  		}
   142  
   143  		if v := g.Selected(g.v.Path(r)); v != "none" {
   144  			list = append(list, g.newVersion(g.v.Path(r), v))
   145  		}
   146  		seenRoot[g.v.Path(r)] = true
   147  	}
   148  	uniqueRoots := list
   149  
   150  	for path, version := range g.selected {
   151  		if !seenRoot[path] {
   152  			list = append(list, g.newVersion(path, version))
   153  		}
   154  	}
   155  	g.sortVersions(list[len(uniqueRoots):])
   156  	return list
   157  }
   158  
   159  func (g *Graph[V]) sortVersions(vs []V) {
   160  	sort.Slice(vs, func(i, j int) bool {
   161  		v0, v1 := vs[i], vs[j]
   162  		if p0, p1 := g.v.Path(v0), g.v.Path(v1); p0 != p1 {
   163  			return p0 < p1
   164  		}
   165  		return g.cmp(g.v.Version(v0), g.v.Version(v1)) < 0
   166  	})
   167  }
   168  
   169  func (g *Graph[V]) newVersion(path string, vers string) V {
   170  	v, err := g.v.New(path, vers)
   171  	if err != nil {
   172  		// Note: can't happen because all paths and versions passed to
   173  		// g.newVersion have already come from valid paths and versions
   174  		// returned from a Versions implementation.
   175  		panic(err)
   176  	}
   177  	return v
   178  }
   179  
   180  // WalkBreadthFirst invokes f once, in breadth-first order, for each module
   181  // version other than "none" that appears in the graph, regardless of whether
   182  // that version is selected.
   183  func (g *Graph[V]) WalkBreadthFirst(f func(m V)) {
   184  	var queue []V
   185  	enqueued := make(map[V]bool)
   186  	for _, m := range g.roots {
   187  		if g.v.Version(m) != "none" {
   188  			queue = append(queue, m)
   189  			enqueued[m] = true
   190  		}
   191  	}
   192  
   193  	for len(queue) > 0 {
   194  		m := queue[0]
   195  		queue = queue[1:]
   196  
   197  		f(m)
   198  
   199  		reqs, _ := g.RequiredBy(m)
   200  		for _, r := range reqs {
   201  			if !enqueued[r] && g.v.Version(r) != "none" {
   202  				queue = append(queue, r)
   203  				enqueued[r] = true
   204  			}
   205  		}
   206  	}
   207  }
   208  
   209  // FindPath reports a shortest requirement path starting at one of the roots of
   210  // the graph and ending at a module version m for which f(m) returns true, or
   211  // nil if no such path exists.
   212  func (g *Graph[V]) FindPath(f func(V) bool) []V {
   213  	// firstRequires[a] = b means that in a breadth-first traversal of the
   214  	// requirement graph, the module version a was first required by b.
   215  	firstRequires := make(map[V]V)
   216  
   217  	queue := g.roots
   218  	for _, m := range g.roots {
   219  		firstRequires[m] = *new(V)
   220  	}
   221  
   222  	for len(queue) > 0 {
   223  		m := queue[0]
   224  		queue = queue[1:]
   225  
   226  		if f(m) {
   227  			// Construct the path reversed (because we're starting from the far
   228  			// endpoint), then reverse it.
   229  			path := []V{m}
   230  			for {
   231  				m = firstRequires[m]
   232  				if g.v.Path(m) == "" {
   233  					break
   234  				}
   235  				path = append(path, m)
   236  			}
   237  
   238  			i, j := 0, len(path)-1
   239  			for i < j {
   240  				path[i], path[j] = path[j], path[i]
   241  				i++
   242  				j--
   243  			}
   244  
   245  			return path
   246  		}
   247  
   248  		reqs, _ := g.RequiredBy(m)
   249  		for _, r := range reqs {
   250  			if _, seen := firstRequires[r]; !seen {
   251  				queue = append(queue, r)
   252  				firstRequires[r] = m
   253  			}
   254  		}
   255  	}
   256  
   257  	return nil
   258  }