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