aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/go
diff options
context:
space:
mode:
authorGerrit Code Review <noreply-gerritcodereview@google.com>2021-07-30 21:29:29 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2021-07-30 21:29:29 +0000
commit8e2ab05dd39cdf8eea2546b220cc5f48afd39c97 (patch)
tree90c5e955bd302b44d7b9466f864caf9e2d4d00cc /src/cmd/go
parent52e970b1c86f18806232adb0e1f42636645d21ff (diff)
parent47cdfa95ae85919c6f050a87b54c69f64c2666fc (diff)
downloadgo-8e2ab05dd39cdf8eea2546b220cc5f48afd39c97.tar.gz
go-8e2ab05dd39cdf8eea2546b220cc5f48afd39c97.zip
Merge "[dev.cmdgo] all: merge master (9eee0ed) into dev.cmdgo" into dev.cmdgo
Diffstat (limited to 'src/cmd/go')
-rw-r--r--src/cmd/go/alldocs.go6
-rw-r--r--src/cmd/go/go_test.go32
-rw-r--r--src/cmd/go/internal/lockedfile/lockedfile_filelock.go3
-rw-r--r--src/cmd/go/internal/lockedfile/lockedfile_plan9.go6
-rw-r--r--src/cmd/go/internal/modcmd/edit.go2
-rw-r--r--src/cmd/go/internal/modcmd/graph.go2
-rw-r--r--src/cmd/go/internal/modcmd/init.go2
-rw-r--r--src/cmd/go/internal/modfetch/codehost/git_test.go49
-rw-r--r--src/cmd/go/internal/modfetch/coderepo.go23
-rw-r--r--src/cmd/go/internal/modfetch/fetch.go17
-rw-r--r--src/cmd/go/internal/modload/init.go9
-rw-r--r--src/cmd/go/internal/modload/load.go39
-rw-r--r--src/cmd/go/internal/modload/modfile.go78
-rw-r--r--src/cmd/go/internal/modload/query.go34
-rw-r--r--src/cmd/go/internal/work/exec.go11
-rw-r--r--src/cmd/go/testdata/script/mod_overlay.txt10
-rw-r--r--src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt2
-rw-r--r--src/cmd/go/testdata/script/mod_tidy_compat_deleted.txt2
-rw-r--r--src/cmd/go/testdata/script/mod_tidy_compat_implicit.txt2
-rw-r--r--src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt2
-rw-r--r--src/cmd/go/testdata/script/mod_update_sum_readonly.txt34
21 files changed, 226 insertions, 139 deletions
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index fb99dccb46..af1ee39684 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -1087,7 +1087,7 @@
//
// Usage:
//
-// go mod edit [editing flags] [go.mod]
+// go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
//
// Edit provides a command-line interface for editing go.mod,
// for use primarily by tools or scripts. It reads only go.mod;
@@ -1202,7 +1202,7 @@
// and one of its requirements. Each module is identified as a string of the form
// path@version, except for the main module, which has no @version suffix.
//
-// The -go flag causes graph to report the module graph as loaded by by the
+// The -go flag causes graph to report the module graph as loaded by the
// given Go version, instead of the version indicated by the 'go' directive
// in the go.mod file.
//
@@ -1213,7 +1213,7 @@
//
// Usage:
//
-// go mod init [module]
+// go mod init [module-path]
//
// Init initializes and writes a new go.mod file in the current directory, in
// effect creating a new module rooted at the current directory. The go.mod file
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index c0c86ab9f5..6ce276537b 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -2848,3 +2848,35 @@ func TestExecInDeletedDir(t *testing.T) {
// `go version` should not fail
tg.run("version")
}
+
+// A missing C compiler should not force the net package to be stale.
+// Issue 47215.
+func TestMissingCC(t *testing.T) {
+ if !canCgo {
+ t.Skip("test is only meaningful on systems with cgo")
+ }
+ cc := os.Getenv("CC")
+ if cc == "" {
+ cc = "gcc"
+ }
+ if filepath.IsAbs(cc) {
+ t.Skipf(`"CC" (%s) is an absolute path`, cc)
+ }
+ _, err := exec.LookPath(cc)
+ if err != nil {
+ t.Skipf(`"CC" (%s) not on PATH`, cc)
+ }
+
+ tg := testgo(t)
+ defer tg.cleanup()
+ netStale, _ := tg.isStale("net")
+ if netStale {
+ t.Skip(`skipping test because "net" package is currently stale`)
+ }
+
+ tg.setenv("PATH", "") // No C compiler on PATH.
+ netStale, _ = tg.isStale("net")
+ if netStale {
+ t.Error(`clearing "PATH" causes "net" to be stale`)
+ }
+}
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
index 729df5c681..e4923f6876 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
@@ -11,7 +11,6 @@ import (
"io/fs"
"os"
- "cmd/go/internal/fsys"
"cmd/go/internal/lockedfile/internal/filelock"
)
@@ -21,7 +20,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
// calls for Linux and Windows anyway, so it's simpler to use that approach
// consistently.
- f, err := fsys.OpenFile(name, flag&^os.O_TRUNC, perm)
+ f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm)
if err != nil {
return nil, err
}
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
index 3d4b97d78e..979118b10a 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
@@ -13,8 +13,6 @@ import (
"os"
"strings"
"time"
-
- "cmd/go/internal/fsys"
)
// Opening an exclusive-use file returns an error.
@@ -59,7 +57,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
// If the file was unpacked or created by some other program, it might not
// have the ModeExclusive bit set. Set it before we call OpenFile, so that we
// can be confident that a successful OpenFile implies exclusive use.
- if fi, err := fsys.Stat(name); err == nil {
+ if fi, err := os.Stat(name); err == nil {
if fi.Mode()&fs.ModeExclusive == 0 {
if err := os.Chmod(name, fi.Mode()|fs.ModeExclusive); err != nil {
return nil, err
@@ -72,7 +70,7 @@ func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
nextSleep := 1 * time.Millisecond
const maxSleep = 500 * time.Millisecond
for {
- f, err := fsys.OpenFile(name, flag, perm|fs.ModeExclusive)
+ f, err := os.OpenFile(name, flag, perm|fs.ModeExclusive)
if err == nil {
return f, nil
}
diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go
index e856e7c630..bb3d521092 100644
--- a/src/cmd/go/internal/modcmd/edit.go
+++ b/src/cmd/go/internal/modcmd/edit.go
@@ -25,7 +25,7 @@ import (
)
var cmdEdit = &base.Command{
- UsageLine: "go mod edit [editing flags] [go.mod]",
+ UsageLine: "go mod edit [editing flags] [-fmt|-print|-json] [go.mod]",
Short: "edit go.mod from tools or scripts",
Long: `
Edit provides a command-line interface for editing go.mod,
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index 5ef1d4ed04..2cbabae044 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -26,7 +26,7 @@ in text form. Each line in the output has two space-separated fields: a module
and one of its requirements. Each module is identified as a string of the form
path@version, except for the main module, which has no @version suffix.
-The -go flag causes graph to report the module graph as loaded by by the
+The -go flag causes graph to report the module graph as loaded by the
given Go version, instead of the version indicated by the 'go' directive
in the go.mod file.
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index 73cc282d81..958c3066ac 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -13,7 +13,7 @@ import (
)
var cmdInit = &base.Command{
- UsageLine: "go mod init [module]",
+ UsageLine: "go mod init [module-path]",
Short: "initialize new module in current directory",
Long: `
Init initializes and writes a new go.mod file in the current directory, in
diff --git a/src/cmd/go/internal/modfetch/codehost/git_test.go b/src/cmd/go/internal/modfetch/codehost/git_test.go
index 89a73baad9..a684fa1a9b 100644
--- a/src/cmd/go/internal/modfetch/codehost/git_test.go
+++ b/src/cmd/go/internal/modfetch/codehost/git_test.go
@@ -8,7 +8,6 @@ import (
"archive/zip"
"bytes"
"flag"
- "fmt"
"internal/testenv"
"io"
"io/fs"
@@ -47,12 +46,6 @@ var altRepos = []string{
var localGitRepo string
func testMain(m *testing.M) int {
- if _, err := exec.LookPath("git"); err != nil {
- fmt.Fprintln(os.Stderr, "skipping because git binary not found")
- fmt.Println("PASS")
- return 0
- }
-
dir, err := os.MkdirTemp("", "gitrepo-test-")
if err != nil {
log.Fatal(err)
@@ -60,23 +53,25 @@ func testMain(m *testing.M) int {
defer os.RemoveAll(dir)
if testenv.HasExternalNetwork() && testenv.HasExec() {
- // Clone gitrepo1 into a local directory.
- // If we use a file:// URL to access the local directory,
- // then git starts up all the usual protocol machinery,
- // which will let us test remote git archive invocations.
- localGitRepo = filepath.Join(dir, "gitrepo2")
- if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
- log.Fatal(err)
- }
- if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
- log.Fatal(err)
+ if _, err := exec.LookPath("git"); err == nil {
+ // Clone gitrepo1 into a local directory.
+ // If we use a file:// URL to access the local directory,
+ // then git starts up all the usual protocol machinery,
+ // which will let us test remote git archive invocations.
+ localGitRepo = filepath.Join(dir, "gitrepo2")
+ if _, err := Run("", "git", "clone", "--mirror", gitrepo1, localGitRepo); err != nil {
+ log.Fatal(err)
+ }
+ if _, err := Run(localGitRepo, "git", "config", "daemon.uploadarch", "true"); err != nil {
+ log.Fatal(err)
+ }
}
}
return m.Run()
}
-func testRepo(remote string) (Repo, error) {
+func testRepo(t *testing.T, remote string) (Repo, error) {
if remote == "localGitRepo" {
// Convert absolute path to file URL. LocalGitRepo will not accept
// Windows absolute paths because they look like a host:path remote.
@@ -87,15 +82,17 @@ func testRepo(remote string) (Repo, error) {
} else {
url = "file:///" + filepath.ToSlash(localGitRepo)
}
+ testenv.MustHaveExecPath(t, "git")
return LocalGitRepo(url)
}
- kind := "git"
+ vcs := "git"
for _, k := range []string{"hg"} {
if strings.Contains(remote, "/"+k+"/") {
- kind = k
+ vcs = k
}
}
- return NewRepo(kind, remote)
+ testenv.MustHaveExecPath(t, vcs)
+ return NewRepo(vcs, remote)
}
var tagsTests = []struct {
@@ -116,7 +113,7 @@ func TestTags(t *testing.T) {
for _, tt := range tagsTests {
f := func(t *testing.T) {
- r, err := testRepo(tt.repo)
+ r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@@ -168,7 +165,7 @@ func TestLatest(t *testing.T) {
for _, tt := range latestTests {
f := func(t *testing.T) {
- r, err := testRepo(tt.repo)
+ r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@@ -221,7 +218,7 @@ func TestReadFile(t *testing.T) {
for _, tt := range readFileTests {
f := func(t *testing.T) {
- r, err := testRepo(tt.repo)
+ r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@@ -412,7 +409,7 @@ func TestReadZip(t *testing.T) {
for _, tt := range readZipTests {
f := func(t *testing.T) {
- r, err := testRepo(tt.repo)
+ r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
@@ -581,7 +578,7 @@ func TestStat(t *testing.T) {
for _, tt := range statTests {
f := func(t *testing.T) {
- r, err := testRepo(tt.repo)
+ r, err := testRepo(t, tt.repo)
if err != nil {
t.Fatal(err)
}
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index f817a04583..dfef9f73c2 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -864,22 +864,25 @@ func (r *codeRepo) GoMod(version string) (data []byte, err error) {
data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
if err != nil {
if os.IsNotExist(err) {
- return r.legacyGoMod(rev, dir), nil
+ return LegacyGoMod(r.modPath), nil
}
return nil, err
}
return data, nil
}
-func (r *codeRepo) legacyGoMod(rev, dir string) []byte {
- // We used to try to build a go.mod reflecting pre-existing
- // package management metadata files, but the conversion
- // was inherently imperfect (because those files don't have
- // exactly the same semantics as go.mod) and, when done
- // for dependencies in the middle of a build, impossible to
- // correct. So we stopped.
- // Return a fake go.mod that simply declares the module path.
- return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath)))
+// LegacyGoMod generates a fake go.mod file for a module that doesn't have one.
+// The go.mod file contains a module directive and nothing else: no go version,
+// no requirements.
+//
+// We used to try to build a go.mod reflecting pre-existing
+// package management metadata files, but the conversion
+// was inherently imperfect (because those files don't have
+// exactly the same semantics as go.mod) and, when done
+// for dependencies in the middle of a build, impossible to
+// correct. So we stopped.
+func LegacyGoMod(modPath string) []byte {
+ return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(modPath)))
}
func (r *codeRepo) modPrefix(rev string) string {
diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go
index 7b3525e914..408b2860ad 100644
--- a/src/cmd/go/internal/modfetch/fetch.go
+++ b/src/cmd/go/internal/modfetch/fetch.go
@@ -22,6 +22,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/lockedfile"
"cmd/go/internal/par"
"cmd/go/internal/robustio"
@@ -416,7 +417,18 @@ func initGoSum() (bool, error) {
goSum.m = make(map[module.Version][]string)
goSum.status = make(map[modSum]modSumStatus)
- data, err := lockedfile.Read(GoSumFile)
+ var (
+ data []byte
+ err error
+ )
+ if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok {
+ // Don't lock go.sum if it's part of the overlay.
+ // On Plan 9, locking requires chmod, and we don't want to modify any file
+ // in the overlay. See #44700.
+ data, err = os.ReadFile(actualSumFile)
+ } else {
+ data, err = lockedfile.Read(GoSumFile)
+ }
if err != nil && !os.IsNotExist(err) {
return false, err
}
@@ -718,6 +730,9 @@ Outer:
if readonly {
return ErrGoSumDirty
}
+ if _, ok := fsys.OverlayPath(GoSumFile); ok {
+ base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
+ }
// Make a best-effort attempt to acquire the side lock, only to exclude
// previous versions of the 'go' command from making simultaneous edits.
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 00dfc8b2dc..a3337d6d23 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -191,7 +191,7 @@ type Root int
const (
// AutoRoot is the default for most commands. modload.Init will look for
// a go.mod file in the current directory or any parent. If none is found,
- // modules may be disabled (GO111MODULE=on) or commands may run in a
+ // modules may be disabled (GO111MODULE=auto) or commands may run in a
// limited module mode.
AutoRoot Root = iota
@@ -1364,6 +1364,13 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
}
return
}
+ gomod := ModFilePath()
+ if _, ok := fsys.OverlayPath(gomod); ok {
+ if dirty {
+ base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
+ }
+ return
+ }
new, err := modFile.Format()
if err != nil {
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index a643cca0a9..67d7ec65da 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -715,20 +715,6 @@ func (mms *MainModuleSet) DirImportPath(ctx context.Context, dir string) (path s
return ".", module.Version{}
}
-// TargetPackages returns the list of packages in the target (top-level) module
-// matching pattern, which may be relative to the working directory, under all
-// build tag settings.
-func TargetPackages(ctx context.Context, pattern string) *search.Match {
- // TargetPackages is relative to the main module, so ensure that the main
- // module is a thing that can contain packages.
- LoadModFile(ctx) // Sets Target.
- ModRoot() // Emits an error if Target cannot contain packages.
-
- m := search.NewMatch(pattern)
- matchPackages(ctx, m, imports.AnyTags(), omitStd, MainModules.Versions())
- return m
-}
-
// ImportMap returns the actual package import path
// for an import path found in source code.
// If the given import path does not appear in the source code
@@ -760,29 +746,6 @@ func PackageModule(path string) module.Version {
return pkg.mod
}
-// PackageImports returns the imports for the package named by the import path.
-// Test imports will be returned as well if tests were loaded for the package
-// (i.e., if "all" was loaded or if LoadTests was set and the path was matched
-// by a command line argument). PackageImports will return nil for
-// unknown package paths.
-func PackageImports(path string) (imports, testImports []string) {
- pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
- if !ok {
- return nil, nil
- }
- imports = make([]string, len(pkg.imports))
- for i, p := range pkg.imports {
- imports[i] = p.path
- }
- if pkg.test != nil {
- testImports = make([]string, len(pkg.test.imports))
- for i, p := range pkg.test.imports {
- testImports[i] = p.path
- }
- }
- return imports, testImports
-}
-
// Lookup returns the source directory, import path, and any loading error for
// the package at path as imported from the package in parentDir.
// Lookup requires that one of the Load functions in this package has already
@@ -1883,7 +1846,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
fmt.Fprintf(os.Stderr, "If reproducibility with go %s is not needed:\n\tgo mod tidy%s -compat=%s\n", ld.TidyCompatibleVersion, goFlag, ld.GoVersion)
// TODO(#46141): Populate the linked wiki page.
- fmt.Fprintf(os.Stderr, "For other options, see:\n\thttps://golang.org/wiki/PruningModules\n")
+ fmt.Fprintf(os.Stderr, "For other options, see:\n\thttps://golang.org/doc/modules/pruning\n")
}
mg, err := rs.Graph(ctx)
diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go
index 79126a46b5..31e79a3646 100644
--- a/src/cmd/go/internal/modload/modfile.go
+++ b/src/cmd/go/internal/modload/modfile.go
@@ -8,6 +8,7 @@ import (
"context"
"errors"
"fmt"
+ "os"
"path/filepath"
"strings"
"sync"
@@ -15,6 +16,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/par"
@@ -614,39 +616,14 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
}
c := rawGoModSummaryCache.Do(m, func() interface{} {
summary := new(modFileSummary)
- var f *modfile.File
- 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)
- }
- gomod := filepath.Join(dir, "go.mod")
-
- data, err := lockedfile.Read(gomod)
- if err != nil {
- return cached{nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(gomod), err))}
- }
- f, err = modfile.ParseLax(gomod, data, nil)
- if err != nil {
- return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err))}
- }
- } else {
- if !semver.IsValid(m.Version) {
- // Disallow the broader queries supported by fetch.Lookup.
- base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
- }
-
- data, err := modfetch.GoMod(m.Path, m.Version)
- if err != nil {
- return cached{nil, err}
- }
- f, err = modfile.ParseLax("go.mod", data, nil)
- if err != nil {
- return cached{nil, module.VersionError(m, fmt.Errorf("parsing go.mod: %v", err))}
- }
+ name, data, err := rawGoModData(m)
+ if err != nil {
+ return cached{nil, err}
+ }
+ f, err := modfile.ParseLax(name, data, nil)
+ if err != nil {
+ return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))}
}
-
if f.Module != nil {
summary.module = f.Module.Mod
summary.deprecated = f.Module.Deprecated
@@ -682,6 +659,43 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
+// rawGoModData returns the content of the go.mod file for module m, ignoring
+// all replacements that may apply to m.
+//
+// rawGoModData cannot be used on the Target module.
+//
+// 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) {
+ 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)
+ }
+ name = filepath.Join(dir, "go.mod")
+ if gomodActual, ok := fsys.OverlayPath(name); ok {
+ // Don't lock go.mod if it's part of the overlay.
+ // On Plan 9, locking requires chmod, and we don't want to modify any file
+ // in the overlay. See #44700.
+ data, err = os.ReadFile(gomodActual)
+ } else {
+ data, err = lockedfile.Read(gomodActual)
+ }
+ if err != nil {
+ return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err))
+ }
+ } else {
+ if !semver.IsValid(m.Version) {
+ // Disallow the broader queries supported by fetch.Lookup.
+ base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version)
+ }
+ name = "go.mod"
+ data, err = modfetch.GoMod(m.Path, m.Version)
+ }
+ return name, data, err
+}
+
// queryLatestVersionIgnoringRetractions looks up the latest version of the
// module with the given path without considering retracted or excluded
// versions.
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index ba137eda1d..a7031856ae 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -5,13 +5,13 @@
package modload
import (
+ "bytes"
"context"
"errors"
"fmt"
"io/fs"
"os"
pathpkg "path"
- "path/filepath"
"sort"
"strings"
"sync"
@@ -933,8 +933,8 @@ func (e *PackageNotInModuleError) ImportPath() string {
return ""
}
-// ModuleHasRootPackage returns whether module m contains a package m.Path.
-func ModuleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
+// moduleHasRootPackage returns whether module m contains a package m.Path.
+func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
needSum := false
root, isLocal, err := fetch(ctx, m, needSum)
if err != nil {
@@ -944,14 +944,32 @@ func ModuleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
return ok, err
}
-func versionHasGoMod(ctx context.Context, m module.Version) (bool, error) {
- needSum := false
- root, _, err := fetch(ctx, m, needSum)
+// versionHasGoMod returns whether a version has a go.mod file.
+//
+// versionHasGoMod fetches the go.mod file (possibly a fake) and true if it
+// contains anything other than a module directive with the same path. When a
+// module does not have a real go.mod file, the go command acts as if it had one
+// that only contained a module directive. Normal go.mod files created after
+// 1.12 at least have a go directive.
+//
+// This function is a heuristic, since it's possible to commit a file that would
+// pass this test. However, we only need a heurstic for determining whether
+// +incompatible versions may be "latest", which is what this function is used
+// for.
+//
+// This heuristic is useful for two reasons: first, when using a proxy,
+// this lets us fetch from the .mod endpoint which is much faster than the .zip
+// endpoint. The .mod file is used anyway, even if the .zip file contains a
+// go.mod with different content. Second, if we don't fetch the .zip, then
+// 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)
if err != nil {
return false, err
}
- fi, err := os.Stat(filepath.Join(root, "go.mod"))
- return err == nil && !fi.IsDir(), nil
+ isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
+ return !isFake, nil
}
// A versionRepo is a subset of modfetch.Repo that can report information about
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 6c646ebf28..f7fae9fdd9 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -252,8 +252,15 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
ccExe := b.ccExe()
fmt.Fprintf(h, "CC=%q %q %q %q\n", ccExe, cppflags, cflags, ldflags)
- if ccID, err := b.gccToolID(ccExe[0], "c"); err == nil {
- fmt.Fprintf(h, "CC ID=%q\n", ccID)
+ // Include the C compiler tool ID so that if the C
+ // compiler changes we rebuild the package.
+ // But don't do that for standard library packages like net,
+ // so that the prebuilt .a files from a Go binary install
+ // don't need to be rebuilt with the local compiler.
+ if !p.Standard {
+ if ccID, err := b.gccToolID(ccExe[0], "c"); err == nil {
+ fmt.Fprintf(h, "CC ID=%q\n", ccID)
+ }
}
if len(p.CXXFiles)+len(p.SwigCXXFiles) > 0 {
cxxExe := b.cxxExe()
diff --git a/src/cmd/go/testdata/script/mod_overlay.txt b/src/cmd/go/testdata/script/mod_overlay.txt
index 92e79c725a..86ab04bd3c 100644
--- a/src/cmd/go/testdata/script/mod_overlay.txt
+++ b/src/cmd/go/testdata/script/mod_overlay.txt
@@ -21,7 +21,7 @@ go list -deps -overlay overlay.json .
cd $WORK/gopath/src/get-doesnt-add-dep
cp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod
! go get -d -overlay overlay.json .
-stderr 'overlaid files can''t be opened for write'
+stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$'
cmp $WORK/overlay/get_doesnt_add_dep_go_mod $WORK/want_go_mod
# Content of overlaid go.sum is used.
@@ -41,10 +41,10 @@ go mod verify -overlay overlay.json
# attempting to update the file
cp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums
! go get -d -overlay overlay.json .
-stderr 'overlaid files can''t be opened for write'
+stderr '^go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay$'
cmp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums
! go mod tidy -overlay overlay.json
-stderr 'overlaid files can''t be opened for write'
+stderr '^go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay$'
cmp incomplete-sum-file $WORK/overlay/overlay-sum-used-correct-sums
# -overlay works with -modfile.
@@ -56,7 +56,7 @@ go list -modfile=alternate.mod -overlay overlay.json .
stdout 'found.the/module'
# Even with -modfile, overlaid files can't be opened for write.
! go get -modfile=alternate.mod -overlay overlay.json -d rsc.io/quote
-stderr 'overlaid files can''t be opened for write'
+stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$'
# Carving out a module by adding an overlaid go.mod file
cd $WORK/gopath/src/carve
@@ -78,7 +78,7 @@ go list -overlay overlay.json all
stdout ^carve2/nomod$
# Editing go.mod file fails because overlay is read only
! go get -overlay overlay.json -d rsc.io/quote
-stderr 'overlaid files can''t be opened for write'
+stderr '^go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay$'
! grep rsc.io/quote $WORK/overlay/carve2-nomod-go.mod
# Editing go.mod file succeeds because we use -modfile to redirect to same file
go get -overlay overlay.json -modfile $WORK/overlay/carve2-nomod-go.mod -d rsc.io/quote
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
index ed1dd53eff..c544cb7413 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt
@@ -23,7 +23,7 @@ cp go.mod go.mod.orig
stderr '^example\.com/m imports\n\texample\.net/indirect imports\n\texample\.net/ambiguous/nested/pkg loaded from example\.net/ambiguous/nested@v0\.1\.0,\n\tbut go 1.16 would fail to locate it:\n\tambiguous import: found package example\.net/ambiguous/nested/pkg in multiple modules:\n\texample\.net/ambiguous v0.1.0 \(.*\)\n\texample\.net/ambiguous/nested v0.1.0 \(.*\)\n\n'
-stderr '\n\nTo proceed despite packages unresolved in go 1\.16:\n\tgo mod tidy -e\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1\.17\nFor other options, see:\n\thttps://golang\.org/wiki/PruningModules\n'
+stderr '\n\nTo proceed despite packages unresolved in go 1\.16:\n\tgo mod tidy -e\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1\.17\nFor other options, see:\n\thttps://golang\.org/doc/modules/pruning\n'
cmp go.mod go.mod.orig
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_deleted.txt b/src/cmd/go/testdata/script/mod_tidy_compat_deleted.txt
index 3aacde2025..dcf13e2049 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_deleted.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_deleted.txt
@@ -19,7 +19,7 @@ cp go.mod go.mod.orig
stderr '^example\.com/m imports\n\texample\.net/deleted loaded from example\.net/deleted@v0\.1\.0,\n\tbut go 1\.16 would fail to locate it in example\.net/deleted@v0\.2\.0\n\n'
-stderr '\n\nTo upgrade to the versions selected by go 1.16, leaving some packages unresolved:\n\tgo mod tidy -e -go=1\.16 && go mod tidy -e -go=1\.17\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1\.17\nFor other options, see:\n\thttps://golang\.org/wiki/PruningModules\n'
+stderr '\n\nTo upgrade to the versions selected by go 1.16, leaving some packages unresolved:\n\tgo mod tidy -e -go=1\.16 && go mod tidy -e -go=1\.17\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1\.17\nFor other options, see:\n\thttps://golang\.org/doc/modules/pruning\n'
# The suggested 'go mod tidy -e' command should proceed anyway.
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_implicit.txt b/src/cmd/go/testdata/script/mod_tidy_compat_implicit.txt
index e00aea930e..186a3f8e67 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_implicit.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_implicit.txt
@@ -33,7 +33,7 @@ env MODFMT='{{with .Module}}{{.Path}} {{.Version}}{{end}}'
cp go.mod go.mod.orig
! go mod tidy
stderr '^example\.com/m imports\n\texample\.net/lazy tested by\n\texample\.net/lazy.test imports\n\texample\.com/retract/incompatible loaded from example\.com/retract/incompatible@v1\.0\.0,\n\tbut go 1\.16 would select v2\.0\.0\+incompatible\n\n'
-stderr '\n\nTo upgrade to the versions selected by go 1\.16:\n\tgo mod tidy -go=1\.16 && go mod tidy -go=1\.17\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1.17\nFor other options, see:\n\thttps://golang\.org/wiki/PruningModules\n'
+stderr '\n\nTo upgrade to the versions selected by go 1\.16:\n\tgo mod tidy -go=1\.16 && go mod tidy -go=1\.17\nIf reproducibility with go 1.16 is not needed:\n\tgo mod tidy -compat=1.17\nFor other options, see:\n\thttps://golang\.org/doc/modules/pruning\n'
cmp go.mod go.mod.orig
diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
index 2d8726544a..ea9e42e87e 100644
--- a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
+++ b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt
@@ -33,7 +33,7 @@ env MODFMT='{{with .Module}}{{.Path}} {{.Version}}{{end}}'
cp go.mod go.mod.orig
! go mod tidy
stderr '^example\.com/m imports\n\texample\.net/lazy imports\n\texample\.com/retract/incompatible loaded from example\.com/retract/incompatible@v1\.0\.0,\n\tbut go 1\.16 would select v2\.0\.0\+incompatible\n\n'
-stderr '\n\nTo upgrade to the versions selected by go 1\.16:\n\tgo mod tidy -go=1\.16 && go mod tidy -go=1\.17\nIf reproducibility with go 1\.16 is not needed:\n\tgo mod tidy -compat=1.17\nFor other options, see:\n\thttps://golang\.org/wiki/PruningModules\n'
+stderr '\n\nTo upgrade to the versions selected by go 1\.16:\n\tgo mod tidy -go=1\.16 && go mod tidy -go=1\.17\nIf reproducibility with go 1\.16 is not needed:\n\tgo mod tidy -compat=1.17\nFor other options, see:\n\thttps://golang\.org/doc/modules/pruning\n'
cmp go.mod go.mod.orig
diff --git a/src/cmd/go/testdata/script/mod_update_sum_readonly.txt b/src/cmd/go/testdata/script/mod_update_sum_readonly.txt
new file mode 100644
index 0000000000..41f12e4084
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_update_sum_readonly.txt
@@ -0,0 +1,34 @@
+# When finding the latest version of a module, we should not download version
+# contents. Previously, we downloaded .zip files to determine whether a real
+# .mod file was present in order to decide whether +incompatible versions
+# could be "latest".
+#
+# Verifies #47377.
+
+# rsc.io/breaker has two versions, neither of which has a .mod file.
+go list -m -versions rsc.io/breaker
+stdout '^rsc.io/breaker v1.0.0 v2.0.0\+incompatible$'
+go mod download rsc.io/breaker@v1.0.0
+! grep '^go' $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v1.0.0.mod
+go mod download rsc.io/breaker@v2.0.0+incompatible
+! grep '^go' $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v2.0.0+incompatible.mod
+
+# Delete downloaded .zip files.
+go clean -modcache
+
+# Check for updates.
+go list -m -u rsc.io/breaker
+stdout '^rsc.io/breaker v1.0.0 \[v2.0.0\+incompatible\]$'
+
+# We should not have downloaded zips.
+! exists $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v1.0.0.zip
+! exists $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v2.0.0+incompatible.zip
+
+-- go.mod --
+module m
+
+go 1.16
+
+require rsc.io/breaker v1.0.0
+-- go.sum --
+rsc.io/breaker v1.0.0/go.mod h1:s5yxDXvD88U1/ESC23I2FK3Lkv4YIKaB1ij/Hbm805g=