}
for _, m := range vendorMods {
- line := moduleLine(m, modload.Replacement(m))
+ replacement, _ := modload.Replacement(m)
+ line := moduleLine(m, replacement)
io.WriteString(w, line)
goVersion := ""
i := i
m := r.buildList[i]
mActual := m
- if mRepl := modload.Replacement(m); mRepl.Path != "" {
+ if mRepl, _ := modload.Replacement(m); mRepl.Path != "" {
mActual = mRepl
}
old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}
continue
}
oldActual := old
- if oldRepl := modload.Replacement(old); oldRepl.Path != "" {
+ if oldRepl, _ := modload.Replacement(old); oldRepl.Path != "" {
oldActual = oldRepl
}
if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(oldActual) {
}
// completeFromModCache fills in the extra fields in m using the module cache.
- completeFromModCache := func(m *modinfo.ModulePublic) {
+ completeFromModCache := func(m *modinfo.ModulePublic, replacedFrom string) {
checksumOk := func(suffix string) bool {
return rs == nil || m.Version == "" || cfg.BuildMod == "mod" ||
modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix})
if m.GoVersion == "" && checksumOk("/go.mod") {
// Load the go.mod file to determine the Go version, since it hasn't
// already been populated from rawGoVersion.
- if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
+ if summary, err := rawGoModSummary(mod, replacedFrom); err == nil && summary.goVersion != "" {
m.GoVersion = summary.goVersion
}
}
if rs == nil {
// If this was an explicitly-versioned argument to 'go mod download' or
// 'go list -m', report the actual requested version, not its replacement.
- completeFromModCache(info) // Will set m.Error in vendor mode.
+ completeFromModCache(info, "") // Will set m.Error in vendor mode.
return info
}
- r := Replacement(m)
+ r, replacedFrom := Replacement(m)
if r.Path == "" {
if cfg.BuildMod == "vendor" {
// It's tempting to fill in the "Dir" field to point within the vendor
// interleave packages from different modules if one module path is a
// prefix of the other.
} else {
- completeFromModCache(info)
+ completeFromModCache(info, "")
}
return info
}
info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
}
if cfg.BuildMod != "vendor" {
- completeFromModCache(info.Replace)
+ completeFromModCache(info.Replace, replacedFrom)
info.Dir = info.Replace.Dir
info.GoMod = info.Replace.GoMod
info.Retracted = info.Replace.Retracted
mv = "(devel)"
}
fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv)
- if r := Replacement(m); r.Path == "" {
+ if r, _ := Replacement(m); r.Path == "" {
fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m))
} else {
fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
// The package path is not valid to fetch remotely,
// so it can only exist in a replaced module,
// and we know from the above loop that it is not.
+ replacement, _ := Replacement(mods[0])
return module.Version{}, &PackageNotInModuleError{
Mod: mods[0],
Query: "latest",
Pattern: path,
- Replacement: Replacement(mods[0]),
+ Replacement: replacement,
}
}
if modRoot := MainModules.ModRoot(mod); modRoot != "" {
return modRoot, true, nil
}
- if r := Replacement(mod); r.Path != "" {
+ if r, _ := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v, ok := rs.rootSelected(prefix); ok && v != "none" {
m := module.Version{Path: prefix, Version: v}
- keep[resolveReplacement(m)] = true
+ r, _ := resolveReplacement(m)
+ keep[r] = true
}
}
continue
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v := mg.Selected(prefix); v != "none" {
m := module.Version{Path: prefix, Version: v}
- keep[resolveReplacement(m)] = true
+ r, _ := resolveReplacement(m)
+ keep[r] = true
}
}
}
// Save sums for the root modules (or their replacements), but don't
// incur the cost of loading the graph just to find and retain the sums.
for _, m := range rs.rootModules {
- r := resolveReplacement(m)
+ r, _ := resolveReplacement(m)
keep[modkey(r)] = true
if which == addBuildListZipSums {
keep[r] = true
// The requirements from m's go.mod file are present in the module graph,
// so they are relevant to the MVS result regardless of whether m was
// actually selected.
- keep[modkey(resolveReplacement(m))] = true
+ r, _ := resolveReplacement(m)
+ keep[modkey(r)] = true
}
})
if which == addBuildListZipSums {
for _, m := range mg.BuildList() {
- keep[resolveReplacement(m)] = true
+ r, _ := resolveReplacement(m)
+ keep[r] = true
}
}
}
tryMod := func(m module.Version) (string, bool) {
var root string
var err error
- if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
+ if repl, _ := Replacement(m); repl.Path != "" && repl.Version == "" {
root = repl.Path
if !filepath.IsAbs(root) {
root = filepath.Join(ModRoot(), root)
firstPath := map[module.Version]string{}
for _, mod := range mods {
- src := resolveReplacement(mod)
+ src, _ := resolveReplacement(mod)
if prev, ok := firstPath[src]; !ok {
firstPath[src] = mod.Path
} else if prev != mod.Path {
// Cannot be retracted.
return nil
}
- if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
+ if repl, _ := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
// All versions of the module were replaced.
// Don't load retractions, since we'd just load the replacement.
return nil
// We load the raw file here: the go.mod file may have a different module
// path that we expect if the module or its repository was renamed.
// We still want to apply retractions to other aliases of the module.
- rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
+ rm, replacedFrom, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
if err != nil {
return err
}
- summary, err := rawGoModSummary(rm)
+ summary, err := rawGoModSummary(rm, replacedFrom)
if err != nil {
return err
}
// Don't look up deprecation.
return "", nil
}
- if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
+ if repl, _ := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
// All versions of the module were replaced.
// We'll look up deprecation separately for the replacement.
return "", nil
}
- latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
+ latest, replacedFrom, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
if err != nil {
return "", err
}
- summary, err := rawGoModSummary(latest)
+ summary, err := rawGoModSummary(latest, replacedFrom)
if err != nil {
return "", err
}
return "", module.Version{}, false
}
-// Replacement returns the replacement for mod, if any, from go.mod.
+// Replacement returns the replacement for mod, if any, and and the module root
+// directory of the main module containing the replace directive.
// If there is no replacement for mod, Replacement returns
// a module.Version with Path == "".
-func Replacement(mod module.Version) module.Version {
+func Replacement(mod module.Version) (module.Version, string) {
_ = TODOWorkspaces("support replaces in the go.work file")
foundFrom, found, foundModRoot := "", module.Version{}, ""
for _, v := range MainModules.Versions() {
_ = TODOWorkspaces("once the go.work file supports replaces, recommend them as a way to override conflicts")
base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v",
mod, modFilePath(foundModRoot), modFilePath(modRoot))
- return found
+ return found, foundModRoot
}
found, foundModRoot = r, modRoot
}
}
}
- return found
+ return found, foundModRoot
}
// resolveReplacement returns the module actually used to load the source code
// for m: either m itself, or the replacement for m (iff m is replaced).
-func resolveReplacement(m module.Version) module.Version {
- if r := Replacement(m); r.Path != "" {
- return r
+// It also returns the modroot of the module providing the replacement if
+// one was found.
+func resolveReplacement(m module.Version) (module.Version, string) {
+ if r, replacedFrom := Replacement(m); r.Path != "" {
+ return r, replacedFrom
}
- return m
+ return m, ""
}
// indexModFile rebuilds the index of modFile.
return summary, nil
}
- actual := resolveReplacement(m)
+ actual, replacedFrom := resolveReplacement(m)
if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" {
key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
if !modfetch.HaveSum(key) {
return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
}
}
- summary, err := rawGoModSummary(actual)
+ summary, err := rawGoModSummary(actual, replacedFrom)
if err != nil {
return nil, err
}
// its dependencies.
//
// rawGoModSummary cannot be used on the Target module.
-func rawGoModSummary(m module.Version) (*modFileSummary, error) {
+
+func rawGoModSummary(m module.Version, replacedFrom string) (*modFileSummary, error) {
if m.Path == "" && MainModules.Contains(m.Path) {
panic("internal error: rawGoModSummary called on the Target module")
}
+ type key struct {
+ m module.Version
+ replacedFrom string
+ }
type cached struct {
summary *modFileSummary
err error
}
- c := rawGoModSummaryCache.Do(m, func() interface{} {
+ c := rawGoModSummaryCache.Do(key{m, replacedFrom}, func() interface{} {
summary := new(modFileSummary)
- name, data, err := rawGoModData(m)
+ name, data, err := rawGoModData(m, replacedFrom)
if err != nil {
return cached{nil, err}
}
//
// Unlike rawGoModSummary, rawGoModData does not cache its results in memory.
// Use rawGoModSummary instead unless you specifically need these bytes.
-func rawGoModData(m module.Version) (name string, data []byte, err error) {
+func rawGoModData(m module.Version, replacedFrom string) (name string, data []byte, err error) {
if m.Version == "" {
// m is a replacement module with only a file path.
dir := m.Path
if !filepath.IsAbs(dir) {
- dir = filepath.Join(ModRoot(), dir)
+ if replacedFrom == "" {
+ panic(fmt.Errorf("missing module root of main module providing replacement with relative path: %v", dir))
+ }
+ dir = filepath.Join(replacedFrom, dir)
}
name = filepath.Join(dir, "go.mod")
if gomodActual, ok := fsys.OverlayPath(name); ok {
//
// If the queried latest version is replaced,
// queryLatestVersionIgnoringRetractions returns the replacement.
-func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
+func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, replacedFrom string, err error) {
type entry struct {
- latest module.Version
- err error
+ latest module.Version
+ replacedFrom string // if latest is a replacement
+ err error
}
e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
defer span.Done()
- if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
+ if repl, replFrom := Replacement(module.Version{Path: path}); repl.Path != "" {
// All versions of the module were replaced.
// No need to query.
- return &entry{latest: repl}
+ return &entry{latest: repl, replacedFrom: replFrom}
}
// Find the latest version of the module.
return &entry{err: err}
}
latest := module.Version{Path: path, Version: rev.Version}
- if repl := resolveReplacement(latest); repl.Path != "" {
- latest = repl
+ if repl, replFrom := resolveReplacement(latest); repl.Path != "" {
+ latest, replacedFrom = repl, replFrom
}
- return &entry{latest: latest}
+ return &entry{latest: latest, replacedFrom: replacedFrom}
}).(*entry)
- return e.latest, e.err
+ return e.latest, e.replacedFrom, e.err
}
var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
if len(pkgMods) == 0 && err == nil {
+ replacement, _ := Replacement(modOnly.Mod)
return nil, &PackageNotInModuleError{
Mod: modOnly.Mod,
- Replacement: Replacement(modOnly.Mod),
+ Replacement: replacement,
Query: query,
Pattern: pattern,
}
if err := firstError(m); err != nil {
return r, err
}
+ replacement, _ := Replacement(r.Mod)
return r, &PackageNotInModuleError{
Mod: r.Mod,
- Replacement: Replacement(r.Mod),
+ Replacement: replacement,
Query: query,
Pattern: pattern,
}
// we don't need to verify it in go.sum. This makes 'go list -m -u' faster
// and simpler.
func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
- _, data, err := rawGoModData(m)
+ _, data, err := rawGoModData(m, "")
if err != nil {
return false, err
}
}
}
- if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
+ if r, _ := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
return info, err
}
return rr.replacementStat(v)
}
for _, mod := range vendorReplaced {
- r := Replacement(mod)
+ r, _ := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue