File: query.go

package info (click to toggle)
golang-github-cue-lang-cue 0.12.0.-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 19,072 kB
  • sloc: sh: 57; makefile: 17
file content (134 lines) | stat: -rw-r--r-- 4,306 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package modload

import (
	"context"
	"fmt"
	"path"
	"runtime"
	"sync"

	"cuelang.org/go/internal/mod/modpkgload"
	"cuelang.org/go/internal/mod/modrequirements"
	"cuelang.org/go/internal/mod/semver"
	"cuelang.org/go/internal/par"
	"cuelang.org/go/mod/module"
)

// queryImport attempts to locate a module that can be added to the
// current build list to provide the package with the given import path.
// It also reports whether a default major version will be required
// to select the candidates (this will be true if pkgPath lacks
// a major version).
//
// It avoids results that are already in the given requirements.
func (ld *loader) queryImport(ctx context.Context, pkgPath string, rs *modrequirements.Requirements) (candidates []module.Version, needsDefault bool, err error) {
	if modpkgload.IsStdlibPackage(pkgPath) {
		// This package isn't in the standard library and isn't in any module already
		// in the build list.
		//
		// Moreover, the import path is reserved for the standard library, so
		// QueryPattern cannot possibly find a module containing this package.
		//
		// Instead of trying QueryPattern, report an ImportMissingError immediately.
		return nil, false, &modpkgload.ImportMissingError{Path: pkgPath}
	}

	// Look up module containing the package, for addition to the build list.
	// Goal is to determine the module, download it to dir,
	// and return m, dir, ImportMissingError.

	// TODO this should probably be a non-debug log message.
	logf("cue: finding module for package %s", pkgPath)

	candidates, needsDefault, err = ld.queryLatestModules(ctx, pkgPath, rs)
	if err != nil {
		return nil, false, err
	}
	if len(candidates) == 0 {
		return nil, false, fmt.Errorf("%v", &modpkgload.ImportMissingError{Path: pkgPath})
	}
	return candidates, needsDefault, nil
}

// queryLatestModules looks for potential modules that might contain the given
// package by looking for the latest module version of all viable prefixes of pkgPath.
// It does not return modules that are already present in the given requirements.
// It also reports whether a default major version will be required.
func (ld *loader) queryLatestModules(ctx context.Context, pkgPath string, rs *modrequirements.Requirements) ([]module.Version, bool, error) {
	parts := module.ParseImportPath(pkgPath)
	latestModuleForPrefix := func(prefix string) (module.Version, error) {
		mv := parts.Version
		if mv == "" {
			var status modrequirements.MajorVersionDefaultStatus
			mv, status = rs.DefaultMajorVersion(prefix)
			if status == modrequirements.AmbiguousDefault {
				// There are already multiple possibilities and
				// we don't have any way of choosing one.
				return module.Version{}, nil
			}
		}
		mpath := prefix
		if mv != "" {
			mpath = prefix + "@" + mv
			if _, ok := rs.RootSelected(mpath); ok {
				// Already present in current requirements.
				return module.Version{}, nil
			}
		}

		versions, err := ld.registry.ModuleVersions(ctx, mpath)
		logf("getting module versions for %q (prefix %q) -> %q, %v", mpath, prefix, versions, err)
		if err != nil {
			return module.Version{}, err
		}
		logf("-> %q", versions)
		if v := latestVersion(versions); v != "" {
			return module.NewVersion(prefix, v)
		}
		return module.Version{}, nil
	}
	work := par.NewQueue(runtime.GOMAXPROCS(0))
	var (
		mu         sync.Mutex
		candidates []module.Version
		queryErr   error
	)
	logf("initial module path %q", parts.Path)
	for prefix := parts.Path; prefix != "."; prefix = path.Dir(prefix) {
		work.Add(func() {
			v, err := latestModuleForPrefix(prefix)
			mu.Lock()
			defer mu.Unlock()
			if err != nil {
				if queryErr == nil {
					queryErr = err
				}
				return
			}
			if v.IsValid() {
				candidates = append(candidates, v)
			}
		})
	}
	<-work.Idle()
	return candidates, parts.Version == "", queryErr
}

// latestVersion returns the latest of any of the given versions,
// ignoring prerelease versions if there is any stable version.
func latestVersion(versions []string) string {
	maxStable := ""
	maxAny := ""
	for _, v := range versions {
		if semver.Prerelease(v) == "" && (maxStable == "" || semver.Compare(v, maxStable) > 0) {
			maxStable = v
		}
		if maxAny == "" || semver.Compare(v, maxAny) > 0 {
			maxAny = v
		}
	}
	if maxStable != "" {
		return maxStable
	}
	return maxAny
}