aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go/internal/modload/modfile.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/modload/modfile.go')
-rw-r--r--src/cmd/go/internal/modload/modfile.go169
1 files changed, 99 insertions, 70 deletions
diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go
index 03e02e73b63..463869910c8 100644
--- a/src/cmd/go/internal/modload/modfile.go
+++ b/src/cmd/go/internal/modload/modfile.go
@@ -56,11 +56,9 @@ const (
go117LazyTODO = false
)
-var modFile *modfile.File
-
// modFileGoVersion returns the (non-empty) Go version at which the requirements
-// in modFile are intepreted, or the latest Go version if modFile is nil.
-func modFileGoVersion() string {
+// in modFile are interpreted, or the latest Go version if modFile is nil.
+func modFileGoVersion(modFile *modfile.File) string {
if modFile == nil {
return LatestGoVersion()
}
@@ -92,9 +90,6 @@ type modFileIndex struct {
exclude map[module.Version]bool
}
-// index is the index of the go.mod file as of when it was last read or written.
-var index *modFileIndex
-
type requireMeta struct {
indirect bool
}
@@ -137,8 +132,10 @@ var ErrDisallowed = errors.New("disallowed module version")
// CheckExclusions returns an error equivalent to ErrDisallowed if module m is
// excluded by the main module's go.mod file.
func CheckExclusions(ctx context.Context, m module.Version) error {
- if index != nil && index.exclude[m] {
- return module.VersionError(m, errExcluded)
+ for _, mainModule := range MainModules.Versions() {
+ if index := MainModules.Index(mainModule); index != nil && index.exclude[m] {
+ return module.VersionError(m, errExcluded)
+ }
}
return nil
}
@@ -170,7 +167,7 @@ func CheckRetractions(ctx context.Context, m module.Version) (err error) {
// 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
@@ -187,11 +184,11 @@ func CheckRetractions(ctx context.Context, m module.Version) (err error) {
// 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
}
@@ -289,51 +286,72 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string
// 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 summary.deprecated, nil
}
-// Replacement returns the replacement for mod, if any, from go.mod.
+func replacement(mod module.Version, index *modFileIndex) (fromVersion string, to module.Version, ok bool) {
+ if r, ok := index.replace[mod]; ok {
+ return mod.Version, r, true
+ }
+ if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
+ return "", r, true
+ }
+ return "", module.Version{}, false
+}
+
+// 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 {
- if index != nil {
- if r, ok := index.replace[mod]; ok {
- return r
- }
- if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
- return r
+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() {
+ if index := MainModules.Index(v); index != nil {
+ if from, r, ok := replacement(mod, index); ok {
+ modRoot := MainModules.ModRoot(v)
+ if foundModRoot != "" && foundFrom != from && found != r {
+ _ = 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, foundModRoot
+ }
+ found, foundModRoot = r, modRoot
+ }
}
}
- return module.Version{}
+ 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.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
-func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
+func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
@@ -345,12 +363,12 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
i.goVersionV = ""
if modFile.Go == nil {
- rawGoVersion.Store(Target, "")
+ rawGoVersion.Store(mod, "")
} else {
// We're going to use the semver package to compare Go versions, so go ahead
// and add the "v" prefix it expects once instead of every time.
i.goVersionV = "v" + modFile.Go.Version
- rawGoVersion.Store(Target, modFile.Go.Version)
+ rawGoVersion.Store(mod, modFile.Go.Version)
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
@@ -490,8 +508,8 @@ type retraction struct {
//
// The caller must not modify the returned summary.
func goModSummary(m module.Version) (*modFileSummary, error) {
- if m == Target {
- panic("internal error: goModSummary called on the Target module")
+ if m.Version == "" && MainModules.Contains(m.Path) {
+ panic("internal error: goModSummary called on a main module")
}
if cfg.BuildMod == "vendor" {
@@ -506,7 +524,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
// For every module other than the target,
// return the full list of modules from modules.txt.
- readVendorList()
+ readVendorList(MainModules.mustGetSingleMainModule())
// We don't know what versions the vendored module actually relies on,
// so assume that it requires everything.
@@ -514,15 +532,15 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
return summary, nil
}
- actual := resolveReplacement(m)
- if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" {
+ 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) {
suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path)
return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
}
}
- summary, err := rawGoModSummary(actual)
+ summary, err := rawGoModSummary(actual, replacedFrom)
if err != nil {
return nil, err
}
@@ -553,27 +571,29 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
}
}
- if index != nil && len(index.exclude) > 0 {
- // Drop any requirements on excluded versions.
- // Don't modify the cached summary though, since we might need the raw
- // summary separately.
- haveExcludedReqs := false
- for _, r := range summary.require {
- if index.exclude[r] {
- haveExcludedReqs = true
- break
- }
- }
- if haveExcludedReqs {
- s := new(modFileSummary)
- *s = *summary
- s.require = make([]module.Version, 0, len(summary.require))
+ for _, mainModule := range MainModules.Versions() {
+ if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 {
+ // Drop any requirements on excluded versions.
+ // Don't modify the cached summary though, since we might need the raw
+ // summary separately.
+ haveExcludedReqs := false
for _, r := range summary.require {
- if !index.exclude[r] {
- s.require = append(s.require, r)
+ if index.exclude[r] {
+ haveExcludedReqs = true
+ break
+ }
+ }
+ if haveExcludedReqs {
+ s := new(modFileSummary)
+ *s = *summary
+ s.require = make([]module.Version, 0, len(summary.require))
+ for _, r := range summary.require {
+ if !index.exclude[r] {
+ s.require = append(s.require, r)
+ }
}
+ summary = s
}
- summary = s
}
}
return summary, nil
@@ -584,18 +604,23 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
// its dependencies.
//
// rawGoModSummary cannot be used on the Target module.
-func rawGoModSummary(m module.Version) (*modFileSummary, error) {
- if m == Target {
+
+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}
}
@@ -645,12 +670,15 @@ var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
//
// 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 {
@@ -685,19 +713,20 @@ func rawGoModData(m module.Version) (name string, data []byte, err error) {
//
// 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.
@@ -709,12 +738,12 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la
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