Commit b8615a09 authored by Russ Cox's avatar Russ Cox

go: add ... patterns in import path arguments

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5530058
parent 22dfc77c
...@@ -30,6 +30,13 @@ lists all the packages on the local system. ...@@ -30,6 +30,13 @@ lists all the packages on the local system.
The special import path "std" is like all but expands to just the The special import path "std" is like all but expands to just the
packages in the standard Go library. packages in the standard Go library.
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 the encoding tree.
An import path can also name a package to be downloaded from An import path can also name a package to be downloaded from
a remote repository. Run 'go help remote' for details. a remote repository. Run 'go help remote' for details.
......
...@@ -12,7 +12,9 @@ import ( ...@@ -12,7 +12,9 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"text/template" "text/template"
) )
...@@ -185,15 +187,22 @@ func help(args []string) { ...@@ -185,15 +187,22 @@ func help(args []string) {
// importPaths returns the import paths to use for the given command line. // importPaths returns the import paths to use for the given command line.
func importPaths(args []string) []string { func importPaths(args []string) []string {
if len(args) == 1 {
if args[0] == "all" || args[0] == "std" {
return allPackages(args[0])
}
}
if len(args) == 0 { if len(args) == 0 {
return []string{"."} return []string{"."}
} }
return args var out []string
for _, a := range args {
if (strings.HasPrefix(a, "./") || strings.HasPrefix(a, "../")) && strings.Contains(a, "...") {
out = append(out, allPackagesInFS(a)...)
continue
}
if a == "all" || a == "std" || strings.Contains(a, "...") {
out = append(out, allPackages(a)...)
continue
}
out = append(out, a)
}
return out
} }
var atexitFuncs []func() var atexitFuncs []func()
...@@ -236,9 +245,29 @@ func run(cmdline ...string) { ...@@ -236,9 +245,29 @@ func run(cmdline ...string) {
} }
} }
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
func matchPattern(pattern string) func(name string) bool {
re := regexp.QuoteMeta(pattern)
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
return reg.MatchString(name)
}
}
// allPackages returns all the packages that can be found // allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT. // under the $GOPATH directories and $GOROOT matching what.
func allPackages(what string) []string { // The pattern is either "all" (all packages), "std" (standard packages)
// or a path including "...".
func allPackages(pattern string) []string {
match := func(string) bool { return true }
if pattern != "all" && pattern != "std" {
match = matchPattern(pattern)
}
have := map[string]bool{ have := map[string]bool{
"builtin": true, // ignore pseudo-package that exists only for documentation "builtin": true, // ignore pseudo-package that exists only for documentation
} }
...@@ -269,13 +298,15 @@ func allPackages(what string) []string { ...@@ -269,13 +298,15 @@ func allPackages(what string) []string {
name = "cmd/" + name name = "cmd/" + name
if !have[name] { if !have[name] {
have[name] = true have[name] = true
pkgs = append(pkgs, name) if match(name) {
pkgs = append(pkgs, name)
}
} }
return nil return nil
}) })
for _, t := range build.Path { for _, t := range build.Path {
if what == "std" && !t.Goroot { if pattern == "std" && !t.Goroot {
continue continue
} }
src := t.SrcDir() + string(filepath.Separator) src := t.SrcDir() + string(filepath.Separator)
...@@ -284,26 +315,29 @@ func allPackages(what string) []string { ...@@ -284,26 +315,29 @@ func allPackages(what string) []string {
return nil return nil
} }
// Avoid testdata directory trees. // Avoid .foo and testdata directory trees.
if strings.HasSuffix(path, string(filepath.Separator)+"testdata") { _, elem := filepath.Split(path)
if strings.HasPrefix(elem, ".") || elem == "testdata" {
return filepath.SkipDir return filepath.SkipDir
} }
name := filepath.ToSlash(path[len(src):]) name := filepath.ToSlash(path[len(src):])
if what == "std" && strings.Contains(name, ".") { if pattern == "std" && strings.Contains(name, ".") {
return filepath.SkipDir return filepath.SkipDir
} }
if have[name] { if have[name] {
return nil return nil
} }
have[name] = true
_, err = build.ScanDir(path) _, err = build.ScanDir(path)
if err != nil { if err != nil {
return nil return nil
} }
pkgs = append(pkgs, name) if match(name) {
have[name] = true pkgs = append(pkgs, name)
}
// Avoid go/build test data. // Avoid go/build test data.
// TODO: Move it into a testdata directory. // TODO: Move it into a testdata directory.
...@@ -314,5 +348,59 @@ func allPackages(what string) []string { ...@@ -314,5 +348,59 @@ func allPackages(what string) []string {
return nil return nil
}) })
} }
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
// allPackagesInFS is like allPackages but is passed a pattern
// beginning ./ or ../, meaning it should scan the tree rooted
// at the given directory. There are ... in the pattern too.
func allPackagesInFS(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
// end of a path.
i := strings.Index(pattern, "...")
dir, _ := path.Split(pattern[:i])
// pattern begins with ./ or ../.
// path.Clean will discard the ./ but not the ../.
// We need to preserve the ./ for pattern matching
// and in the returned import paths.
prefix := ""
if strings.HasPrefix(pattern, "./") {
prefix = "./"
}
match := matchPattern(pattern)
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
}
// Avoid .foo and testdata directory trees.
_, elem := filepath.Split(path)
if strings.HasPrefix(elem, ".") || elem == "testdata" {
return filepath.SkipDir
}
name := prefix + filepath.ToSlash(path)
if !match(name) {
return nil
}
if _, err = build.ScanDir(path); err != nil {
return nil
}
pkgs = append(pkgs, name)
return nil
})
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs return pkgs
} }
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