Commit ab724d43 authored by Jay Conrod's avatar Jay Conrod

cmd/go: make 'go get -t' consider test dependencies in module mode

Fixes #32037

Change-Id: I696fe2029e383746252f37fe8d30df71b5ac8a6c
Reviewed-on: https://go-review.googlesource.com/c/go/+/177677
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent 776e1709
...@@ -557,7 +557,7 @@ ...@@ -557,7 +557,7 @@
// //
// Usage: // Usage:
// //
// go get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages] // go get [-d] [-m] [-t] [-u] [-v] [-insecure] [build flags] [packages]
// //
// Get resolves and adds dependencies to the current development module // Get resolves and adds dependencies to the current development module
// and then builds and installs them. // and then builds and installs them.
...@@ -600,6 +600,9 @@ ...@@ -600,6 +600,9 @@
// are competing requirements for a particular module, then 'go get' resolves // are competing requirements for a particular module, then 'go get' resolves
// those requirements by taking the maximum requested version.) // those requirements by taking the maximum requested version.)
// //
// The -t flag instructs get to consider modules needed to build tests of
// packages specified on the command line.
//
// The -u flag instructs get to update dependencies to use newer minor or // The -u flag instructs get to update dependencies to use newer minor or
// patch releases when available. Continuing the previous example, // patch releases when available. Continuing the previous example,
// 'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). // 'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3).
...@@ -610,6 +613,9 @@ ...@@ -610,6 +613,9 @@
// 'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3), // 'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3),
// while 'go get -u=patch A' will use a patch release of A instead. // while 'go get -u=patch A' will use a patch release of A instead.
// //
// When the -t and -u flags are used together, get will update
// test dependencies as well.
//
// In general, adding a new dependency may require upgrading // In general, adding a new dependency may require upgrading
// existing dependencies to keep a working build, and 'go get' does // existing dependencies to keep a working build, and 'go get' does
// this automatically. Similarly, downgrading one dependency may // this automatically. Similarly, downgrading one dependency may
......
...@@ -31,7 +31,7 @@ import ( ...@@ -31,7 +31,7 @@ import (
var CmdGet = &base.Command{ var CmdGet = &base.Command{
// Note: -d -m -u are listed explicitly because they are the most common get flags. // Note: -d -m -u are listed explicitly because they are the most common get flags.
// Do not send CLs removing them because they're covered by [get flags]. // Do not send CLs removing them because they're covered by [get flags].
UsageLine: "go get [-d] [-m] [-u] [-v] [-insecure] [build flags] [packages]", UsageLine: "go get [-d] [-m] [-t] [-u] [-v] [-insecure] [build flags] [packages]",
Short: "add dependencies to current module and install them", Short: "add dependencies to current module and install them",
Long: ` Long: `
Get resolves and adds dependencies to the current development module Get resolves and adds dependencies to the current development module
...@@ -75,6 +75,9 @@ will use the latest A but then use B v1.2.3, as requested by A. (If there ...@@ -75,6 +75,9 @@ will use the latest A but then use B v1.2.3, as requested by A. (If there
are competing requirements for a particular module, then 'go get' resolves are competing requirements for a particular module, then 'go get' resolves
those requirements by taking the maximum requested version.) those requirements by taking the maximum requested version.)
The -t flag instructs get to consider modules needed to build tests of
packages specified on the command line.
The -u flag instructs get to update dependencies to use newer minor or The -u flag instructs get to update dependencies to use newer minor or
patch releases when available. Continuing the previous example, patch releases when available. Continuing the previous example,
'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). 'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3).
...@@ -85,6 +88,9 @@ Continuing the previous example, ...@@ -85,6 +88,9 @@ Continuing the previous example,
'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3), 'go get -u=patch A@latest' will use the latest A with B v1.2.4 (not B v1.2.3),
while 'go get -u=patch A' will use a patch release of A instead. while 'go get -u=patch A' will use a patch release of A instead.
When the -t and -u flags are used together, get will update
test dependencies as well.
In general, adding a new dependency may require upgrading In general, adding a new dependency may require upgrading
existing dependencies to keep a working build, and 'go get' does existing dependencies to keep a working build, and 'go get' does
this automatically. Similarly, downgrading one dependency may this automatically. Similarly, downgrading one dependency may
...@@ -261,9 +267,7 @@ func runGet(cmd *base.Command, args []string) { ...@@ -261,9 +267,7 @@ func runGet(cmd *base.Command, args []string) {
if *getFix { if *getFix {
fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
} }
if *getT { modload.LoadTests = *getT
fmt.Fprintf(os.Stderr, "go get: -t flag is a no-op when using modules\n")
}
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
base.Fatalf("go get: disabled by -mod=%s", cfg.BuildMod) base.Fatalf("go get: disabled by -mod=%s", cfg.BuildMod)
...@@ -781,25 +785,26 @@ func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader { ...@@ -781,25 +785,26 @@ func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
// Initialize work queue with root packages. // Initialize work queue with root packages.
seen := make(map[string]bool) seen := make(map[string]bool)
var work []string var work []string
for pkg := range pkgs { add := func(path string) {
seen[pkg] = true if !seen[path] {
for _, imp := range modload.PackageImports(pkg) { seen[path] = true
if !pkgs[imp] && !seen[imp] { work = append(work, path)
seen[imp] = true
work = append(work, imp)
} }
} }
for pkg := range pkgs {
add(pkg)
} }
for len(work) > 0 { for len(work) > 0 {
pkg := work[0] pkg := work[0]
work = work[1:] work = work[1:]
m := modload.PackageModule(pkg) m := modload.PackageModule(pkg)
u.upgrade[m.Path] = true u.upgrade[m.Path] = true
for _, imp := range modload.PackageImports(pkg) { imports, testImports := modload.PackageImports(pkg)
if !seen[imp] { for _, imp := range imports {
seen[imp] = true add(imp)
work = append(work, imp)
} }
for _, imp := range testImports {
add(imp)
} }
} }
} }
......
...@@ -496,17 +496,26 @@ func PackageModule(path string) module.Version { ...@@ -496,17 +496,26 @@ func PackageModule(path string) module.Version {
} }
// PackageImports returns the imports for the package named by the import path. // PackageImports returns the imports for the package named by the import path.
// It does not include test imports. It returns nil for unknown packages. // Test imports will be returned as well if tests were loaded for the package
func PackageImports(path string) []string { // (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) pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok { if !ok {
return nil return nil, nil
} }
imports := make([]string, len(pkg.imports)) imports = make([]string, len(pkg.imports))
for i, p := range pkg.imports { for i, p := range pkg.imports {
imports[i] = p.path imports[i] = p.path
} }
return imports 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
} }
// ModuleUsedDirectly reports whether the main module directly imports // ModuleUsedDirectly reports whether the main module directly imports
......
env GO111MODULE=on
# By default, 'go get' should ignore tests
cp go.mod.empty go.mod
go get m/a
! grep rsc.io/quote go.mod
# 'go get -t' should consider test dependencies of the named package.
cp go.mod.empty go.mod
go get -d -t m/a
grep 'rsc.io/quote v1.5.2$' go.mod
# 'go get -t' should not consider test dependencies of imported packages,
# including packages imported from tests.
cp go.mod.empty go.mod
go get -d -t m/b
! grep rsc.io/quote go.mod
# 'go get -t -u' should update test dependencies of the named package.
cp go.mod.empty go.mod
go mod edit -require=rsc.io/quote@v1.5.1
go get -d -t -u m/a
grep 'rsc.io/quote v1.5.2$' go.mod
# 'go get -t -u' should not add or update test dependencies
# of imported packages, including packages imported from tests.
cp go.mod.empty go.mod
go get -d -t -u m/b
! grep rsc.io/quote go.mod
go mod edit -require=rsc.io/quote@v1.5.1
go get -d -t -u m/b
grep 'rsc.io/quote v1.5.1$' go.mod
# 'go get all' should consider test dependencies with or without -t.
cp go.mod.empty go.mod
go get all
grep 'rsc.io/quote v1.5.2$' go.mod
-- go.mod.empty --
module m
-- a/a.go --
package a
-- a/a_test.go --
package a_test
import _ "rsc.io/quote"
-- b/b.go --
package b
import _ "m/a"
-- b/b_test.go --
package b_test
import _ "m/a"
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