cuelang.org/go@v0.10.1/internal/mod/modload/query.go (about) 1 package modload 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 "runtime" 8 "sync" 9 10 "cuelang.org/go/internal/mod/modpkgload" 11 "cuelang.org/go/internal/mod/modrequirements" 12 "cuelang.org/go/internal/mod/semver" 13 "cuelang.org/go/internal/par" 14 "cuelang.org/go/mod/module" 15 ) 16 17 // queryImport attempts to locate a module that can be added to the 18 // current build list to provide the package with the given import path. 19 // It also reports whether a default major version will be required 20 // to select the candidates (this will be true if pkgPath lacks 21 // a major version). 22 // 23 // It avoids results that are already in the given requirements. 24 func (ld *loader) queryImport(ctx context.Context, pkgPath string, rs *modrequirements.Requirements) (candidates []module.Version, needsDefault bool, err error) { 25 if modpkgload.IsStdlibPackage(pkgPath) { 26 // This package isn't in the standard library and isn't in any module already 27 // in the build list. 28 // 29 // Moreover, the import path is reserved for the standard library, so 30 // QueryPattern cannot possibly find a module containing this package. 31 // 32 // Instead of trying QueryPattern, report an ImportMissingError immediately. 33 return nil, false, &modpkgload.ImportMissingError{Path: pkgPath} 34 } 35 36 // Look up module containing the package, for addition to the build list. 37 // Goal is to determine the module, download it to dir, 38 // and return m, dir, ImportMissingError. 39 40 // TODO this should probably be a non-debug log message. 41 logf("cue: finding module for package %s", pkgPath) 42 43 candidates, needsDefault, err = ld.queryLatestModules(ctx, pkgPath, rs) 44 if err != nil { 45 return nil, false, err 46 } 47 if len(candidates) == 0 { 48 return nil, false, fmt.Errorf("%v", &modpkgload.ImportMissingError{Path: pkgPath}) 49 } 50 return candidates, needsDefault, nil 51 } 52 53 // queryLatestModules looks for potential modules that might contain the given 54 // package by looking for the latest module version of all viable prefixes of pkgPath. 55 // It does not return modules that are already present in the given requirements. 56 // It also reports whether a default major version will be required. 57 func (ld *loader) queryLatestModules(ctx context.Context, pkgPath string, rs *modrequirements.Requirements) ([]module.Version, bool, error) { 58 parts := module.ParseImportPath(pkgPath) 59 latestModuleForPrefix := func(prefix string) (module.Version, error) { 60 mv := parts.Version 61 if mv == "" { 62 var status modrequirements.MajorVersionDefaultStatus 63 mv, status = rs.DefaultMajorVersion(prefix) 64 if status == modrequirements.AmbiguousDefault { 65 // There are already multiple possibilities and 66 // we don't have any way of choosing one. 67 return module.Version{}, nil 68 } 69 } 70 mpath := prefix 71 if mv != "" { 72 mpath = prefix + "@" + mv 73 if _, ok := rs.RootSelected(mpath); ok { 74 // Already present in current requirements. 75 return module.Version{}, nil 76 } 77 } 78 79 versions, err := ld.registry.ModuleVersions(ctx, mpath) 80 logf("getting module versions for %q (prefix %q) -> %q, %v", mpath, prefix, versions, err) 81 if err != nil { 82 return module.Version{}, err 83 } 84 logf("-> %q", versions) 85 if v := latestVersion(versions); v != "" { 86 return module.NewVersion(prefix, v) 87 } 88 return module.Version{}, nil 89 } 90 work := par.NewQueue(runtime.GOMAXPROCS(0)) 91 var ( 92 mu sync.Mutex 93 candidates []module.Version 94 queryErr error 95 ) 96 logf("initial module path %q", parts.Path) 97 for prefix := parts.Path; prefix != "."; prefix = path.Dir(prefix) { 98 work.Add(func() { 99 v, err := latestModuleForPrefix(prefix) 100 mu.Lock() 101 defer mu.Unlock() 102 if err != nil { 103 if queryErr == nil { 104 queryErr = err 105 } 106 return 107 } 108 if v.IsValid() { 109 candidates = append(candidates, v) 110 } 111 }) 112 } 113 <-work.Idle() 114 return candidates, parts.Version == "", queryErr 115 } 116 117 // latestVersion returns the latest of any of the given versions, 118 // ignoring prerelease versions if there is any stable version. 119 func latestVersion(versions []string) string { 120 maxStable := "" 121 maxAny := "" 122 for _, v := range versions { 123 if semver.Prerelease(v) == "" && (maxStable == "" || semver.Compare(v, maxStable) > 0) { 124 maxStable = v 125 } 126 if maxAny == "" || semver.Compare(v, maxAny) > 0 { 127 maxAny = v 128 } 129 } 130 if maxStable != "" { 131 return maxStable 132 } 133 return maxAny 134 }