//
// Usage:
//
-// go env [var ...]
+// go env [-json] [var ...]
//
// Env prints Go environment information.
//
// names is given as arguments, env prints the value of
// each named variable on its own line.
//
+// The -json flag prints the environment in JSON format
+// instead of as a shell script.
+//
//
// Start a bug report
//
// 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. As a special case, x/... matches x as well as x's subdirectories.
-// For example, net/... expands to net and packages in its subdirectories.
+// patterns.
+//
+// To make common patterns more convenient, there are two special cases.
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separated pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
+// See golang.org/s/go15vendor for more about vendoring.
//
// An import path can also name a package to be downloaded from
// a remote repository. Run 'go help importpath' for details.
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. As a special case, x/... matches x as well as x's subdirectories.
-For example, net/... expands to net and packages in its subdirectories.
+patterns.
+
+To make common patterns more convenient, there are two special cases.
+First, /... at the end of the pattern can match an empty string,
+so that net/... matches both net and packages in its subdirectories, like net/http.
+Second, any slash-separated pattern element containing a wildcard never
+participates in a match of the "vendor" element in the path of a vendored
+package, so that ./... does not match packages in subdirectories of
+./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+Note, however, that a directory named vendor that itself contains code
+is not a vendored package: cmd/vendor would be a command named vendor,
+and the pattern cmd/... matches it.
+See golang.org/s/go15vendor for more about vendoring.
An import path can also name a package to be downloaded from
a remote repository. Run 'go help importpath' for details.
match net net/http netchan
not not/http not/net/http
+ # Special cases. Quoting docs:
+
+ # First, /... at the end of the pattern can match an empty string,
+ # so that net/... matches both net and packages in its subdirectories, like net/http.
pattern net/...
match net net/http
not not/http not/net/http netchan
+
+ # Second, any slash-separted pattern element containing a wildcard never
+ # participates in a match of the "vendor" element in the path of a vendored
+ # package, so that ./... does not match packages in subdirectories of
+ # ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+ # Note, however, that a directory named vendor that itself contains code
+ # is not a vendored package: cmd/vendor would be a command named vendor,
+ # and the pattern cmd/... matches it.
+ pattern ./...
+ match ./vendor ./mycode/vendor
+ not ./vendor/foo ./mycode/vendor/foo
+
+ pattern ./vendor/...
+ match ./vendor/foo ./vendor/foo/vendor
+ not ./vendor/foo/vendor/bar
+
+ pattern mycode/vendor/...
+ match mycode/vendor mycode/vendor/foo mycode/vendor/foo/vendor
+ not mycode/vendor/foo/vendor/bar
+
+ pattern x/vendor/y
+ match x/vendor/y
+ not x/vendor
+
+ pattern x/vendor/y/...
+ match x/vendor/y x/vendor/y/z x/vendor/y/vendor x/vendor/y/z/vendor
+ not x/vendor/y/vendor/z
+
+ pattern .../vendor/...
+ match x/vendor/y x/vendor/y/z x/vendor/y/vendor x/vendor/y/z/vendor
`
func TestMatchPattern(t *testing.T) {
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
+// Unfortunately, there are two special cases. Quoting "go help packages":
+//
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separted pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
func matchPattern(pattern string) func(name string) bool {
+ // Convert pattern to regular expression.
+ // The strategy for the trailing /... is to nest it in an explicit ? expression.
+ // The strategy for the vendor exclusion is to change the unmatchable
+ // vendor strings to a disallowed code point (vendorChar) and to use
+ // "(anything but that codepoint)*" as the implementation of the ... wildcard.
+ // This is a bit complicated but the obvious alternative,
+ // namely a hand-written search like in most shell glob matchers,
+ // is too easy to make accidentally exponential.
+ // Using package regexp guarantees linear-time matching.
+
+ const vendorChar = "\x00"
+
+ if strings.Contains(pattern, vendorChar) {
+ return func(name string) bool { return false }
+ }
+
re := regexp.QuoteMeta(pattern)
- re = strings.Replace(re, `\.\.\.`, `.*`, -1)
- // Special case: foo/... matches foo too.
- if strings.HasSuffix(re, `/.*`) {
- re = re[:len(re)-len(`/.*`)] + `(/.*)?`
+ re = replaceVendor(re, vendorChar)
+ switch {
+ case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
+ re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case re == vendorChar+`/\.\.\.`:
+ re = `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case strings.HasSuffix(re, `/\.\.\.`):
+ re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
}
+ re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
+
reg := regexp.MustCompile(`^` + re + `$`)
+
return func(name string) bool {
- return reg.MatchString(name)
+ if strings.Contains(name, vendorChar) {
+ return false
+ }
+ return reg.MatchString(replaceVendor(name, vendorChar))
+ }
+}
+
+// replaceVendor returns the result of replacing
+// non-trailing vendor path elements in x with repl.
+func replaceVendor(x, repl string) string {
+ if !strings.Contains(x, "vendor") {
+ return x
+ }
+ elem := strings.Split(x, "/")
+ for i := 0; i < len(elem)-1; i++ {
+ if elem[i] == "vendor" {
+ elem[i] = repl
+ }
}
+ return strings.Join(elem, "/")
}
// ImportPaths returns the import paths to use for the given command line.
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
- tg.run("list", "-f", "{{.ImportPath}} {{.Imports}}", "vend/...")
+ tg.run("list", "-f", "{{.ImportPath}} {{.Imports}}", "vend/...", "vend/vendor/...", "vend/x/vendor/...")
want := `
vend [vend/vendor/p r]
vend/dir1 []
vend/hello [fmt vend/vendor/strings]
vend/subdir [vend/vendor/p r]
+ vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r vend/dir1 vend/vendor/vend/dir1/dir2]
+ vend/x/invalid [vend/x/invalid/vendor/foo]
vend/vendor/p []
vend/vendor/q []
vend/vendor/strings []
vend/vendor/vend/dir1/dir2 []
- vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r vend/dir1 vend/vendor/vend/dir1/dir2]
- vend/x/invalid [vend/x/invalid/vendor/foo]
vend/x/vendor/p []
vend/x/vendor/p/p [notfound]
vend/x/vendor/r []