CmdModInit bool // running 'go mod init'
CmdModModule string // module argument for 'go mod init'
+
+ allowMissingModuleImports bool
)
// ModFile returns the parsed go.mod file.
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.
+ // Commands like 'go build', 'go run', 'go list' have no go.mod file to
+ // read or write. They would need to find and download the latest versions
+ // of a potentially large number of modules with no way to save version
+ // information. We can succeed slowly (but not reproducibly), but that's
+ // not usually a good experience.
//
- // 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.
+ // Instead, we forbid resolving import paths to modules other than std and
+ // cmd. Users may still build packages specified with .go files on the
+ // command line, but they'll see an error if those files import anything
+ // outside std.
//
- // TODO(bcmills): In Go 1.13, check sums when outside the main module.
+ // This can be overridden by calling AllowMissingModuleImports.
+ // For example, 'go get' does this, since it is expected to resolve paths.
//
- // 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.
+ // See golang.org/issue/32027.
} else {
modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
search.SetModRoot(modRoot)
}
}
+// AllowMissingModuleImports allows import paths to be resolved to modules
+// when there is no module root. Normally, this is forbidden because it's slow
+// and there's no way to make the result reproducible, but some commands
+// like 'go get' are expected to do this.
+func AllowMissingModuleImports() {
+ allowMissingModuleImports = true
+}
+
// modFileToBuildList initializes buildList from the modFile.
func modFileToBuildList() {
Target = modFile.Module.Mod
stdout '^command-line-arguments$'
# '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
+! go list ./needmod
stderr 'cannot find main module'
# 'go list all' lists the transitive import graph of the main module,
stdout '^fmt$'
# 'go list' should work with file arguments.
-go list ./foo/foo.go
+go list ./needmod/needmod.go
stdout 'command-line-arguments'
# 'go list -m' with an explicit version should resolve that version.
stderr 'cannot find main module'
! go get -u
stderr 'cannot find main module'
-! go get -u ./foo
+! go get -u ./needmod
stderr 'cannot find main module'
# 'go get -u all' upgrades the transitive import graph of the main module,
# 'go build' without arguments implicitly operates on the current directory, and should fail.
-cd foo
+cd needmod
! go build
stderr 'cannot find main module'
cd ..
# 'go build' of a non-module directory should fail too.
-! go build ./foo
+! go build ./needmod
stderr 'cannot find main module'
-# However, 'go build' should succeed for standard-library packages.
+# 'go build' of source files should fail if they import anything outside std.
+! go build -n ./needmod/needmod.go
+stderr 'needmod[/\\]needmod.go:10:2: cannot find module providing package example.com/version: working directory is not part of a module'
+
+# 'go build' of source files should succeed if they do not import anything outside std.
+go build -n -o ignore ./stdonly/stdonly.go
+
+# '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' without arguments implicitly operates on the current directory, and should fail.
+# TODO(golang.org/issue/32027): currently, it succeeds.
+cd needmod
+go doc
+cd ..
+
+# 'go doc' of a non-module directory should also succeed.
+go doc ./needmod
+
+# 'go doc' should succeed for standard-library packages.
+go doc fmt
+
+# 'go doc' should fail for a package path outside a module.
! go doc example.com/version
-stderr 'no such package'
+stderr 'doc: cannot find module providing package example.com/version: working directory is not part of a module'
# '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'
+# 'go install' should fail if a package argument must be resolved to a module.
+! go install example.com/printversion
+stderr 'cannot find module providing package example.com/printversion: working directory is not part of a module'
+
+# 'go install' should fail if a source file imports a package that must be
+# resolved to a module.
+! go install ./needmod/needmod.go
+stderr 'needmod[/\\]needmod.go:10:2: cannot find module providing package example.com/version: working directory is not part of a module'
+
+
+# 'go run' with a verison should fail due to syntax.
+! go run example.com/printversion@v1.0.0
+stderr 'can only use path@version syntax with'
+
+# 'go run' should fail if a package argument must be resolved to a module.
+! go run example.com/printversion
+stderr 'cannot find module providing package example.com/printversion: working directory is not part of a module'
+
+# 'go run' should fail if a source file imports a package that must be
+# resolved to a module.
+! go run ./needmod/needmod.go
+stderr 'needmod[/\\]needmod.go:10:2: cannot find module providing package example.com/version: working directory is not part of a module'
+
# 'go fmt' should be able to format files outside of a module.
-go fmt foo/foo.go
+go fmt needmod/needmod.go
# 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.
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
+# 'go run' should work with file arguments if they don't import anything
+# outside std.
+go run ./stdonly/stdonly.go
stdout 'path is command-line-arguments$'
stdout 'main is command-line-arguments \(devel\)'
-stdout 'using example.com/version v1.1.0'
# 'go generate' should work with file arguments.
-[exec:touch] go generate ./foo/foo.go
-[exec:touch] exists ./foo/gen.txt
+[exec:touch] go generate ./needmod/needmod.go
+[exec:touch] exists ./needmod/gen.txt
# 'go install' should work with file arguments.
-go install ./foo/foo.go
+go install ./stdonly/stdonly.go
# 'go test' should work with file arguments.
-go test -v ./foo/foo_test.go
-stdout 'foo was tested'
+go test -v ./stdonly/stdonly_test.go
+stdout 'stdonly was tested'
# 'go vet' should work with file arguments.
-go vet ./foo/foo.go
+go vet ./stdonly/stdonly.go
-- README.txt --
There is no go.mod file in the working directory.
--- foo/foo.go --
+-- needmod/needmod.go --
//go:generate touch gen.txt
package main
}
}
--- foo/foo_test.go --
+-- stdonly/stdonly.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime/debug"
+)
+
+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)
+ }
+}
+
+-- stdonly/stdonly_test.go --
package main
import (
"testing"
)
-func TestFoo(t *testing.T) {
- fmt.Println("foo was tested")
+func Test(t *testing.T) {
+ fmt.Println("stdonly was tested")
}
+