}
func runClean(cmd *base.Command, args []string) {
- if len(args) == 0 && modload.Failed() {
- // Don't try to clean current directory,
- // which will cause modload to base.Fatalf.
- } else {
+ if len(args) > 0 || !modload.Enabled() || modload.HasModRoot() {
for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg)
}
// ExtraEnvVars returns environment variables that should not leak into child processes.
func ExtraEnvVars() []cfg.EnvVar {
gomod := ""
- if modload.Init(); modload.ModRoot != "" {
- gomod = filepath.Join(modload.ModRoot, "go.mod")
+ if modload.HasModRoot() {
+ gomod = filepath.Join(modload.ModRoot(), "go.mod")
+ } else if modload.Enabled() {
+ gomod = os.DevNull
}
return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod},
}
if cfg.ModulesEnabled {
- p.Module = ModPackageModuleInfo(p.ImportPath)
+ if !p.Internal.CmdlineFiles {
+ p.Module = ModPackageModuleInfo(p.ImportPath)
+ }
if p.Name == "main" {
p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps)
}
if len(args) == 1 {
gomod = args[0]
} else {
- modload.MustInit()
- gomod = filepath.Join(modload.ModRoot, "go.mod")
+ gomod = filepath.Join(modload.ModRoot(), "go.mod")
}
if *editModule != "" {
}
pkgs := modload.LoadVendor()
- vdir := filepath.Join(modload.ModRoot, "vendor")
+ vdir := filepath.Join(modload.ModRoot(), "vendor")
if err := os.RemoveAll(vdir); err != nil {
base.Fatalf("go mod vendor: %v", err)
}
base.Errorf("go get %s: %v", arg, err)
continue
}
- if !str.HasFilePathPrefix(abs, modload.ModRoot) {
- base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot)
+ if !str.HasFilePathPrefix(abs, modload.ModRoot()) {
+ base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot())
continue
}
// TODO: Check if abs is inside a nested module.
Path: m.Path,
Version: m.Version,
Main: true,
- Dir: ModRoot,
- GoMod: filepath.Join(ModRoot, "go.mod"),
+ Dir: ModRoot(),
+ GoMod: filepath.Join(ModRoot(), "go.mod"),
}
if modFile.Go != nil {
info.GoVersion = modFile.Go.Version
}
if cfg.BuildMod == "vendor" {
- info.Dir = filepath.Join(ModRoot, "vendor", m.Path)
+ info.Dir = filepath.Join(ModRoot(), "vendor", m.Path)
return info
}
if filepath.IsAbs(r.Path) {
info.Replace.Dir = r.Path
} else {
- info.Replace.Dir = filepath.Join(ModRoot, r.Path)
+ info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
}
}
complete(info.Replace)
// -mod=vendor is special.
// Everything must be in the main module or the main module's vendor directory.
if cfg.BuildMod == "vendor" {
- mainDir, mainOK := dirInModule(path, Target.Path, ModRoot, true)
- vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot, "vendor"), false)
+ mainDir, mainOK := dirInModule(path, Target.Path, ModRoot(), true)
+ vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
if mainOK && vendorOK {
return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir)
}
"path"
"path/filepath"
"regexp"
+ "runtime/debug"
"strconv"
"strings"
)
var (
- cwd string
+ cwd string // TODO(bcmills): Is this redundant with base.Cwd?
MustUseModules = mustUseModules()
initialized bool
- ModRoot string
+ modRoot string
modFile *modfile.File
modFileData []byte
excluded map[module.Version]bool
// To make permanent changes to the require statements
// in go.mod, edit it before calling ImportPaths or LoadBuildList.
func ModFile() *modfile.File {
+ Init()
+ if modFile == nil {
+ die()
+ }
return modFile
}
func BinDir() string {
- MustInit()
+ Init()
return filepath.Join(gopath, "bin")
}
var inGOPATH bool // running in GOPATH/src
+// Init determines whether module mode is enabled, locates the root of the
+// current module (if any), sets environment variables for Git subprocesses, and
+// configures the cfg, codehost, load, modfetch, and search packages for use
+// with modules.
func Init() {
if initialized {
return
}
if inGOPATH && !MustUseModules {
+ if CmdModInit {
+ die() // Don't init a module that we're just going to ignore.
+ }
// No automatic enabling in GOPATH.
if root, _ := FindModuleRoot(cwd, "", false); root != "" {
cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
if CmdModInit {
// Running 'go mod init': go.mod will be created in current directory.
- ModRoot = cwd
+ modRoot = cwd
} else {
- ModRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
- if !MustUseModules {
- if ModRoot == "" {
- return
- }
- if search.InDir(ModRoot, os.TempDir()) == "." {
- // If you create /tmp/go.mod for experimenting,
- // then any tests that create work directories under /tmp
- // will find it and get modules when they're not expecting them.
- // It's a bit of a peculiar thing to disallow but quite mysterious
- // when it happens. See golang.org/issue/26708.
- ModRoot = ""
- fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
+ modRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
+ if modRoot == "" {
+ if !MustUseModules {
+ // GO111MODULE is 'auto' (or unset), and we can't find a module root.
+ // Stay in GOPATH mode.
return
}
+ } else if search.InDir(modRoot, os.TempDir()) == "." {
+ // If you create /tmp/go.mod for experimenting,
+ // then any tests that create work directories under /tmp
+ // will find it and get modules when they're not expecting them.
+ // It's a bit of a peculiar thing to disallow but quite mysterious
+ // when it happens. See golang.org/issue/26708.
+ modRoot = ""
+ fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
}
}
+ // We're in module mode. Install the hooks to make it work.
+
+ if c := cache.Default(); c == nil {
+ // With modules, there are no install locations for packages
+ // other than the build cache.
+ base.Fatalf("go: cannot use modules with build cache disabled")
+ }
+
+ list := filepath.SplitList(cfg.BuildContext.GOPATH)
+ if len(list) == 0 || list[0] == "" {
+ base.Fatalf("missing $GOPATH")
+ }
+ gopath = list[0]
+ if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
+ base.Fatalf("$GOPATH/go.mod exists but should not")
+ }
+
+ oldSrcMod := filepath.Join(list[0], "src/mod")
+ pkgMod := filepath.Join(list[0], "pkg/mod")
+ infoOld, errOld := os.Stat(oldSrcMod)
+ _, errMod := os.Stat(pkgMod)
+ if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
+ os.Rename(oldSrcMod, pkgMod)
+ }
+
+ modfetch.PkgMod = pkgMod
+ codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
+
cfg.ModulesEnabled = true
load.ModBinDir = BinDir
load.ModLookup = Lookup
load.ModImportFromFiles = ImportFromFiles
load.ModDirImportPath = DirImportPath
- search.SetModRoot(ModRoot)
+ if modRoot == "" {
+ // We're in module mode, but not inside a module.
+ //
+ // If the command is 'go get' or 'go list' and all of the args are in the
+ // same existing module, we could use that module's download directory in
+ // the module cache as the module root, applying any replacements and/or
+ // exclusions specified by that module. However, that would leave us in a
+ // strange state: we want 'go get' to be consistent with 'go list', and 'go
+ // list' should be able to operate on multiple modules. Moreover, the 'get'
+ // target might specify relative file paths (e.g. in the same repository) as
+ // replacements, and we would not be able to apply those anyway: we would
+ // need to either error out or ignore just those replacements, when a build
+ // from an empty module could proceed without error.
+ //
+ // Instead, we'll operate as though we're in some ephemeral external module,
+ // ignoring all replacements and exclusions uniformly.
+
+ // Normally we check sums using the go.sum file from the main module, but
+ // without a main module we do not have an authoritative go.sum file.
+ //
+ // TODO(bcmills): In Go 1.13, check sums when outside the main module.
+ //
+ // One possible approach is to merge the go.sum files from all of the
+ // modules we download: that doesn't protect us against bad top-level
+ // modules, but it at least ensures consistency for transitive dependencies.
+ } else {
+ modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
+ search.SetModRoot(modRoot)
+ }
}
func init() {
}
// Enabled reports whether modules are (or must be) enabled.
-// If modules must be enabled but are not, Enabled returns true
+// If modules are enabled but there is no main module, Enabled returns true
// and then the first use of module information will call die
-// (usually through InitMod and MustInit).
+// (usually through MustModRoot).
func Enabled() bool {
- if !initialized {
- panic("go: Enabled called before Init")
- }
- return ModRoot != "" || MustUseModules
+ Init()
+ return modRoot != "" || MustUseModules
}
-// MustInit calls Init if needed and checks that
-// modules are enabled and the main module has been found.
-// If not, MustInit calls base.Fatalf with an appropriate message.
-func MustInit() {
- if Init(); ModRoot == "" {
+// ModRoot returns the root of the main module.
+// It calls base.Fatalf if there is no main module.
+func ModRoot() string {
+ if !HasModRoot() {
die()
}
- if c := cache.Default(); c == nil {
- // With modules, there are no install locations for packages
- // other than the build cache.
- base.Fatalf("go: cannot use modules with build cache disabled")
- }
+ return modRoot
}
-// Failed reports whether module loading failed.
-// If Failed returns true, then any use of module information will call die.
-func Failed() bool {
+// HasModRoot reports whether a main module is present.
+// HasModRoot may return false even if Enabled returns true: for example, 'get'
+// does not require a main module.
+func HasModRoot() bool {
Init()
- return cfg.ModulesEnabled && ModRoot == ""
+ return modRoot != ""
}
+// printStackInDie causes die to print a stack trace.
+//
+// It is enabled by the testgo tag, and helps to diagnose paths that
+// unexpectedly require a main module.
+var printStackInDie = false
+
func die() {
+ if printStackInDie {
+ debug.PrintStack()
+ }
if os.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
+// InitMod sets Target and, if there is a main module, parses the initial build
+// list from its go.mod file, creating and populating that file if needed.
func InitMod() {
- MustInit()
- if modFile != nil {
+ if len(buildList) > 0 {
return
}
- list := filepath.SplitList(cfg.BuildContext.GOPATH)
- if len(list) == 0 || list[0] == "" {
- base.Fatalf("missing $GOPATH")
- }
- gopath = list[0]
- if _, err := os.Stat(filepath.Join(gopath, "go.mod")); err == nil {
- base.Fatalf("$GOPATH/go.mod exists but should not")
- }
-
- oldSrcMod := filepath.Join(list[0], "src/mod")
- pkgMod := filepath.Join(list[0], "pkg/mod")
- infoOld, errOld := os.Stat(oldSrcMod)
- _, errMod := os.Stat(pkgMod)
- if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
- os.Rename(oldSrcMod, pkgMod)
+ Init()
+ if modRoot == "" {
+ Target = module.Version{Path: "main"}
+ buildList = []module.Version{Target}
+ return
}
- modfetch.PkgMod = pkgMod
- modfetch.GoSumFile = filepath.Join(ModRoot, "go.sum")
- codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
-
if CmdModInit {
// Running go mod init: do legacy module conversion
legacyModInit()
return
}
- gomod := filepath.Join(ModRoot, "go.mod")
+ gomod := filepath.Join(modRoot, "go.mod")
data, err := ioutil.ReadFile(gomod)
if err != nil {
if os.IsNotExist(err) {
if len(f.Syntax.Stmt) == 0 || f.Module == nil {
// Empty mod file. Must add module path.
- path, err := FindModulePath(ModRoot)
+ path, err := FindModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
func legacyModInit() {
if modFile == nil {
- path, err := FindModulePath(ModRoot)
+ path, err := FindModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
addGoStmt()
for _, name := range altConfigs {
- cfg := filepath.Join(ModRoot, name)
+ cfg := filepath.Join(modRoot, name)
data, err := ioutil.ReadFile(cfg)
if err == nil {
convert := modconv.Converters[name]
return
}
+ // If we aren't in a module, we don't have anywhere to write a go.mod file.
+ if modRoot == "" {
+ return
+ }
+
if loaded != nil {
reqs := MinReqs()
min, err := reqs.Required(Target)
unlock := modfetch.SideLock()
defer unlock()
- file := filepath.Join(ModRoot, "go.mod")
+ file := filepath.Join(modRoot, "go.mod")
old, err := ioutil.ReadFile(file)
if !bytes.Equal(old, modFileData) {
if bytes.Equal(old, new) {
)
func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
- mods := listModules(args)
+ mods := listModules(args, listVersions)
if listU || listVersions {
var work par.Work
for _, m := range mods {
return mods
}
-func listModules(args []string) []*modinfo.ModulePublic {
+func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
LoadBuildList()
if len(args) == 0 {
return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)}
}
matched := false
for i, m := range buildList {
+ if i == 0 && !HasModRoot() {
+ // The root module doesn't actually exist: omit it.
+ continue
+ }
if match(m.Path) {
matched = true
if !matchedBuildList[i] {
}
if !matched {
if literal {
+ if listVersions {
+ // Don't make the user provide an explicit '@latest' when they're
+ // explicitly asking what the available versions are.
+ // Instead, resolve the module, even if it isn't an existing dependency.
+ info, err := Query(arg, "latest", nil)
+ if err == nil {
+ mods = append(mods, moduleInfo(module.Version{Path: arg, Version: info.Version}, false))
+ continue
+ }
+ }
mods = append(mods, &modinfo.ModulePublic{
Path: arg,
Error: &modinfo.ModuleError{
// Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks.
- if dir == ModRoot {
+ if modRoot != "" && dir == modRoot {
pkg = Target.Path
- } else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) && !strings.Contains(dir[len(ModRoot):], "@") {
- suffix := filepath.ToSlash(dir[len(ModRoot):])
+ } else if modRoot != "" && strings.HasPrefix(dir, modRoot+string(filepath.Separator)) && !strings.Contains(dir[len(modRoot):], "@") {
+ suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
// TODO getmode vendor check
pkg = strings.TrimPrefix(suffix, "/vendor/")
} else {
pkg = ""
if !iterating {
+ ModRoot()
base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
}
}
// DirImportPath returns the effective import path for dir,
// provided it is within the main module, or else returns ".".
func DirImportPath(dir string) string {
+ if modRoot == "" {
+ return "."
+ }
+
if !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, dir)
} else {
dir = filepath.Clean(dir)
}
- if dir == ModRoot {
+ if dir == modRoot {
return Target.Path
}
- if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
- suffix := filepath.ToSlash(dir[len(ModRoot):])
+ if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
+ suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
return strings.TrimPrefix(suffix, "/vendor/")
}
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if modFile == nil {
- // Happens during testing.
+ // Happens during testing and if invoking 'go get' or 'go list' outside a module.
return module.Version{}
}
vendorOnce.Do(func() {
vendorList = nil
vendorMap = make(map[string]module.Version)
- data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "vendor/modules.txt"))
+ data, _ := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
var m module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
- if modFile.Go != nil {
+ if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
var list []module.Version
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
- dir = filepath.Join(ModRoot, dir)
+ dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod)
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
- return ModRoot, true, nil
+ return ModRoot(), true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
- dir = filepath.Join(ModRoot, dir)
+ dir = filepath.Join(ModRoot(), dir)
}
return dir, true, nil
}
// If the path is in the main module and the query is "latest",
// QueryPackage returns Target as the version.
func QueryPackage(path, query string, allowed func(module.Version) bool) (module.Version, *modfetch.RevInfo, error) {
- if _, ok := dirInModule(path, Target.Path, ModRoot, true); ok {
- if query != "latest" {
- return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path)
- }
- if !allowed(Target) {
- return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path)
+ if HasModRoot() {
+ if _, ok := dirInModule(path, Target.Path, modRoot, true); ok {
+ if query != "latest" {
+ return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path)
+ }
+ if !allowed(Target) {
+ return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path)
+ }
+ return Target, &modfetch.RevInfo{Version: Target.Version}, nil
}
- return Target, &modfetch.RevInfo{Version: Target.Version}, nil
}
finalErr := errMissing
}
var root string
if mod.Version == "" {
- root = ModRoot
+ if !HasModRoot() {
+ continue // If there is no main module, we can't search in it.
+ }
+ root = ModRoot()
} else {
var err error
root, _, err = fetch(mod)
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modload
+
+func init() {
+ printStackInDie = true
+}
--- /dev/null
+example.com/printversion v0.1.0
+
+-- .mod --
+module example.com/printversion
+-- .info --
+{"Version":"v0.1.0"}
+-- README.txt --
+There is no go.mod file for this version of the module.
+-- printversion.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime/debug"
+
+ _ "example.com/version"
+)
+
+func main() {
+ info, _ := debug.ReadBuildInfo()
+ fmt.Fprintf(os.Stdout, "path is %s\n", info.Path)
+ fmt.Fprintf(os.Stdout, "main is %s %s\n", info.Main.Path, info.Main.Version)
+ for _, m := range info.Deps {
+ fmt.Fprintf(os.Stdout, "using %s %s\n", m.Path, m.Version)
+ }
+}
--- /dev/null
+example.com/printversion v1.0.0
+
+-- .mod --
+module example.com/printversion
+
+require example.com/version v1.0.0
+replace example.com/version v1.0.0 => ../oops v0.0.0
+exclude example.com/version v1.1.0
+-- .info --
+{"Version":"v1.0.0"}
+-- go.mod --
+module example.com/printversion
+
+require example.com/version v1.0.0
+replace example.com/version v1.0.0 => ../oops v0.0.0
+exclude example.com/version v1.0.1
+-- printversion.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime/debug"
+
+ _ "example.com/version"
+)
+
+func main() {
+ info, _ := debug.ReadBuildInfo()
+ fmt.Fprintf(os.Stdout, "path is %s\n", info.Path)
+ fmt.Fprintf(os.Stdout, "main is %s %s\n", info.Main.Path, info.Main.Version)
+ for _, m := range info.Deps {
+ fmt.Fprintf(os.Stdout, "using %s %s\n", m.Path, m.Version)
+ }
+}
--- /dev/null
+example.com/version v1.0.0
+written by hand
+
+-- .mod --
+module example.com/version
+-- .info --
+{"Version":"v1.0.0"}
+-- version.go --
+package version
+
+const V = "v1.0.0"
--- /dev/null
+example.com/version v1.0.1
+written by hand
+
+-- .mod --
+module example.com/version
+-- .info --
+{"Version":"v1.0.1"}
+-- version.go --
+package version
+
+const V = "v1.0.1"
--- /dev/null
+example.com/version v1.1.0
+written by hand
+
+-- .mod --
+module example.com/version
+-- .info --
+{"Version":"v1.1.0"}
+-- version.go --
+package version
+
+const V = "v1.1.0"
cd $GOPATH/src/x/y
go env GOMOD
-! stdout .
+stdout 'NUL|/dev/null'
! go list -m
stderr 'cannot find main module'
! go get
! go install
! go list
-! go run x.go
+! go run
! go test
! go vet
--- /dev/null
+env GO111MODULE=on
+
+# This script tests commands in module mode outside of any module.
+#
+# First, ensure that we really are in module mode, and that we really don't have
+# a go.mod file.
+go env GOMOD
+stdout 'NUL|/dev/null'
+
+
+# 'go list' without arguments implicitly operates on the current directory,
+# which is not in a module.
+! go list
+stderr 'cannot find main module'
+! go list -m
+stderr 'cannot find main module'
+# 'go list' in the working directory should fail even if there is a a 'package
+# main' present: without a main module, we do not know its package path.
+! go list ./foo
+stderr 'cannot find main module'
+
+# 'go list all' lists the transitive import graph of the main module,
+# which is empty if there is no main module.
+go list all
+! stdout .
+stderr 'warning: "all" matched no packages'
+go list -m all
+stderr 'warning: pattern "all" matched no module dependencies'
+
+# 'go list' on standard-library packages should work, since they do not depend
+# on the contents of any module.
+go list -deps cmd
+stdout '^fmt$'
+stdout '^cmd/go$'
+
+go list $GOROOT/src/fmt
+stdout '^fmt$'
+
+
+# 'go list -m' with an explicit version should resolve that version.
+go list -m example.com/version@latest
+stdout 'example.com/version v1.1.0'
+
+# 'go list -m -versions' should succeed even without an explicit version.
+go list -m -versions example.com/version
+stdout 'v1.0.0\s+v1.0.1\s+v1.1.0'
+
+# 'go list -m <mods> all' does not include the dependencies of <mods> in the computation of 'all'.
+go list -m example.com/printversion@v1.0.0 all
+stdout 'example.com/printversion v1.0.0'
+stderr 'warning: pattern "all" matched no module dependencies'
+! stdout 'example.com/version'
+
+
+# 'go clean' should skip the current directory if it isn't in a module.
+go clean -n
+! stdout .
+! stderr .
+
+# 'go mod graph' should not display anything, since there are no active modules.
+go mod graph
+! stdout .
+! stderr .
+
+# 'go mod why' should report that nothing is a dependency.
+go mod why -m example.com/version
+stdout 'does not need'
+
+
+# 'go mod edit', 'go mod tidy', and 'go mod fmt' should fail:
+# there is no go.mod file to edit.
+! go mod tidy
+stderr 'cannot find main module'
+! go mod edit -fmt
+stderr 'cannot find main module'
+! go mod edit -require example.com/version@v1.0.0
+stderr 'cannot find main module'
+
+
+# 'go mod download' should download exactly the requested module without dependencies.
+rm -r $GOPATH/pkg/mod/cache/download/example.com
+go mod download example.com/printversion@v1.0.0
+exists $GOPATH/pkg/mod/cache/download/example.com/printversion/@v/v1.0.0.zip
+! exists $GOPATH/pkg/mod/cache/download/example.com/version/@v/v1.0.0.zip
+
+# 'go mod vendor' should fail: it starts by clearing the existing vendor
+# directory, and we don't know where that is.
+! go mod vendor
+stderr 'cannot find main module'
+
+# 'go mod verify' should succeed: we have no modules to verify.
+go mod verify
+stdout 'all modules verified'
+! stderr .
+
+
+# 'go get' without arguments implicitly operates on the main module, and thus
+# should fail.
+! go get
+stderr 'cannot find main module'
+! go get -u
+stderr 'cannot find main module'
+! go get -u ./foo
+stderr 'cannot find main module'
+
+# 'go get -u all' upgrades the transitive import graph of the main module,
+# which is empty.
+go get -u all
+! stdout .
+stderr 'warning: "all" matched no packages'
+
+# 'go get -m' should check the proposed module graph for consistency,
+# even though it will not be saved anywhere.
+! go get -m example.com/printversion@v1.0.0 example.com/version@none
+stderr 'inconsistent versions'
+
+# 'go get -d' should download and extract the source code needed to build the requested version.
+rm -r $GOPATH/pkg/mod/example.com
+go get -d example.com/printversion@v1.0.0
+exists $GOPATH/pkg/mod/example.com/printversion@v1.0.0
+exists $GOPATH/pkg/mod/example.com/version@v1.0.0
+
+
+# 'go build' without arguments implicitly operates on the current directory, and should fail.
+cd foo
+! go build
+stderr 'cannot find main module'
+cd ..
+
+# 'go build' of a non-module directory should fail too.
+! go build ./foo
+stderr 'cannot find main module'
+
+# However, 'go build' should succeed for standard-library packages.
+go build -n fmt
+
+
+# TODO(golang.org/issue/28992): 'go doc' should document the latest version.
+# For now it does not.
+! go doc example.com/version
+stderr 'no such package'
+
+# 'go install' with a version should fail due to syntax.
+! go install example.com/printversion@v1.0.0
+stderr 'can only use path@version syntax with'
+
+
+# The remainder of the test checks dependencies by linking and running binaries.
+[short] stop
+
+# 'go get' of a binary without a go.mod should install the requested version,
+# resolving outside dependencies to the latest available versions.
+go get example.com/printversion@v0.1.0
+exec ../bin/printversion
+stdout 'path is example.com/printversion'
+stdout 'main is example.com/printversion v0.1.0'
+stdout 'using example.com/version v1.1.0'
+
+# 'go get' of a versioned binary should build and install the latest version
+# using its minimal module requirements, ignoring replacements and exclusions.
+go get example.com/printversion
+exec ../bin/printversion
+stdout 'path is example.com/printversion'
+stdout 'main is example.com/printversion v1.0.0'
+stdout 'using example.com/version v1.0.0'
+
+# 'go get -u=patch' should patch dependencies before installing,
+# again ignoring replacements and exclusions.
+go get -u=patch example.com/printversion@v1.0.0
+exec ../bin/printversion
+stdout 'path is example.com/printversion'
+stdout 'main is example.com/printversion v1.0.0'
+stdout 'using example.com/version v1.0.1'
+
+# 'go install' without a version should install the latest version
+# using its minimal module requirements.
+go install example.com/printversion
+exec ../bin/printversion
+stdout 'path is example.com/printversion'
+stdout 'main is example.com/printversion v1.0.0'
+stdout 'using example.com/version v1.0.0'
+
+# 'go run' should use 'main' as the effective module and import path.
+go run ./foo/foo.go
+stdout 'path is \.$'
+stdout 'main is main \(devel\)'
+stdout 'using example.com/version v1.1.0'
+
+
+-- README.txt --
+There is no go.mod file in the working directory.
+
+-- foo/foo.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime/debug"
+
+ _ "example.com/version"
+)
+
+func main() {
+ info, ok := debug.ReadBuildInfo()
+ if !ok {
+ panic("missing build info")
+ }
+ fmt.Fprintf(os.Stdout, "path is %s\n", info.Path)
+ fmt.Fprintf(os.Stdout, "main is %s %s\n", info.Main.Path, info.Main.Version)
+ for _, m := range info.Deps {
+ fmt.Fprintf(os.Stdout, "using %s %s\n", m.Path, m.Version)
+ }
+}