cuelang.org/go@v0.13.0/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  	"cmp"
     9  	"fmt"
    10  	"slices"
    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  	slices.SortFunc(vs, func(a, b V) int {
   161  		if c := cmp.Compare(g.v.Path(a), g.v.Path(b)); c != 0 {
   162  			return c
   163  		}
   164  		return g.cmp(g.v.Version(a), g.v.Version(b))
   165  	})
   166  }
   167  
   168  func (g *Graph[V]) newVersion(path string, vers string) V {
   169  	v, err := g.v.New(path, vers)
   170  	if err != nil {
   171  		// Note: can't happen because all paths and versions passed to
   172  		// g.newVersion have already come from valid paths and versions
   173  		// returned from a Versions implementation.
   174  		panic(err)
   175  	}
   176  	return v
   177  }
   178  
   179  // WalkBreadthFirst invokes f once, in breadth-first order, for each module
   180  // version other than "none" that appears in the graph, regardless of whether
   181  // that version is selected.
   182  func (g *Graph[V]) WalkBreadthFirst(f func(m V)) {
   183  	var queue []V
   184  	enqueued := make(map[V]bool)
   185  	for _, m := range g.roots {
   186  		if g.v.Version(m) != "none" {
   187  			queue = append(queue, m)
   188  			enqueued[m] = true
   189  		}
   190  	}
   191  
   192  	for len(queue) > 0 {
   193  		m := queue[0]
   194  		queue = queue[1:]
   195  
   196  		f(m)
   197  
   198  		reqs, _ := g.RequiredBy(m)
   199  		for _, r := range reqs {
   200  			if !enqueued[r] && g.v.Version(r) != "none" {
   201  				queue = append(queue, r)
   202  				enqueued[r] = true
   203  			}
   204  		}
   205  	}
   206  }
   207  
   208  // FindPath reports a shortest requirement path starting at one of the roots of
   209  // the graph and ending at a module version m for which f(m) returns true, or
   210  // nil if no such path exists.
   211  func (g *Graph[V]) FindPath(f func(V) bool) []V {
   212  	// firstRequires[a] = b means that in a breadth-first traversal of the
   213  	// requirement graph, the module version a was first required by b.
   214  	firstRequires := make(map[V]V)
   215  
   216  	queue := g.roots
   217  	for _, m := range g.roots {
   218  		firstRequires[m] = *new(V)
   219  	}
   220  
   221  	for len(queue) > 0 {
   222  		m := queue[0]
   223  		queue = queue[1:]
   224  
   225  		if f(m) {
   226  			// Construct the path reversed (because we're starting from the far
   227  			// endpoint), then reverse it.
   228  			path := []V{m}
   229  			for {
   230  				m = firstRequires[m]
   231  				if g.v.Path(m) == "" {
   232  					break
   233  				}
   234  				path = append(path, m)
   235  			}
   236  
   237  			i, j := 0, len(path)-1
   238  			for i < j {
   239  				path[i], path[j] = path[j], path[i]
   240  				i++
   241  				j--
   242  			}
   243  
   244  			return path
   245  		}
   246  
   247  		reqs, _ := g.RequiredBy(m)
   248  		for _, r := range reqs {
   249  			if _, seen := firstRequires[r]; !seen {
   250  				queue = append(queue, r)
   251  				firstRequires[r] = m
   252  			}
   253  		}
   254  	}
   255  
   256  	return nil
   257  }