diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 45278a6d714..eb9c38b6396 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -508,9 +508,8 @@ An import path is a pattern if it includes one or more "..." wildcards, each of which can match any string, including the empty string and strings containing slashes. Such a pattern expands to all package directories found in the GOPATH trees with names matching the -patterns. For example, encoding/... expands to all packages -in subdirectories of the encoding tree, while net... expands to -net and all its subdirectories. +patterns. As a special case, x/... matches x as well as x's subdirectories. +For example, net/... expands to net and packages in its subdirectories. An import path can also name a package to be downloaded from a remote repository. Run 'go help remote' for details. diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index 1d1dae37d19..26640d833c5 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -36,9 +36,8 @@ An import path is a pattern if it includes one or more "..." wildcards, each of which can match any string, including the empty string and strings containing slashes. Such a pattern expands to all package directories found in the GOPATH trees with names matching the -patterns. For example, encoding/... expands to all packages -in subdirectories of the encoding tree, while net... expands to -net and all its subdirectories. +patterns. As a special case, x/... matches x as well as x's subdirectories. +For example, net/... expands to net and packages in its subdirectories. An import path can also name a package to be downloaded from a remote repository. Run 'go help remote' for details. diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 2cc23d9bd3c..2f8209c86f4 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -247,8 +247,9 @@ func help(args []string) { os.Exit(2) // failed at 'go help cmd' } -// importPaths returns the import paths to use for the given command line. -func importPaths(args []string) []string { +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func importPathsNoDotExpansion(args []string) []string { if len(args) == 0 { return []string{"."} } @@ -270,13 +271,26 @@ func importPaths(args []string) []string { } else { a = path.Clean(a) } - - if build.IsLocalImport(a) && strings.Contains(a, "...") { - out = append(out, allPackagesInFS(a)...) + if a == "all" || a == "std" { + out = append(out, allPackages(a)...) continue } - if a == "all" || a == "std" || strings.Contains(a, "...") { - out = append(out, allPackages(a)...) + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, allPackagesInFS(a)...) + } else { + out = append(out, allPackages(a)...) + } continue } out = append(out, a) @@ -345,6 +359,10 @@ func runOut(dir string, cmdargs ...interface{}) []byte { func matchPattern(pattern string) func(name string) bool { re := regexp.QuoteMeta(pattern) re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } reg := regexp.MustCompile(`^` + re + `$`) return func(name string) bool { return reg.MatchString(name) @@ -356,6 +374,14 @@ func matchPattern(pattern string) func(name string) bool { // The pattern is either "all" (all packages), "std" (standard packages) // or a path including "...". func allPackages(pattern string) []string { + pkgs := matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackages(pattern string) []string { match := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) @@ -432,10 +458,6 @@ func allPackages(pattern string) []string { return nil }) } - - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } return pkgs } @@ -443,6 +465,14 @@ func allPackages(pattern string) []string { // beginning ./ or ../, meaning it should scan the tree rooted // at the given directory. There are ... in the pattern too. func allPackagesInFS(pattern string) []string { + pkgs := matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackagesInFS(pattern string) []string { // Find directory to begin the scan. // Could be smarter but this one optimization // is enough for now, since ... is usually at the @@ -482,10 +512,6 @@ func allPackagesInFS(pattern string) []string { pkgs = append(pkgs, name) return nil }) - - if len(pkgs) == 0 { - fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) - } return pkgs } diff --git a/src/cmd/go/match_test.go b/src/cmd/go/match_test.go new file mode 100644 index 00000000000..f058f235a1e --- /dev/null +++ b/src/cmd/go/match_test.go @@ -0,0 +1,36 @@ +// Copyright 2012 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 main + +import "testing" + +var matchTests = []struct { + pattern string + path string + match bool +}{ + {"...", "foo", true}, + {"net", "net", true}, + {"net", "net/http", false}, + {"net/http", "net", false}, + {"net/http", "net/http", true}, + {"net...", "netchan", true}, + {"net...", "net", true}, + {"net...", "net/http", true}, + {"net...", "not/http", false}, + {"net/...", "netchan", false}, + {"net/...", "net", true}, + {"net/...", "net/http", true}, + {"net/...", "not/http", false}, +} + +func TestMatchPattern(t *testing.T) { + for _, tt := range matchTests { + match := matchPattern(tt.pattern)(tt.path) + if match != tt.match { + t.Errorf("matchPattern(%q)(%q) = %v, want %v", tt.pattern, tt.path, match, tt.match) + } + } +}