pax_global_header00006660000000000000000000000064132433401420014506gustar00rootroot0000000000000052 comment=80517062f582ea3340cd4baf70e86d539ae7d84d gotool-1.0.0/000077500000000000000000000000001324334014200130075ustar00rootroot00000000000000gotool-1.0.0/.travis.yml000066400000000000000000000004551324334014200151240ustar00rootroot00000000000000sudo: false language: go go: - 1.2 - 1.3 - 1.4 - 1.5 - 1.6 - 1.7 - 1.8 - 1.9 - master matrix: allow_failures: - go: master fast_finish: true install: - # Skip. script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d .) - go tool vet . - go test -v -race ./... gotool-1.0.0/LEGAL000066400000000000000000000033101324334014200135530ustar00rootroot00000000000000All the files in this distribution are covered under either the MIT license (see the file LICENSE) except some files mentioned below. match.go, match_test.go: Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gotool-1.0.0/LICENSE000066400000000000000000000020701324334014200140130ustar00rootroot00000000000000Copyright (c) 2013 Kamil Kisiel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gotool-1.0.0/README.md000066400000000000000000000006371324334014200142740ustar00rootroot00000000000000gotool ====== [![GoDoc](https://godoc.org/github.com/kisielk/gotool?status.svg)](https://godoc.org/github.com/kisielk/gotool) [![Build Status](https://travis-ci.org/kisielk/gotool.svg?branch=master)](https://travis-ci.org/kisielk/gotool) Package gotool contains utility functions used to implement the standard "cmd/go" tool, provided as a convenience to developers who want to write tools with similar semantics. gotool-1.0.0/go.mod000066400000000000000000000000431324334014200141120ustar00rootroot00000000000000module "github.com/kisielk/gotool" gotool-1.0.0/go13.go000066400000000000000000000003251324334014200141070ustar00rootroot00000000000000// +build !go1.4 package gotool import ( "go/build" "path/filepath" "runtime" ) var gorootSrc = filepath.Join(runtime.GOROOT(), "src", "pkg") func shouldIgnoreImport(p *build.Package) bool { return true } gotool-1.0.0/go14-15.go000066400000000000000000000003241324334014200143320ustar00rootroot00000000000000// +build go1.4,!go1.6 package gotool import ( "go/build" "path/filepath" "runtime" ) var gorootSrc = filepath.Join(runtime.GOROOT(), "src") func shouldIgnoreImport(p *build.Package) bool { return true } gotool-1.0.0/go16-18.go000066400000000000000000000003661324334014200143450ustar00rootroot00000000000000// +build go1.6,!go1.9 package gotool import ( "go/build" "path/filepath" "runtime" ) var gorootSrc = filepath.Join(runtime.GOROOT(), "src") func shouldIgnoreImport(p *build.Package) bool { return p == nil || len(p.InvalidGoFiles) == 0 } gotool-1.0.0/internal/000077500000000000000000000000001324334014200146235ustar00rootroot00000000000000gotool-1.0.0/internal/load/000077500000000000000000000000001324334014200155425ustar00rootroot00000000000000gotool-1.0.0/internal/load/match_test.go000066400000000000000000000073521324334014200202330ustar00rootroot00000000000000// 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. // +build go1.9 package load import ( "strings" "testing" ) var matchPatternTests = ` pattern ... match foo pattern net match net not net/http pattern net/http match net/http not net pattern net... 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) { testPatterns(t, "matchPattern", matchPatternTests, func(pattern, name string) bool { return matchPattern(pattern)(name) }) } var treeCanMatchPatternTests = ` pattern ... match foo pattern net match net not net/http pattern net/http match net net/http pattern net... match net netchan net/http not not/http not/net/http pattern net/... match net net/http not not/http netchan pattern abc.../def match abcxyz not xyzabc pattern x/y/z/... match x x/y x/y/z x/y/z/w pattern x/y/z match x x/y x/y/z not x/y/z/w pattern x/.../y/z match x/a/b/c not y/x/a/b/c ` func TestTreeCanMatchPattern(t *testing.T) { testPatterns(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { return treeCanMatchPattern(pattern)(name) }) } var hasPathPrefixTests = []stringPairTest{ {"abc", "a", false}, {"a/bc", "a", true}, {"a", "a", true}, {"a/bc", "a/", true}, } func TestHasPathPrefix(t *testing.T) { testStringPairs(t, "hasPathPrefix", hasPathPrefixTests, hasPathPrefix) } type stringPairTest struct { in1 string in2 string out bool } func testStringPairs(t *testing.T, name string, tests []stringPairTest, f func(string, string) bool) { for _, tt := range tests { if out := f(tt.in1, tt.in2); out != tt.out { t.Errorf("%s(%q, %q) = %v, want %v", name, tt.in1, tt.in2, out, tt.out) } } } func testPatterns(t *testing.T, name, tests string, fn func(string, string) bool) { var patterns []string for _, line := range strings.Split(tests, "\n") { if i := strings.Index(line, "#"); i >= 0 { line = line[:i] } f := strings.Fields(line) if len(f) == 0 { continue } switch f[0] { default: t.Fatalf("unknown directive %q", f[0]) case "pattern": patterns = f[1:] case "match", "not": want := f[0] == "match" for _, pattern := range patterns { for _, in := range f[1:] { if fn(pattern, in) != want { t.Errorf("%s(%q, %q) = %v, want %v", name, pattern, in, !want, want) } } } } } } gotool-1.0.0/internal/load/path.go000066400000000000000000000011461324334014200170270ustar00rootroot00000000000000// Copyright 2017 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. // +build go1.9 package load import ( "strings" ) // hasPathPrefix reports whether the path s begins with the // elements in prefix. func hasPathPrefix(s, prefix string) bool { switch { default: return false case len(s) == len(prefix): return s == prefix case len(s) > len(prefix): if prefix != "" && prefix[len(prefix)-1] == '/' { return strings.HasPrefix(s, prefix) } return s[len(prefix)] == '/' && s[:len(prefix)] == prefix } } gotool-1.0.0/internal/load/pkg.go000066400000000000000000000013111324334014200166460ustar00rootroot00000000000000// Copyright 2011 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. // +build go1.9 // Package load loads packages. package load import ( "strings" ) // isStandardImportPath reports whether $GOROOT/src/path should be considered // part of the standard distribution. For historical reasons we allow people to add // their own code to $GOROOT instead of using $GOPATH, but we assume that // code will start with a domain name (dot in the first element). func isStandardImportPath(path string) bool { i := strings.Index(path, "/") if i < 0 { i = len(path) } elem := path[:i] return !strings.Contains(elem, ".") } gotool-1.0.0/internal/load/search.go000066400000000000000000000256561324334014200173540ustar00rootroot00000000000000// Copyright 2017 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. // +build go1.9 package load import ( "fmt" "go/build" "log" "os" "path" "path/filepath" "regexp" "strings" ) // Context specifies values for operation of ImportPaths that would // otherwise come from cmd/go/internal/cfg package. // // This is a construct added for gotool purposes and doesn't have // an equivalent upstream in cmd/go. type Context struct { // BuildContext is the build context to use. BuildContext build.Context // GOROOTsrc is the location of the src directory in GOROOT. // At this time, it's used only in MatchPackages to skip // GOOROOT/src entry from BuildContext.SrcDirs output. GOROOTsrc string } // allPackages returns all the packages that can be found // under the $GOPATH directories and $GOROOT matching pattern. // The pattern is either "all" (all packages), "std" (standard packages), // "cmd" (standard commands), or a path including "...". func (c *Context) allPackages(pattern string) []string { pkgs := c.MatchPackages(pattern) 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 (c *Context) allPackagesInFS(pattern string) []string { pkgs := c.MatchPackagesInFS(pattern) if len(pkgs) == 0 { fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) } return pkgs } // MatchPackages returns a list of package paths matching pattern // (see go help packages for pattern syntax). func (c *Context) MatchPackages(pattern string) []string { match := func(string) bool { return true } treeCanMatch := func(string) bool { return true } if !IsMetaPackage(pattern) { match = matchPattern(pattern) treeCanMatch = treeCanMatchPattern(pattern) } have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !c.BuildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } var pkgs []string for _, src := range c.BuildContext.SrcDirs() { if (pattern == "std" || pattern == "cmd") && src != c.GOROOTsrc { continue } src = filepath.Clean(src) + string(filepath.Separator) root := src if pattern == "cmd" { root += "cmd" + string(filepath.Separator) } filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil || path == src { return nil } want := true // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { want = false } name := filepath.ToSlash(path[len(src):]) if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { // The name "std" is only the standard library. // If the name is cmd, it's the root of the command tree. want = false } if !treeCanMatch(name) { want = false } if !fi.IsDir() { if fi.Mode()&os.ModeSymlink != 0 && want { if target, err := os.Stat(path); err == nil && target.IsDir() { fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) } } return nil } if !want { return filepath.SkipDir } if have[name] { return nil } have[name] = true if !match(name) { return nil } pkg, err := c.BuildContext.ImportDir(path, 0) if err != nil { if _, noGo := err.(*build.NoGoError); noGo { return nil } } // If we are expanding "cmd", skip main // packages under cmd/vendor. At least as of // March, 2017, there is one there for the // vendored pprof tool. if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" { return nil } pkgs = append(pkgs, name) return nil }) } return pkgs } // MatchPackagesInFS returns a list of package paths matching pattern, // which must begin with ./ or ../ // (see go help packages for pattern syntax). func (c *Context) 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 // 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 } if path == dir { // filepath.Walk starts at dir and recurses. For the recursive case, // the path is the result of filepath.Join, which calls filepath.Clean. // The initial case is not Cleaned, though, so we do this explicitly. // // This converts a path like "./io/" to "io". Without this step, running // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io // package, because prepending the prefix "./" to the unclean path would // result in "././io", and match("././io") returns false. path = filepath.Clean(path) } // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". _, elem := filepath.Split(path) dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := prefix + filepath.ToSlash(path) if !match(name) { return nil } // We keep the directory if we can import it, or if we can't import it // due to invalid Go source files. This means that directories containing // parse errors will be built (and fail) instead of being silently skipped // as not matching the pattern. Go 1.5 and earlier skipped, but that // behavior means people miss serious mistakes. // See golang.org/issue/11407. if p, err := c.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { if _, noGo := err.(*build.NoGoError); !noGo { log.Print(err) } return nil } pkgs = append(pkgs, name) return nil }) return pkgs } // treeCanMatchPattern(pattern)(name) reports whether // name or children of name can possibly match pattern. // Pattern is the same limited glob accepted by matchPattern. func treeCanMatchPattern(pattern string) func(name string) bool { wildCard := false if i := strings.Index(pattern, "..."); i >= 0 { wildCard = true pattern = pattern[:i] } return func(name string) bool { return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || wildCard && strings.HasPrefix(name, pattern) } } // 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. // 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 = 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 { 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. func (c *Context) ImportPaths(args []string) []string { args = c.ImportPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { out = append(out, c.allPackagesInFS(a)...) } else { out = append(out, c.allPackages(a)...) } continue } out = append(out, a) } return out } // ImportPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func (c *Context) ImportPathsNoDotExpansion(args []string) []string { if len(args) == 0 { return []string{"."} } var out []string for _, a := range args { // Arguments are supposed to be import paths, but // as a courtesy to Windows developers, rewrite \ to / // in command-line arguments. Handles .\... and so on. if filepath.Separator == '\\' { a = strings.Replace(a, `\`, `/`, -1) } // Put argument in canonical form, but preserve leading ./. if strings.HasPrefix(a, "./") { a = "./" + path.Clean(a) if a == "./." { a = "." } } else { a = path.Clean(a) } if IsMetaPackage(a) { out = append(out, c.allPackages(a)...) continue } out = append(out, a) } return out } // IsMetaPackage checks if name is a reserved package name that expands to multiple packages. func IsMetaPackage(name string) bool { return name == "std" || name == "cmd" || name == "all" } gotool-1.0.0/match.go000066400000000000000000000043031324334014200144320ustar00rootroot00000000000000// Copyright (c) 2009 The Go Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build go1.9 package gotool import ( "path/filepath" "github.com/kisielk/gotool/internal/load" ) // importPaths returns the import paths to use for the given command line. func (c *Context) importPaths(args []string) []string { lctx := load.Context{ BuildContext: c.BuildContext, GOROOTsrc: c.joinPath(c.BuildContext.GOROOT, "src"), } return lctx.ImportPaths(args) } // joinPath calls c.BuildContext.JoinPath (if not nil) or else filepath.Join. // // It's a copy of the unexported build.Context.joinPath helper. func (c *Context) joinPath(elem ...string) string { if f := c.BuildContext.JoinPath; f != nil { return f(elem...) } return filepath.Join(elem...) } gotool-1.0.0/match18.go000066400000000000000000000240031324334014200146020ustar00rootroot00000000000000// Copyright (c) 2009 The Go Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build !go1.9 package gotool import ( "fmt" "go/build" "log" "os" "path" "path/filepath" "regexp" "strings" ) // This file contains code from the Go distribution. // 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) // Special case: foo/... matches foo too. if strings.HasSuffix(re, `/.*`) { re = re[:len(re)-len(`/.*`)] + `(/.*)?` } reg := regexp.MustCompile(`^` + re + `$`) return reg.MatchString } // matchPackages returns a list of package paths matching pattern // (see go help packages for pattern syntax). func (c *Context) matchPackages(pattern string) []string { match := func(string) bool { return true } treeCanMatch := func(string) bool { return true } if !isMetaPackage(pattern) { match = matchPattern(pattern) treeCanMatch = treeCanMatchPattern(pattern) } have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !c.BuildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } var pkgs []string for _, src := range c.BuildContext.SrcDirs() { if (pattern == "std" || pattern == "cmd") && src != gorootSrc { continue } src = filepath.Clean(src) + string(filepath.Separator) root := src if pattern == "cmd" { root += "cmd" + string(filepath.Separator) } filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() || path == src { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := filepath.ToSlash(path[len(src):]) if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { // The name "std" is only the standard library. // If the name is cmd, it's the root of the command tree. return filepath.SkipDir } if !treeCanMatch(name) { return filepath.SkipDir } if have[name] { return nil } have[name] = true if !match(name) { return nil } _, err = c.BuildContext.ImportDir(path, 0) if err != nil { if _, noGo := err.(*build.NoGoError); noGo { return nil } } pkgs = append(pkgs, name) return nil }) } return pkgs } // importPathsNoDotExpansion returns the import paths to use for the given // command line, but it does no ... expansion. func (c *Context) importPathsNoDotExpansion(args []string) []string { if len(args) == 0 { return []string{"."} } var out []string for _, a := range args { // Arguments are supposed to be import paths, but // as a courtesy to Windows developers, rewrite \ to / // in command-line arguments. Handles .\... and so on. if filepath.Separator == '\\' { a = strings.Replace(a, `\`, `/`, -1) } // Put argument in canonical form, but preserve leading ./. if strings.HasPrefix(a, "./") { a = "./" + path.Clean(a) if a == "./." { a = "." } } else { a = path.Clean(a) } if isMetaPackage(a) { out = append(out, c.allPackages(a)...) continue } out = append(out, a) } return out } // importPaths returns the import paths to use for the given command line. func (c *Context) importPaths(args []string) []string { args = c.importPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { out = append(out, c.allPackagesInFS(a)...) } else { out = append(out, c.allPackages(a)...) } continue } out = append(out, a) } return out } // allPackages returns all the packages that can be found // under the $GOPATH directories and $GOROOT matching pattern. // The pattern is either "all" (all packages), "std" (standard packages), // "cmd" (standard commands), or a path including "...". func (c *Context) allPackages(pattern string) []string { pkgs := c.matchPackages(pattern) 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 (c *Context) allPackagesInFS(pattern string) []string { pkgs := c.matchPackagesInFS(pattern) if len(pkgs) == 0 { fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) } return pkgs } // matchPackagesInFS returns a list of package paths matching pattern, // which must begin with ./ or ../ // (see go help packages for pattern syntax). func (c *Context) 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 // 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 } if path == dir { // filepath.Walk starts at dir and recurses. For the recursive case, // the path is the result of filepath.Join, which calls filepath.Clean. // The initial case is not Cleaned, though, so we do this explicitly. // // This converts a path like "./io/" to "io". Without this step, running // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io // package, because prepending the prefix "./" to the unclean path would // result in "././io", and match("././io") returns false. path = filepath.Clean(path) } // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". _, elem := filepath.Split(path) dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := prefix + filepath.ToSlash(path) if !match(name) { return nil } // We keep the directory if we can import it, or if we can't import it // due to invalid Go source files. This means that directories containing // parse errors will be built (and fail) instead of being silently skipped // as not matching the pattern. Go 1.5 and earlier skipped, but that // behavior means people miss serious mistakes. // See golang.org/issue/11407. if p, err := c.BuildContext.ImportDir(path, 0); err != nil && shouldIgnoreImport(p) { if _, noGo := err.(*build.NoGoError); !noGo { log.Print(err) } return nil } pkgs = append(pkgs, name) return nil }) return pkgs } // isMetaPackage checks if name is a reserved package name that expands to multiple packages. func isMetaPackage(name string) bool { return name == "std" || name == "cmd" || name == "all" } // isStandardImportPath reports whether $GOROOT/src/path should be considered // part of the standard distribution. For historical reasons we allow people to add // their own code to $GOROOT instead of using $GOPATH, but we assume that // code will start with a domain name (dot in the first element). func isStandardImportPath(path string) bool { i := strings.Index(path, "/") if i < 0 { i = len(path) } elem := path[:i] return !strings.Contains(elem, ".") } // hasPathPrefix reports whether the path s begins with the // elements in prefix. func hasPathPrefix(s, prefix string) bool { switch { default: return false case len(s) == len(prefix): return s == prefix case len(s) > len(prefix): if prefix != "" && prefix[len(prefix)-1] == '/' { return strings.HasPrefix(s, prefix) } return s[len(prefix)] == '/' && s[:len(prefix)] == prefix } } // treeCanMatchPattern(pattern)(name) reports whether // name or children of name can possibly match pattern. // Pattern is the same limited glob accepted by matchPattern. func treeCanMatchPattern(pattern string) func(name string) bool { wildCard := false if i := strings.Index(pattern, "..."); i >= 0 { wildCard = true pattern = pattern[:i] } return func(name string) bool { return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || wildCard && strings.HasPrefix(name, pattern) } } gotool-1.0.0/match18_test.go000066400000000000000000000105231324334014200156430ustar00rootroot00000000000000// Copyright (c) 2009 The Go Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build !go1.9 package gotool import ( "sort" "testing" ) // This file contains code from the Go distribution. var matchPatternTests = []stringPairTest{ {"...", "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) { testStringPairs(t, "matchPattern", matchPatternTests, func(pattern, name string) bool { return matchPattern(pattern)(name) }) } var treeCanMatchPatternTests = []stringPairTest{ {"...", "foo", true}, {"net", "net", true}, {"net", "net/http", false}, {"net/http", "net", true}, {"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}, {"abc.../def", "abcxyz", true}, {"abc.../def", "xyxabc", false}, {"x/y/z/...", "x", true}, {"x/y/z/...", "x/y", true}, {"x/y/z/...", "x/y/z", true}, {"x/y/z/...", "x/y/z/w", true}, {"x/y/z", "x", true}, {"x/y/z", "x/y", true}, {"x/y/z", "x/y/z", true}, {"x/y/z", "x/y/z/w", false}, {"x/.../y/z", "x/a/b/c", true}, {"x/.../y/z", "y/x/a/b/c", false}, } func TestChildrenCanMatchPattern(t *testing.T) { testStringPairs(t, "treeCanMatchPattern", treeCanMatchPatternTests, func(pattern, name string) bool { return treeCanMatchPattern(pattern)(name) }) } var hasPathPrefixTests = []stringPairTest{ {"abc", "a", false}, {"a/bc", "a", true}, {"a", "a", true}, {"a/bc", "a/", true}, } func TestHasPathPrefix(t *testing.T) { testStringPairs(t, "hasPathPrefix", hasPathPrefixTests, hasPathPrefix) } type stringPairTest struct { in1 string in2 string out bool } func testStringPairs(t *testing.T, name string, tests []stringPairTest, f func(string, string) bool) { for _, tt := range tests { if out := f(tt.in1, tt.in2); out != tt.out { t.Errorf("%s(%q, %q) = %v, want %v", name, tt.in1, tt.in2, out, tt.out) } } } // containsString reports whether strings contains x. strings is assumed to be sorted. func containsString(strings []string, x string) bool { return strings[sort.SearchStrings(strings, x)] == x } func TestMatchStdPackages(t *testing.T) { packages := DefaultContext.matchPackages("std") sort.Strings(packages) // some common packages all Go versions should have commonPackages := []string{"bufio", "bytes", "crypto", "fmt", "io", "os"} for _, p := range commonPackages { if !containsString(packages, p) { t.Errorf("std package set doesn't contain expected package %s", p) } } } gotool-1.0.0/tool.go000066400000000000000000000040211324334014200143100ustar00rootroot00000000000000// Package gotool contains utility functions used to implement the standard // "cmd/go" tool, provided as a convenience to developers who want to write // tools with similar semantics. package gotool import "go/build" // Export functions here to make it easier to keep the implementations up to date with upstream. // DefaultContext is the default context that uses build.Default. var DefaultContext = Context{ BuildContext: build.Default, } // A Context specifies the supporting context. type Context struct { // BuildContext is the build.Context that is used when computing import paths. BuildContext build.Context } // ImportPaths returns the import paths to use for the given command line. // // The path "all" is expanded to all packages in $GOPATH and $GOROOT. // The path "std" is expanded to all packages in the Go standard library. // The path "cmd" is expanded to all Go standard commands. // The string "..." is treated as a wildcard within a path. // When matching recursively, directories are ignored if they are prefixed with // a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata". // Relative import paths are not converted to full import paths. // If args is empty, a single element "." is returned. func (c *Context) ImportPaths(args []string) []string { return c.importPaths(args) } // ImportPaths returns the import paths to use for the given command line // using default context. // // The path "all" is expanded to all packages in $GOPATH and $GOROOT. // The path "std" is expanded to all packages in the Go standard library. // The path "cmd" is expanded to all Go standard commands. // The string "..." is treated as a wildcard within a path. // When matching recursively, directories are ignored if they are prefixed with // a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata". // Relative import paths are not converted to full import paths. // If args is empty, a single element "." is returned. func ImportPaths(args []string) []string { return DefaultContext.importPaths(args) }