Commit cdbd4d49 authored by Bryan C. Mills's avatar Bryan C. Mills

cmd/go: enable module mode without a main module when GO111MODULE=on

This is as minimal a change as I could comfortably make to enable 'go
get' outside of a module for 1.12.

In general, commands invoked in module mode while outside of a module
operate as though they are in a module with an initially-empty go.mod
file. ('go env GOMOD' reports os.DevNull.)

Commands that operate on the current directory (such as 'go list' and
'go get -u' without arguments) fail: without a module definition, we
don't know the package path. Likewise, commands whose sole purpose is
to write files within the main module (such as 'go mod edit' and 'go
mod vendor') fail, since we don't know where to write their output.

Since the go.sum file for the main module is authoritative, we do not
check go.sum files when operating outside of a module. I plan to
revisit that when the tree opens for 1.13.

We may also want to revisit the behavior of 'go list': it would be
useful to be able to query individual packages (and dependencies of
those packages) within versioned modules, but today we only allow
versioned paths in conjunction with the '-m' flag.

Fixes #24250

RELNOTE=yes

Change-Id: I028c323ddea27693a92ad0aa4a6a55d5e3f43f2c
Reviewed-on: https://go-review.googlesource.com/c/148517
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent a2b4ac6c
...@@ -105,10 +105,7 @@ func init() { ...@@ -105,10 +105,7 @@ func init() {
} }
func runClean(cmd *base.Command, args []string) { func runClean(cmd *base.Command, args []string) {
if len(args) == 0 && modload.Failed() { if len(args) > 0 || !modload.Enabled() || modload.HasModRoot() {
// Don't try to clean current directory,
// which will cause modload to base.Fatalf.
} else {
for _, pkg := range load.PackagesAndErrors(args) { for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg) clean(pkg)
} }
......
...@@ -115,8 +115,10 @@ func findEnv(env []cfg.EnvVar, name string) string { ...@@ -115,8 +115,10 @@ func findEnv(env []cfg.EnvVar, name string) string {
// ExtraEnvVars returns environment variables that should not leak into child processes. // ExtraEnvVars returns environment variables that should not leak into child processes.
func ExtraEnvVars() []cfg.EnvVar { func ExtraEnvVars() []cfg.EnvVar {
gomod := "" gomod := ""
if modload.Init(); modload.ModRoot != "" { if modload.HasModRoot() {
gomod = filepath.Join(modload.ModRoot, "go.mod") gomod = filepath.Join(modload.ModRoot(), "go.mod")
} else if modload.Enabled() {
gomod = os.DevNull
} }
return []cfg.EnvVar{ return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod}, {Name: "GOMOD", Value: gomod},
......
...@@ -1515,7 +1515,9 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { ...@@ -1515,7 +1515,9 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
} }
if cfg.ModulesEnabled { if cfg.ModulesEnabled {
p.Module = ModPackageModuleInfo(p.ImportPath) if !p.Internal.CmdlineFiles {
p.Module = ModPackageModuleInfo(p.ImportPath)
}
if p.Name == "main" { if p.Name == "main" {
p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps) p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps)
} }
......
...@@ -157,8 +157,7 @@ func runEdit(cmd *base.Command, args []string) { ...@@ -157,8 +157,7 @@ func runEdit(cmd *base.Command, args []string) {
if len(args) == 1 { if len(args) == 1 {
gomod = args[0] gomod = args[0]
} else { } else {
modload.MustInit() gomod = filepath.Join(modload.ModRoot(), "go.mod")
gomod = filepath.Join(modload.ModRoot, "go.mod")
} }
if *editModule != "" { if *editModule != "" {
......
...@@ -43,7 +43,7 @@ func runVendor(cmd *base.Command, args []string) { ...@@ -43,7 +43,7 @@ func runVendor(cmd *base.Command, args []string) {
} }
pkgs := modload.LoadVendor() pkgs := modload.LoadVendor()
vdir := filepath.Join(modload.ModRoot, "vendor") vdir := filepath.Join(modload.ModRoot(), "vendor")
if err := os.RemoveAll(vdir); err != nil { if err := os.RemoveAll(vdir); err != nil {
base.Fatalf("go mod vendor: %v", err) base.Fatalf("go mod vendor: %v", err)
} }
......
...@@ -281,8 +281,8 @@ func runGet(cmd *base.Command, args []string) { ...@@ -281,8 +281,8 @@ func runGet(cmd *base.Command, args []string) {
base.Errorf("go get %s: %v", arg, err) base.Errorf("go get %s: %v", arg, err)
continue continue
} }
if !str.HasFilePathPrefix(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) base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot())
continue continue
} }
// TODO: Check if abs is inside a nested module. // TODO: Check if abs is inside a nested module.
......
...@@ -98,8 +98,8 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { ...@@ -98,8 +98,8 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
Path: m.Path, Path: m.Path,
Version: m.Version, Version: m.Version,
Main: true, Main: true,
Dir: ModRoot, Dir: ModRoot(),
GoMod: filepath.Join(ModRoot, "go.mod"), GoMod: filepath.Join(ModRoot(), "go.mod"),
} }
if modFile.Go != nil { if modFile.Go != nil {
info.GoVersion = modFile.Go.Version info.GoVersion = modFile.Go.Version
...@@ -117,7 +117,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { ...@@ -117,7 +117,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
} }
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
info.Dir = filepath.Join(ModRoot, "vendor", m.Path) info.Dir = filepath.Join(ModRoot(), "vendor", m.Path)
return info return info
} }
...@@ -171,7 +171,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic { ...@@ -171,7 +171,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
if filepath.IsAbs(r.Path) { if filepath.IsAbs(r.Path) {
info.Replace.Dir = r.Path info.Replace.Dir = r.Path
} else { } else {
info.Replace.Dir = filepath.Join(ModRoot, r.Path) info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
} }
} }
complete(info.Replace) complete(info.Replace)
......
...@@ -67,8 +67,8 @@ func Import(path string) (m module.Version, dir string, err error) { ...@@ -67,8 +67,8 @@ func Import(path string) (m module.Version, dir string, err error) {
// -mod=vendor is special. // -mod=vendor is special.
// Everything must be in the main module or the main module's vendor directory. // Everything must be in the main module or the main module's vendor directory.
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
mainDir, mainOK := dirInModule(path, Target.Path, ModRoot, true) mainDir, mainOK := dirInModule(path, Target.Path, ModRoot(), true)
vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot, "vendor"), false) vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
if mainOK && vendorOK { if mainOK && vendorOK {
return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir) return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir)
} }
......
This diff is collapsed.
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
) )
func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic { func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
mods := listModules(args) mods := listModules(args, listVersions)
if listU || listVersions { if listU || listVersions {
var work par.Work var work par.Work
for _, m := range mods { for _, m := range mods {
...@@ -39,7 +39,7 @@ func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePubli ...@@ -39,7 +39,7 @@ func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePubli
return mods return mods
} }
func listModules(args []string) []*modinfo.ModulePublic { func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
LoadBuildList() LoadBuildList()
if len(args) == 0 { if len(args) == 0 {
return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)} return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)}
...@@ -83,6 +83,10 @@ func listModules(args []string) []*modinfo.ModulePublic { ...@@ -83,6 +83,10 @@ func listModules(args []string) []*modinfo.ModulePublic {
} }
matched := false matched := false
for i, m := range buildList { for i, m := range buildList {
if i == 0 && !HasModRoot() {
// The root module doesn't actually exist: omit it.
continue
}
if match(m.Path) { if match(m.Path) {
matched = true matched = true
if !matchedBuildList[i] { if !matchedBuildList[i] {
...@@ -93,6 +97,16 @@ func listModules(args []string) []*modinfo.ModulePublic { ...@@ -93,6 +97,16 @@ func listModules(args []string) []*modinfo.ModulePublic {
} }
if !matched { if !matched {
if literal { 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{ mods = append(mods, &modinfo.ModulePublic{
Path: arg, Path: arg,
Error: &modinfo.ModuleError{ Error: &modinfo.ModuleError{
......
...@@ -101,10 +101,10 @@ func ImportPaths(patterns []string) []*search.Match { ...@@ -101,10 +101,10 @@ func ImportPaths(patterns []string) []*search.Match {
// Note: The checks for @ here are just to avoid misinterpreting // Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar). // the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks. // It's not strictly necessary but helpful to keep the checks.
if dir == ModRoot { if modRoot != "" && dir == modRoot {
pkg = Target.Path pkg = Target.Path
} else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) && !strings.Contains(dir[len(ModRoot):], "@") { } else if modRoot != "" && strings.HasPrefix(dir, modRoot+string(filepath.Separator)) && !strings.Contains(dir[len(modRoot):], "@") {
suffix := filepath.ToSlash(dir[len(ModRoot):]) suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") { if strings.HasPrefix(suffix, "/vendor/") {
// TODO getmode vendor check // TODO getmode vendor check
pkg = strings.TrimPrefix(suffix, "/vendor/") pkg = strings.TrimPrefix(suffix, "/vendor/")
...@@ -118,6 +118,7 @@ func ImportPaths(patterns []string) []*search.Match { ...@@ -118,6 +118,7 @@ func ImportPaths(patterns []string) []*search.Match {
} else { } else {
pkg = "" pkg = ""
if !iterating { if !iterating {
ModRoot()
base.Errorf("go: directory %s outside available modules", base.ShortPath(dir)) base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
} }
} }
...@@ -251,17 +252,21 @@ func ImportFromFiles(gofiles []string) { ...@@ -251,17 +252,21 @@ func ImportFromFiles(gofiles []string) {
// DirImportPath returns the effective import path for dir, // DirImportPath returns the effective import path for dir,
// provided it is within the main module, or else returns ".". // provided it is within the main module, or else returns ".".
func DirImportPath(dir string) string { func DirImportPath(dir string) string {
if modRoot == "" {
return "."
}
if !filepath.IsAbs(dir) { if !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, dir) dir = filepath.Join(cwd, dir)
} else { } else {
dir = filepath.Clean(dir) dir = filepath.Clean(dir)
} }
if dir == ModRoot { if dir == modRoot {
return Target.Path return Target.Path
} }
if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) { if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
suffix := filepath.ToSlash(dir[len(ModRoot):]) suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") { if strings.HasPrefix(suffix, "/vendor/") {
return strings.TrimPrefix(suffix, "/vendor/") return strings.TrimPrefix(suffix, "/vendor/")
} }
...@@ -810,7 +815,7 @@ func WhyDepth(path string) int { ...@@ -810,7 +815,7 @@ func WhyDepth(path string) int {
// a module.Version with Path == "". // a module.Version with Path == "".
func Replacement(mod module.Version) module.Version { func Replacement(mod module.Version) module.Version {
if modFile == nil { if modFile == nil {
// Happens during testing. // Happens during testing and if invoking 'go get' or 'go list' outside a module.
return module.Version{} return module.Version{}
} }
...@@ -887,7 +892,7 @@ func readVendorList() { ...@@ -887,7 +892,7 @@ func readVendorList() {
vendorOnce.Do(func() { vendorOnce.Do(func() {
vendorList = nil vendorList = nil
vendorMap = make(map[string]module.Version) 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 var m module.Version
for _, line := range strings.Split(string(data), "\n") { for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") { if strings.HasPrefix(line, "# ") {
...@@ -917,7 +922,7 @@ func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version { ...@@ -917,7 +922,7 @@ func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target { if mod == Target {
if modFile.Go != nil { if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version) r.versions.LoadOrStore(mod, modFile.Go.Version)
} }
var list []module.Version var list []module.Version
...@@ -937,7 +942,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) { ...@@ -937,7 +942,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
// TODO: need to slip the new version into the tags list etc. // TODO: need to slip the new version into the tags list etc.
dir := repl.Path dir := repl.Path
if !filepath.IsAbs(dir) { if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot, dir) dir = filepath.Join(ModRoot(), dir)
} }
gomod := filepath.Join(dir, "go.mod") gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod) data, err := ioutil.ReadFile(gomod)
...@@ -1052,13 +1057,13 @@ func (*mvsReqs) next(m module.Version) (module.Version, error) { ...@@ -1052,13 +1057,13 @@ func (*mvsReqs) next(m module.Version) (module.Version, error) {
func fetch(mod module.Version) (dir string, isLocal bool, err error) { func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target { if mod == Target {
return ModRoot, true, nil return ModRoot(), true, nil
} }
if r := Replacement(mod); r.Path != "" { if r := Replacement(mod); r.Path != "" {
if r.Version == "" { if r.Version == "" {
dir = r.Path dir = r.Path
if !filepath.IsAbs(dir) { if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot, dir) dir = filepath.Join(ModRoot(), dir)
} }
return dir, true, nil return dir, true, nil
} }
......
...@@ -210,14 +210,16 @@ func matchSemverPrefix(p, v string) bool { ...@@ -210,14 +210,16 @@ func matchSemverPrefix(p, v string) bool {
// If the path is in the main module and the query is "latest", // If the path is in the main module and the query is "latest",
// QueryPackage returns Target as the version. // QueryPackage returns Target as the version.
func QueryPackage(path, query string, allowed func(module.Version) bool) (module.Version, *modfetch.RevInfo, error) { 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 HasModRoot() {
if query != "latest" { if _, ok := dirInModule(path, Target.Path, modRoot, true); ok {
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 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 !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 finalErr := errMissing
......
...@@ -118,7 +118,10 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules [] ...@@ -118,7 +118,10 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
} }
var root string var root string
if mod.Version == "" { if mod.Version == "" {
root = ModRoot if !HasModRoot() {
continue // If there is no main module, we can't search in it.
}
root = ModRoot()
} else { } else {
var err error var err error
root, _, err = fetch(mod) root, _, err = fetch(mod)
......
// 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
}
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)
}
}
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)
}
}
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"
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"
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"
...@@ -38,7 +38,7 @@ stdout z[/\\]go.mod ...@@ -38,7 +38,7 @@ stdout z[/\\]go.mod
cd $GOPATH/src/x/y cd $GOPATH/src/x/y
go env GOMOD go env GOMOD
! stdout . stdout 'NUL|/dev/null'
! go list -m ! go list -m
stderr 'cannot find main module' stderr 'cannot find main module'
......
...@@ -16,7 +16,7 @@ go mod edit -json x.mod ...@@ -16,7 +16,7 @@ go mod edit -json x.mod
! go get ! go get
! go install ! go install
! go list ! go list
! go run x.go ! go run
! go test ! go test
! go vet ! go vet
......
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)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment