├── .gitignore ├── fileutils ├── _testdata │ └── copyfile │ │ └── a │ │ └── rick ├── fileutils_test.go ├── path_test.go └── fileutils.go ├── vendor ├── golang.org │ └── x │ │ └── tools │ │ ├── go │ │ └── ast │ │ │ └── astutil │ │ │ ├── util.go │ │ │ ├── LICENSE │ │ │ ├── imports.go │ │ │ └── enclosing.go │ │ ├── cmd │ │ └── goimports │ │ │ ├── goimports_not_gc.go │ │ │ ├── goimports_gc.go │ │ │ ├── LICENSE │ │ │ ├── doc.go │ │ │ └── goimports.go │ │ ├── imports │ │ ├── fastwalk_dirent_ino.go │ │ ├── fastwalk_dirent_fileno.go │ │ ├── fastwalk_portable.go │ │ ├── LICENSE │ │ ├── mkstdlib.go │ │ ├── fastwalk_unix.go │ │ ├── mkindex.go │ │ ├── fastwalk.go │ │ ├── sortimports.go │ │ ├── imports.go │ │ └── fix.go │ │ └── cover │ │ ├── LICENSE │ │ └── profile.go ├── github.com │ └── wadey │ │ └── gocovmerge │ │ ├── LICENSE │ │ └── gocovmerge.go └── manifest ├── .travis.yml ├── README.md ├── LICENSE ├── list.go ├── delete.go ├── gbvendor ├── discovery.go ├── manifest_test.go ├── repo_test.go ├── imports_test.go ├── imports.go ├── manifest.go └── repo.go ├── help.go ├── main.go ├── downloader.go ├── Makefile ├── alldocs.go ├── update.go ├── restore.go ├── README.old.md └── fetch.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.GOPATH 2 | /bin 3 | -------------------------------------------------------------------------------- /fileutils/_testdata/copyfile/a/rick: -------------------------------------------------------------------------------- 1 | /never/going/to/give/you/up -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/ast/astutil/util.go: -------------------------------------------------------------------------------- 1 | package astutil 2 | 3 | import "go/ast" 4 | 5 | // Unparen returns e with any enclosing parentheses stripped. 6 | func Unparen(e ast.Expr) ast.Expr { 7 | for { 8 | p, ok := e.(*ast.ParenExpr) 9 | if !ok { 10 | return e 11 | } 12 | e = p.X 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cmd/goimports/goimports_not_gc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !gc 6 | 7 | package main 8 | 9 | func doTrace() func() { 10 | return func() {} 11 | } 12 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fastwalk_dirent_ino.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,!appengine darwin 6 | 7 | package imports 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Ino) 13 | } 14 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fastwalk_dirent_fileno.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build freebsd openbsd netbsd 6 | 7 | package imports 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Fileno) 13 | } 14 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cmd/goimports/goimports_gc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build gc 6 | 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "runtime/trace" 12 | ) 13 | 14 | var traceProfile = flag.String("trace", "", "trace profile output") 15 | 16 | func doTrace() func() { 17 | if *traceProfile != "" { 18 | bw, flush := bufferedFileWriter(*traceProfile) 19 | trace.Start(bw) 20 | return func() { 21 | flush() 22 | trace.Stop() 23 | } 24 | } 25 | return func() {} 26 | } 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.5.3 5 | - 1.7.3 6 | - tip 7 | 8 | matrix: 9 | allow_failures: 10 | - go: tip 11 | 12 | sudo: false 13 | 14 | script: 15 | - if [[ "$TRAVIS_GO_VERSION" == "1.5.3" ]]; then make gvt; fi 16 | - if [[ "$TRAVIS_GO_VERSION" != "1.5.3" ]]; then make test; fi 17 | 18 | os: 19 | - linux 20 | - osx 21 | 22 | # ssh_known_hosts is broken on OS X, run ssh-keyscan manually instead 23 | # See https://github.com/travis-ci/travis-ci/issues/5596 24 | # addons: 25 | # ssh_known_hosts: 26 | # - bitbucket.org 27 | before_install: 28 | - ssh-keyscan -t rsa -T 30 -H bitbucket.org | tee -a $HOME/.ssh/known_hosts 29 | 30 | install: 31 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi 32 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install bazaar; fi 33 | -------------------------------------------------------------------------------- /fileutils/fileutils_test.go: -------------------------------------------------------------------------------- 1 | package fileutils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "testing" 9 | ) 10 | 11 | func TestCopypathSymlinks(t *testing.T) { 12 | if runtime.GOOS == "windows" { 13 | t.Skip("no symlinks on windows y'all") 14 | } 15 | dst := mktemp(t) 16 | defer RemoveAll(dst) 17 | src := filepath.Join("_testdata", "copyfile") 18 | if err := Copypath(dst, src, true, false); err != nil { 19 | t.Fatalf("copypath(%s, %s): %v", dst, src, err) 20 | } 21 | res, err := os.Readlink(filepath.Join(dst, "a", "rick")) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | if res != "/never/going/to/give/you/up" { 26 | t.Fatalf("target == %s, expected /never/going/to/give/you/up", res) 27 | } 28 | } 29 | 30 | func mktemp(t *testing.T) string { 31 | s, err := ioutil.TempDir("", "fileutils_test") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | return s 36 | } 37 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fastwalk_portable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd 6 | 7 | package imports 8 | 9 | import ( 10 | "io/ioutil" 11 | "os" 12 | ) 13 | 14 | // readDir calls fn for each directory entry in dirName. 15 | // It does not descend into directories or follow symlinks. 16 | // If fn returns a non-nil error, readDir returns with that error 17 | // immediately. 18 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { 19 | fis, err := ioutil.ReadDir(dirName) 20 | if err != nil { 21 | return err 22 | } 23 | for _, fi := range fis { 24 | if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `gvt` was a minimalistic Go vendoring tool made for the `vendor/` folder (once known as the 2 | [GO15VENDOREXPERIMENT](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/edit)). 3 | 4 | It was based on [gb-vendor](https://github.com/constabulary/gb) by Dave Cheney. 5 | 6 | Since Go 1.11, the `go` tool supports *modules*, a native solution to the dependency problem. 7 | 8 | The `go` tool understands `gvt` manifest files, so you just have to run 9 | 10 | ``` 11 | GO111MODULE=on go mod init 12 | GO111MODULE=on go mod vendor 13 | ``` 14 | 15 | to migrate and still populate the `vendor/` folder for backwards compatibility. 16 | 17 | Read more [in the docs](https://tip.golang.org/cmd/go/#hdr-Modules__module_versions__and_more) or [on the wiki](https://golang.org/wiki/Modules). 18 | 19 | Modules support is experimental in 1.11, but it will probably serve you better than gvt would. 20 | 21 | — So long, and thanks for all the fish! 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 constabulary 4 | Copyright (c) 2015 Filippo Valsorda 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/wadey/gocovmerge/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Wade Simmons 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "html/template" 7 | "os" 8 | "text/tabwriter" 9 | 10 | "github.com/FiloSottile/gvt/gbvendor" 11 | ) 12 | 13 | var ( 14 | format string 15 | ) 16 | 17 | func addListFlags(fs *flag.FlagSet) { 18 | fs.StringVar(&format, "f", "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}", "format template") 19 | } 20 | 21 | var cmdList = &Command{ 22 | Name: "list", 23 | UsageLine: "list [-f format]", 24 | Short: "list dependencies one per line", 25 | Long: `list formats the contents of the manifest file. 26 | 27 | Flags: 28 | -f 29 | controls the template used for printing each manifest entry. If not supplied 30 | the default value is "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}" 31 | 32 | `, 33 | Run: func(args []string) error { 34 | m, err := vendor.ReadManifest(manifestFile) 35 | if err != nil { 36 | return fmt.Errorf("could not load manifest: %v", err) 37 | } 38 | tmpl, err := template.New("list").Parse(format) 39 | if err != nil { 40 | return fmt.Errorf("unable to parse template %q: %v", format, err) 41 | } 42 | w := tabwriter.NewWriter(os.Stdout, 1, 2, 1, ' ', 0) 43 | for _, dep := range m.Dependencies { 44 | if err := tmpl.Execute(w, dep); err != nil { 45 | return fmt.Errorf("unable to execute template: %v", err) 46 | } 47 | fmt.Fprintln(w) 48 | } 49 | return w.Flush() 50 | }, 51 | AddFlags: addListFlags, 52 | } 53 | -------------------------------------------------------------------------------- /vendor/manifest: -------------------------------------------------------------------------------- 1 | { 2 | "generator": "github.com/FiloSottile/gvt", 3 | "dependencies": [ 4 | { 5 | "importpath": "github.com/wadey/gocovmerge", 6 | "repository": "https://github.com/wadey/gocovmerge", 7 | "vcs": "git", 8 | "revision": "b5bfa59ec0adc420475f97f89b58045c721d761c", 9 | "branch": "master", 10 | "notests": true 11 | }, 12 | { 13 | "importpath": "golang.org/x/tools/cmd/goimports", 14 | "repository": "https://go.googlesource.com/tools", 15 | "vcs": "git", 16 | "revision": "8b84dae17391c154ca50b0162662aa1fc9ff84c2", 17 | "branch": "master", 18 | "path": "/cmd/goimports", 19 | "notests": true 20 | }, 21 | { 22 | "importpath": "golang.org/x/tools/cover", 23 | "repository": "https://go.googlesource.com/tools", 24 | "vcs": "git", 25 | "revision": "8b84dae17391c154ca50b0162662aa1fc9ff84c2", 26 | "branch": "master", 27 | "path": "/cover", 28 | "notests": true 29 | }, 30 | { 31 | "importpath": "golang.org/x/tools/go/ast/astutil", 32 | "repository": "https://go.googlesource.com/tools", 33 | "vcs": "git", 34 | "revision": "8b84dae17391c154ca50b0162662aa1fc9ff84c2", 35 | "branch": "master", 36 | "path": "go/ast/astutil", 37 | "notests": true 38 | }, 39 | { 40 | "importpath": "golang.org/x/tools/imports", 41 | "repository": "https://go.googlesource.com/tools", 42 | "vcs": "git", 43 | "revision": "8b84dae17391c154ca50b0162662aa1fc9ff84c2", 44 | "branch": "master", 45 | "path": "imports", 46 | "notests": true 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cover/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cmd/goimports/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/ast/astutil/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cmd/goimports/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Command goimports updates your Go import lines, 4 | adding missing ones and removing unreferenced ones. 5 | 6 | $ go get golang.org/x/tools/cmd/goimports 7 | 8 | It's a drop-in replacement for your editor's gofmt-on-save hook. 9 | It has the same command-line interface as gofmt and formats 10 | your code in the same way. 11 | 12 | For emacs, make sure you have the latest go-mode.el: 13 | https://github.com/dominikh/go-mode.el 14 | Then in your .emacs file: 15 | (setq gofmt-command "goimports") 16 | (add-to-list 'load-path "/home/you/somewhere/emacs/") 17 | (require 'go-mode-load) 18 | (add-hook 'before-save-hook 'gofmt-before-save) 19 | 20 | For vim, set "gofmt_command" to "goimports": 21 | https://golang.org/change/39c724dd7f252 22 | https://golang.org/wiki/IDEsAndTextEditorPlugins 23 | etc 24 | 25 | For GoSublime, follow the steps described here: 26 | http://michaelwhatcott.com/gosublime-goimports/ 27 | 28 | For other editors, you probably know what to do. 29 | 30 | To exclude directories in your $GOPATH from being scanned for Go 31 | files, goimports respects a configuration file at 32 | $GOPATH/src/.goimportsignore which may contain blank lines, comment 33 | lines (beginning with '#'), or lines naming a directory relative to 34 | the configuration file to ignore when scanning. No globbing or regex 35 | patterns are allowed. Use the "-v" verbose flag to verify it's 36 | working and see what goimports is doing. 37 | 38 | File bugs or feature requests at: 39 | 40 | https://golang.org/issues/new?title=x/tools/cmd/goimports:+ 41 | 42 | Happy hacking! 43 | 44 | */ 45 | package main // import "golang.org/x/tools/cmd/goimports" 46 | -------------------------------------------------------------------------------- /delete.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "path/filepath" 7 | 8 | "github.com/FiloSottile/gvt/fileutils" 9 | 10 | "github.com/FiloSottile/gvt/gbvendor" 11 | ) 12 | 13 | var ( 14 | deleteAll bool // delete all dependencies 15 | ) 16 | 17 | func addDeleteFlags(fs *flag.FlagSet) { 18 | fs.BoolVar(&deleteAll, "all", false, "delete all dependencies") 19 | } 20 | 21 | var cmdDelete = &Command{ 22 | Name: "delete", 23 | UsageLine: "delete [-all] importpath", 24 | Short: "delete a local dependency", 25 | Long: `delete removes a dependency from the vendor directory and the manifest 26 | 27 | Flags: 28 | -all 29 | remove all dependencies 30 | 31 | `, 32 | Run: func(args []string) error { 33 | if len(args) != 1 && !deleteAll { 34 | return fmt.Errorf("delete: import path or --all flag is missing") 35 | } else if len(args) == 1 && deleteAll { 36 | return fmt.Errorf("delete: you cannot specify path and --all flag at once") 37 | } 38 | 39 | m, err := vendor.ReadManifest(manifestFile) 40 | if err != nil { 41 | return fmt.Errorf("could not load manifest: %v", err) 42 | } 43 | 44 | var dependencies []vendor.Dependency 45 | if deleteAll { 46 | dependencies = make([]vendor.Dependency, len(m.Dependencies)) 47 | copy(dependencies, m.Dependencies) 48 | } else { 49 | p := args[0] 50 | dependency, err := m.GetDependencyForImportpath(p) 51 | if err != nil { 52 | return fmt.Errorf("could not get dependency: %v", err) 53 | } 54 | if p != dependency.Importpath { 55 | return fmt.Errorf("a parent of the specified dependency is vendored, remove that instead: %v", 56 | dependency.Importpath) 57 | } 58 | dependencies = append(dependencies, dependency) 59 | } 60 | 61 | for _, d := range dependencies { 62 | path := d.Importpath 63 | 64 | if err := m.RemoveDependency(d); err != nil { 65 | return fmt.Errorf("dependency could not be deleted: %v", err) 66 | } 67 | 68 | if err := fileutils.RemoveAll(filepath.Join(vendorDir, filepath.FromSlash(path))); err != nil { 69 | // TODO(dfc) need to apply vendor.cleanpath here to remove indermediate directories. 70 | return fmt.Errorf("dependency could not be deleted: %v", err) 71 | } 72 | } 73 | return vendor.WriteManifest(manifestFile, m) 74 | }, 75 | AddFlags: addDeleteFlags, 76 | } 77 | -------------------------------------------------------------------------------- /gbvendor/discovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vendor 6 | 7 | import ( 8 | "encoding/xml" 9 | "fmt" 10 | "io" 11 | "strings" 12 | ) 13 | 14 | // charsetReader returns a reader for the given charset. Currently 15 | // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful 16 | // error which is printed by go get, so the user can find why the package 17 | // wasn't downloaded if the encoding is not supported. Note that, in 18 | // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters 19 | // greater than 0x7f are not rejected). 20 | func charsetReader(charset string, input io.Reader) (io.Reader, error) { 21 | switch strings.ToLower(charset) { 22 | case "ascii": 23 | return input, nil 24 | default: 25 | return nil, fmt.Errorf("can't decode XML document using charset %q", charset) 26 | } 27 | } 28 | 29 | type metaImport struct { 30 | Prefix, VCS, RepoRoot string 31 | } 32 | 33 | // parseMetaGoImports returns meta imports from the HTML in r. 34 | // Parsing ends at the end of the section or the beginning of the . 35 | func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { 36 | d := xml.NewDecoder(r) 37 | d.CharsetReader = charsetReader 38 | d.Strict = false 39 | var t xml.Token 40 | for { 41 | t, err = d.RawToken() 42 | if err != nil { 43 | if err == io.EOF || len(imports) > 0 { 44 | err = nil 45 | } 46 | return 47 | } 48 | if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { 49 | return 50 | } 51 | if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { 52 | return 53 | } 54 | e, ok := t.(xml.StartElement) 55 | if !ok || !strings.EqualFold(e.Name.Local, "meta") { 56 | continue 57 | } 58 | if attrValue(e.Attr, "name") != "go-import" { 59 | continue 60 | } 61 | if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { 62 | imports = append(imports, metaImport{ 63 | Prefix: f[0], 64 | VCS: f[1], 65 | RepoRoot: f[2], 66 | }) 67 | } 68 | } 69 | } 70 | 71 | // attrValue returns the attribute value for the case-insensitive key 72 | // `name', or the empty string if nothing is found. 73 | func attrValue(attrs []xml.Attr, name string) string { 74 | for _, a := range attrs { 75 | if strings.EqualFold(a.Name.Local, name) { 76 | return a.Value 77 | } 78 | } 79 | return "" 80 | } 81 | -------------------------------------------------------------------------------- /help.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | "text/template" 11 | "unicode" 12 | "unicode/utf8" 13 | ) 14 | 15 | var helpTemplate = `usage: gvt {{.UsageLine}} 16 | 17 | {{.Long | trim}} 18 | ` 19 | 20 | // help implements the 'help' command. 21 | func help(args []string) { 22 | if len(args) == 0 { 23 | printUsage(os.Stdout) 24 | return 25 | } 26 | if len(args) != 1 { 27 | fmt.Fprintf(os.Stderr, "usage: gvt help command\n\nToo many arguments given.\n") 28 | os.Exit(2) 29 | } 30 | 31 | arg := args[0] 32 | 33 | // 'gvt help documentation' generates alldocs.go. 34 | if arg == "documentation" { 35 | var u bytes.Buffer 36 | printUsage(&u) 37 | f, _ := os.Create("alldocs.go") 38 | tmpl(f, documentationTemplate, struct { 39 | Usage string 40 | Commands []*Command 41 | }{ 42 | u.String(), 43 | commands, 44 | }) 45 | f.Close() 46 | return 47 | } 48 | 49 | for _, cmd := range commands { 50 | if cmd.Name == arg { 51 | tmpl(os.Stdout, helpTemplate, cmd) 52 | return 53 | } 54 | } 55 | 56 | fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'gvt help'.\n", arg) 57 | os.Exit(2) 58 | } 59 | 60 | var usageTemplate = `gvt, a simple go vendoring tool based on gb-vendor. 61 | 62 | Usage: 63 | gvt command [arguments] 64 | 65 | The commands are: 66 | {{range .}} 67 | {{.Name | printf "%-11s"}} {{.Short}}{{end}} 68 | 69 | Use "gvt help [command]" for more information about a command. 70 | ` 71 | 72 | var documentationTemplate = `// DO NOT EDIT THIS FILE. 73 | //go:generate gvt help documentation 74 | 75 | /* 76 | {{ .Usage }} 77 | 78 | {{range .Commands}}{{if .Short}}{{.Short | capitalize}} 79 | {{end}} 80 | Usage: 81 | gvt {{.UsageLine}} 82 | 83 | {{.Long | trim}} 84 | 85 | {{end}}*/ 86 | package main 87 | ` 88 | 89 | // tmpl executes the given template text on data, writing the result to w. 90 | func tmpl(w io.Writer, text string, data interface{}) { 91 | t := template.New("top") 92 | t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) 93 | template.Must(t.Parse(text)) 94 | if err := t.Execute(w, data); err != nil { 95 | panic(err) 96 | } 97 | } 98 | 99 | func capitalize(s string) string { 100 | if s == "" { 101 | return s 102 | } 103 | r, n := utf8.DecodeRuneInString(s) 104 | return string(unicode.ToTitle(r)) + s[n:] 105 | } 106 | 107 | func printUsage(w io.Writer) { 108 | bw := bufio.NewWriter(w) 109 | tmpl(bw, usageTemplate, commands) 110 | bw.Flush() 111 | } 112 | -------------------------------------------------------------------------------- /gbvendor/manifest_test.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/FiloSottile/gvt/fileutils" 10 | ) 11 | 12 | func mktemp(t *testing.T) string { 13 | s, err := mktmp() 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | return s 18 | } 19 | 20 | func assertNotExists(t *testing.T, path string) { 21 | _, err := os.Stat(path) 22 | if err == nil || !os.IsNotExist(err) { 23 | t.Fatalf("expected %q to be not found, got %v", path, err) 24 | } 25 | } 26 | 27 | func assertExists(t *testing.T, path string) { 28 | _, err := os.Stat(path) 29 | if err != nil { 30 | t.Fatalf("expected %q to be found, got %v", path, err) 31 | } 32 | } 33 | 34 | func TestManifest(t *testing.T) { 35 | root := mktemp(t) 36 | defer fileutils.RemoveAll(root) 37 | 38 | mf := filepath.Join(root, "vendor") 39 | 40 | // check that reading an nonexistent manifest 41 | // does not return an error 42 | m, err := ReadManifest(mf) 43 | if err != nil { 44 | t.Fatalf("reading a non existant manifest should not fail: %v", err) 45 | } 46 | 47 | // check that no manifest file was created 48 | assertNotExists(t, mf) 49 | 50 | // add a dep 51 | m.Dependencies = append(m.Dependencies, Dependency{ 52 | Importpath: "github.com/foo/bar/baz", 53 | Repository: "https://github.com/foo/bar", 54 | Revision: "cafebad", 55 | Branch: "master", 56 | Path: "/baz", 57 | }) 58 | 59 | // write it back 60 | if err := WriteManifest(mf, m); err != nil { 61 | t.Fatalf("WriteManifest failed: %v", err) 62 | } 63 | 64 | // check the manifest was written 65 | assertExists(t, mf) 66 | 67 | // remove it 68 | m.Dependencies = nil 69 | if err := WriteManifest(mf, m); err != nil { 70 | t.Fatalf("WriteManifest failed: %v", err) 71 | } 72 | 73 | // check that no manifest file was removed 74 | assertNotExists(t, mf) 75 | } 76 | 77 | func TestEmptyPathIsNotWritten(t *testing.T) { 78 | m := Manifest{ 79 | Version: 0, 80 | Dependencies: []Dependency{{ 81 | Importpath: "github.com/foo/bar", 82 | Repository: "https://github.com/foo/bar", 83 | VCS: "git", 84 | Revision: "abcdef", 85 | Branch: "master", 86 | }}, 87 | } 88 | var buf bytes.Buffer 89 | if err := writeManifest(&buf, &m); err != nil { 90 | t.Fatal(err) 91 | } 92 | want := `{ 93 | "version": 0, 94 | "dependencies": [ 95 | { 96 | "importpath": "github.com/foo/bar", 97 | "repository": "https://github.com/foo/bar", 98 | "vcs": "git", 99 | "revision": "abcdef", 100 | "branch": "master" 101 | } 102 | ] 103 | }` 104 | got := buf.String() 105 | if want != got { 106 | t.Fatalf("want: %s, got %s", want, got) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/mkstdlib.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // mkstdlib generates the zstdlib.go file, containing the Go standard 4 | // library API symbols. It's baked into the binary to avoid scanning 5 | // GOPATH in the common case. 6 | package main 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "fmt" 12 | "go/format" 13 | "io" 14 | "io/ioutil" 15 | "log" 16 | "os" 17 | "path" 18 | "path/filepath" 19 | "regexp" 20 | "sort" 21 | "strings" 22 | ) 23 | 24 | func mustOpen(name string) io.Reader { 25 | f, err := os.Open(name) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | return f 30 | } 31 | 32 | func api(base string) string { 33 | return filepath.Join(os.Getenv("GOROOT"), "api", base) 34 | } 35 | 36 | var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`) 37 | 38 | func main() { 39 | var buf bytes.Buffer 40 | outf := func(format string, args ...interface{}) { 41 | fmt.Fprintf(&buf, format, args...) 42 | } 43 | outf("// AUTO-GENERATED BY mkstdlib.go\n\n") 44 | outf("package imports\n") 45 | outf("var stdlib = map[string]string{\n") 46 | f := io.MultiReader( 47 | mustOpen(api("go1.txt")), 48 | mustOpen(api("go1.1.txt")), 49 | mustOpen(api("go1.2.txt")), 50 | mustOpen(api("go1.3.txt")), 51 | mustOpen(api("go1.4.txt")), 52 | mustOpen(api("go1.5.txt")), 53 | mustOpen(api("go1.6.txt")), 54 | mustOpen(api("go1.7.txt")), 55 | ) 56 | sc := bufio.NewScanner(f) 57 | fullImport := map[string]string{} // "zip.NewReader" => "archive/zip" 58 | ambiguous := map[string]bool{} 59 | var keys []string 60 | for sc.Scan() { 61 | l := sc.Text() 62 | has := func(v string) bool { return strings.Contains(l, v) } 63 | if has("struct, ") || has("interface, ") || has(", method (") { 64 | continue 65 | } 66 | if m := sym.FindStringSubmatch(l); m != nil { 67 | full := m[1] 68 | key := path.Base(full) + "." + m[2] 69 | if exist, ok := fullImport[key]; ok { 70 | if exist != full { 71 | ambiguous[key] = true 72 | } 73 | } else { 74 | fullImport[key] = full 75 | keys = append(keys, key) 76 | } 77 | } 78 | } 79 | if err := sc.Err(); err != nil { 80 | log.Fatal(err) 81 | } 82 | sort.Strings(keys) 83 | for _, key := range keys { 84 | if ambiguous[key] { 85 | outf("\t// %q is ambiguous\n", key) 86 | } else { 87 | outf("\t%q: %q,\n", key, fullImport[key]) 88 | } 89 | } 90 | outf("\n") 91 | for _, sym := range [...]string{"Alignof", "ArbitraryType", "Offsetof", "Pointer", "Sizeof"} { 92 | outf("\t%q: %q,\n", "unsafe."+sym, "unsafe") 93 | } 94 | outf("}\n") 95 | fmtbuf, err := format.Source(buf.Bytes()) 96 | if err != nil { 97 | log.Fatal(err) 98 | } 99 | err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666) 100 | if err != nil { 101 | log.Fatal(err) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/build" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | var fs = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 14 | 15 | func init() { 16 | fs.Usage = func() {} 17 | } 18 | 19 | type Command struct { 20 | Name string 21 | UsageLine string 22 | Short string 23 | Long string 24 | Run func(args []string) error 25 | AddFlags func(fs *flag.FlagSet) 26 | } 27 | 28 | var commands = []*Command{ 29 | cmdFetch, 30 | cmdRestore, 31 | cmdUpdate, 32 | cmdList, 33 | cmdDelete, 34 | } 35 | 36 | func main() { 37 | args := os.Args[1:] 38 | 39 | switch { 40 | case len(args) < 1, args[0] == "-h", args[0] == "-help": 41 | printUsage(os.Stdout) 42 | os.Exit(0) 43 | case args[0] == "help": 44 | help(args[1:]) 45 | return 46 | case args[0] == "rebuild": 47 | // rebuild was renamed restore, alias for backwards compatibility 48 | args[0] = "restore" 49 | } 50 | 51 | for _, command := range commands { 52 | if command.Name == args[0] { 53 | 54 | // add extra flags if necessary 55 | if command.AddFlags != nil { 56 | command.AddFlags(fs) 57 | } 58 | 59 | if err := fs.Parse(args[1:]); err != nil { 60 | if err == flag.ErrHelp { 61 | help(args[:1]) 62 | os.Exit(0) 63 | } 64 | fmt.Fprint(os.Stderr, "\n") 65 | help(args[:1]) 66 | os.Exit(3) 67 | } 68 | 69 | if err := command.Run(fs.Args()); err != nil { 70 | log.Fatalf("command %q failed: %v", command.Name, err) 71 | } 72 | if err := GlobalDownloader.Flush(); err != nil { 73 | log.Fatalf("failed to delete tempdirs: %v", err) 74 | } 75 | return 76 | } 77 | } 78 | fmt.Fprintf(os.Stderr, "unknown command: %q\n\n", args[0]) 79 | printUsage(os.Stderr) 80 | os.Exit(3) 81 | } 82 | 83 | var ( 84 | vendorDir, manifestFile string 85 | importPath string 86 | ) 87 | 88 | func init() { 89 | wd, err := os.Getwd() 90 | if err != nil { 91 | log.Fatal(err) 92 | } 93 | vendorDir = filepath.Join(wd, "vendor") 94 | manifestFile = filepath.Join(vendorDir, "manifest") 95 | var srcTree []string 96 | for _, p := range filepath.SplitList(build.Default.GOPATH) { 97 | srcTree = append(srcTree, filepath.Join(p, "src")+string(filepath.Separator)) 98 | } 99 | 100 | var pathMismatch int 101 | for _, p := range srcTree { 102 | if !strings.HasPrefix(wd, p) && wd != p[:len(p)-1] { 103 | pathMismatch++ 104 | continue 105 | } 106 | importPath = filepath.ToSlash(strings.TrimPrefix(wd, p)) 107 | break 108 | } 109 | if _, err := os.Stat(filepath.Join(wd, "Makefile")); os.IsNotExist(err) { 110 | if build.Default.GOPATH == "" || len(srcTree) == pathMismatch { 111 | log.Println("WARNING: for go vendoring to work your project needs to be somewhere under $GOPATH/src/") 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /downloader.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "sync" 6 | 7 | "github.com/FiloSottile/gvt/gbvendor" 8 | ) 9 | 10 | type cacheKey struct { 11 | url, repoType string 12 | branch, tag, revision string 13 | } 14 | 15 | type cacheEntry struct { 16 | wg sync.WaitGroup 17 | v vendor.WorkingCopy 18 | err error 19 | } 20 | 21 | // Downloader acts as a cache for downloaded repositories 22 | type Downloader struct { 23 | wcsMu sync.Mutex 24 | wcs map[cacheKey]*cacheEntry 25 | 26 | reposMu sync.RWMutex 27 | repos map[string]vendor.RemoteRepo 28 | reposI map[string]vendor.RemoteRepo 29 | } 30 | 31 | var GlobalDownloader = Downloader{} 32 | 33 | func init() { 34 | GlobalDownloader.wcs = make(map[cacheKey]*cacheEntry) 35 | GlobalDownloader.repos = make(map[string]vendor.RemoteRepo) 36 | GlobalDownloader.reposI = make(map[string]vendor.RemoteRepo) 37 | } 38 | 39 | // Get returns a cached WorkingCopy, or runs RemoteRepo.Checkout 40 | func (d *Downloader) Get(repo vendor.RemoteRepo, branch, tag, revision string) (vendor.WorkingCopy, error) { 41 | key := cacheKey{ 42 | url: repo.URL(), repoType: repo.Type(), 43 | branch: branch, tag: tag, revision: revision, 44 | } 45 | d.wcsMu.Lock() 46 | if entry, ok := d.wcs[key]; ok { 47 | d.wcsMu.Unlock() 48 | entry.wg.Wait() 49 | return entry.v, entry.err 50 | } 51 | 52 | entry := &cacheEntry{} 53 | entry.wg.Add(1) 54 | d.wcs[key] = entry 55 | d.wcsMu.Unlock() 56 | 57 | entry.v, entry.err = repo.Checkout(branch, tag, revision) 58 | entry.wg.Done() 59 | return entry.v, entry.err 60 | } 61 | 62 | func (d *Downloader) Flush() error { 63 | d.wcsMu.Lock() 64 | defer d.wcsMu.Unlock() 65 | 66 | for _, entry := range d.wcs { 67 | entry.wg.Wait() 68 | if entry.err != nil { 69 | continue 70 | } 71 | if err := entry.v.Destroy(); err != nil { 72 | return err 73 | } 74 | } 75 | return nil 76 | } 77 | 78 | // DeduceRemoteRepo is a cached version of vendor.DeduceRemoteRepo 79 | func (d *Downloader) DeduceRemoteRepo(path string, insecure bool) (vendor.RemoteRepo, string, error) { 80 | cache := d.repos 81 | if insecure { 82 | cache = d.reposI 83 | } 84 | 85 | d.reposMu.RLock() 86 | for p, repo := range cache { 87 | if path == p || strings.HasPrefix(path, p+"/") { 88 | d.reposMu.RUnlock() 89 | extra := strings.Trim(strings.TrimPrefix(path, p), "/") 90 | return repo, extra, nil 91 | } 92 | } 93 | d.reposMu.RUnlock() 94 | 95 | repo, extra, err := vendor.DeduceRemoteRepo(path, insecure) 96 | if err != nil { 97 | return repo, extra, err 98 | } 99 | 100 | if !strings.HasSuffix(path, extra) { 101 | // Shouldn't happen, but in case just bypass the cache 102 | return repo, extra, err 103 | } 104 | basePath := strings.Trim(strings.TrimSuffix(path, extra), "/") 105 | d.reposMu.Lock() 106 | cache[basePath] = repo 107 | d.reposMu.Unlock() 108 | 109 | return repo, extra, err 110 | } 111 | -------------------------------------------------------------------------------- /fileutils/path_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | package fileutils 5 | 6 | import ( 7 | "os" 8 | "runtime" 9 | "testing" 10 | ) 11 | 12 | var isReadonlyError = func(error) bool { return false } 13 | 14 | func TestRemoveAll(t *testing.T) { 15 | tmpDir := os.TempDir() 16 | // Work directory. 17 | path := tmpDir + "/_TestRemoveAll_" 18 | fpath := path + "/file" 19 | dpath := path + "/dir" 20 | 21 | // Make directory with 1 file and remove. 22 | if err := os.MkdirAll(path, 0777); err != nil { 23 | t.Fatalf("MkdirAll %q: %s", path, err) 24 | } 25 | fd, err := os.Create(fpath) 26 | if err != nil { 27 | t.Fatalf("create %q: %s", fpath, err) 28 | } 29 | fd.Close() 30 | if err = RemoveAll(path); err != nil { 31 | t.Fatalf("RemoveAll %q (first): %s", path, err) 32 | } 33 | if _, err = os.Lstat(path); err == nil { 34 | t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path) 35 | } 36 | 37 | // Make directory with file and subdirectory and remove. 38 | if err = os.MkdirAll(dpath, 0777); err != nil { 39 | t.Fatalf("MkdirAll %q: %s", dpath, err) 40 | } 41 | fd, err = os.Create(fpath) 42 | if err != nil { 43 | t.Fatalf("create %q: %s", fpath, err) 44 | } 45 | fd.Close() 46 | fd, err = os.Create(dpath + "/file") 47 | if err != nil { 48 | t.Fatalf("create %q: %s", fpath, err) 49 | } 50 | fd.Close() 51 | if err = RemoveAll(path); err != nil { 52 | t.Fatalf("RemoveAll %q (second): %s", path, err) 53 | } 54 | if _, err := os.Lstat(path); err == nil { 55 | t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path) 56 | } 57 | 58 | // Determine if we should run the following test. 59 | testit := true 60 | if runtime.GOOS == "windows" { 61 | // Chmod is not supported under windows. 62 | testit = false 63 | } else { 64 | // Test fails as root. 65 | testit = os.Getuid() != 0 66 | } 67 | if testit { 68 | // Make directory with file and subdirectory and trigger error. 69 | if err = os.MkdirAll(dpath, 0777); err != nil { 70 | t.Fatalf("MkdirAll %q: %s", dpath, err) 71 | } 72 | 73 | for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { 74 | fd, err = os.Create(s) 75 | if err != nil { 76 | t.Fatalf("create %q: %s", s, err) 77 | } 78 | fd.Close() 79 | } 80 | if err = os.Chmod(dpath, 0); err != nil { 81 | t.Fatalf("Chmod %q 0: %s", dpath, err) 82 | } 83 | 84 | // No error checking here: either RemoveAll 85 | // will or won't be able to remove dpath; 86 | // either way we want to see if it removes fpath 87 | // and path/zzz. Reasons why RemoveAll might 88 | // succeed in removing dpath as well include: 89 | // * running as root 90 | // * running on a file system without permissions (FAT) 91 | RemoveAll(path) 92 | os.Chmod(dpath, 0777) 93 | 94 | for _, s := range []string{fpath, path + "/zzz"} { 95 | if _, err = os.Lstat(s); err == nil { 96 | t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) 97 | } 98 | } 99 | } 100 | if err = RemoveAll(path); err != nil { 101 | t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) 102 | } 103 | if _, err = os.Lstat(path); err == nil { 104 | t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/wadey/gocovmerge/gocovmerge.go: -------------------------------------------------------------------------------- 1 | // gocovmerge takes the results from multiple `go test -coverprofile` runs and 2 | // merges them into one profile 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | "sort" 12 | 13 | "golang.org/x/tools/cover" 14 | ) 15 | 16 | func mergeProfiles(p *cover.Profile, merge *cover.Profile) { 17 | if p.Mode != merge.Mode { 18 | log.Fatalf("cannot merge profiles with different modes") 19 | } 20 | // Since the blocks are sorted, we can keep track of where the last block 21 | // was inserted and only look at the blocks after that as targets for merge 22 | startIndex := 0 23 | for _, b := range merge.Blocks { 24 | startIndex = mergeProfileBlock(p, b, startIndex) 25 | } 26 | } 27 | 28 | func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int { 29 | sortFunc := func(i int) bool { 30 | pi := p.Blocks[i+startIndex] 31 | return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol) 32 | } 33 | 34 | i := 0 35 | if sortFunc(i) != true { 36 | i = sort.Search(len(p.Blocks)-startIndex, sortFunc) 37 | } 38 | i += startIndex 39 | if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol { 40 | if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol { 41 | log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb) 42 | } 43 | switch p.Mode { 44 | case "set": 45 | p.Blocks[i].Count |= pb.Count 46 | case "count", "atomic": 47 | p.Blocks[i].Count += pb.Count 48 | default: 49 | log.Fatalf("unsupported covermode: '%s'", p.Mode) 50 | } 51 | } else { 52 | if i > 0 { 53 | pa := p.Blocks[i-1] 54 | if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) { 55 | log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb) 56 | } 57 | } 58 | if i < len(p.Blocks)-1 { 59 | pa := p.Blocks[i+1] 60 | if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) { 61 | log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb) 62 | } 63 | } 64 | p.Blocks = append(p.Blocks, cover.ProfileBlock{}) 65 | copy(p.Blocks[i+1:], p.Blocks[i:]) 66 | p.Blocks[i] = pb 67 | } 68 | return i + 1 69 | } 70 | 71 | func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile { 72 | i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName }) 73 | if i < len(profiles) && profiles[i].FileName == p.FileName { 74 | mergeProfiles(profiles[i], p) 75 | } else { 76 | profiles = append(profiles, nil) 77 | copy(profiles[i+1:], profiles[i:]) 78 | profiles[i] = p 79 | } 80 | return profiles 81 | } 82 | 83 | func dumpProfiles(profiles []*cover.Profile, out io.Writer) { 84 | if len(profiles) == 0 { 85 | return 86 | } 87 | fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode) 88 | for _, p := range profiles { 89 | for _, b := range p.Blocks { 90 | fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count) 91 | } 92 | } 93 | } 94 | 95 | func main() { 96 | flag.Parse() 97 | 98 | var merged []*cover.Profile 99 | 100 | for _, file := range flag.Args() { 101 | profiles, err := cover.ParseProfiles(file) 102 | if err != nil { 103 | log.Fatalf("failed to parse profiles: %v", err) 104 | } 105 | for _, p := range profiles { 106 | merged = addProfile(merged, p) 107 | } 108 | } 109 | 110 | dumpProfiles(merged, os.Stdout) 111 | } 112 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMPORT_PATH := github.com/FiloSottile/gvt 2 | V := 1 # When V is 1, print commands and build progress. 3 | IGNORED_PACKAGES := /vendor/ 4 | 5 | .PHONY: gvt 6 | gvt: .GOPATH/.ok 7 | $Q go install $(if $V,-v) $(VERSION_FLAGS) github.com/FiloSottile/gvt 8 | 9 | ##### =====> Utility targets <===== ##### 10 | 11 | .PHONY: clean test list cover format 12 | 13 | clean: 14 | $Q rm -rf bin .GOPATH 15 | 16 | test: .GOPATH/.ok 17 | $Q go test $(if $V,-v) -i -race $(allpackages) # install -race libs to speed up next run 18 | ifndef CI 19 | $Q go vet $(allpackages) 20 | $Q GODEBUG=cgocheck=2 go test -race $(allpackages) 21 | else 22 | $Q ( go vet $(allpackages); echo $$? ) | \ 23 | tee .GOPATH/test/vet.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/vet.txt) 24 | $Q ( GODEBUG=cgocheck=2 go test -v -race $(allpackages); echo $$? ) | \ 25 | tee .GOPATH/test/output.txt | sed '$$ d'; exit $$(tail -1 .GOPATH/test/output.txt) 26 | endif 27 | 28 | list: .GOPATH/.ok 29 | @echo $(allpackages) 30 | 31 | cover: bin/gocovmerge .GOPATH/.ok 32 | $Q rm -f .GOPATH/cover/*.out .GOPATH/cover/all.merged 33 | $(if $V,@echo "-- go test -coverpkg=./... -coverprofile=.GOPATH/cover/... ./...") 34 | $Q for MOD in $(allpackages); do \ 35 | go test -coverpkg=`echo $(allpackages)|tr " " ","` \ 36 | -coverprofile=.GOPATH/cover/unit-`echo $$MOD|tr "/" "_"`.out \ 37 | $$MOD 2>&1 | grep -v "no packages being tested depend on" || exit 1; \ 38 | done 39 | $Q ./bin/gocovmerge .GOPATH/cover/*.out > .GOPATH/cover/all.merged 40 | ifndef CI 41 | $Q go tool cover -html .GOPATH/cover/all.merged 42 | else 43 | $Q go tool cover -html .GOPATH/cover/all.merged -o .GOPATH/cover/all.html 44 | endif 45 | @echo "" 46 | @echo "=====> Total test coverage: <=====" 47 | @echo "" 48 | $Q go tool cover -func .GOPATH/cover/all.merged 49 | 50 | format: bin/goimports .GOPATH/.ok 51 | $Q find .GOPATH/src/$(IMPORT_PATH)/ -iname \*.go | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) | xargs ./bin/goimports -w 52 | 53 | ##### =====> Internals <===== ##### 54 | 55 | .PHONY: setup 56 | setup: 57 | @if grep "/.GOPATH" .gitignore > /dev/null 2>&1; then \ 58 | echo "This project seems already set up."; exit 1; fi 59 | @if ! which gvt > /dev/null; then echo "You need gvt to run the automated setup."; \ 60 | echo "Install it with: go get -u github.com/FiloSottile/gvt"; exit 1; fi 61 | gvt fetch golang.org/x/tools/cmd/goimports 62 | gvt fetch github.com/wadey/gocovmerge 63 | echo "/.GOPATH" >> .gitignore 64 | echo "/bin" >> .gitignore 65 | 66 | VERSION := $(shell git describe --tags --always --dirty="-dev") 67 | DATE := $(shell date -u '+%Y-%m-%d-%H%M UTC') 68 | VERSION_FLAGS := -ldflags='-X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"' 69 | 70 | # cd into the GOPATH to workaround ./... not following symlinks 71 | _allpackages = $(shell ( cd $(CURDIR)/.GOPATH/src/$(IMPORT_PATH) && \ 72 | GOPATH=$(CURDIR)/.GOPATH go list ./... 2>&1 1>&3 | \ 73 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES)) 1>&2 ) 3>&1 | \ 74 | grep -v -e "^$$" $(addprefix -e ,$(IGNORED_PACKAGES))) 75 | 76 | # memoize allpackages, so that it's executed only once and only if used 77 | allpackages = $(if $(__allpackages),,$(eval __allpackages := $$(_allpackages)))$(__allpackages) 78 | 79 | export GOPATH := $(CURDIR)/.GOPATH 80 | 81 | Q := $(if $V,,@) 82 | 83 | .GOPATH/.ok: 84 | $Q mkdir -p "$(dir .GOPATH/src/$(IMPORT_PATH))" 85 | $Q ln -s ../../../.. ".GOPATH/src/$(IMPORT_PATH)" 86 | $Q mkdir -p .GOPATH/test .GOPATH/cover 87 | $Q mkdir -p bin 88 | $Q ln -s ../bin .GOPATH/bin 89 | $Q touch $@ 90 | 91 | .PHONY: bin/gocovmerge bin/goimports 92 | bin/gocovmerge: .GOPATH/.ok 93 | $Q go install $(IMPORT_PATH)/vendor/github.com/wadey/gocovmerge 94 | bin/goimports: .GOPATH/.ok 95 | $Q go install $(IMPORT_PATH)/vendor/golang.org/x/tools/cmd/goimports 96 | -------------------------------------------------------------------------------- /alldocs.go: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT THIS FILE. 2 | //go:generate gvt help documentation 3 | 4 | /* 5 | gvt, a simple go vendoring tool based on gb-vendor. 6 | 7 | Usage: 8 | gvt command [arguments] 9 | 10 | The commands are: 11 | 12 | fetch fetch a remote dependency 13 | restore restore dependencies from manifest 14 | update update a local dependency 15 | list list dependencies one per line 16 | delete delete a local dependency 17 | 18 | Use "gvt help [command]" for more information about a command. 19 | 20 | 21 | Fetch a remote dependency 22 | 23 | Usage: 24 | gvt fetch [-branch branch] [-revision rev | -tag tag] [-precaire] [-no-recurse] [-t|-a] importpath 25 | 26 | fetch vendors an upstream import path. 27 | 28 | Recursive dependencies are fetched (at their master/tip/HEAD revision), unless they 29 | or their parent package are already present. 30 | 31 | If a subpackage of a dependency being fetched is already present, it will be deleted. 32 | 33 | The import path may include a url scheme. This may be useful when fetching dependencies 34 | from private repositories that cannot be probed. 35 | 36 | Flags: 37 | -t 38 | fetch also _test.go files and testdata. 39 | -a 40 | fetch all files and subfolders, ignoring ONLY .git, .hg and .bzr. 41 | -branch branch 42 | fetch from the named branch. Will also be used by gvt update. 43 | If not supplied the default upstream branch will be used. 44 | -no-recurse 45 | do not fetch recursively. 46 | -tag tag 47 | fetch the specified tag. 48 | -revision rev 49 | fetch the specific revision from the branch or repository. 50 | If no revision supplied, the latest available will be fetched. 51 | -precaire 52 | allow the use of insecure protocols. 53 | 54 | Restore dependencies from manifest 55 | 56 | Usage: 57 | gvt restore [-precaire] [-connections N] 58 | 59 | restore fetches the dependencies listed in the manifest. 60 | 61 | It's meant for workflows that don't include checking in to VCS the vendored 62 | source, for example if .gitignore includes lines like 63 | 64 | vendor/** 65 | !vendor/manifest 66 | 67 | Note that such a setup requires "gvt restore" to build the source, relies on 68 | the availability of the dependencies repositories and breaks "go get". 69 | 70 | Flags: 71 | -precaire 72 | allow the use of insecure protocols. 73 | -connections 74 | count of parallel download connections. 75 | 76 | Update a local dependency 77 | 78 | Usage: 79 | gvt update [ -all | importpath ] 80 | 81 | update replaces the source with the latest available from the head of the fetched branch. 82 | 83 | Updating from one copy of a dependency to another is ONLY possible when the 84 | dependency was fetched by branch, without using -tag or -revision. It will be 85 | updated to the HEAD of that branch, switching branches is not supported. 86 | 87 | To update across branches, or from one tag/revision to another, you must first 88 | use delete to remove the dependency, then fetch [ -tag | -revision | -branch ] 89 | to replace it. 90 | 91 | Flags: 92 | -all 93 | update all dependencies in the manifest. 94 | -precaire 95 | allow the use of insecure protocols. 96 | 97 | List dependencies one per line 98 | 99 | Usage: 100 | gvt list [-f format] 101 | 102 | list formats the contents of the manifest file. 103 | 104 | Flags: 105 | -f 106 | controls the template used for printing each manifest entry. If not supplied 107 | the default value is "{{.Importpath}}\t{{.Repository}}{{.Path}}\t{{.Branch}}\t{{.Revision}}" 108 | 109 | Delete a local dependency 110 | 111 | Usage: 112 | gvt delete [-all] importpath 113 | 114 | delete removes a dependency from the vendor directory and the manifest 115 | 116 | Flags: 117 | -all 118 | remove all dependencies 119 | 120 | */ 121 | package main 122 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fastwalk_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux,!appengine darwin freebsd openbsd netbsd 6 | 7 | package imports 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "os" 13 | "syscall" 14 | "unsafe" 15 | ) 16 | 17 | const blockSize = 8 << 10 18 | 19 | // unknownFileMode is a sentinel (and bogus) os.FileMode 20 | // value used to represent a syscall.DT_UNKNOWN Dirent.Type. 21 | const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice 22 | 23 | func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { 24 | fd, err := syscall.Open(dirName, 0, 0) 25 | if err != nil { 26 | return err 27 | } 28 | defer syscall.Close(fd) 29 | 30 | // The buffer must be at least a block long. 31 | buf := make([]byte, blockSize) // stack-allocated; doesn't escape 32 | bufp := 0 // starting read position in buf 33 | nbuf := 0 // end valid data in buf 34 | for { 35 | if bufp >= nbuf { 36 | bufp = 0 37 | nbuf, err = syscall.ReadDirent(fd, buf) 38 | if err != nil { 39 | return os.NewSyscallError("readdirent", err) 40 | } 41 | if nbuf <= 0 { 42 | return nil 43 | } 44 | } 45 | consumed, name, typ := parseDirEnt(buf[bufp:nbuf]) 46 | bufp += consumed 47 | if name == "" || name == "." || name == ".." { 48 | continue 49 | } 50 | // Fallback for filesystems (like old XFS) that don't 51 | // support Dirent.Type and have DT_UNKNOWN (0) there 52 | // instead. 53 | if typ == unknownFileMode { 54 | fi, err := os.Lstat(dirName + "/" + name) 55 | if err != nil { 56 | // It got deleted in the meantime. 57 | if os.IsNotExist(err) { 58 | continue 59 | } 60 | return err 61 | } 62 | typ = fi.Mode() & os.ModeType 63 | } 64 | if err := fn(dirName, name, typ); err != nil { 65 | return err 66 | } 67 | } 68 | } 69 | 70 | func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) { 71 | // golang.org/issue/15653 72 | dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0])) 73 | if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v { 74 | panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v)) 75 | } 76 | if len(buf) < int(dirent.Reclen) { 77 | panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen)) 78 | } 79 | consumed = int(dirent.Reclen) 80 | if direntInode(dirent) == 0 { // File absent in directory. 81 | return 82 | } 83 | switch dirent.Type { 84 | case syscall.DT_REG: 85 | typ = 0 86 | case syscall.DT_DIR: 87 | typ = os.ModeDir 88 | case syscall.DT_LNK: 89 | typ = os.ModeSymlink 90 | case syscall.DT_BLK: 91 | typ = os.ModeDevice 92 | case syscall.DT_FIFO: 93 | typ = os.ModeNamedPipe 94 | case syscall.DT_SOCK: 95 | typ = os.ModeSocket 96 | case syscall.DT_UNKNOWN: 97 | typ = unknownFileMode 98 | default: 99 | // Skip weird things. 100 | // It's probably a DT_WHT (http://lwn.net/Articles/325369/) 101 | // or something. Revisit if/when this package is moved outside 102 | // of goimports. goimports only cares about regular files, 103 | // symlinks, and directories. 104 | return 105 | } 106 | 107 | nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0])) 108 | nameLen := bytes.IndexByte(nameBuf[:], 0) 109 | if nameLen < 0 { 110 | panic("failed to find terminating 0 byte in dirent") 111 | } 112 | 113 | // Special cases for common things: 114 | if nameLen == 1 && nameBuf[0] == '.' { 115 | name = "." 116 | } else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' { 117 | name = ".." 118 | } else { 119 | name = string(nameBuf[:nameLen]) 120 | } 121 | return 122 | } 123 | -------------------------------------------------------------------------------- /gbvendor/repo_test.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestDeduceRemoteRepo(t *testing.T) { 10 | if testing.Short() { 11 | t.Skipf("skipping network tests in -short mode") 12 | } 13 | tests := []struct { 14 | path string 15 | want RemoteRepo 16 | extra string 17 | err error 18 | insecure bool 19 | }{{ 20 | path: "", 21 | err: fmt.Errorf(`"" is not a valid import path`), 22 | }, { 23 | path: "corporate", 24 | err: fmt.Errorf(`"corporate" is not a valid import path`), 25 | }, { 26 | path: "github.com/cznic/b", 27 | want: &gitrepo{ 28 | url: "https://github.com/cznic/b", 29 | }, 30 | }, { 31 | path: "github.com/pkg/sftp", 32 | want: &gitrepo{ 33 | url: "https://github.com/pkg/sftp", 34 | }, 35 | }, { 36 | path: "github.com/pkg/sftp/examples/gsftp", 37 | want: &gitrepo{ 38 | url: "https://github.com/pkg/sftp", 39 | }, 40 | extra: "/examples/gsftp", 41 | }, { 42 | path: "github.com/coreos/go-etcd", 43 | want: &gitrepo{ 44 | url: "https://github.com/coreos/go-etcd", 45 | }, 46 | /* 47 | bitbucket cannot maintain a stable ssh key across their app servers 48 | and this mucks up ci testing because mercurial does not have any 49 | way of unconditionally accepting new ssh keys for the host. 50 | Great work TEAM. 51 | }, { 52 | path: "bitbucket.org/davecheney/gitrepo/cmd/main", 53 | want: &gitrepo{ 54 | url: "https://bitbucket.org/davecheney/gitrepo", 55 | }, 56 | extra: "/cmd/main", 57 | }, { 58 | path: "bitbucket.org/davecheney/hgrepo/cmd/main", 59 | want: &hgrepo{ 60 | url: "https://bitbucket.org/davecheney/hgrepo", 61 | }, 62 | extra: "/cmd/main", 63 | */ 64 | }, { 65 | path: "git.eclipse.org/gitroot/epf/org.eclipse.epf.docs.git", 66 | want: &gitrepo{ 67 | url: "https://git.eclipse.org/gitroot/epf/org.eclipse.epf.docs.git", 68 | }, 69 | }, { 70 | path: "git.apache.org/thrift.git/lib/go/thrift", 71 | want: &gitrepo{ 72 | url: "https://git.apache.org/thrift.git", 73 | }, 74 | extra: "/lib/go/thrift", 75 | }, { 76 | path: "gopkg.in/check.v1", 77 | want: &gitrepo{ 78 | url: "https://gopkg.in/check.v1", 79 | }, 80 | extra: "", 81 | }, { 82 | path: "goji.io", 83 | want: &gitrepo{ 84 | url: "https://github.com/goji/goji", 85 | }, 86 | extra: "", 87 | }, { 88 | path: "golang.org/x/tools/go/vcs", 89 | want: &gitrepo{ 90 | url: "https://go.googlesource.com/tools", 91 | }, 92 | extra: "/go/vcs", 93 | }, { 94 | path: "labix.org/v2/mgo", 95 | want: &bzrrepo{ 96 | url: "https://launchpad.net/mgo/v2", 97 | }, 98 | insecure: true, 99 | }, { 100 | path: "launchpad.net/gnuflag", 101 | want: &bzrrepo{ 102 | url: "https://launchpad.net/gnuflag", 103 | }, 104 | }, { 105 | path: "https://github.com/pkg/sftp", 106 | want: &gitrepo{ 107 | url: "https://github.com/pkg/sftp", 108 | }, 109 | }, { 110 | path: "git://github.com/pkg/sftp", 111 | want: &gitrepo{ 112 | url: "git://github.com/pkg/sftp", 113 | }, 114 | insecure: true, 115 | }} 116 | 117 | for _, tt := range tests { 118 | t.Run(fmt.Sprintf("DeduceRemoteRepo(%q, %v)", tt.path, tt.insecure), func(t *testing.T) { 119 | got, extra, err := DeduceRemoteRepo(tt.path, tt.insecure) 120 | if !reflect.DeepEqual(err, tt.err) { 121 | t.Fatalf("DeduceRemoteRepo(%q): want err: %v, got err: %v", tt.path, tt.err, err) 122 | } 123 | if !reflect.DeepEqual(got, tt.want) || extra != tt.extra { 124 | t.Errorf("DeduceRemoteRepo(%q): want %#v, %v, got %#v, %v", tt.path, tt.want, tt.extra, got, extra) 125 | } 126 | 127 | if tt.want != nil { 128 | got, err := NewRemoteRepo(tt.want.URL(), tt.want.Type(), tt.insecure) 129 | if err != nil { 130 | t.Fatal(err) 131 | } 132 | if !reflect.DeepEqual(got, tt.want) { 133 | t.Errorf("NewRemoteRepo(%s, %s): want %#v, got %#v", tt.want.URL(), tt.want.Type(), tt.want, got) 134 | } 135 | } 136 | }) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /update.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "path/filepath" 7 | 8 | "github.com/FiloSottile/gvt/fileutils" 9 | "github.com/FiloSottile/gvt/gbvendor" 10 | ) 11 | 12 | var ( 13 | updateAll bool // update all dependencies 14 | ) 15 | 16 | func addUpdateFlags(fs *flag.FlagSet) { 17 | fs.BoolVar(&updateAll, "all", false, "update all dependencies") 18 | fs.BoolVar(&insecure, "precaire", false, "allow the use of insecure protocols") 19 | } 20 | 21 | var cmdUpdate = &Command{ 22 | Name: "update", 23 | UsageLine: "update [ -all | importpath ]", 24 | Short: "update a local dependency", 25 | Long: `update replaces the source with the latest available from the head of the fetched branch. 26 | 27 | Updating from one copy of a dependency to another is ONLY possible when the 28 | dependency was fetched by branch, without using -tag or -revision. It will be 29 | updated to the HEAD of that branch, switching branches is not supported. 30 | 31 | To update across branches, or from one tag/revision to another, you must first 32 | use delete to remove the dependency, then fetch [ -tag | -revision | -branch ] 33 | to replace it. 34 | 35 | Flags: 36 | -all 37 | update all dependencies in the manifest. 38 | -precaire 39 | allow the use of insecure protocols. 40 | 41 | `, 42 | Run: func(args []string) error { 43 | if len(args) != 1 && !updateAll { 44 | return fmt.Errorf("update: import path or -all flag is missing") 45 | } else if len(args) == 1 && updateAll { 46 | return fmt.Errorf("update: you cannot specify path and -all flag at once") 47 | } 48 | 49 | m, err := vendor.ReadManifest(manifestFile) 50 | if err != nil { 51 | return fmt.Errorf("could not load manifest: %v", err) 52 | } 53 | 54 | var dependencies []vendor.Dependency 55 | if updateAll { 56 | dependencies = make([]vendor.Dependency, len(m.Dependencies)) 57 | copy(dependencies, m.Dependencies) 58 | } else { 59 | p := args[0] 60 | dependency, err := m.GetDependencyForImportpath(p) 61 | if err != nil { 62 | return fmt.Errorf("could not get dependency: %v", err) 63 | } 64 | dependencies = append(dependencies, dependency) 65 | } 66 | 67 | for _, d := range dependencies { 68 | err = m.RemoveDependency(d) 69 | if err != nil { 70 | return fmt.Errorf("dependency could not be deleted from manifest: %v", err) 71 | } 72 | 73 | repo, err := vendor.NewRemoteRepo(d.Repository, d.VCS, insecure) 74 | if err != nil { 75 | return fmt.Errorf("could not determine repository for import %q", d.Importpath) 76 | } 77 | 78 | wc, err := GlobalDownloader.Get(repo, d.Branch, "", "") 79 | if err != nil { 80 | return err 81 | } 82 | 83 | rev, err := wc.Revision() 84 | if err != nil { 85 | return err 86 | } 87 | 88 | branch, err := wc.Branch() 89 | if err != nil { 90 | return err 91 | } 92 | 93 | dep := vendor.Dependency{ 94 | Importpath: d.Importpath, 95 | Repository: repo.URL(), 96 | VCS: repo.Type(), 97 | Revision: rev, 98 | Branch: branch, 99 | Path: d.Path, 100 | NoTests: d.NoTests, 101 | AllFiles: d.AllFiles, 102 | } 103 | 104 | if err := fileutils.RemoveAll(filepath.Join(vendorDir, filepath.FromSlash(d.Importpath))); err != nil { 105 | // TODO(dfc) need to apply vendor.cleanpath here to remove intermediate directories. 106 | return fmt.Errorf("dependency could not be deleted: %v", err) 107 | } 108 | 109 | dst := filepath.Join(vendorDir, filepath.FromSlash(dep.Importpath)) 110 | src := filepath.Join(wc.Dir(), dep.Path) 111 | 112 | if err := fileutils.Copypath(dst, src, !d.NoTests, d.AllFiles); err != nil { 113 | return err 114 | } 115 | 116 | if err := fileutils.CopyLicense(dst, wc.Dir()); err != nil { 117 | return err 118 | } 119 | 120 | if err := m.AddDependency(dep); err != nil { 121 | return err 122 | } 123 | 124 | if err := vendor.WriteManifest(manifestFile, m); err != nil { 125 | return err 126 | } 127 | } 128 | 129 | return nil 130 | }, 131 | AddFlags: addUpdateFlags, 132 | } 133 | -------------------------------------------------------------------------------- /gbvendor/imports_test.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestFetchMetadata(t *testing.T) { 12 | if testing.Short() { 13 | t.Skipf("skipping network tests in -short mode") 14 | } 15 | type testParams struct { 16 | path string 17 | want string 18 | insecure bool 19 | } 20 | tests := []testParams{{ 21 | path: "golang.org/x/tools/cmd/godoc", 22 | want: ` 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Nothing to see here; move along. 32 | 33 | 34 | `, 35 | }, { 36 | path: "gopkg.in/check.v1", 37 | want: ` 38 | 39 | 40 | 41 | 42 | 43 | 44 | go get gopkg.in/check.v1 45 | 46 | 47 | `, 48 | }} 49 | 50 | for _, tt := range tests { 51 | r, err := FetchMetadata(tt.path, tt.insecure) 52 | if err != nil { 53 | t.Error(err) 54 | continue 55 | } 56 | var buf bytes.Buffer 57 | if _, err := io.Copy(&buf, r); err != nil { 58 | t.Error(err) 59 | r.Close() 60 | continue 61 | } 62 | r.Close() 63 | got := buf.String() 64 | if got != tt.want { 65 | t.Errorf("FetchMetadata(%q): want %q, got %q", tt.path, tt.want, got) 66 | } 67 | } 68 | 69 | // Test for error catch. 70 | errTests := []testParams{{ 71 | path: "any.inaccessible.server/the.project", 72 | want: `unable to determine remote metadata protocol: failed to access url "http://any.inaccessible.server/the.project?go-get=1"`, 73 | insecure: true, 74 | }, { 75 | path: "any.inaccessible.server/the.project", 76 | want: `unable to determine remote metadata protocol: failed to access url "https://any.inaccessible.server/the.project?go-get=1"`, 77 | insecure: false, 78 | }} 79 | 80 | for _, ett := range errTests { 81 | r, err := FetchMetadata(ett.path, ett.insecure) 82 | if err == nil { 83 | t.Errorf("Access to url %q without any error, but the error should be happen.", ett.path) 84 | if r != nil { 85 | r.Close() 86 | } 87 | continue 88 | } 89 | got := err.Error() 90 | if got != ett.want { 91 | t.Errorf("FetchMetadata(%q): want %q, got %q", ett.path, ett.want, got) 92 | } 93 | } 94 | } 95 | 96 | func TestParseMetadata(t *testing.T) { 97 | if testing.Short() { 98 | t.Skipf("skipping network tests in -short mode") 99 | } 100 | tests := []struct { 101 | path string 102 | importpath string 103 | vcs string 104 | reporoot string 105 | insecure bool 106 | err error 107 | }{{ 108 | path: "golang.org/x/tools/cmd/godoc", 109 | importpath: "golang.org/x/tools", 110 | vcs: "git", 111 | reporoot: "https://go.googlesource.com/tools", 112 | }, { 113 | path: "gopkg.in/check.v1", 114 | importpath: "gopkg.in/check.v1", 115 | vcs: "git", 116 | reporoot: "https://gopkg.in/check.v1", 117 | }, { 118 | path: "gopkg.in/mgo.v2/bson", 119 | importpath: "gopkg.in/mgo.v2", 120 | vcs: "git", 121 | reporoot: "https://gopkg.in/mgo.v2", 122 | }, { 123 | path: "speter.net/go/exp", 124 | err: fmt.Errorf("go-import metadata not found"), 125 | }} 126 | 127 | for _, tt := range tests { 128 | importpath, vcs, reporoot, err := ParseMetadata(tt.path, tt.insecure) 129 | if !reflect.DeepEqual(err, tt.err) { 130 | t.Error(err) 131 | continue 132 | } 133 | if importpath != tt.importpath || vcs != tt.vcs || reporoot != tt.reporoot { 134 | t.Errorf("ParseMetadata(%q): want %s %s %s, got %s %s %s ", tt.path, tt.importpath, tt.vcs, tt.reporoot, importpath, vcs, reporoot) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /restore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "sync" 10 | "sync/atomic" 11 | 12 | "github.com/FiloSottile/gvt/fileutils" 13 | "github.com/FiloSottile/gvt/gbvendor" 14 | ) 15 | 16 | var ( 17 | rbInsecure bool // Allow the use of insecure protocols 18 | rbConnections uint // Count of concurrent download connections 19 | ) 20 | 21 | func addRestoreFlags(fs *flag.FlagSet) { 22 | fs.BoolVar(&rbInsecure, "precaire", false, "allow the use of insecure protocols") 23 | fs.UintVar(&rbConnections, "connections", 8, "count of parallel download connections") 24 | } 25 | 26 | var cmdRestore = &Command{ 27 | Name: "restore", 28 | UsageLine: "restore [-precaire] [-connections N]", 29 | Short: "restore dependencies from manifest", 30 | Long: `restore fetches the dependencies listed in the manifest. 31 | 32 | It's meant for workflows that don't include checking in to VCS the vendored 33 | source, for example if .gitignore includes lines like 34 | 35 | vendor/** 36 | !vendor/manifest 37 | 38 | Note that such a setup requires "gvt restore" to build the source, relies on 39 | the availability of the dependencies repositories and breaks "go get". 40 | 41 | Flags: 42 | -precaire 43 | allow the use of insecure protocols. 44 | -connections 45 | count of parallel download connections. 46 | `, 47 | Run: func(args []string) error { 48 | switch len(args) { 49 | case 0: 50 | return restore(manifestFile) 51 | default: 52 | return fmt.Errorf("restore takes no arguments") 53 | } 54 | }, 55 | AddFlags: addRestoreFlags, 56 | } 57 | 58 | func restore(manFile string) error { 59 | m, err := vendor.ReadManifest(manFile) 60 | if err != nil { 61 | return fmt.Errorf("could not load manifest: %v", err) 62 | } 63 | 64 | var errors uint32 65 | var wg sync.WaitGroup 66 | depC := make(chan vendor.Dependency) 67 | for i := 0; i < int(rbConnections); i++ { 68 | wg.Add(1) 69 | go func() { 70 | defer wg.Done() 71 | for d := range depC { 72 | if err := downloadDependency(d, &errors, vendorDir, false); err != nil { 73 | log.Printf("%s: %v", d.Importpath, err) 74 | atomic.AddUint32(&errors, 1) 75 | } 76 | } 77 | }() 78 | } 79 | 80 | for _, dep := range m.Dependencies { 81 | depC <- dep 82 | } 83 | close(depC) 84 | wg.Wait() 85 | 86 | if errors > 0 { 87 | return fmt.Errorf("failed to fetch %d dependencies", errors) 88 | } 89 | 90 | return nil 91 | } 92 | 93 | func downloadDependency(dep vendor.Dependency, errors *uint32, vendorDir string, recursive bool) error { 94 | extraMsg := "" 95 | if !dep.NoTests { 96 | extraMsg = "(including tests)" 97 | } 98 | if dep.AllFiles { 99 | extraMsg = "(without file exclusions)" 100 | } 101 | if recursive { 102 | log.Printf("fetching recursive %s %s", dep.Importpath, extraMsg) 103 | } else { 104 | log.Printf("fetching %s %s", dep.Importpath, extraMsg) 105 | } 106 | 107 | repo, err := vendor.NewRemoteRepo(dep.Repository, dep.VCS, rbInsecure) 108 | if err != nil { 109 | return fmt.Errorf("dependency could not be processed: %s", err) 110 | } 111 | // We can't pass the branch here, and benefit from narrow clones, as the 112 | // revision might not be in the branch tree anymore. Thanks rebase. 113 | wc, err := GlobalDownloader.Get(repo, "", "", dep.Revision) 114 | if err != nil { 115 | return fmt.Errorf("dependency could not be fetched: %s", err) 116 | } 117 | dst := filepath.Join(vendorDir, dep.Importpath) 118 | src := filepath.Join(wc.Dir(), dep.Path) 119 | 120 | if _, err := os.Stat(dst); err == nil { 121 | if err := fileutils.RemoveAll(dst); err != nil { 122 | return fmt.Errorf("dependency could not be deleted: %v", err) 123 | } 124 | } 125 | 126 | if err := fileutils.Copypath(dst, src, !dep.NoTests, dep.AllFiles); err != nil { 127 | return err 128 | } 129 | 130 | if err := fileutils.CopyLicense(dst, wc.Dir()); err != nil { 131 | return err 132 | } 133 | 134 | // Check for for manifests in dependencies 135 | man := filepath.Join(dst, "vendor", "manifest") 136 | venDir := filepath.Join(dst, "vendor") 137 | if _, err := os.Stat(man); err == nil { 138 | m, err := vendor.ReadManifest(man) 139 | if err != nil { 140 | return fmt.Errorf("could not load manifest: %v", err) 141 | } 142 | for _, d := range m.Dependencies { 143 | if err := downloadDependency(d, errors, venDir, true); err != nil { 144 | log.Printf("%s: %v", d.Importpath, err) 145 | atomic.AddUint32(errors, 1) 146 | } 147 | } 148 | } 149 | 150 | return nil 151 | } 152 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/mkindex.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // Copyright 2013 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // Command mkindex creates the file "pkgindex.go" containing an index of the Go 8 | // standard library. The file is intended to be built as part of the imports 9 | // package, so that the package may be used in environments where a GOROOT is 10 | // not available (such as App Engine). 11 | package main 12 | 13 | import ( 14 | "bytes" 15 | "fmt" 16 | "go/ast" 17 | "go/build" 18 | "go/format" 19 | "go/parser" 20 | "go/token" 21 | "io/ioutil" 22 | "log" 23 | "os" 24 | "path" 25 | "path/filepath" 26 | "strings" 27 | ) 28 | 29 | var ( 30 | pkgIndex = make(map[string][]pkg) 31 | exports = make(map[string]map[string]bool) 32 | ) 33 | 34 | func main() { 35 | // Don't use GOPATH. 36 | ctx := build.Default 37 | ctx.GOPATH = "" 38 | 39 | // Populate pkgIndex global from GOROOT. 40 | for _, path := range ctx.SrcDirs() { 41 | f, err := os.Open(path) 42 | if err != nil { 43 | log.Print(err) 44 | continue 45 | } 46 | children, err := f.Readdir(-1) 47 | f.Close() 48 | if err != nil { 49 | log.Print(err) 50 | continue 51 | } 52 | for _, child := range children { 53 | if child.IsDir() { 54 | loadPkg(path, child.Name()) 55 | } 56 | } 57 | } 58 | // Populate exports global. 59 | for _, ps := range pkgIndex { 60 | for _, p := range ps { 61 | e := loadExports(p.dir) 62 | if e != nil { 63 | exports[p.dir] = e 64 | } 65 | } 66 | } 67 | 68 | // Construct source file. 69 | var buf bytes.Buffer 70 | fmt.Fprint(&buf, pkgIndexHead) 71 | fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex) 72 | fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports) 73 | src := buf.Bytes() 74 | 75 | // Replace main.pkg type name with pkg. 76 | src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1) 77 | // Replace actual GOROOT with "/go". 78 | src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1) 79 | // Add some line wrapping. 80 | src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1) 81 | src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1) 82 | 83 | var err error 84 | src, err = format.Source(src) 85 | if err != nil { 86 | log.Fatal(err) 87 | } 88 | 89 | // Write out source file. 90 | err = ioutil.WriteFile("pkgindex.go", src, 0644) 91 | if err != nil { 92 | log.Fatal(err) 93 | } 94 | } 95 | 96 | const pkgIndexHead = `package imports 97 | 98 | func init() { 99 | pkgIndexOnce.Do(func() { 100 | pkgIndex.m = pkgIndexMaster 101 | }) 102 | loadExports = func(dir string) map[string]bool { 103 | return exportsMaster[dir] 104 | } 105 | } 106 | ` 107 | 108 | type pkg struct { 109 | importpath string // full pkg import path, e.g. "net/http" 110 | dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt" 111 | } 112 | 113 | var fset = token.NewFileSet() 114 | 115 | func loadPkg(root, importpath string) { 116 | shortName := path.Base(importpath) 117 | if shortName == "testdata" { 118 | return 119 | } 120 | 121 | dir := filepath.Join(root, importpath) 122 | pkgIndex[shortName] = append(pkgIndex[shortName], pkg{ 123 | importpath: importpath, 124 | dir: dir, 125 | }) 126 | 127 | pkgDir, err := os.Open(dir) 128 | if err != nil { 129 | return 130 | } 131 | children, err := pkgDir.Readdir(-1) 132 | pkgDir.Close() 133 | if err != nil { 134 | return 135 | } 136 | for _, child := range children { 137 | name := child.Name() 138 | if name == "" { 139 | continue 140 | } 141 | if c := name[0]; c == '.' || ('0' <= c && c <= '9') { 142 | continue 143 | } 144 | if child.IsDir() { 145 | loadPkg(root, filepath.Join(importpath, name)) 146 | } 147 | } 148 | } 149 | 150 | func loadExports(dir string) map[string]bool { 151 | exports := make(map[string]bool) 152 | buildPkg, err := build.ImportDir(dir, 0) 153 | if err != nil { 154 | if strings.Contains(err.Error(), "no buildable Go source files in") { 155 | return nil 156 | } 157 | log.Printf("could not import %q: %v", dir, err) 158 | return nil 159 | } 160 | for _, file := range buildPkg.GoFiles { 161 | f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0) 162 | if err != nil { 163 | log.Printf("could not parse %q: %v", file, err) 164 | continue 165 | } 166 | for name := range f.Scope.Objects { 167 | if ast.IsExported(name) { 168 | exports[name] = true 169 | } 170 | } 171 | } 172 | return exports 173 | } 174 | -------------------------------------------------------------------------------- /gbvendor/imports.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "fmt" 5 | "go/parser" 6 | "go/token" 7 | "io" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "os" 12 | "path" 13 | "path/filepath" 14 | "strings" 15 | 16 | "github.com/FiloSottile/gvt/fileutils" 17 | ) 18 | 19 | // ParseImports parses Go packages from a specific root returning a set of import paths. 20 | // vendorRoot is how deep to go looking for vendor folders, usually the repo root. 21 | // vendorPrefix is the vendorRoot import path. 22 | func ParseImports(root, vendorRoot, vendorPrefix string, tests, all bool) (map[string]bool, error) { 23 | pkgs := make(map[string]bool) 24 | 25 | var walkFn = func(p string, info os.FileInfo, err error) error { 26 | if err != nil { 27 | return err 28 | } 29 | 30 | if fileutils.ShouldSkip(p, info, tests, all) { 31 | if info.IsDir() { 32 | return filepath.SkipDir 33 | } 34 | return nil 35 | } 36 | 37 | if info.IsDir() || filepath.Ext(p) != ".go" { 38 | return nil 39 | } 40 | 41 | fs := token.NewFileSet() 42 | f, err := parser.ParseFile(fs, p, nil, parser.ImportsOnly) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | for _, s := range f.Imports { 48 | pkg := strings.Replace(s.Path.Value, "\"", "", -1) 49 | if strings.HasPrefix(pkg, "./") { 50 | middle, err := filepath.Rel(vendorRoot, filepath.Dir(p)) 51 | if err != nil { 52 | panic(err) 53 | } 54 | pkg = path.Join(vendorPrefix, middle, pkg) 55 | } 56 | if vp := findVendor(vendorRoot, filepath.Dir(p), pkg); vp != "" { 57 | pkg = path.Join(vendorPrefix, vp) 58 | } 59 | pkgs[pkg] = true 60 | } 61 | return nil 62 | } 63 | 64 | err := filepath.Walk(root, walkFn) 65 | return pkgs, err 66 | } 67 | 68 | // findVendor looks for pkgName in a vendor folder at start/vendor or deeper, stopping 69 | // at root. start is expected to match or be a subfolder of root. 70 | // 71 | // It returns the path to pkgName inside the vendor folder, relative to root. 72 | func findVendor(root, start, pkgName string) string { 73 | if !strings.HasPrefix(start, root) { 74 | log.Fatalln("Assertion failed:", root, "prefix of", start) 75 | } 76 | 77 | levels := strings.Split(strings.TrimPrefix(start, root), string(filepath.Separator)) 78 | for { 79 | candidate := filepath.Join(append(append([]string{root}, levels...), "vendor", pkgName)...) 80 | 81 | files, err := ioutil.ReadDir(candidate) 82 | if err != nil { 83 | files = nil 84 | } 85 | isPackage := false 86 | for _, f := range files { 87 | if !f.IsDir() && filepath.Ext(f.Name()) == ".go" { 88 | isPackage = true 89 | break 90 | } 91 | } 92 | 93 | if isPackage { 94 | return strings.TrimPrefix(candidate, root) 95 | } 96 | 97 | if len(levels) == 0 { 98 | return "" 99 | } 100 | levels = levels[:len(levels)-1] 101 | } 102 | } 103 | 104 | // FetchMetadata fetchs the remote metadata for path. 105 | func FetchMetadata(path string, insecure bool) (rc io.ReadCloser, err error) { 106 | defer func() { 107 | if err != nil { 108 | err = fmt.Errorf("unable to determine remote metadata protocol: %s", err) 109 | } 110 | }() 111 | // try https first 112 | rc, err = fetchMetadata("https", path) 113 | if err == nil { 114 | return 115 | } 116 | // try http if supported 117 | if insecure { 118 | rc, err = fetchMetadata("http", path) 119 | } 120 | return 121 | } 122 | 123 | func fetchMetadata(scheme, path string) (io.ReadCloser, error) { 124 | url := fmt.Sprintf("%s://%s?go-get=1", scheme, path) 125 | switch scheme { 126 | case "https", "http": 127 | resp, err := http.Get(url) 128 | if err != nil { 129 | return nil, fmt.Errorf("failed to access url %q", url) 130 | } 131 | return resp.Body, nil 132 | default: 133 | return nil, fmt.Errorf("unknown remote protocol scheme: %q", scheme) 134 | } 135 | } 136 | 137 | // ParseMetadata fetchs and decodes remote metadata for path. 138 | func ParseMetadata(path string, insecure bool) (string, string, string, error) { 139 | rc, err := FetchMetadata(path, insecure) 140 | if err != nil { 141 | return "", "", "", err 142 | } 143 | defer rc.Close() 144 | 145 | imports, err := parseMetaGoImports(rc) 146 | if err != nil { 147 | return "", "", "", err 148 | } 149 | match := -1 150 | for i, im := range imports { 151 | if !strings.HasPrefix(path, im.Prefix) { 152 | continue 153 | } 154 | if match != -1 { 155 | return "", "", "", fmt.Errorf("multiple meta tags match import path %q", path) 156 | } 157 | match = i 158 | } 159 | if match == -1 { 160 | return "", "", "", fmt.Errorf("go-import metadata not found") 161 | } 162 | return imports[match].Prefix, imports[match].VCS, imports[match].RepoRoot, nil 163 | } 164 | -------------------------------------------------------------------------------- /fileutils/fileutils.go: -------------------------------------------------------------------------------- 1 | // package fileutils provides utililty methods to copy and move files and directories. 2 | package fileutils 3 | 4 | import ( 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "runtime" 11 | "strings" 12 | ) 13 | 14 | // https://golang.org/cmd/go/#hdr-File_types 15 | var goFileTypes = []string{ 16 | ".go", 17 | ".c", ".h", 18 | ".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx", 19 | ".m", 20 | ".s", ".S", 21 | ".swig", ".swigcxx", 22 | ".syso", 23 | } 24 | 25 | var licenseFiles = []string{ 26 | "LICENSE", "LICENCE", "UNLICENSE", "COPYING", "COPYRIGHT", 27 | } 28 | 29 | func ShouldSkip(path string, info os.FileInfo, tests, all bool) bool { 30 | name := filepath.Base(path) 31 | 32 | relevantFile := false 33 | for _, ext := range goFileTypes { 34 | if strings.HasSuffix(name, ext) { 35 | relevantFile = true 36 | break 37 | } 38 | } 39 | 40 | testdata := false 41 | for _, n := range strings.Split(filepath.Dir(path), string(filepath.Separator)) { 42 | if n == "testdata" || n == "_testdata" { 43 | testdata = true 44 | } 45 | } 46 | 47 | skip := false 48 | switch { 49 | case all && !(name == ".git" && info.IsDir()) && name != ".bzr" && name != ".hg": 50 | skip = false 51 | 52 | // Include all files in a testdata folder 53 | case tests && testdata: 54 | skip = false 55 | 56 | // https://golang.org/cmd/go/#hdr-Description_of_package_lists 57 | case strings.HasPrefix(name, "."): 58 | skip = true 59 | case strings.HasPrefix(name, "_") && name != "_testdata": 60 | skip = true 61 | 62 | case !tests && name == "_testdata" && info.IsDir(): 63 | skip = true 64 | case !tests && name == "testdata" && info.IsDir(): 65 | skip = true 66 | case !tests && strings.HasSuffix(name, "_test.go") && !info.IsDir(): 67 | skip = true 68 | 69 | case !relevantFile && !info.IsDir(): 70 | skip = true 71 | } 72 | 73 | return skip 74 | } 75 | 76 | // Copypath copies the contents of src to dst, excluding any file that is not 77 | // relevant to the Go compiler. 78 | func Copypath(dst string, src string, tests, all bool) error { 79 | err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { 80 | if err != nil { 81 | return err 82 | } 83 | 84 | skip := ShouldSkip(path, info, tests, all) 85 | 86 | if skip { 87 | if info.IsDir() { 88 | return filepath.SkipDir 89 | } 90 | return nil 91 | } 92 | 93 | if info.IsDir() { 94 | return nil 95 | } 96 | 97 | dst := filepath.Join(dst, path[len(src):]) 98 | 99 | if info.Mode()&os.ModeSymlink != 0 { 100 | return Copylink(dst, path) 101 | } 102 | 103 | return Copyfile(dst, path) 104 | }) 105 | if err != nil { 106 | // if there was an error during copying, remove the partial copy. 107 | RemoveAll(dst) 108 | } 109 | return err 110 | } 111 | 112 | func Copyfile(dst, src string) error { 113 | err := mkdir(filepath.Dir(dst)) 114 | if err != nil { 115 | return fmt.Errorf("copyfile: mkdirall: %v", err) 116 | } 117 | r, err := os.Open(src) 118 | if err != nil { 119 | return fmt.Errorf("copyfile: open(%q): %v", src, err) 120 | } 121 | defer r.Close() 122 | w, err := os.Create(dst) 123 | if err != nil { 124 | return fmt.Errorf("copyfile: create(%q): %v", dst, err) 125 | } 126 | defer w.Close() 127 | _, err = io.Copy(w, r) 128 | return err 129 | } 130 | 131 | func Copylink(dst, src string) error { 132 | target, err := os.Readlink(src) 133 | if err != nil { 134 | return fmt.Errorf("copylink: readlink: %v", err) 135 | } 136 | if err := mkdir(filepath.Dir(dst)); err != nil { 137 | return fmt.Errorf("copylink: mkdirall: %v", err) 138 | } 139 | if err := os.Symlink(target, dst); err != nil { 140 | return fmt.Errorf("copylink: symlink: %v", err) 141 | } 142 | return nil 143 | } 144 | 145 | // RemoveAll removes path and any children it contains. Unlike os.RemoveAll it 146 | // deletes read only files on Windows. 147 | func RemoveAll(path string) error { 148 | if runtime.GOOS == "windows" { 149 | // Simple case: if Remove works, we're done. 150 | err := os.Remove(path) 151 | if err == nil || os.IsNotExist(err) { 152 | return nil 153 | } 154 | // make sure all files are writable so we can delete them 155 | filepath.Walk(path, func(path string, info os.FileInfo, err error) error { 156 | if err != nil { 157 | // walk gave us some error, give it back. 158 | return err 159 | } 160 | mode := info.Mode() 161 | if mode|0200 == mode { 162 | return nil 163 | } 164 | return os.Chmod(path, mode|0200) 165 | }) 166 | } 167 | return os.RemoveAll(path) 168 | } 169 | 170 | // CopyLicense copies the license file from folder src to folder dst. 171 | func CopyLicense(dst, src string) error { 172 | files, err := ioutil.ReadDir(src) 173 | if err != nil { 174 | return err 175 | } 176 | for _, f := range files { 177 | if f.IsDir() { 178 | continue 179 | } 180 | for _, candidate := range licenseFiles { 181 | if strings.ToLower(candidate) == strings.TrimSuffix( 182 | strings.TrimSuffix(strings.ToLower(f.Name()), ".md"), ".txt") { 183 | if err := Copyfile(filepath.Join(dst, f.Name()), 184 | filepath.Join(src, f.Name())); err != nil { 185 | return err 186 | } 187 | } 188 | } 189 | } 190 | return nil 191 | } 192 | 193 | func mkdir(path string) error { 194 | return os.MkdirAll(path, 0755) 195 | } 196 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fastwalk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // A faster implementation of filepath.Walk. 6 | // 7 | // filepath.Walk's design necessarily calls os.Lstat on each file, 8 | // even if the caller needs less info. And goimports only need to know 9 | // the type of each file. The kernel interface provides the type in 10 | // the Readdir call but the standard library ignored it. 11 | // fastwalk_unix.go contains a fork of the syscall routines. 12 | // 13 | // See golang.org/issue/16399 14 | 15 | package imports 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | "path/filepath" 21 | "runtime" 22 | ) 23 | 24 | // traverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir. 25 | var traverseLink = errors.New("traverse symlink, assuming target is a directory") 26 | 27 | // fastWalk walks the file tree rooted at root, calling walkFn for 28 | // each file or directory in the tree, including root. 29 | // 30 | // If fastWalk returns filepath.SkipDir, the directory is skipped. 31 | // 32 | // Unlike filepath.Walk: 33 | // * file stat calls must be done by the user. 34 | // The only provided metadata is the file type, which does not include 35 | // any permission bits. 36 | // * multiple goroutines stat the filesystem concurrently. The provided 37 | // walkFn must be safe for concurrent use. 38 | // * fastWalk can follow symlinks if walkFn returns the traverseLink 39 | // sentinel error. It is the walkFn's responsibility to prevent 40 | // fastWalk from going into symlink cycles. 41 | func fastWalk(root string, walkFn func(path string, typ os.FileMode) error) error { 42 | // TODO(bradfitz): make numWorkers configurable? We used a 43 | // minimum of 4 to give the kernel more info about multiple 44 | // things we want, in hopes its I/O scheduling can take 45 | // advantage of that. Hopefully most are in cache. Maybe 4 is 46 | // even too low of a minimum. Profile more. 47 | numWorkers := 4 48 | if n := runtime.NumCPU(); n > numWorkers { 49 | numWorkers = n 50 | } 51 | w := &walker{ 52 | fn: walkFn, 53 | enqueuec: make(chan walkItem, numWorkers), // buffered for performance 54 | workc: make(chan walkItem, numWorkers), // buffered for performance 55 | donec: make(chan struct{}), 56 | 57 | // buffered for correctness & not leaking goroutines: 58 | resc: make(chan error, numWorkers), 59 | } 60 | defer close(w.donec) 61 | // TODO(bradfitz): start the workers as needed? maybe not worth it. 62 | for i := 0; i < numWorkers; i++ { 63 | go w.doWork() 64 | } 65 | todo := []walkItem{{dir: root}} 66 | out := 0 67 | for { 68 | workc := w.workc 69 | var workItem walkItem 70 | if len(todo) == 0 { 71 | workc = nil 72 | } else { 73 | workItem = todo[len(todo)-1] 74 | } 75 | select { 76 | case workc <- workItem: 77 | todo = todo[:len(todo)-1] 78 | out++ 79 | case it := <-w.enqueuec: 80 | todo = append(todo, it) 81 | case err := <-w.resc: 82 | out-- 83 | if err != nil { 84 | return err 85 | } 86 | if out == 0 && len(todo) == 0 { 87 | // It's safe to quit here, as long as the buffered 88 | // enqueue channel isn't also readable, which might 89 | // happen if the worker sends both another unit of 90 | // work and its result before the other select was 91 | // scheduled and both w.resc and w.enqueuec were 92 | // readable. 93 | select { 94 | case it := <-w.enqueuec: 95 | todo = append(todo, it) 96 | default: 97 | return nil 98 | } 99 | } 100 | } 101 | } 102 | } 103 | 104 | // doWork reads directories as instructed (via workc) and runs the 105 | // user's callback function. 106 | func (w *walker) doWork() { 107 | for { 108 | select { 109 | case <-w.donec: 110 | return 111 | case it := <-w.workc: 112 | w.resc <- w.walk(it.dir, !it.callbackDone) 113 | } 114 | } 115 | } 116 | 117 | type walker struct { 118 | fn func(path string, typ os.FileMode) error 119 | 120 | donec chan struct{} // closed on fastWalk's return 121 | workc chan walkItem // to workers 122 | enqueuec chan walkItem // from workers 123 | resc chan error // from workers 124 | } 125 | 126 | type walkItem struct { 127 | dir string 128 | callbackDone bool // callback already called; don't do it again 129 | } 130 | 131 | func (w *walker) enqueue(it walkItem) { 132 | select { 133 | case w.enqueuec <- it: 134 | case <-w.donec: 135 | } 136 | } 137 | 138 | func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { 139 | joined := dirName + string(os.PathSeparator) + baseName 140 | if typ == os.ModeDir { 141 | w.enqueue(walkItem{dir: joined}) 142 | return nil 143 | } 144 | 145 | err := w.fn(joined, typ) 146 | if typ == os.ModeSymlink { 147 | if err == traverseLink { 148 | // Set callbackDone so we don't call it twice for both the 149 | // symlink-as-symlink and the symlink-as-directory later: 150 | w.enqueue(walkItem{dir: joined, callbackDone: true}) 151 | return nil 152 | } 153 | if err == filepath.SkipDir { 154 | // Permit SkipDir on symlinks too. 155 | return nil 156 | } 157 | } 158 | return err 159 | } 160 | func (w *walker) walk(root string, runUserCallback bool) error { 161 | if runUserCallback { 162 | err := w.fn(root, os.ModeDir) 163 | if err == filepath.SkipDir { 164 | return nil 165 | } 166 | if err != nil { 167 | return err 168 | } 169 | } 170 | 171 | return readDir(root, w.onDirEnt) 172 | } 173 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cover/profile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cover provides support for parsing coverage profiles 6 | // generated by "go test -coverprofile=cover.out". 7 | package cover // import "golang.org/x/tools/cover" 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "math" 13 | "os" 14 | "regexp" 15 | "sort" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // Profile represents the profiling data for a specific file. 21 | type Profile struct { 22 | FileName string 23 | Mode string 24 | Blocks []ProfileBlock 25 | } 26 | 27 | // ProfileBlock represents a single block of profiling data. 28 | type ProfileBlock struct { 29 | StartLine, StartCol int 30 | EndLine, EndCol int 31 | NumStmt, Count int 32 | } 33 | 34 | type byFileName []*Profile 35 | 36 | func (p byFileName) Len() int { return len(p) } 37 | func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } 38 | func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 39 | 40 | // ParseProfiles parses profile data in the specified file and returns a 41 | // Profile for each source file described therein. 42 | func ParseProfiles(fileName string) ([]*Profile, error) { 43 | pf, err := os.Open(fileName) 44 | if err != nil { 45 | return nil, err 46 | } 47 | defer pf.Close() 48 | 49 | files := make(map[string]*Profile) 50 | buf := bufio.NewReader(pf) 51 | // First line is "mode: foo", where foo is "set", "count", or "atomic". 52 | // Rest of file is in the format 53 | // encoding/base64/base64.go:34.44,37.40 3 1 54 | // where the fields are: name.go:line.column,line.column numberOfStatements count 55 | s := bufio.NewScanner(buf) 56 | mode := "" 57 | for s.Scan() { 58 | line := s.Text() 59 | if mode == "" { 60 | const p = "mode: " 61 | if !strings.HasPrefix(line, p) || line == p { 62 | return nil, fmt.Errorf("bad mode line: %v", line) 63 | } 64 | mode = line[len(p):] 65 | continue 66 | } 67 | m := lineRe.FindStringSubmatch(line) 68 | if m == nil { 69 | return nil, fmt.Errorf("line %q doesn't match expected format: %v", line, lineRe) 70 | } 71 | fn := m[1] 72 | p := files[fn] 73 | if p == nil { 74 | p = &Profile{ 75 | FileName: fn, 76 | Mode: mode, 77 | } 78 | files[fn] = p 79 | } 80 | p.Blocks = append(p.Blocks, ProfileBlock{ 81 | StartLine: toInt(m[2]), 82 | StartCol: toInt(m[3]), 83 | EndLine: toInt(m[4]), 84 | EndCol: toInt(m[5]), 85 | NumStmt: toInt(m[6]), 86 | Count: toInt(m[7]), 87 | }) 88 | } 89 | if err := s.Err(); err != nil { 90 | return nil, err 91 | } 92 | for _, p := range files { 93 | sort.Sort(blocksByStart(p.Blocks)) 94 | } 95 | // Generate a sorted slice. 96 | profiles := make([]*Profile, 0, len(files)) 97 | for _, profile := range files { 98 | profiles = append(profiles, profile) 99 | } 100 | sort.Sort(byFileName(profiles)) 101 | return profiles, nil 102 | } 103 | 104 | type blocksByStart []ProfileBlock 105 | 106 | func (b blocksByStart) Len() int { return len(b) } 107 | func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 108 | func (b blocksByStart) Less(i, j int) bool { 109 | bi, bj := b[i], b[j] 110 | return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol 111 | } 112 | 113 | var lineRe = regexp.MustCompile(`^(.+):([0-9]+).([0-9]+),([0-9]+).([0-9]+) ([0-9]+) ([0-9]+)$`) 114 | 115 | func toInt(s string) int { 116 | i, err := strconv.Atoi(s) 117 | if err != nil { 118 | panic(err) 119 | } 120 | return i 121 | } 122 | 123 | // Boundary represents the position in a source file of the beginning or end of a 124 | // block as reported by the coverage profile. In HTML mode, it will correspond to 125 | // the opening or closing of a tag and will be used to colorize the source 126 | type Boundary struct { 127 | Offset int // Location as a byte offset in the source file. 128 | Start bool // Is this the start of a block? 129 | Count int // Event count from the cover profile. 130 | Norm float64 // Count normalized to [0..1]. 131 | } 132 | 133 | // Boundaries returns a Profile as a set of Boundary objects within the provided src. 134 | func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) { 135 | // Find maximum count. 136 | max := 0 137 | for _, b := range p.Blocks { 138 | if b.Count > max { 139 | max = b.Count 140 | } 141 | } 142 | // Divisor for normalization. 143 | divisor := math.Log(float64(max)) 144 | 145 | // boundary returns a Boundary, populating the Norm field with a normalized Count. 146 | boundary := func(offset int, start bool, count int) Boundary { 147 | b := Boundary{Offset: offset, Start: start, Count: count} 148 | if !start || count == 0 { 149 | return b 150 | } 151 | if max <= 1 { 152 | b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS. 153 | } else if count > 0 { 154 | b.Norm = math.Log(float64(count)) / divisor 155 | } 156 | return b 157 | } 158 | 159 | line, col := 1, 2 // TODO: Why is this 2? 160 | for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); { 161 | b := p.Blocks[bi] 162 | if b.StartLine == line && b.StartCol == col { 163 | boundaries = append(boundaries, boundary(si, true, b.Count)) 164 | } 165 | if b.EndLine == line && b.EndCol == col || line > b.EndLine { 166 | boundaries = append(boundaries, boundary(si, false, 0)) 167 | bi++ 168 | continue // Don't advance through src; maybe the next block starts here. 169 | } 170 | if src[si] == '\n' { 171 | line++ 172 | col = 0 173 | } 174 | col++ 175 | si++ 176 | } 177 | sort.Sort(boundariesByPos(boundaries)) 178 | return 179 | } 180 | 181 | type boundariesByPos []Boundary 182 | 183 | func (b boundariesByPos) Len() int { return len(b) } 184 | func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 185 | func (b boundariesByPos) Less(i, j int) bool { 186 | if b[i].Offset == b[j].Offset { 187 | return !b[i].Start && b[j].Start 188 | } 189 | return b[i].Offset < b[j].Offset 190 | } 191 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/sortimports.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Hacked up copy of go/ast/import.go 6 | 7 | package imports 8 | 9 | import ( 10 | "go/ast" 11 | "go/token" 12 | "sort" 13 | "strconv" 14 | ) 15 | 16 | // sortImports sorts runs of consecutive import lines in import blocks in f. 17 | // It also removes duplicate imports when it is possible to do so without data loss. 18 | func sortImports(fset *token.FileSet, f *ast.File) { 19 | for i, d := range f.Decls { 20 | d, ok := d.(*ast.GenDecl) 21 | if !ok || d.Tok != token.IMPORT { 22 | // Not an import declaration, so we're done. 23 | // Imports are always first. 24 | break 25 | } 26 | 27 | if len(d.Specs) == 0 { 28 | // Empty import block, remove it. 29 | f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) 30 | } 31 | 32 | if !d.Lparen.IsValid() { 33 | // Not a block: sorted by default. 34 | continue 35 | } 36 | 37 | // Identify and sort runs of specs on successive lines. 38 | i := 0 39 | specs := d.Specs[:0] 40 | for j, s := range d.Specs { 41 | if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { 42 | // j begins a new run. End this one. 43 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...) 44 | i = j 45 | } 46 | } 47 | specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...) 48 | d.Specs = specs 49 | 50 | // Deduping can leave a blank line before the rparen; clean that up. 51 | if len(d.Specs) > 0 { 52 | lastSpec := d.Specs[len(d.Specs)-1] 53 | lastLine := fset.Position(lastSpec.Pos()).Line 54 | if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 { 55 | fset.File(d.Rparen).MergeLine(rParenLine - 1) 56 | } 57 | } 58 | } 59 | } 60 | 61 | func importPath(s ast.Spec) string { 62 | t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value) 63 | if err == nil { 64 | return t 65 | } 66 | return "" 67 | } 68 | 69 | func importName(s ast.Spec) string { 70 | n := s.(*ast.ImportSpec).Name 71 | if n == nil { 72 | return "" 73 | } 74 | return n.Name 75 | } 76 | 77 | func importComment(s ast.Spec) string { 78 | c := s.(*ast.ImportSpec).Comment 79 | if c == nil { 80 | return "" 81 | } 82 | return c.Text() 83 | } 84 | 85 | // collapse indicates whether prev may be removed, leaving only next. 86 | func collapse(prev, next ast.Spec) bool { 87 | if importPath(next) != importPath(prev) || importName(next) != importName(prev) { 88 | return false 89 | } 90 | return prev.(*ast.ImportSpec).Comment == nil 91 | } 92 | 93 | type posSpan struct { 94 | Start token.Pos 95 | End token.Pos 96 | } 97 | 98 | func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec { 99 | // Can't short-circuit here even if specs are already sorted, 100 | // since they might yet need deduplication. 101 | // A lone import, however, may be safely ignored. 102 | if len(specs) <= 1 { 103 | return specs 104 | } 105 | 106 | // Record positions for specs. 107 | pos := make([]posSpan, len(specs)) 108 | for i, s := range specs { 109 | pos[i] = posSpan{s.Pos(), s.End()} 110 | } 111 | 112 | // Identify comments in this range. 113 | // Any comment from pos[0].Start to the final line counts. 114 | lastLine := fset.Position(pos[len(pos)-1].End).Line 115 | cstart := len(f.Comments) 116 | cend := len(f.Comments) 117 | for i, g := range f.Comments { 118 | if g.Pos() < pos[0].Start { 119 | continue 120 | } 121 | if i < cstart { 122 | cstart = i 123 | } 124 | if fset.Position(g.End()).Line > lastLine { 125 | cend = i 126 | break 127 | } 128 | } 129 | comments := f.Comments[cstart:cend] 130 | 131 | // Assign each comment to the import spec preceding it. 132 | importComment := map[*ast.ImportSpec][]*ast.CommentGroup{} 133 | specIndex := 0 134 | for _, g := range comments { 135 | for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() { 136 | specIndex++ 137 | } 138 | s := specs[specIndex].(*ast.ImportSpec) 139 | importComment[s] = append(importComment[s], g) 140 | } 141 | 142 | // Sort the import specs by import path. 143 | // Remove duplicates, when possible without data loss. 144 | // Reassign the import paths to have the same position sequence. 145 | // Reassign each comment to abut the end of its spec. 146 | // Sort the comments by new position. 147 | sort.Sort(byImportSpec(specs)) 148 | 149 | // Dedup. Thanks to our sorting, we can just consider 150 | // adjacent pairs of imports. 151 | deduped := specs[:0] 152 | for i, s := range specs { 153 | if i == len(specs)-1 || !collapse(s, specs[i+1]) { 154 | deduped = append(deduped, s) 155 | } else { 156 | p := s.Pos() 157 | fset.File(p).MergeLine(fset.Position(p).Line) 158 | } 159 | } 160 | specs = deduped 161 | 162 | // Fix up comment positions 163 | for i, s := range specs { 164 | s := s.(*ast.ImportSpec) 165 | if s.Name != nil { 166 | s.Name.NamePos = pos[i].Start 167 | } 168 | s.Path.ValuePos = pos[i].Start 169 | s.EndPos = pos[i].End 170 | for _, g := range importComment[s] { 171 | for _, c := range g.List { 172 | c.Slash = pos[i].End 173 | } 174 | } 175 | } 176 | 177 | sort.Sort(byCommentPos(comments)) 178 | 179 | return specs 180 | } 181 | 182 | type byImportSpec []ast.Spec // slice of *ast.ImportSpec 183 | 184 | func (x byImportSpec) Len() int { return len(x) } 185 | func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 186 | func (x byImportSpec) Less(i, j int) bool { 187 | ipath := importPath(x[i]) 188 | jpath := importPath(x[j]) 189 | 190 | igroup := importGroup(ipath) 191 | jgroup := importGroup(jpath) 192 | if igroup != jgroup { 193 | return igroup < jgroup 194 | } 195 | 196 | if ipath != jpath { 197 | return ipath < jpath 198 | } 199 | iname := importName(x[i]) 200 | jname := importName(x[j]) 201 | 202 | if iname != jname { 203 | return iname < jname 204 | } 205 | return importComment(x[i]) < importComment(x[j]) 206 | } 207 | 208 | type byCommentPos []*ast.CommentGroup 209 | 210 | func (x byCommentPos) Len() int { return len(x) } 211 | func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 212 | func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() } 213 | -------------------------------------------------------------------------------- /gbvendor/manifest.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | "reflect" 12 | "sort" 13 | "strings" 14 | ) 15 | 16 | // gb-vendor manifest support 17 | 18 | // Manifest describes the layout of $PROJECT/vendor/manifest. 19 | type Manifest struct { 20 | // Manifest version. Current manifest version is 0. 21 | Version int `json:"version"` 22 | 23 | // Depenencies is a list of vendored dependencies. 24 | Dependencies []Dependency `json:"dependencies"` 25 | } 26 | 27 | var ( 28 | DepPresent = errors.New("dependency already present") 29 | DepSubPkgPresent = errors.New("subpackages of this dependency are already present") 30 | DepMissing = errors.New("dependency does not exist") 31 | ) 32 | 33 | // AddDependency adds a Dependency to the current Manifest. 34 | // If the dependency exists already then it returns and error. 35 | func (m *Manifest) AddDependency(dep Dependency) error { 36 | if m.HasImportpath(dep.Importpath) { 37 | return DepPresent 38 | } 39 | if m.GetSubpackages(dep.Importpath) != nil { 40 | return DepSubPkgPresent 41 | } 42 | m.Dependencies = append(m.Dependencies, dep) 43 | return nil 44 | } 45 | 46 | // RemoveDependency removes a Dependency from the current Manifest. 47 | // If the dependency does not exist then it returns an error. 48 | func (m *Manifest) RemoveDependency(dep Dependency) error { 49 | for i, d := range m.Dependencies { 50 | if reflect.DeepEqual(d, dep) { 51 | m.Dependencies = append(m.Dependencies[:i], m.Dependencies[i+1:]...) 52 | return nil 53 | } 54 | } 55 | return DepMissing 56 | } 57 | 58 | // HasImportpath reports whether the Manifest contains the import path, 59 | // or a parent of it. 60 | func (m *Manifest) HasImportpath(path string) bool { 61 | _, err := m.GetDependencyForImportpath(path) 62 | return err == nil 63 | } 64 | 65 | // GetDependencyForRepository return a dependency for specified import 66 | // path. Note that it might be a parent of the specified path. 67 | // If the dependency does not exist it returns an error. 68 | func (m *Manifest) GetDependencyForImportpath(path string) (Dependency, error) { 69 | for _, d := range m.Dependencies { 70 | if path == d.Importpath || strings.HasPrefix(path, d.Importpath+"/") { 71 | return d, nil 72 | } 73 | } 74 | return Dependency{}, fmt.Errorf("dependency for %s does not exist", path) 75 | } 76 | 77 | // GetSubpackages returns any Dependency in the Manifest that is a subpackage 78 | // of the given import path. 79 | func (m *Manifest) GetSubpackages(path string) (deps []Dependency) { 80 | for _, d := range m.Dependencies { 81 | if path != d.Importpath && strings.HasPrefix(d.Importpath, path+"/") { 82 | deps = append(deps, d) 83 | } 84 | } 85 | return 86 | } 87 | 88 | // Dependency describes one vendored import path of code 89 | // A Dependency is an Importpath sources from a Respository 90 | // at Revision from Path. 91 | type Dependency struct { 92 | // Importpath is name by which this dependency is known. 93 | Importpath string `json:"importpath"` 94 | 95 | // Repository is the remote DVCS location that this 96 | // dependency was fetched from. 97 | Repository string `json:"repository"` 98 | 99 | // VCS is the DVCS system found at Repository. 100 | VCS string `json:"vcs"` 101 | 102 | // Revision is the revision that describes the dependency's 103 | // remote revision. 104 | Revision string `json:"revision"` 105 | 106 | // Branch is the branch the Revision was located on. 107 | // Can be blank if not needed. 108 | Branch string `json:"branch"` 109 | 110 | // Path is the path inside the Repository where the 111 | // dependency was fetched from. 112 | Path string `json:"path,omitempty"` 113 | 114 | // NoTests indicates that test files were ignored. 115 | // In the negative for backwards compatibility. 116 | NoTests bool `json:"notests,omitempty"` 117 | 118 | // AllFiles indicates that no files were ignored. 119 | AllFiles bool `json:"allfiles,omitempty"` 120 | } 121 | 122 | // WriteManifest writes a Manifest to the path. If the manifest does 123 | // not exist, it is created. If it does exist, it will be overwritten. 124 | // If the manifest file is empty (0 dependencies) it will be deleted. 125 | // The dependencies will be ordered by import path to reduce churn when making 126 | // changes. 127 | // TODO(dfc) write to temporary file and move atomically to avoid 128 | // destroying a working vendorfile. 129 | func WriteManifest(path string, m *Manifest) error { 130 | if len(m.Dependencies) == 0 { 131 | err := os.Remove(path) 132 | if !os.IsNotExist(err) { 133 | return err 134 | } 135 | return nil 136 | } 137 | 138 | f, err := os.Create(path) 139 | if err != nil { 140 | return err 141 | } 142 | if err := writeManifest(f, m); err != nil { 143 | f.Close() 144 | return err 145 | } 146 | return f.Close() 147 | } 148 | 149 | func writeManifest(w io.Writer, m *Manifest) error { 150 | sort.Sort(byImportpath(m.Dependencies)) 151 | buf, err := json.MarshalIndent(m, "", "\t") 152 | if err != nil { 153 | return err 154 | } 155 | _, err = io.Copy(w, bytes.NewReader(buf)) 156 | return err 157 | } 158 | 159 | // ReadManifest reads a Manifest from path. If the Manifest is not 160 | // found, a blank Manifest will be returned. 161 | func ReadManifest(path string) (*Manifest, error) { 162 | f, err := os.Open(path) 163 | if err != nil { 164 | if os.IsNotExist(err) { 165 | return new(Manifest), nil 166 | } 167 | return nil, err 168 | } 169 | defer f.Close() 170 | 171 | var m Manifest 172 | d := json.NewDecoder(f) 173 | if err := d.Decode(&m); err != nil { 174 | return nil, err 175 | } 176 | 177 | // Pass all dependencies through AddDependency to detect overlap 178 | deps := m.Dependencies 179 | m.Dependencies = nil 180 | sort.Sort(byImportpath(deps)) // so that subpackages come after parents 181 | for _, d := range deps { 182 | if err := m.AddDependency(d); err == DepPresent { 183 | log.Println("WARNING: overlapping dependency detected:", d.Importpath) 184 | log.Println("The subpackage will be ignored to fix undefined behavior. See https://git.io/vr8Mu") 185 | } else if err != nil { 186 | return nil, err 187 | } 188 | } 189 | 190 | return &m, err 191 | } 192 | 193 | type byImportpath []Dependency 194 | 195 | func (s byImportpath) Len() int { return len(s) } 196 | func (s byImportpath) Less(i, j int) bool { return s[i].Importpath < s[j].Importpath } 197 | func (s byImportpath) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 198 | -------------------------------------------------------------------------------- /README.old.md: -------------------------------------------------------------------------------- 1 | # gvt, the Go vendoring tool 2 | [![GoDoc](https://godoc.org/github.com/FiloSottile/gvt?status.svg)](https://godoc.org/github.com/FiloSottile/gvt) 3 | [![Build Status](https://travis-ci.org/FiloSottile/gvt.svg?branch=master)](https://travis-ci.org/FiloSottile/gvt) 4 | 5 | `gvt` is a simple vendoring tool made for Go native vendoring (aka 6 | [GO15VENDOREXPERIMENT](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo/edit)), 7 | based on [gb-vendor](https://github.com/constabulary/gb). 8 | 9 | It lets you easily and "idiomatically" include external dependencies in your repository to get 10 | reproducible builds. 11 | 12 | * No need to learn a new tool or format! 13 | You already know how to use `gvt`: just run `gvt fetch` when and like you would run `go get`. 14 | You can imagine what `gvt update` and `gvt delete` do. In addition, `gvt` [also allows](https://godoc.org/github.com/FiloSottile/gvt#hdr-Fetch_a_remote_dependency) 15 | fetching specific commits or branch versions in packages, and fully accommodates private repos. 16 | 17 | * No need to change how you build your project! 18 | `gvt` downloads packages to `./vendor/...`. The stock Go compiler will find and use those 19 | dependencies automatically without import path rewriting or GOPATH changes. 20 | (Go 1.6+, or Go 1.5 with `GO15VENDOREXPERIMENT=1` set required.) 21 | 22 | * No need to manually chase, copy or cleanup dependencies! 23 | `gvt` works recursively as you would expect, and lets you update vendored dependencies. It also 24 | writes a manifest to `./vendor/manifest` and never touches your system GOPATH. Finally, it 25 | strips the VCS metadata so that you can commit the vendored source cleanly. 26 | 27 | * No need for your users and occasional contributors to install **or even know about** gvt! 28 | Packages whose dependencies are vendored with `gvt` are `go build`-able and `go get`-able out of 29 | the box by Go 1.6+, or Go 1.5 with `GO15VENDOREXPERIMENT=1` set. 30 | 31 | *Note that projects must live within the GOPATH tree in order to be `go build`-able with native vendoring.* 32 | 33 | ## Installation 34 | 35 | With a [correctly configured](https://golang.org/doc/code.html#GOPATH) Go installation: 36 | 37 | ``` 38 | go get -u github.com/FiloSottile/gvt 39 | ``` 40 | 41 | ## Basic usage 42 | 43 | When you would use `go get`, just use `gvt fetch` instead. 44 | 45 | ``` 46 | $ gvt fetch github.com/fatih/color 47 | 2015/09/05 02:38:06 fetching recursive dependency github.com/mattn/go-isatty 48 | 2015/09/05 02:38:07 fetching recursive dependency github.com/shiena/ansicolor 49 | ``` 50 | 51 | `gvt fetch` downloads the dependency into the `vendor` folder. 52 | 53 | Files and folders starting with `.` or `_` are ignored. Only [files relevant to the Go compiler](https://golang.org/cmd/go/#hdr-File_types) are fetched. LICENSE files are always included, too. 54 | Test files and `testdata` folders can be included with `-t`. To include all files (except the repository metadata), use `-a`. 55 | 56 | ``` 57 | $ tree -d 58 | . 59 | └── vendor 60 | └── github.com 61 | ├── fatih 62 | │ └── color 63 | ├── mattn 64 | │ └── go-isatty 65 | └── shiena 66 | └── ansicolor 67 | └── ansicolor 68 | 69 | 9 directories 70 | ``` 71 | 72 | There's no step 2, you are ready to use the fetched dependency as you would normally do. 73 | 74 | (Requires Go 1.6+, or 1.5 with `GO15VENDOREXPERIMENT=1` set.) 75 | 76 | ``` 77 | $ cat > main.go 78 | package main 79 | import "github.com/fatih/color" 80 | func main() { 81 | color.Red("Hello, world!") 82 | } 83 | 84 | # Only needed with Go 1.5, vendoring is on by default in 1.6 85 | $ export GO15VENDOREXPERIMENT=1 86 | 87 | $ go build . 88 | $ ./hello 89 | Hello, world! 90 | ``` 91 | 92 | Finally, remember to check in and commit the `vendor` folder. 93 | 94 | ``` 95 | $ git add main.go vendor/ && git commit 96 | ``` 97 | 98 | ## Full usage 99 | 100 | `fetch` offers options to download specific versions, and there are `update`, `list` and `delete` commands that do what you would expect. 101 | 102 | View the full manual on GoDoc: https://godoc.org/github.com/FiloSottile/gvt 103 | 104 | ## Alternative: not checking in vendored source 105 | 106 | Some developers prefer not to check in the source of the vendored dependencies. In that case you can 107 | add lines like these to e.g. your `.gitignore` 108 | 109 | vendor/** 110 | !vendor/manifest 111 | 112 | When you check out the source again, you can then run `gvt restore` to fetch all the dependencies at 113 | the revisions specified in the `vendor/manifest` file. 114 | 115 | Please consider that this approach has the following consequences: 116 | 117 | * the package consumer will need gvt to fetch the dependencies 118 | * the dependencies will need to remain available from the source repositories: if the original 119 | repository goes down or rewrites history, build reproducibility is lost 120 | * `go get` won't work on your package 121 | * unless you pin the gvt version, bugs and unintended changes introduced in how `gvt restore` 122 | behaves can affect your build 123 | 124 | ## Vendoring a different fork 125 | 126 | You might have your own version of a repository (i.e. a fork) but still want to 127 | vendor it at the original import path. 128 | 129 | Since this is not a common use-case, there's no support in `gvt fetch` for it, 130 | however, you can manually edit the `vendor/manifest` file, changing `repository` 131 | and `revision`, and then run `gvt restore`. 132 | 133 | `gvt update` will stay on your fork. 134 | 135 | ## Overlapping dependencies 136 | 137 | Since in the current manifest, inherited from gb-vendor, a dependency includes 138 | all subpackages, it is possible to get conflicts in the form of overlapping dependencies. 139 | For example, if we had one version of example.com/a and a different one of example.com/a/b. 140 | 141 | To solve this cleanly, overlaps are disallowed. Subpackages of existing dependencies are 142 | silently treated as existing dependencies. Parents of existing dependencies are treated 143 | as missing and cause the subpackages to be deleted when they are fetched. 144 | 145 | This rule might be arbitrary, but it is required to have determinism in situations like 146 | recursive fetches, where the orders and priorities of fetches are undefined. 147 | If it causes incompatibilities, they were for the human to fix anyway. 148 | 149 | (There's an exception, if you want to nitpick, and it's that if you fetch a package at a revision, 150 | and its parent ends up being fetched by the recursive resolution, the parent will be fetched at 151 | the revision, not at master, because that's probably what you meant.) 152 | 153 | ## Troubleshooting 154 | 155 | ### `fatal: Not a git repository [...]` 156 | ### `error: tag 'fetch' not found.` 157 | 158 | These errors can occur because you have an alias for `gvt` pointing to `git verify-tag` 159 | (default if using oh-my-zsh). 160 | 161 | Recent versions of oh-my-zsh [removed the alias](https://github.com/robbyrussell/oh-my-zsh/pull/4841). You can update with `upgrade_oh_my_zsh`. 162 | 163 | Alternatively, run this, and preferably add it to your `~/.bashrc` / `~/.zshrc`: `unalias gvt`. 164 | 165 | ### `go build` can't find the vendored package 166 | 167 | Make sure you are using at least Go 1.5, set `GO15VENDOREXPERIMENT=1` if you 168 | are using Go 1.5 and didn't set `GO15VENDOREXPERIMENT=0` if you are using Go 1.6. 169 | 170 | Also note that native vendoring does not work outside the GOPATH source tree. 171 | That is, your project MUST be somewhere in a subfolder of `$GOPATH/src/`. 172 | 173 | ## License 174 | 175 | MIT licensed. See the LICENSE file for details. 176 | -------------------------------------------------------------------------------- /fetch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net/url" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/FiloSottile/gvt/fileutils" 13 | "github.com/FiloSottile/gvt/gbvendor" 14 | ) 15 | 16 | var ( 17 | branch string 18 | revision string // revision (commit) 19 | tag string 20 | noRecurse bool 21 | insecure bool // Allow the use of insecure protocols 22 | tests bool 23 | all bool 24 | ) 25 | 26 | func addFetchFlags(fs *flag.FlagSet) { 27 | fs.StringVar(&branch, "branch", "", "branch of the package") 28 | fs.StringVar(&revision, "revision", "", "revision of the package") 29 | fs.StringVar(&tag, "tag", "", "tag of the package") 30 | fs.BoolVar(&noRecurse, "no-recurse", false, "do not fetch recursively") 31 | fs.BoolVar(&insecure, "precaire", false, "allow the use of insecure protocols") 32 | fs.BoolVar(&tests, "t", false, "fetch _test.go files and testdata") 33 | fs.BoolVar(&all, "a", false, "fetch all files and subfolders") 34 | } 35 | 36 | var cmdFetch = &Command{ 37 | Name: "fetch", 38 | UsageLine: "fetch [-branch branch] [-revision rev | -tag tag] [-precaire] [-no-recurse] [-t|-a] importpath", 39 | Short: "fetch a remote dependency", 40 | Long: `fetch vendors an upstream import path. 41 | 42 | Recursive dependencies are fetched (at their master/tip/HEAD revision), unless they 43 | or their parent package are already present. 44 | 45 | If a subpackage of a dependency being fetched is already present, it will be deleted. 46 | 47 | The import path may include a url scheme. This may be useful when fetching dependencies 48 | from private repositories that cannot be probed. 49 | 50 | Flags: 51 | -t 52 | fetch also _test.go files and testdata. 53 | -a 54 | fetch all files and subfolders, ignoring ONLY .git, .hg and .bzr. 55 | -branch branch 56 | fetch from the named branch. Will also be used by gvt update. 57 | If not supplied the default upstream branch will be used. 58 | -no-recurse 59 | do not fetch recursively. 60 | -tag tag 61 | fetch the specified tag. 62 | -revision rev 63 | fetch the specific revision from the branch or repository. 64 | If no revision supplied, the latest available will be fetched. 65 | -precaire 66 | allow the use of insecure protocols. 67 | 68 | `, 69 | Run: func(args []string) error { 70 | switch len(args) { 71 | case 0: 72 | return fmt.Errorf("fetch: import path missing") 73 | case 1: 74 | path := args[0] 75 | return fetch(path) 76 | default: 77 | return fmt.Errorf("more than one import path supplied") 78 | } 79 | }, 80 | AddFlags: addFetchFlags, 81 | } 82 | 83 | var ( 84 | fetchRoot string // where the current session started 85 | rootRepoURL string // the url of the repo from which the root comes from 86 | fetchedToday []string // packages fetched during this session 87 | ) 88 | 89 | func fetch(path string) error { 90 | m, err := vendor.ReadManifest(manifestFile) 91 | if err != nil { 92 | return fmt.Errorf("could not load manifest: %v", err) 93 | } 94 | 95 | fetchRoot = stripscheme(path) 96 | return fetchRecursive(m, path, 0) 97 | } 98 | 99 | func fetchRecursive(m *vendor.Manifest, fullPath string, level int) error { 100 | path := stripscheme(fullPath) 101 | 102 | // Don't even bother the user about skipping packages we just fetched 103 | for _, p := range fetchedToday { 104 | if contains(p, path) { 105 | return nil 106 | } 107 | } 108 | 109 | // First, check if this or a parent is already vendored 110 | if m.HasImportpath(path) { 111 | if level == 0 { 112 | return fmt.Errorf("%s or a parent of it is already vendored", path) 113 | } else { 114 | // TODO: print a different message for packages fetched during this session 115 | logIndent(level, "Skipping (existing):", path) 116 | return nil 117 | } 118 | } 119 | 120 | // Next, check if we are trying to vendor from the same repository we are in 121 | if importPath != "" && contains(importPath, path) { 122 | if level == 0 { 123 | return fmt.Errorf("refusing to vendor a subpackage of \".\"") 124 | } else { 125 | logIndent(level, "Skipping (subpackage of \".\"):", path) 126 | return nil 127 | } 128 | } 129 | 130 | if level == 0 { 131 | log.Println("Fetching:", path) 132 | } else { 133 | logIndent(level, "Fetching recursive dependency:", path) 134 | } 135 | 136 | // Finally, check if we already vendored a subpackage and remove it 137 | for _, subp := range m.GetSubpackages(path) { 138 | if !contains(subp.Importpath, fetchRoot) { // ignore parents of the root 139 | ignore := false 140 | for _, d := range fetchedToday { 141 | if contains(d, subp.Importpath) { 142 | ignore = true // No need to warn the user if we just downloaded it 143 | } 144 | } 145 | if !ignore { 146 | logIndent(level, "Deleting existing subpackage to prevent overlap:", subp.Importpath) 147 | } 148 | } 149 | if err := m.RemoveDependency(subp); err != nil { 150 | return fmt.Errorf("failed to remove subpackage: %v", err) 151 | } 152 | } 153 | if err := fileutils.RemoveAll(filepath.Join(vendorDir, path)); err != nil && !os.IsNotExist(err) { 154 | return fmt.Errorf("failed to remove existing folder: %v", err) 155 | } 156 | 157 | // Find and download the repository 158 | 159 | repo, extra, err := GlobalDownloader.DeduceRemoteRepo(fullPath, insecure) 160 | if err != nil { 161 | return err 162 | } 163 | 164 | if level == 0 { 165 | rootRepoURL = repo.URL() 166 | } 167 | 168 | var wc vendor.WorkingCopy 169 | if repo.URL() == rootRepoURL { 170 | wc, err = GlobalDownloader.Get(repo, branch, tag, revision) 171 | } else { 172 | wc, err = GlobalDownloader.Get(repo, "", "", "") 173 | } 174 | if err != nil { 175 | return err 176 | } 177 | 178 | // Add the dependency to the manifest 179 | 180 | rev, err := wc.Revision() 181 | if err != nil { 182 | return err 183 | } 184 | 185 | b, err := wc.Branch() 186 | if err != nil { 187 | return err 188 | } 189 | 190 | dep := vendor.Dependency{ 191 | Importpath: path, 192 | Repository: repo.URL(), 193 | VCS: repo.Type(), 194 | Revision: rev, 195 | Branch: b, 196 | Path: extra, 197 | NoTests: !tests, 198 | AllFiles: all, 199 | } 200 | 201 | if err := m.AddDependency(dep); err != nil { 202 | return err 203 | } 204 | 205 | // Copy the code to the vendor folder 206 | 207 | dst := filepath.Join(vendorDir, dep.Importpath) 208 | src := filepath.Join(wc.Dir(), dep.Path) 209 | 210 | if err := fileutils.Copypath(dst, src, !dep.NoTests, dep.AllFiles); err != nil { 211 | return err 212 | } 213 | 214 | if err := fileutils.CopyLicense(dst, wc.Dir()); err != nil { 215 | return err 216 | } 217 | 218 | if err := vendor.WriteManifest(manifestFile, m); err != nil { 219 | return err 220 | } 221 | 222 | // Recurse 223 | 224 | fetchedToday = append(fetchedToday, path) 225 | 226 | if !noRecurse { 227 | // Look for dependencies in src, not going past wc.Dir() when looking for /vendor/, 228 | // knowing that wc.Dir() corresponds to rootRepoPath 229 | if !strings.HasSuffix(dep.Importpath, dep.Path) { 230 | return fmt.Errorf("unable to derive the root repo import path") 231 | } 232 | rootRepoPath := strings.TrimRight(strings.TrimSuffix(dep.Importpath, dep.Path), "/") 233 | deps, err := vendor.ParseImports(src, wc.Dir(), rootRepoPath, tests, all) 234 | if err != nil { 235 | return fmt.Errorf("failed to parse imports: %s", err) 236 | } 237 | 238 | for d := range deps { 239 | if strings.Index(d, ".") == -1 { // TODO: replace this silly heuristic 240 | continue 241 | } 242 | if err := fetchRecursive(m, d, level+1); err != nil { 243 | if strings.HasPrefix(err.Error(), "error fetching") { // I know, ok? 244 | return err 245 | } else { 246 | return fmt.Errorf("error fetching %s: %s", d, err) 247 | } 248 | } 249 | } 250 | } 251 | 252 | return nil 253 | } 254 | 255 | func logIndent(level int, v ...interface{}) { 256 | prefix := strings.Repeat("·", level) 257 | v = append([]interface{}{prefix}, v...) 258 | log.Println(v...) 259 | } 260 | 261 | // stripscheme removes any scheme components from url like paths. 262 | func stripscheme(path string) string { 263 | u, err := url.Parse(path) 264 | if err != nil { 265 | panic(err) 266 | } 267 | return u.Host + u.Path 268 | } 269 | 270 | // Package a contains package b? 271 | func contains(a, b string) bool { 272 | return a == b || strings.HasPrefix(b, a+"/") 273 | } 274 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/cmd/goimports/goimports.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "errors" 11 | "flag" 12 | "fmt" 13 | "go/scanner" 14 | "io" 15 | "io/ioutil" 16 | "log" 17 | "os" 18 | "os/exec" 19 | "path/filepath" 20 | "runtime" 21 | "runtime/pprof" 22 | "strings" 23 | 24 | "golang.org/x/tools/imports" 25 | ) 26 | 27 | var ( 28 | // main operation modes 29 | list = flag.Bool("l", false, "list files whose formatting differs from goimport's") 30 | write = flag.Bool("w", false, "write result to (source) file instead of stdout") 31 | doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") 32 | srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.") 33 | verbose bool // verbose logging 34 | 35 | cpuProfile = flag.String("cpuprofile", "", "CPU profile output") 36 | memProfile = flag.String("memprofile", "", "memory profile output") 37 | memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate") 38 | 39 | options = &imports.Options{ 40 | TabWidth: 8, 41 | TabIndent: true, 42 | Comments: true, 43 | Fragment: true, 44 | } 45 | exitCode = 0 46 | ) 47 | 48 | func init() { 49 | flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)") 50 | flag.StringVar(&imports.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages") 51 | } 52 | 53 | func report(err error) { 54 | scanner.PrintError(os.Stderr, err) 55 | exitCode = 2 56 | } 57 | 58 | func usage() { 59 | fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n") 60 | flag.PrintDefaults() 61 | os.Exit(2) 62 | } 63 | 64 | func isGoFile(f os.FileInfo) bool { 65 | // ignore non-Go files 66 | name := f.Name() 67 | return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") 68 | } 69 | 70 | // argumentType is which mode goimports was invoked as. 71 | type argumentType int 72 | 73 | const ( 74 | // fromStdin means the user is piping their source into goimports. 75 | fromStdin argumentType = iota 76 | 77 | // singleArg is the common case from editors, when goimports is run on 78 | // a single file. 79 | singleArg 80 | 81 | // multipleArg is when the user ran "goimports file1.go file2.go" 82 | // or ran goimports on a directory tree. 83 | multipleArg 84 | ) 85 | 86 | func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error { 87 | opt := options 88 | if argType == fromStdin { 89 | nopt := *options 90 | nopt.Fragment = true 91 | opt = &nopt 92 | } 93 | 94 | if in == nil { 95 | f, err := os.Open(filename) 96 | if err != nil { 97 | return err 98 | } 99 | defer f.Close() 100 | in = f 101 | } 102 | 103 | src, err := ioutil.ReadAll(in) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | target := filename 109 | if *srcdir != "" { 110 | // Determine whether the provided -srcdirc is a directory or file 111 | // and then use it to override the target. 112 | // 113 | // See https://github.com/dominikh/go-mode.el/issues/146 114 | if isFile(*srcdir) { 115 | if argType == multipleArg { 116 | return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories") 117 | } 118 | target = *srcdir 119 | } else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) { 120 | // For a file which doesn't exist on disk yet, but might shortly. 121 | // e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk. 122 | // The goimports on-save hook writes the buffer to a temp file 123 | // first and runs goimports before the actual save to newfile.go. 124 | // The editor's buffer is named "newfile.go" so that is passed to goimports as: 125 | // goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go 126 | // and then the editor reloads the result from the tmp file and writes 127 | // it to newfile.go. 128 | target = *srcdir 129 | } else { 130 | // Pretend that file is from *srcdir in order to decide 131 | // visible imports correctly. 132 | target = filepath.Join(*srcdir, filepath.Base(filename)) 133 | } 134 | } 135 | 136 | res, err := imports.Process(target, src, opt) 137 | if err != nil { 138 | return err 139 | } 140 | 141 | if !bytes.Equal(src, res) { 142 | // formatting has changed 143 | if *list { 144 | fmt.Fprintln(out, filename) 145 | } 146 | if *write { 147 | err = ioutil.WriteFile(filename, res, 0) 148 | if err != nil { 149 | return err 150 | } 151 | } 152 | if *doDiff { 153 | data, err := diff(src, res) 154 | if err != nil { 155 | return fmt.Errorf("computing diff: %s", err) 156 | } 157 | fmt.Printf("diff %s gofmt/%s\n", filename, filename) 158 | out.Write(data) 159 | } 160 | } 161 | 162 | if !*list && !*write && !*doDiff { 163 | _, err = out.Write(res) 164 | } 165 | 166 | return err 167 | } 168 | 169 | func visitFile(path string, f os.FileInfo, err error) error { 170 | if err == nil && isGoFile(f) { 171 | err = processFile(path, nil, os.Stdout, multipleArg) 172 | } 173 | if err != nil { 174 | report(err) 175 | } 176 | return nil 177 | } 178 | 179 | func walkDir(path string) { 180 | filepath.Walk(path, visitFile) 181 | } 182 | 183 | func main() { 184 | runtime.GOMAXPROCS(runtime.NumCPU()) 185 | 186 | // call gofmtMain in a separate function 187 | // so that it can use defer and have them 188 | // run before the exit. 189 | gofmtMain() 190 | os.Exit(exitCode) 191 | } 192 | 193 | // parseFlags parses command line flags and returns the paths to process. 194 | // It's a var so that custom implementations can replace it in other files. 195 | var parseFlags = func() []string { 196 | flag.BoolVar(&verbose, "v", false, "verbose logging") 197 | 198 | flag.Parse() 199 | return flag.Args() 200 | } 201 | 202 | func bufferedFileWriter(dest string) (w io.Writer, close func()) { 203 | f, err := os.Create(dest) 204 | if err != nil { 205 | log.Fatal(err) 206 | } 207 | bw := bufio.NewWriter(f) 208 | return bw, func() { 209 | if err := bw.Flush(); err != nil { 210 | log.Fatalf("error flushing %v: %v", dest, err) 211 | } 212 | if err := f.Close(); err != nil { 213 | log.Fatal(err) 214 | } 215 | } 216 | } 217 | 218 | func gofmtMain() { 219 | flag.Usage = usage 220 | paths := parseFlags() 221 | 222 | if *cpuProfile != "" { 223 | bw, flush := bufferedFileWriter(*cpuProfile) 224 | pprof.StartCPUProfile(bw) 225 | defer flush() 226 | defer pprof.StopCPUProfile() 227 | } 228 | // doTrace is a conditionally compiled wrapper around runtime/trace. It is 229 | // used to allow goimports to compile under gccgo, which does not support 230 | // runtime/trace. See https://golang.org/issue/15544. 231 | defer doTrace()() 232 | if *memProfileRate > 0 { 233 | runtime.MemProfileRate = *memProfileRate 234 | bw, flush := bufferedFileWriter(*memProfile) 235 | defer func() { 236 | runtime.GC() // materialize all statistics 237 | if err := pprof.WriteHeapProfile(bw); err != nil { 238 | log.Fatal(err) 239 | } 240 | flush() 241 | }() 242 | } 243 | 244 | if verbose { 245 | log.SetFlags(log.LstdFlags | log.Lmicroseconds) 246 | imports.Debug = true 247 | } 248 | if options.TabWidth < 0 { 249 | fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth) 250 | exitCode = 2 251 | return 252 | } 253 | 254 | if len(paths) == 0 { 255 | if err := processFile("", os.Stdin, os.Stdout, fromStdin); err != nil { 256 | report(err) 257 | } 258 | return 259 | } 260 | 261 | argType := singleArg 262 | if len(paths) > 1 { 263 | argType = multipleArg 264 | } 265 | 266 | for _, path := range paths { 267 | switch dir, err := os.Stat(path); { 268 | case err != nil: 269 | report(err) 270 | case dir.IsDir(): 271 | walkDir(path) 272 | default: 273 | if err := processFile(path, nil, os.Stdout, argType); err != nil { 274 | report(err) 275 | } 276 | } 277 | } 278 | } 279 | 280 | func diff(b1, b2 []byte) (data []byte, err error) { 281 | f1, err := ioutil.TempFile("", "gofmt") 282 | if err != nil { 283 | return 284 | } 285 | defer os.Remove(f1.Name()) 286 | defer f1.Close() 287 | 288 | f2, err := ioutil.TempFile("", "gofmt") 289 | if err != nil { 290 | return 291 | } 292 | defer os.Remove(f2.Name()) 293 | defer f2.Close() 294 | 295 | f1.Write(b1) 296 | f2.Write(b2) 297 | 298 | data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() 299 | if len(data) > 0 { 300 | // diff exits with a non-zero status when the files don't match. 301 | // Ignore that failure as long as we get output. 302 | err = nil 303 | } 304 | return 305 | } 306 | 307 | // isFile reports whether name is a file. 308 | func isFile(name string) bool { 309 | fi, err := os.Stat(name) 310 | return err == nil && fi.Mode().IsRegular() 311 | } 312 | 313 | // isDir reports whether name is a directory. 314 | func isDir(name string) bool { 315 | fi, err := os.Stat(name) 316 | return err == nil && fi.IsDir() 317 | } 318 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/imports.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:generate go run mkstdlib.go 6 | 7 | // Package imports implements a Go pretty-printer (like package "go/format") 8 | // that also adds or removes import statements as necessary. 9 | package imports // import "golang.org/x/tools/imports" 10 | 11 | import ( 12 | "bufio" 13 | "bytes" 14 | "fmt" 15 | "go/ast" 16 | "go/format" 17 | "go/parser" 18 | "go/printer" 19 | "go/token" 20 | "io" 21 | "regexp" 22 | "strconv" 23 | "strings" 24 | 25 | "golang.org/x/tools/go/ast/astutil" 26 | ) 27 | 28 | // Options specifies options for processing files. 29 | type Options struct { 30 | Fragment bool // Accept fragment of a source file (no package statement) 31 | AllErrors bool // Report all errors (not just the first 10 on different lines) 32 | 33 | Comments bool // Print comments (true if nil *Options provided) 34 | TabIndent bool // Use tabs for indent (true if nil *Options provided) 35 | TabWidth int // Tab width (8 if nil *Options provided) 36 | 37 | FormatOnly bool // Disable the insertion and deletion of imports 38 | } 39 | 40 | // Process formats and adjusts imports for the provided file. 41 | // If opt is nil the defaults are used. 42 | // 43 | // Note that filename's directory influences which imports can be chosen, 44 | // so it is important that filename be accurate. 45 | // To process data ``as if'' it were in filename, pass the data as a non-nil src. 46 | func Process(filename string, src []byte, opt *Options) ([]byte, error) { 47 | if opt == nil { 48 | opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} 49 | } 50 | 51 | fileSet := token.NewFileSet() 52 | file, adjust, err := parse(fileSet, filename, src, opt) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | if !opt.FormatOnly { 58 | _, err = fixImports(fileSet, file, filename) 59 | if err != nil { 60 | return nil, err 61 | } 62 | } 63 | 64 | sortImports(fileSet, file) 65 | imps := astutil.Imports(fileSet, file) 66 | 67 | var spacesBefore []string // import paths we need spaces before 68 | for _, impSection := range imps { 69 | // Within each block of contiguous imports, see if any 70 | // import lines are in different group numbers. If so, 71 | // we'll need to put a space between them so it's 72 | // compatible with gofmt. 73 | lastGroup := -1 74 | for _, importSpec := range impSection { 75 | importPath, _ := strconv.Unquote(importSpec.Path.Value) 76 | groupNum := importGroup(importPath) 77 | if groupNum != lastGroup && lastGroup != -1 { 78 | spacesBefore = append(spacesBefore, importPath) 79 | } 80 | lastGroup = groupNum 81 | } 82 | 83 | } 84 | 85 | printerMode := printer.UseSpaces 86 | if opt.TabIndent { 87 | printerMode |= printer.TabIndent 88 | } 89 | printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth} 90 | 91 | var buf bytes.Buffer 92 | err = printConfig.Fprint(&buf, fileSet, file) 93 | if err != nil { 94 | return nil, err 95 | } 96 | out := buf.Bytes() 97 | if adjust != nil { 98 | out = adjust(src, out) 99 | } 100 | if len(spacesBefore) > 0 { 101 | out = addImportSpaces(bytes.NewReader(out), spacesBefore) 102 | } 103 | 104 | out, err = format.Source(out) 105 | if err != nil { 106 | return nil, err 107 | } 108 | return out, nil 109 | } 110 | 111 | // parse parses src, which was read from filename, 112 | // as a Go source file or statement list. 113 | func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) { 114 | parserMode := parser.Mode(0) 115 | if opt.Comments { 116 | parserMode |= parser.ParseComments 117 | } 118 | if opt.AllErrors { 119 | parserMode |= parser.AllErrors 120 | } 121 | 122 | // Try as whole source file. 123 | file, err := parser.ParseFile(fset, filename, src, parserMode) 124 | if err == nil { 125 | return file, nil, nil 126 | } 127 | // If the error is that the source file didn't begin with a 128 | // package line and we accept fragmented input, fall through to 129 | // try as a source fragment. Stop and return on any other error. 130 | if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") { 131 | return nil, nil, err 132 | } 133 | 134 | // If this is a declaration list, make it a source file 135 | // by inserting a package clause. 136 | // Insert using a ;, not a newline, so that the line numbers 137 | // in psrc match the ones in src. 138 | psrc := append([]byte("package main;"), src...) 139 | file, err = parser.ParseFile(fset, filename, psrc, parserMode) 140 | if err == nil { 141 | // If a main function exists, we will assume this is a main 142 | // package and leave the file. 143 | if containsMainFunc(file) { 144 | return file, nil, nil 145 | } 146 | 147 | adjust := func(orig, src []byte) []byte { 148 | // Remove the package clause. 149 | // Gofmt has turned the ; into a \n. 150 | src = src[len("package main\n"):] 151 | return matchSpace(orig, src) 152 | } 153 | return file, adjust, nil 154 | } 155 | // If the error is that the source file didn't begin with a 156 | // declaration, fall through to try as a statement list. 157 | // Stop and return on any other error. 158 | if !strings.Contains(err.Error(), "expected declaration") { 159 | return nil, nil, err 160 | } 161 | 162 | // If this is a statement list, make it a source file 163 | // by inserting a package clause and turning the list 164 | // into a function body. This handles expressions too. 165 | // Insert using a ;, not a newline, so that the line numbers 166 | // in fsrc match the ones in src. 167 | fsrc := append(append([]byte("package p; func _() {"), src...), '}') 168 | file, err = parser.ParseFile(fset, filename, fsrc, parserMode) 169 | if err == nil { 170 | adjust := func(orig, src []byte) []byte { 171 | // Remove the wrapping. 172 | // Gofmt has turned the ; into a \n\n. 173 | src = src[len("package p\n\nfunc _() {"):] 174 | src = src[:len(src)-len("}\n")] 175 | // Gofmt has also indented the function body one level. 176 | // Remove that indent. 177 | src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) 178 | return matchSpace(orig, src) 179 | } 180 | return file, adjust, nil 181 | } 182 | 183 | // Failed, and out of options. 184 | return nil, nil, err 185 | } 186 | 187 | // containsMainFunc checks if a file contains a function declaration with the 188 | // function signature 'func main()' 189 | func containsMainFunc(file *ast.File) bool { 190 | for _, decl := range file.Decls { 191 | if f, ok := decl.(*ast.FuncDecl); ok { 192 | if f.Name.Name != "main" { 193 | continue 194 | } 195 | 196 | if len(f.Type.Params.List) != 0 { 197 | continue 198 | } 199 | 200 | if f.Type.Results != nil && len(f.Type.Results.List) != 0 { 201 | continue 202 | } 203 | 204 | return true 205 | } 206 | } 207 | 208 | return false 209 | } 210 | 211 | func cutSpace(b []byte) (before, middle, after []byte) { 212 | i := 0 213 | for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { 214 | i++ 215 | } 216 | j := len(b) 217 | for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { 218 | j-- 219 | } 220 | if i <= j { 221 | return b[:i], b[i:j], b[j:] 222 | } 223 | return nil, nil, b[j:] 224 | } 225 | 226 | // matchSpace reformats src to use the same space context as orig. 227 | // 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. 228 | // 2) matchSpace copies the indentation of the first non-blank line in orig 229 | // to every non-blank line in src. 230 | // 3) matchSpace copies the trailing space from orig and uses it in place 231 | // of src's trailing space. 232 | func matchSpace(orig []byte, src []byte) []byte { 233 | before, _, after := cutSpace(orig) 234 | i := bytes.LastIndex(before, []byte{'\n'}) 235 | before, indent := before[:i+1], before[i+1:] 236 | 237 | _, src, _ = cutSpace(src) 238 | 239 | var b bytes.Buffer 240 | b.Write(before) 241 | for len(src) > 0 { 242 | line := src 243 | if i := bytes.IndexByte(line, '\n'); i >= 0 { 244 | line, src = line[:i+1], line[i+1:] 245 | } else { 246 | src = nil 247 | } 248 | if len(line) > 0 && line[0] != '\n' { // not blank 249 | b.Write(indent) 250 | } 251 | b.Write(line) 252 | } 253 | b.Write(after) 254 | return b.Bytes() 255 | } 256 | 257 | var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`) 258 | 259 | func addImportSpaces(r io.Reader, breaks []string) []byte { 260 | var out bytes.Buffer 261 | sc := bufio.NewScanner(r) 262 | inImports := false 263 | done := false 264 | for sc.Scan() { 265 | s := sc.Text() 266 | 267 | if !inImports && !done && strings.HasPrefix(s, "import") { 268 | inImports = true 269 | } 270 | if inImports && (strings.HasPrefix(s, "var") || 271 | strings.HasPrefix(s, "func") || 272 | strings.HasPrefix(s, "const") || 273 | strings.HasPrefix(s, "type")) { 274 | done = true 275 | inImports = false 276 | } 277 | if inImports && len(breaks) > 0 { 278 | if m := impLine.FindStringSubmatch(s); m != nil { 279 | if m[1] == string(breaks[0]) { 280 | out.WriteByte('\n') 281 | breaks = breaks[1:] 282 | } 283 | } 284 | } 285 | 286 | fmt.Fprintln(&out, s) 287 | } 288 | return out.Bytes() 289 | } 290 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/ast/astutil/imports.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package astutil contains common utilities for working with the Go AST. 6 | package astutil // import "golang.org/x/tools/go/ast/astutil" 7 | 8 | import ( 9 | "fmt" 10 | "go/ast" 11 | "go/token" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | // AddImport adds the import path to the file f, if absent. 17 | func AddImport(fset *token.FileSet, f *ast.File, ipath string) (added bool) { 18 | return AddNamedImport(fset, f, "", ipath) 19 | } 20 | 21 | // AddNamedImport adds the import path to the file f, if absent. 22 | // If name is not empty, it is used to rename the import. 23 | // 24 | // For example, calling 25 | // AddNamedImport(fset, f, "pathpkg", "path") 26 | // adds 27 | // import pathpkg "path" 28 | func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) { 29 | if imports(f, ipath) { 30 | return false 31 | } 32 | 33 | newImport := &ast.ImportSpec{ 34 | Path: &ast.BasicLit{ 35 | Kind: token.STRING, 36 | Value: strconv.Quote(ipath), 37 | }, 38 | } 39 | if name != "" { 40 | newImport.Name = &ast.Ident{Name: name} 41 | } 42 | 43 | // Find an import decl to add to. 44 | // The goal is to find an existing import 45 | // whose import path has the longest shared 46 | // prefix with ipath. 47 | var ( 48 | bestMatch = -1 // length of longest shared prefix 49 | lastImport = -1 // index in f.Decls of the file's final import decl 50 | impDecl *ast.GenDecl // import decl containing the best match 51 | impIndex = -1 // spec index in impDecl containing the best match 52 | ) 53 | for i, decl := range f.Decls { 54 | gen, ok := decl.(*ast.GenDecl) 55 | if ok && gen.Tok == token.IMPORT { 56 | lastImport = i 57 | // Do not add to import "C", to avoid disrupting the 58 | // association with its doc comment, breaking cgo. 59 | if declImports(gen, "C") { 60 | continue 61 | } 62 | 63 | // Match an empty import decl if that's all that is available. 64 | if len(gen.Specs) == 0 && bestMatch == -1 { 65 | impDecl = gen 66 | } 67 | 68 | // Compute longest shared prefix with imports in this group. 69 | for j, spec := range gen.Specs { 70 | impspec := spec.(*ast.ImportSpec) 71 | n := matchLen(importPath(impspec), ipath) 72 | if n > bestMatch { 73 | bestMatch = n 74 | impDecl = gen 75 | impIndex = j 76 | } 77 | } 78 | } 79 | } 80 | 81 | // If no import decl found, add one after the last import. 82 | if impDecl == nil { 83 | impDecl = &ast.GenDecl{ 84 | Tok: token.IMPORT, 85 | } 86 | if lastImport >= 0 { 87 | impDecl.TokPos = f.Decls[lastImport].End() 88 | } else { 89 | // There are no existing imports. 90 | // Our new import goes after the package declaration and after 91 | // the comment, if any, that starts on the same line as the 92 | // package declaration. 93 | impDecl.TokPos = f.Package 94 | 95 | file := fset.File(f.Package) 96 | pkgLine := file.Line(f.Package) 97 | for _, c := range f.Comments { 98 | if file.Line(c.Pos()) > pkgLine { 99 | break 100 | } 101 | impDecl.TokPos = c.End() 102 | } 103 | } 104 | f.Decls = append(f.Decls, nil) 105 | copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) 106 | f.Decls[lastImport+1] = impDecl 107 | } 108 | 109 | // Insert new import at insertAt. 110 | insertAt := 0 111 | if impIndex >= 0 { 112 | // insert after the found import 113 | insertAt = impIndex + 1 114 | } 115 | impDecl.Specs = append(impDecl.Specs, nil) 116 | copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) 117 | impDecl.Specs[insertAt] = newImport 118 | pos := impDecl.Pos() 119 | if insertAt > 0 { 120 | // If there is a comment after an existing import, preserve the comment 121 | // position by adding the new import after the comment. 122 | if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil { 123 | pos = spec.Comment.End() 124 | } else { 125 | // Assign same position as the previous import, 126 | // so that the sorter sees it as being in the same block. 127 | pos = impDecl.Specs[insertAt-1].Pos() 128 | } 129 | } 130 | if newImport.Name != nil { 131 | newImport.Name.NamePos = pos 132 | } 133 | newImport.Path.ValuePos = pos 134 | newImport.EndPos = pos 135 | 136 | // Clean up parens. impDecl contains at least one spec. 137 | if len(impDecl.Specs) == 1 { 138 | // Remove unneeded parens. 139 | impDecl.Lparen = token.NoPos 140 | } else if !impDecl.Lparen.IsValid() { 141 | // impDecl needs parens added. 142 | impDecl.Lparen = impDecl.Specs[0].Pos() 143 | } 144 | 145 | f.Imports = append(f.Imports, newImport) 146 | 147 | if len(f.Decls) <= 1 { 148 | return true 149 | } 150 | 151 | // Merge all the import declarations into the first one. 152 | var first *ast.GenDecl 153 | for i := 0; i < len(f.Decls); i++ { 154 | decl := f.Decls[i] 155 | gen, ok := decl.(*ast.GenDecl) 156 | if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { 157 | continue 158 | } 159 | if first == nil { 160 | first = gen 161 | continue // Don't touch the first one. 162 | } 163 | // We now know there is more than one package in this import 164 | // declaration. Ensure that it ends up parenthesized. 165 | first.Lparen = first.Pos() 166 | // Move the imports of the other import declaration to the first one. 167 | for _, spec := range gen.Specs { 168 | spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() 169 | first.Specs = append(first.Specs, spec) 170 | } 171 | f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) 172 | i-- 173 | } 174 | 175 | return true 176 | } 177 | 178 | // DeleteImport deletes the import path from the file f, if present. 179 | func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) { 180 | return DeleteNamedImport(fset, f, "", path) 181 | } 182 | 183 | // DeleteNamedImport deletes the import with the given name and path from the file f, if present. 184 | func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) { 185 | var delspecs []*ast.ImportSpec 186 | var delcomments []*ast.CommentGroup 187 | 188 | // Find the import nodes that import path, if any. 189 | for i := 0; i < len(f.Decls); i++ { 190 | decl := f.Decls[i] 191 | gen, ok := decl.(*ast.GenDecl) 192 | if !ok || gen.Tok != token.IMPORT { 193 | continue 194 | } 195 | for j := 0; j < len(gen.Specs); j++ { 196 | spec := gen.Specs[j] 197 | impspec := spec.(*ast.ImportSpec) 198 | if impspec.Name == nil && name != "" { 199 | continue 200 | } 201 | if impspec.Name != nil && impspec.Name.Name != name { 202 | continue 203 | } 204 | if importPath(impspec) != path { 205 | continue 206 | } 207 | 208 | // We found an import spec that imports path. 209 | // Delete it. 210 | delspecs = append(delspecs, impspec) 211 | deleted = true 212 | copy(gen.Specs[j:], gen.Specs[j+1:]) 213 | gen.Specs = gen.Specs[:len(gen.Specs)-1] 214 | 215 | // If this was the last import spec in this decl, 216 | // delete the decl, too. 217 | if len(gen.Specs) == 0 { 218 | copy(f.Decls[i:], f.Decls[i+1:]) 219 | f.Decls = f.Decls[:len(f.Decls)-1] 220 | i-- 221 | break 222 | } else if len(gen.Specs) == 1 { 223 | if impspec.Doc != nil { 224 | delcomments = append(delcomments, impspec.Doc) 225 | } 226 | if impspec.Comment != nil { 227 | delcomments = append(delcomments, impspec.Comment) 228 | } 229 | for _, cg := range f.Comments { 230 | // Found comment on the same line as the import spec. 231 | if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line { 232 | delcomments = append(delcomments, cg) 233 | break 234 | } 235 | } 236 | 237 | gen.Lparen = token.NoPos // drop parens 238 | spec := gen.Specs[0].(*ast.ImportSpec) 239 | if spec.Doc != nil { 240 | // Move the documentation above the import statement. 241 | gen.TokPos = spec.Doc.End() + 1 242 | } 243 | 244 | for _, cg := range f.Comments { 245 | if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line { 246 | for fset.Position(gen.TokPos).Line != fset.Position(spec.Pos()).Line { 247 | fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) 248 | } 249 | break 250 | } 251 | } 252 | } 253 | if j > 0 { 254 | lastImpspec := gen.Specs[j-1].(*ast.ImportSpec) 255 | lastLine := fset.Position(lastImpspec.Path.ValuePos).Line 256 | line := fset.Position(impspec.Path.ValuePos).Line 257 | 258 | // We deleted an entry but now there may be 259 | // a blank line-sized hole where the import was. 260 | if line-lastLine > 1 { 261 | // There was a blank line immediately preceding the deleted import, 262 | // so there's no need to close the hole. 263 | // Do nothing. 264 | } else { 265 | // There was no blank line. Close the hole. 266 | fset.File(gen.Rparen).MergeLine(line) 267 | } 268 | } 269 | j-- 270 | } 271 | } 272 | 273 | // Delete imports from f.Imports. 274 | for i := 0; i < len(f.Imports); i++ { 275 | imp := f.Imports[i] 276 | for j, del := range delspecs { 277 | if imp == del { 278 | copy(f.Imports[i:], f.Imports[i+1:]) 279 | f.Imports = f.Imports[:len(f.Imports)-1] 280 | copy(delspecs[j:], delspecs[j+1:]) 281 | delspecs = delspecs[:len(delspecs)-1] 282 | i-- 283 | break 284 | } 285 | } 286 | } 287 | 288 | // Delete comments from f.Comments. 289 | for i := 0; i < len(f.Comments); i++ { 290 | cg := f.Comments[i] 291 | for j, del := range delcomments { 292 | if cg == del { 293 | copy(f.Comments[i:], f.Comments[i+1:]) 294 | f.Comments = f.Comments[:len(f.Comments)-1] 295 | copy(delcomments[j:], delcomments[j+1:]) 296 | delcomments = delcomments[:len(delcomments)-1] 297 | i-- 298 | break 299 | } 300 | } 301 | } 302 | 303 | if len(delspecs) > 0 { 304 | panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs)) 305 | } 306 | 307 | return 308 | } 309 | 310 | // RewriteImport rewrites any import of path oldPath to path newPath. 311 | func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) { 312 | for _, imp := range f.Imports { 313 | if importPath(imp) == oldPath { 314 | rewrote = true 315 | // record old End, because the default is to compute 316 | // it using the length of imp.Path.Value. 317 | imp.EndPos = imp.End() 318 | imp.Path.Value = strconv.Quote(newPath) 319 | } 320 | } 321 | return 322 | } 323 | 324 | // UsesImport reports whether a given import is used. 325 | func UsesImport(f *ast.File, path string) (used bool) { 326 | spec := importSpec(f, path) 327 | if spec == nil { 328 | return 329 | } 330 | 331 | name := spec.Name.String() 332 | switch name { 333 | case "": 334 | // If the package name is not explicitly specified, 335 | // make an educated guess. This is not guaranteed to be correct. 336 | lastSlash := strings.LastIndex(path, "/") 337 | if lastSlash == -1 { 338 | name = path 339 | } else { 340 | name = path[lastSlash+1:] 341 | } 342 | case "_", ".": 343 | // Not sure if this import is used - err on the side of caution. 344 | return true 345 | } 346 | 347 | ast.Walk(visitFn(func(n ast.Node) { 348 | sel, ok := n.(*ast.SelectorExpr) 349 | if ok && isTopName(sel.X, name) { 350 | used = true 351 | } 352 | }), f) 353 | 354 | return 355 | } 356 | 357 | type visitFn func(node ast.Node) 358 | 359 | func (fn visitFn) Visit(node ast.Node) ast.Visitor { 360 | fn(node) 361 | return fn 362 | } 363 | 364 | // imports returns true if f imports path. 365 | func imports(f *ast.File, path string) bool { 366 | return importSpec(f, path) != nil 367 | } 368 | 369 | // importSpec returns the import spec if f imports path, 370 | // or nil otherwise. 371 | func importSpec(f *ast.File, path string) *ast.ImportSpec { 372 | for _, s := range f.Imports { 373 | if importPath(s) == path { 374 | return s 375 | } 376 | } 377 | return nil 378 | } 379 | 380 | // importPath returns the unquoted import path of s, 381 | // or "" if the path is not properly quoted. 382 | func importPath(s *ast.ImportSpec) string { 383 | t, err := strconv.Unquote(s.Path.Value) 384 | if err == nil { 385 | return t 386 | } 387 | return "" 388 | } 389 | 390 | // declImports reports whether gen contains an import of path. 391 | func declImports(gen *ast.GenDecl, path string) bool { 392 | if gen.Tok != token.IMPORT { 393 | return false 394 | } 395 | for _, spec := range gen.Specs { 396 | impspec := spec.(*ast.ImportSpec) 397 | if importPath(impspec) == path { 398 | return true 399 | } 400 | } 401 | return false 402 | } 403 | 404 | // matchLen returns the length of the longest path segment prefix shared by x and y. 405 | func matchLen(x, y string) int { 406 | n := 0 407 | for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ { 408 | if x[i] == '/' { 409 | n++ 410 | } 411 | } 412 | return n 413 | } 414 | 415 | // isTopName returns true if n is a top-level unresolved identifier with the given name. 416 | func isTopName(n ast.Expr, name string) bool { 417 | id, ok := n.(*ast.Ident) 418 | return ok && id.Name == name && id.Obj == nil 419 | } 420 | 421 | // Imports returns the file imports grouped by paragraph. 422 | func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec { 423 | var groups [][]*ast.ImportSpec 424 | 425 | for _, decl := range f.Decls { 426 | genDecl, ok := decl.(*ast.GenDecl) 427 | if !ok || genDecl.Tok != token.IMPORT { 428 | break 429 | } 430 | 431 | group := []*ast.ImportSpec{} 432 | 433 | var lastLine int 434 | for _, spec := range genDecl.Specs { 435 | importSpec := spec.(*ast.ImportSpec) 436 | pos := importSpec.Path.ValuePos 437 | line := fset.Position(pos).Line 438 | if lastLine > 0 && pos > 0 && line-lastLine > 1 { 439 | groups = append(groups, group) 440 | group = []*ast.ImportSpec{} 441 | } 442 | group = append(group, importSpec) 443 | lastLine = line 444 | } 445 | groups = append(groups, group) 446 | } 447 | 448 | return groups 449 | } 450 | -------------------------------------------------------------------------------- /gbvendor/repo.go: -------------------------------------------------------------------------------- 1 | package vendor 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "net/url" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "regexp" 14 | "strings" 15 | 16 | "github.com/FiloSottile/gvt/fileutils" 17 | ) 18 | 19 | // RemoteRepo describes a remote dvcs repository. 20 | type RemoteRepo interface { 21 | 22 | // Checkout checks out a specific branch, tag, or revision. 23 | // The interpretation of these three values is impementation 24 | // specific. 25 | Checkout(branch, tag, revision string) (WorkingCopy, error) 26 | 27 | // URL returns the URL the clone was/will be taken from. 28 | URL() string 29 | 30 | // Type returns the repository type (git, hg, ...) 31 | Type() string 32 | } 33 | 34 | // WorkingCopy represents a local copy of a remote dvcs repository. 35 | type WorkingCopy interface { 36 | 37 | // Dir is the root of this working copy. 38 | Dir() string 39 | 40 | // Revision returns the revision of this working copy. 41 | Revision() (string, error) 42 | 43 | // Branch returns the branch to which this working copy belongs. 44 | Branch() (string, error) 45 | 46 | // Destroy removes the working copy. 47 | Destroy() error 48 | } 49 | 50 | var ( 51 | ghregex = regexp.MustCompile(`^(?Pgithub\.com/([A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`) 52 | bbregex = regexp.MustCompile(`^(?Pbitbucket\.org/(?P[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`) 53 | lpregex = regexp.MustCompile(`^launchpad.net/([A-Za-z0-9-._]+)(/[A-Za-z0-9-._]+)?(/.+)?`) 54 | gcregex = regexp.MustCompile(`^(?Pcode\.google\.com/[pr]/(?P[a-z0-9\-]+)(\.(?P[a-z0-9\-]+))?)(/[A-Za-z0-9_.\-]+)*$`) 55 | genericre = regexp.MustCompile(`^(?P(?P([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/~]*?)\.(?Pbzr|git|hg|svn))([/A-Za-z0-9_.\-~]+)*$`) 56 | ) 57 | 58 | // DeduceRemoteRepo takes a potential import path and returns a RemoteRepo 59 | // representing the remote location of the source of an import path. 60 | // Remote repositories can be bare import paths, or urls including a checkout scheme. 61 | // If deduction would cause traversal of an insecure host, a message will be 62 | // printed and the travelsal path will be ignored. 63 | func DeduceRemoteRepo(path string, insecure bool) (RemoteRepo, string, error) { 64 | u, err := url.Parse(path) 65 | if err != nil { 66 | return nil, "", fmt.Errorf("%q is not a valid import path", path) 67 | } 68 | 69 | var schemes []string 70 | if u.Scheme != "" { 71 | schemes = append(schemes, u.Scheme) 72 | } 73 | 74 | path = u.Host + u.Path 75 | if !regexp.MustCompile(`^([A-Za-z0-9-]+)(\.[A-Za-z0-9-]+)+(/[A-Za-z0-9-_.~]+)*$`).MatchString(path) { 76 | return nil, "", fmt.Errorf("%q is not a valid import path", path) 77 | } 78 | 79 | switch { 80 | case ghregex.MatchString(path): 81 | v := ghregex.FindStringSubmatch(path) 82 | url := &url.URL{ 83 | Host: "github.com", 84 | Path: v[2], 85 | } 86 | repo, err := Gitrepo(url, insecure, schemes...) 87 | return repo, v[0][len(v[1]):], err 88 | case bbregex.MatchString(path): 89 | v := bbregex.FindStringSubmatch(path) 90 | url := &url.URL{ 91 | Host: "bitbucket.org", 92 | Path: v[2], 93 | } 94 | repo, err := Gitrepo(url, insecure, schemes...) 95 | if err == nil { 96 | return repo, v[0][len(v[1]):], nil 97 | } 98 | repo, err = Hgrepo(url, insecure) 99 | if err == nil { 100 | return repo, v[0][len(v[1]):], nil 101 | } 102 | return nil, "", fmt.Errorf("unknown repository type") 103 | case gcregex.MatchString(path): 104 | v := gcregex.FindStringSubmatch(path) 105 | url := &url.URL{ 106 | Host: "code.google.com", 107 | Path: "p/" + v[2], 108 | } 109 | repo, err := Hgrepo(url, insecure, schemes...) 110 | if err == nil { 111 | return repo, v[0][len(v[1]):], nil 112 | } 113 | repo, err = Gitrepo(url, insecure, schemes...) 114 | if err == nil { 115 | return repo, v[0][len(v[1]):], nil 116 | } 117 | return nil, "", fmt.Errorf("unknown repository type") 118 | case lpregex.MatchString(path): 119 | v := lpregex.FindStringSubmatch(path) 120 | v = append(v, "", "") 121 | if v[2] == "" { 122 | // launchpad.net/project" 123 | repo, err := Bzrrepo(fmt.Sprintf("https://launchpad.net/%v", v[1])) 124 | return repo, "", err 125 | } 126 | // launchpad.net/project/series" 127 | repo, err := Bzrrepo(fmt.Sprintf("https://launchpad.net/%s/%s", v[1], v[2])) 128 | return repo, v[3], err 129 | } 130 | 131 | // try the general syntax 132 | if genericre.MatchString(path) { 133 | v := genericre.FindStringSubmatch(path) 134 | switch v[5] { 135 | case "git": 136 | x := strings.SplitN(v[1], "/", 2) 137 | url := &url.URL{ 138 | Host: x[0], 139 | Path: x[1], 140 | } 141 | repo, err := Gitrepo(url, insecure, schemes...) 142 | return repo, v[6], err 143 | case "hg": 144 | x := strings.SplitN(v[1], "/", 2) 145 | url := &url.URL{ 146 | Host: x[0], 147 | Path: x[1], 148 | } 149 | repo, err := Hgrepo(url, insecure, schemes...) 150 | return repo, v[6], err 151 | case "bzr": 152 | repo, err := Bzrrepo("https://" + v[1]) 153 | return repo, v[6], err 154 | default: 155 | return nil, "", fmt.Errorf("unknown repository type: %q", v[5]) 156 | 157 | } 158 | } 159 | 160 | // no idea, try to resolve as a vanity import 161 | importpath, vcs, reporoot, err := ParseMetadata(path, insecure) 162 | if err != nil { 163 | return nil, "", err 164 | } 165 | u, err = url.Parse(reporoot) 166 | if err != nil { 167 | return nil, "", err 168 | } 169 | extra := path[len(importpath):] 170 | switch vcs { 171 | case "git": 172 | u.Path = u.Path[1:] 173 | repo, err := Gitrepo(u, insecure, u.Scheme) 174 | return repo, extra, err 175 | case "hg": 176 | u.Path = u.Path[1:] 177 | repo, err := Hgrepo(u, insecure, u.Scheme) 178 | return repo, extra, err 179 | case "bzr": 180 | repo, err := Bzrrepo(reporoot) 181 | return repo, extra, err 182 | default: 183 | return nil, "", fmt.Errorf("unknown repository type: %q", vcs) 184 | } 185 | } 186 | 187 | func NewRemoteRepo(repoURL, vcs string, insecure bool) (RemoteRepo, error) { 188 | u, err := url.Parse(repoURL) 189 | if err != nil { 190 | return nil, fmt.Errorf("%q is not a valid import path", repoURL) 191 | } 192 | switch vcs { 193 | case "git": 194 | return Gitrepo(u, insecure, u.Scheme) 195 | case "hg": 196 | return Hgrepo(u, insecure, u.Scheme) 197 | case "bzr": 198 | return Bzrrepo(repoURL) 199 | case "": 200 | // for backwards compatibility with manifests that miss the VCS entry 201 | if repo, err := Gitrepo(u, insecure, u.Scheme); err == nil { 202 | return repo, nil 203 | } 204 | if repo, err := Hgrepo(u, insecure, u.Scheme); err == nil { 205 | return repo, nil 206 | } 207 | if repo, err := Bzrrepo(repoURL); err == nil { 208 | return repo, nil 209 | } 210 | return nil, fmt.Errorf("can't reach %q", repoURL) 211 | } 212 | return nil, fmt.Errorf("%q is not a valid VCS", vcs) 213 | } 214 | 215 | // Gitrepo returns a RemoteRepo representing a remote git repository. 216 | func Gitrepo(url *url.URL, insecure bool, schemes ...string) (RemoteRepo, error) { 217 | if len(schemes) == 0 { 218 | schemes = []string{"https", "git", "ssh", "http"} 219 | } 220 | u, err := probeGitUrl(url, insecure, schemes) 221 | if err != nil { 222 | return nil, err 223 | } 224 | return &gitrepo{ 225 | url: u, 226 | }, nil 227 | } 228 | 229 | func probeGitUrl(u *url.URL, insecure bool, schemes []string) (string, error) { 230 | git := func(url *url.URL) error { 231 | out, err := run("git", "ls-remote", url.String(), "HEAD") 232 | if err != nil { 233 | return err 234 | } 235 | 236 | if !bytes.Contains(out, []byte("HEAD")) { 237 | return fmt.Errorf("not a git repo") 238 | } 239 | return nil 240 | } 241 | return probe(git, u, insecure, schemes...) 242 | } 243 | 244 | func probeHgUrl(u *url.URL, insecure bool, schemes []string) (string, error) { 245 | hg := func(url *url.URL) error { 246 | _, err := run("hg", "identify", url.String()) 247 | return err 248 | } 249 | return probe(hg, u, insecure, schemes...) 250 | } 251 | 252 | func probeBzrUrl(u string) error { 253 | bzr := func(url *url.URL) error { 254 | _, err := run("bzr", "info", url.String()) 255 | return err 256 | } 257 | url, err := url.Parse(u) 258 | if err != nil { 259 | return err 260 | } 261 | _, err = probe(bzr, url, false, "https") 262 | return err 263 | } 264 | 265 | // probe calls the supplied vcs function to probe a variety of url constructions. 266 | // If vcs returns non nil, it is assumed that the url is not a valid repo. 267 | func probe(vcs func(*url.URL) error, url *url.URL, insecure bool, schemes ...string) (string, error) { 268 | var unsuccessful []string 269 | for _, scheme := range schemes { 270 | 271 | // make copy of url and apply scheme 272 | url := *url 273 | url.Scheme = scheme 274 | 275 | switch url.Scheme { 276 | case "git+ssh", "https", "ssh": 277 | if err := vcs(&url); err == nil { 278 | return url.String(), nil 279 | } 280 | case "http", "git": 281 | if !insecure { 282 | log.Printf("skipping insecure protocol: %s", url.String()) 283 | continue 284 | } 285 | if err := vcs(&url); err == nil { 286 | return url.String(), nil 287 | } 288 | default: 289 | return "", fmt.Errorf("unsupported scheme: %v", url.Scheme) 290 | } 291 | unsuccessful = append(unsuccessful, url.String()) 292 | } 293 | return "", fmt.Errorf("vcs probe failed, tried: %s", strings.Join(unsuccessful, ",")) 294 | } 295 | 296 | // gitrepo is a git RemoteRepo. 297 | type gitrepo struct { 298 | 299 | // remote repository url, see man 1 git-clone 300 | url string 301 | } 302 | 303 | func (g *gitrepo) URL() string { 304 | return g.url 305 | } 306 | 307 | func (g *gitrepo) Type() string { 308 | return "git" 309 | } 310 | 311 | // Checkout fetchs the remote branch, tag, or revision. If the branch is blank, 312 | // then the default remote branch will be used. If the branch is "HEAD" and 313 | // revision is empty, an impossible update is assumed. 314 | func (g *gitrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { 315 | if branch == "HEAD" && revision == "" { 316 | return nil, fmt.Errorf("cannot update %q as it has been previously fetched with -tag or -revision. Please use gvt delete then fetch again.", g.url) 317 | } 318 | if !atMostOne(tag, revision) { 319 | return nil, fmt.Errorf("only one of tag or revision may be supplied") 320 | } 321 | if !atMostOne(branch, tag) { 322 | return nil, fmt.Errorf("only one of branch or tag may be supplied") 323 | } 324 | dir, err := mktmp() 325 | if err != nil { 326 | return nil, err 327 | } 328 | wc := workingcopy{ 329 | path: dir, 330 | } 331 | 332 | quiet := false 333 | args := []string{ 334 | "clone", 335 | "-q", // silence progress report to stderr 336 | g.url, 337 | dir, 338 | } 339 | if branch != "" && branch != "HEAD" { 340 | args = append(args, "--branch", branch, "--single-branch") 341 | } 342 | if tag != "" { 343 | quiet = true // git REALLY wants to tell you how awesome 'detached HEAD' is... 344 | args = append(args, "--branch", tag, "--single-branch") 345 | args = append(args, "--depth", "1") 346 | } 347 | if revision == "" { 348 | args = append(args, "--depth", "1") 349 | } 350 | 351 | if quiet { 352 | err = runQuiet("git", args...) 353 | } else { 354 | _, err = run("git", args...) 355 | } 356 | if err != nil { 357 | wc.Destroy() 358 | return nil, err 359 | } 360 | 361 | if revision != "" { 362 | if err := runOutPath(os.Stderr, dir, "git", "checkout", "-q", revision); err != nil { 363 | wc.Destroy() 364 | return nil, err 365 | } 366 | } 367 | 368 | return &GitClone{wc}, nil 369 | } 370 | 371 | type workingcopy struct { 372 | path string 373 | } 374 | 375 | func (w workingcopy) Dir() string { return w.path } 376 | 377 | func (w workingcopy) Destroy() error { 378 | return fileutils.RemoveAll(w.path) 379 | } 380 | 381 | // GitClone is a git WorkingCopy. 382 | type GitClone struct { 383 | workingcopy 384 | } 385 | 386 | func (g *GitClone) Revision() (string, error) { 387 | rev, err := runPath(g.path, "git", "rev-parse", "HEAD") 388 | return strings.TrimSpace(string(rev)), err 389 | } 390 | 391 | func (g *GitClone) Branch() (string, error) { 392 | rev, err := runPath(g.path, "git", "rev-parse", "--abbrev-ref", "HEAD") 393 | return strings.TrimSpace(string(rev)), err 394 | } 395 | 396 | // Hgrepo returns a RemoteRepo representing a remote git repository. 397 | func Hgrepo(u *url.URL, insecure bool, schemes ...string) (RemoteRepo, error) { 398 | if len(schemes) == 0 { 399 | schemes = []string{"https", "http"} 400 | } 401 | url, err := probeHgUrl(u, insecure, schemes) 402 | if err != nil { 403 | return nil, err 404 | } 405 | return &hgrepo{ 406 | url: url, 407 | }, nil 408 | } 409 | 410 | // hgrepo is a Mercurial repo. 411 | type hgrepo struct { 412 | 413 | // remote repository url, see man 1 hg 414 | url string 415 | } 416 | 417 | func (h *hgrepo) URL() string { return h.url } 418 | func (h *hgrepo) Type() string { return "hg" } 419 | 420 | func (h *hgrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { 421 | if !atMostOne(tag, revision) { 422 | return nil, fmt.Errorf("only one of tag or revision may be supplied") 423 | } 424 | dir, err := mktmp() 425 | if err != nil { 426 | return nil, err 427 | } 428 | args := []string{ 429 | "clone", 430 | h.url, 431 | dir, 432 | "--noninteractive", 433 | } 434 | 435 | if branch != "" { 436 | args = append(args, "--branch", branch) 437 | } 438 | if err := runOut(os.Stderr, "hg", args...); err != nil { 439 | fileutils.RemoveAll(dir) 440 | return nil, err 441 | } 442 | if revision != "" { 443 | if err := runOut(os.Stderr, "hg", "--cwd", dir, "update", "-r", revision); err != nil { 444 | fileutils.RemoveAll(dir) 445 | return nil, err 446 | } 447 | } 448 | 449 | return &HgClone{ 450 | workingcopy{ 451 | path: dir, 452 | }, 453 | }, nil 454 | } 455 | 456 | // HgClone is a mercurial WorkingCopy. 457 | type HgClone struct { 458 | workingcopy 459 | } 460 | 461 | func (h *HgClone) Revision() (string, error) { 462 | rev, err := run("hg", "--cwd", h.path, "id", "-i") 463 | return strings.TrimSpace(string(rev)), err 464 | } 465 | 466 | func (h *HgClone) Branch() (string, error) { 467 | rev, err := run("hg", "--cwd", h.path, "branch") 468 | return strings.TrimSpace(string(rev)), err 469 | } 470 | 471 | // Bzrrepo returns a RemoteRepo representing a remote bzr repository. 472 | func Bzrrepo(url string) (RemoteRepo, error) { 473 | if err := probeBzrUrl(url); err != nil { 474 | return nil, err 475 | } 476 | return &bzrrepo{ 477 | url: url, 478 | }, nil 479 | } 480 | 481 | // bzrrepo is a bzr RemoteRepo. 482 | type bzrrepo struct { 483 | 484 | // remote repository url 485 | url string 486 | } 487 | 488 | func (b *bzrrepo) URL() string { 489 | return b.url 490 | } 491 | 492 | func (b *bzrrepo) Type() string { 493 | return "bzr" 494 | } 495 | 496 | func (b *bzrrepo) Checkout(branch, tag, revision string) (WorkingCopy, error) { 497 | if !atMostOne(tag, revision) { 498 | return nil, fmt.Errorf("only one of tag or revision may be supplied") 499 | } 500 | dir, err := mktmp() 501 | if err != nil { 502 | return nil, err 503 | } 504 | wc := filepath.Join(dir, "wc") 505 | if err := runOut(os.Stderr, "bzr", "branch", b.url, wc); err != nil { 506 | fileutils.RemoveAll(dir) 507 | return nil, err 508 | } 509 | 510 | return &BzrClone{ 511 | workingcopy{ 512 | path: wc, 513 | }, 514 | }, nil 515 | } 516 | 517 | // BzrClone is a bazaar WorkingCopy. 518 | type BzrClone struct { 519 | workingcopy 520 | } 521 | 522 | func (b *BzrClone) Revision() (string, error) { 523 | return "1", nil 524 | } 525 | 526 | func (b *BzrClone) Branch() (string, error) { 527 | return "master", nil 528 | } 529 | 530 | func (b *BzrClone) Destroy() error { 531 | if err := (workingcopy{b.path}).Destroy(); err != nil { 532 | return err 533 | } 534 | parent := filepath.Dir(b.path) 535 | return os.Remove(parent) 536 | } 537 | 538 | func cleanPath(path string) error { 539 | if files, _ := ioutil.ReadDir(path); len(files) > 0 || filepath.Base(path) == "vendor" { 540 | return nil 541 | } 542 | parent := filepath.Dir(path) 543 | if err := fileutils.RemoveAll(path); err != nil { 544 | return err 545 | } 546 | return cleanPath(parent) 547 | } 548 | 549 | func mktmp() (string, error) { 550 | return ioutil.TempDir("", "gvt-") 551 | } 552 | 553 | func run(c string, args ...string) ([]byte, error) { 554 | var buf bytes.Buffer 555 | err := runOut(&buf, c, args...) 556 | return buf.Bytes(), err 557 | } 558 | 559 | func runOut(w io.Writer, c string, args ...string) error { 560 | cmd := exec.Command(c, args...) 561 | cmd.Stdin = nil 562 | cmd.Stdout = w 563 | cmd.Stderr = os.Stderr 564 | return cmd.Run() 565 | } 566 | 567 | func runQuiet(c string, args ...string) error { 568 | cmd := exec.Command(c, args...) 569 | cmd.Stdin = nil 570 | cmd.Stdout = nil 571 | cmd.Stderr = nil 572 | return cmd.Run() 573 | } 574 | 575 | func runPath(path string, c string, args ...string) ([]byte, error) { 576 | var buf bytes.Buffer 577 | err := runOutPath(&buf, path, c, args...) 578 | return buf.Bytes(), err 579 | } 580 | 581 | func runOutPath(w io.Writer, path string, c string, args ...string) error { 582 | cmd := exec.Command(c, args...) 583 | cmd.Dir = path 584 | cmd.Stdin = nil 585 | cmd.Stdout = w 586 | cmd.Stderr = os.Stderr 587 | return cmd.Run() 588 | } 589 | 590 | // atMostOne returns true if no more than one string supplied is not empty. 591 | func atMostOne(args ...string) bool { 592 | var c int 593 | for _, arg := range args { 594 | if arg != "" { 595 | c++ 596 | } 597 | } 598 | return c < 2 599 | } 600 | 601 | // oneof returns the first non empty string 602 | func oneOf(args ...string) string { 603 | for _, arg := range args { 604 | if arg != "" { 605 | return arg 606 | } 607 | } 608 | return "" 609 | } 610 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/ast/astutil/enclosing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package astutil 6 | 7 | // This file defines utilities for working with source positions. 8 | 9 | import ( 10 | "fmt" 11 | "go/ast" 12 | "go/token" 13 | "sort" 14 | ) 15 | 16 | // PathEnclosingInterval returns the node that encloses the source 17 | // interval [start, end), and all its ancestors up to the AST root. 18 | // 19 | // The definition of "enclosing" used by this function considers 20 | // additional whitespace abutting a node to be enclosed by it. 21 | // In this example: 22 | // 23 | // z := x + y // add them 24 | // <-A-> 25 | // <----B-----> 26 | // 27 | // the ast.BinaryExpr(+) node is considered to enclose interval B 28 | // even though its [Pos()..End()) is actually only interval A. 29 | // This behaviour makes user interfaces more tolerant of imperfect 30 | // input. 31 | // 32 | // This function treats tokens as nodes, though they are not included 33 | // in the result. e.g. PathEnclosingInterval("+") returns the 34 | // enclosing ast.BinaryExpr("x + y"). 35 | // 36 | // If start==end, the 1-char interval following start is used instead. 37 | // 38 | // The 'exact' result is true if the interval contains only path[0] 39 | // and perhaps some adjacent whitespace. It is false if the interval 40 | // overlaps multiple children of path[0], or if it contains only 41 | // interior whitespace of path[0]. 42 | // In this example: 43 | // 44 | // z := x + y // add them 45 | // <--C--> <---E--> 46 | // ^ 47 | // D 48 | // 49 | // intervals C, D and E are inexact. C is contained by the 50 | // z-assignment statement, because it spans three of its children (:=, 51 | // x, +). So too is the 1-char interval D, because it contains only 52 | // interior whitespace of the assignment. E is considered interior 53 | // whitespace of the BlockStmt containing the assignment. 54 | // 55 | // Precondition: [start, end) both lie within the same file as root. 56 | // TODO(adonovan): return (nil, false) in this case and remove precond. 57 | // Requires FileSet; see loader.tokenFileContainsPos. 58 | // 59 | // Postcondition: path is never nil; it always contains at least 'root'. 60 | // 61 | func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) { 62 | // fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging 63 | 64 | // Precondition: node.[Pos..End) and adjoining whitespace contain [start, end). 65 | var visit func(node ast.Node) bool 66 | visit = func(node ast.Node) bool { 67 | path = append(path, node) 68 | 69 | nodePos := node.Pos() 70 | nodeEnd := node.End() 71 | 72 | // fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging 73 | 74 | // Intersect [start, end) with interval of node. 75 | if start < nodePos { 76 | start = nodePos 77 | } 78 | if end > nodeEnd { 79 | end = nodeEnd 80 | } 81 | 82 | // Find sole child that contains [start, end). 83 | children := childrenOf(node) 84 | l := len(children) 85 | for i, child := range children { 86 | // [childPos, childEnd) is unaugmented interval of child. 87 | childPos := child.Pos() 88 | childEnd := child.End() 89 | 90 | // [augPos, augEnd) is whitespace-augmented interval of child. 91 | augPos := childPos 92 | augEnd := childEnd 93 | if i > 0 { 94 | augPos = children[i-1].End() // start of preceding whitespace 95 | } 96 | if i < l-1 { 97 | nextChildPos := children[i+1].Pos() 98 | // Does [start, end) lie between child and next child? 99 | if start >= augEnd && end <= nextChildPos { 100 | return false // inexact match 101 | } 102 | augEnd = nextChildPos // end of following whitespace 103 | } 104 | 105 | // fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n", 106 | // i, augPos, augEnd, start, end) // debugging 107 | 108 | // Does augmented child strictly contain [start, end)? 109 | if augPos <= start && end <= augEnd { 110 | _, isToken := child.(tokenNode) 111 | return isToken || visit(child) 112 | } 113 | 114 | // Does [start, end) overlap multiple children? 115 | // i.e. left-augmented child contains start 116 | // but LR-augmented child does not contain end. 117 | if start < childEnd && end > augEnd { 118 | break 119 | } 120 | } 121 | 122 | // No single child contained [start, end), 123 | // so node is the result. Is it exact? 124 | 125 | // (It's tempting to put this condition before the 126 | // child loop, but it gives the wrong result in the 127 | // case where a node (e.g. ExprStmt) and its sole 128 | // child have equal intervals.) 129 | if start == nodePos && end == nodeEnd { 130 | return true // exact match 131 | } 132 | 133 | return false // inexact: overlaps multiple children 134 | } 135 | 136 | if start > end { 137 | start, end = end, start 138 | } 139 | 140 | if start < root.End() && end > root.Pos() { 141 | if start == end { 142 | end = start + 1 // empty interval => interval of size 1 143 | } 144 | exact = visit(root) 145 | 146 | // Reverse the path: 147 | for i, l := 0, len(path); i < l/2; i++ { 148 | path[i], path[l-1-i] = path[l-1-i], path[i] 149 | } 150 | } else { 151 | // Selection lies within whitespace preceding the 152 | // first (or following the last) declaration in the file. 153 | // The result nonetheless always includes the ast.File. 154 | path = append(path, root) 155 | } 156 | 157 | return 158 | } 159 | 160 | // tokenNode is a dummy implementation of ast.Node for a single token. 161 | // They are used transiently by PathEnclosingInterval but never escape 162 | // this package. 163 | // 164 | type tokenNode struct { 165 | pos token.Pos 166 | end token.Pos 167 | } 168 | 169 | func (n tokenNode) Pos() token.Pos { 170 | return n.pos 171 | } 172 | 173 | func (n tokenNode) End() token.Pos { 174 | return n.end 175 | } 176 | 177 | func tok(pos token.Pos, len int) ast.Node { 178 | return tokenNode{pos, pos + token.Pos(len)} 179 | } 180 | 181 | // childrenOf returns the direct non-nil children of ast.Node n. 182 | // It may include fake ast.Node implementations for bare tokens. 183 | // it is not safe to call (e.g.) ast.Walk on such nodes. 184 | // 185 | func childrenOf(n ast.Node) []ast.Node { 186 | var children []ast.Node 187 | 188 | // First add nodes for all true subtrees. 189 | ast.Inspect(n, func(node ast.Node) bool { 190 | if node == n { // push n 191 | return true // recur 192 | } 193 | if node != nil { // push child 194 | children = append(children, node) 195 | } 196 | return false // no recursion 197 | }) 198 | 199 | // Then add fake Nodes for bare tokens. 200 | switch n := n.(type) { 201 | case *ast.ArrayType: 202 | children = append(children, 203 | tok(n.Lbrack, len("[")), 204 | tok(n.Elt.End(), len("]"))) 205 | 206 | case *ast.AssignStmt: 207 | children = append(children, 208 | tok(n.TokPos, len(n.Tok.String()))) 209 | 210 | case *ast.BasicLit: 211 | children = append(children, 212 | tok(n.ValuePos, len(n.Value))) 213 | 214 | case *ast.BinaryExpr: 215 | children = append(children, tok(n.OpPos, len(n.Op.String()))) 216 | 217 | case *ast.BlockStmt: 218 | children = append(children, 219 | tok(n.Lbrace, len("{")), 220 | tok(n.Rbrace, len("}"))) 221 | 222 | case *ast.BranchStmt: 223 | children = append(children, 224 | tok(n.TokPos, len(n.Tok.String()))) 225 | 226 | case *ast.CallExpr: 227 | children = append(children, 228 | tok(n.Lparen, len("(")), 229 | tok(n.Rparen, len(")"))) 230 | if n.Ellipsis != 0 { 231 | children = append(children, tok(n.Ellipsis, len("..."))) 232 | } 233 | 234 | case *ast.CaseClause: 235 | if n.List == nil { 236 | children = append(children, 237 | tok(n.Case, len("default"))) 238 | } else { 239 | children = append(children, 240 | tok(n.Case, len("case"))) 241 | } 242 | children = append(children, tok(n.Colon, len(":"))) 243 | 244 | case *ast.ChanType: 245 | switch n.Dir { 246 | case ast.RECV: 247 | children = append(children, tok(n.Begin, len("<-chan"))) 248 | case ast.SEND: 249 | children = append(children, tok(n.Begin, len("chan<-"))) 250 | case ast.RECV | ast.SEND: 251 | children = append(children, tok(n.Begin, len("chan"))) 252 | } 253 | 254 | case *ast.CommClause: 255 | if n.Comm == nil { 256 | children = append(children, 257 | tok(n.Case, len("default"))) 258 | } else { 259 | children = append(children, 260 | tok(n.Case, len("case"))) 261 | } 262 | children = append(children, tok(n.Colon, len(":"))) 263 | 264 | case *ast.Comment: 265 | // nop 266 | 267 | case *ast.CommentGroup: 268 | // nop 269 | 270 | case *ast.CompositeLit: 271 | children = append(children, 272 | tok(n.Lbrace, len("{")), 273 | tok(n.Rbrace, len("{"))) 274 | 275 | case *ast.DeclStmt: 276 | // nop 277 | 278 | case *ast.DeferStmt: 279 | children = append(children, 280 | tok(n.Defer, len("defer"))) 281 | 282 | case *ast.Ellipsis: 283 | children = append(children, 284 | tok(n.Ellipsis, len("..."))) 285 | 286 | case *ast.EmptyStmt: 287 | // nop 288 | 289 | case *ast.ExprStmt: 290 | // nop 291 | 292 | case *ast.Field: 293 | // TODO(adonovan): Field.{Doc,Comment,Tag}? 294 | 295 | case *ast.FieldList: 296 | children = append(children, 297 | tok(n.Opening, len("(")), 298 | tok(n.Closing, len(")"))) 299 | 300 | case *ast.File: 301 | // TODO test: Doc 302 | children = append(children, 303 | tok(n.Package, len("package"))) 304 | 305 | case *ast.ForStmt: 306 | children = append(children, 307 | tok(n.For, len("for"))) 308 | 309 | case *ast.FuncDecl: 310 | // TODO(adonovan): FuncDecl.Comment? 311 | 312 | // Uniquely, FuncDecl breaks the invariant that 313 | // preorder traversal yields tokens in lexical order: 314 | // in fact, FuncDecl.Recv precedes FuncDecl.Type.Func. 315 | // 316 | // As a workaround, we inline the case for FuncType 317 | // here and order things correctly. 318 | // 319 | children = nil // discard ast.Walk(FuncDecl) info subtrees 320 | children = append(children, tok(n.Type.Func, len("func"))) 321 | if n.Recv != nil { 322 | children = append(children, n.Recv) 323 | } 324 | children = append(children, n.Name) 325 | if n.Type.Params != nil { 326 | children = append(children, n.Type.Params) 327 | } 328 | if n.Type.Results != nil { 329 | children = append(children, n.Type.Results) 330 | } 331 | if n.Body != nil { 332 | children = append(children, n.Body) 333 | } 334 | 335 | case *ast.FuncLit: 336 | // nop 337 | 338 | case *ast.FuncType: 339 | if n.Func != 0 { 340 | children = append(children, 341 | tok(n.Func, len("func"))) 342 | } 343 | 344 | case *ast.GenDecl: 345 | children = append(children, 346 | tok(n.TokPos, len(n.Tok.String()))) 347 | if n.Lparen != 0 { 348 | children = append(children, 349 | tok(n.Lparen, len("(")), 350 | tok(n.Rparen, len(")"))) 351 | } 352 | 353 | case *ast.GoStmt: 354 | children = append(children, 355 | tok(n.Go, len("go"))) 356 | 357 | case *ast.Ident: 358 | children = append(children, 359 | tok(n.NamePos, len(n.Name))) 360 | 361 | case *ast.IfStmt: 362 | children = append(children, 363 | tok(n.If, len("if"))) 364 | 365 | case *ast.ImportSpec: 366 | // TODO(adonovan): ImportSpec.{Doc,EndPos}? 367 | 368 | case *ast.IncDecStmt: 369 | children = append(children, 370 | tok(n.TokPos, len(n.Tok.String()))) 371 | 372 | case *ast.IndexExpr: 373 | children = append(children, 374 | tok(n.Lbrack, len("{")), 375 | tok(n.Rbrack, len("}"))) 376 | 377 | case *ast.InterfaceType: 378 | children = append(children, 379 | tok(n.Interface, len("interface"))) 380 | 381 | case *ast.KeyValueExpr: 382 | children = append(children, 383 | tok(n.Colon, len(":"))) 384 | 385 | case *ast.LabeledStmt: 386 | children = append(children, 387 | tok(n.Colon, len(":"))) 388 | 389 | case *ast.MapType: 390 | children = append(children, 391 | tok(n.Map, len("map"))) 392 | 393 | case *ast.ParenExpr: 394 | children = append(children, 395 | tok(n.Lparen, len("(")), 396 | tok(n.Rparen, len(")"))) 397 | 398 | case *ast.RangeStmt: 399 | children = append(children, 400 | tok(n.For, len("for")), 401 | tok(n.TokPos, len(n.Tok.String()))) 402 | 403 | case *ast.ReturnStmt: 404 | children = append(children, 405 | tok(n.Return, len("return"))) 406 | 407 | case *ast.SelectStmt: 408 | children = append(children, 409 | tok(n.Select, len("select"))) 410 | 411 | case *ast.SelectorExpr: 412 | // nop 413 | 414 | case *ast.SendStmt: 415 | children = append(children, 416 | tok(n.Arrow, len("<-"))) 417 | 418 | case *ast.SliceExpr: 419 | children = append(children, 420 | tok(n.Lbrack, len("[")), 421 | tok(n.Rbrack, len("]"))) 422 | 423 | case *ast.StarExpr: 424 | children = append(children, tok(n.Star, len("*"))) 425 | 426 | case *ast.StructType: 427 | children = append(children, tok(n.Struct, len("struct"))) 428 | 429 | case *ast.SwitchStmt: 430 | children = append(children, tok(n.Switch, len("switch"))) 431 | 432 | case *ast.TypeAssertExpr: 433 | children = append(children, 434 | tok(n.Lparen-1, len(".")), 435 | tok(n.Lparen, len("(")), 436 | tok(n.Rparen, len(")"))) 437 | 438 | case *ast.TypeSpec: 439 | // TODO(adonovan): TypeSpec.{Doc,Comment}? 440 | 441 | case *ast.TypeSwitchStmt: 442 | children = append(children, tok(n.Switch, len("switch"))) 443 | 444 | case *ast.UnaryExpr: 445 | children = append(children, tok(n.OpPos, len(n.Op.String()))) 446 | 447 | case *ast.ValueSpec: 448 | // TODO(adonovan): ValueSpec.{Doc,Comment}? 449 | 450 | case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt: 451 | // nop 452 | } 453 | 454 | // TODO(adonovan): opt: merge the logic of ast.Inspect() into 455 | // the switch above so we can make interleaved callbacks for 456 | // both Nodes and Tokens in the right order and avoid the need 457 | // to sort. 458 | sort.Sort(byPos(children)) 459 | 460 | return children 461 | } 462 | 463 | type byPos []ast.Node 464 | 465 | func (sl byPos) Len() int { 466 | return len(sl) 467 | } 468 | func (sl byPos) Less(i, j int) bool { 469 | return sl[i].Pos() < sl[j].Pos() 470 | } 471 | func (sl byPos) Swap(i, j int) { 472 | sl[i], sl[j] = sl[j], sl[i] 473 | } 474 | 475 | // NodeDescription returns a description of the concrete type of n suitable 476 | // for a user interface. 477 | // 478 | // TODO(adonovan): in some cases (e.g. Field, FieldList, Ident, 479 | // StarExpr) we could be much more specific given the path to the AST 480 | // root. Perhaps we should do that. 481 | // 482 | func NodeDescription(n ast.Node) string { 483 | switch n := n.(type) { 484 | case *ast.ArrayType: 485 | return "array type" 486 | case *ast.AssignStmt: 487 | return "assignment" 488 | case *ast.BadDecl: 489 | return "bad declaration" 490 | case *ast.BadExpr: 491 | return "bad expression" 492 | case *ast.BadStmt: 493 | return "bad statement" 494 | case *ast.BasicLit: 495 | return "basic literal" 496 | case *ast.BinaryExpr: 497 | return fmt.Sprintf("binary %s operation", n.Op) 498 | case *ast.BlockStmt: 499 | return "block" 500 | case *ast.BranchStmt: 501 | switch n.Tok { 502 | case token.BREAK: 503 | return "break statement" 504 | case token.CONTINUE: 505 | return "continue statement" 506 | case token.GOTO: 507 | return "goto statement" 508 | case token.FALLTHROUGH: 509 | return "fall-through statement" 510 | } 511 | case *ast.CallExpr: 512 | if len(n.Args) == 1 && !n.Ellipsis.IsValid() { 513 | return "function call (or conversion)" 514 | } 515 | return "function call" 516 | case *ast.CaseClause: 517 | return "case clause" 518 | case *ast.ChanType: 519 | return "channel type" 520 | case *ast.CommClause: 521 | return "communication clause" 522 | case *ast.Comment: 523 | return "comment" 524 | case *ast.CommentGroup: 525 | return "comment group" 526 | case *ast.CompositeLit: 527 | return "composite literal" 528 | case *ast.DeclStmt: 529 | return NodeDescription(n.Decl) + " statement" 530 | case *ast.DeferStmt: 531 | return "defer statement" 532 | case *ast.Ellipsis: 533 | return "ellipsis" 534 | case *ast.EmptyStmt: 535 | return "empty statement" 536 | case *ast.ExprStmt: 537 | return "expression statement" 538 | case *ast.Field: 539 | // Can be any of these: 540 | // struct {x, y int} -- struct field(s) 541 | // struct {T} -- anon struct field 542 | // interface {I} -- interface embedding 543 | // interface {f()} -- interface method 544 | // func (A) func(B) C -- receiver, param(s), result(s) 545 | return "field/method/parameter" 546 | case *ast.FieldList: 547 | return "field/method/parameter list" 548 | case *ast.File: 549 | return "source file" 550 | case *ast.ForStmt: 551 | return "for loop" 552 | case *ast.FuncDecl: 553 | return "function declaration" 554 | case *ast.FuncLit: 555 | return "function literal" 556 | case *ast.FuncType: 557 | return "function type" 558 | case *ast.GenDecl: 559 | switch n.Tok { 560 | case token.IMPORT: 561 | return "import declaration" 562 | case token.CONST: 563 | return "constant declaration" 564 | case token.TYPE: 565 | return "type declaration" 566 | case token.VAR: 567 | return "variable declaration" 568 | } 569 | case *ast.GoStmt: 570 | return "go statement" 571 | case *ast.Ident: 572 | return "identifier" 573 | case *ast.IfStmt: 574 | return "if statement" 575 | case *ast.ImportSpec: 576 | return "import specification" 577 | case *ast.IncDecStmt: 578 | if n.Tok == token.INC { 579 | return "increment statement" 580 | } 581 | return "decrement statement" 582 | case *ast.IndexExpr: 583 | return "index expression" 584 | case *ast.InterfaceType: 585 | return "interface type" 586 | case *ast.KeyValueExpr: 587 | return "key/value association" 588 | case *ast.LabeledStmt: 589 | return "statement label" 590 | case *ast.MapType: 591 | return "map type" 592 | case *ast.Package: 593 | return "package" 594 | case *ast.ParenExpr: 595 | return "parenthesized " + NodeDescription(n.X) 596 | case *ast.RangeStmt: 597 | return "range loop" 598 | case *ast.ReturnStmt: 599 | return "return statement" 600 | case *ast.SelectStmt: 601 | return "select statement" 602 | case *ast.SelectorExpr: 603 | return "selector" 604 | case *ast.SendStmt: 605 | return "channel send" 606 | case *ast.SliceExpr: 607 | return "slice expression" 608 | case *ast.StarExpr: 609 | return "*-operation" // load/store expr or pointer type 610 | case *ast.StructType: 611 | return "struct type" 612 | case *ast.SwitchStmt: 613 | return "switch statement" 614 | case *ast.TypeAssertExpr: 615 | return "type assertion" 616 | case *ast.TypeSpec: 617 | return "type specification" 618 | case *ast.TypeSwitchStmt: 619 | return "type switch" 620 | case *ast.UnaryExpr: 621 | return fmt.Sprintf("unary %s operation", n.Op) 622 | case *ast.ValueSpec: 623 | return "value specification" 624 | 625 | } 626 | panic(fmt.Sprintf("unexpected node type: %T", n)) 627 | } 628 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/imports/fix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package imports 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "fmt" 11 | "go/ast" 12 | "go/build" 13 | "go/parser" 14 | "go/token" 15 | "io/ioutil" 16 | "log" 17 | "os" 18 | "path" 19 | "path/filepath" 20 | "sort" 21 | "strings" 22 | "sync" 23 | 24 | "golang.org/x/tools/go/ast/astutil" 25 | ) 26 | 27 | // Debug controls verbose logging. 28 | var Debug = false 29 | 30 | var ( 31 | inTests = false // set true by fix_test.go; if false, no need to use testMu 32 | testMu sync.RWMutex // guards globals reset by tests; used only if inTests 33 | ) 34 | 35 | // If set, LocalPrefix instructs Process to sort import paths with the given 36 | // prefix into another group after 3rd-party packages. 37 | var LocalPrefix string 38 | 39 | // importToGroup is a list of functions which map from an import path to 40 | // a group number. 41 | var importToGroup = []func(importPath string) (num int, ok bool){ 42 | func(importPath string) (num int, ok bool) { 43 | if LocalPrefix != "" && strings.HasPrefix(importPath, LocalPrefix) { 44 | return 3, true 45 | } 46 | return 47 | }, 48 | func(importPath string) (num int, ok bool) { 49 | if strings.HasPrefix(importPath, "appengine") { 50 | return 2, true 51 | } 52 | return 53 | }, 54 | func(importPath string) (num int, ok bool) { 55 | if strings.Contains(importPath, ".") { 56 | return 1, true 57 | } 58 | return 59 | }, 60 | } 61 | 62 | func importGroup(importPath string) int { 63 | for _, fn := range importToGroup { 64 | if n, ok := fn(importPath); ok { 65 | return n 66 | } 67 | } 68 | return 0 69 | } 70 | 71 | // packageInfo is a summary of features found in a package. 72 | type packageInfo struct { 73 | Globals map[string]bool // symbol => true 74 | } 75 | 76 | // dirPackageInfo gets information from other files in the package. 77 | func dirPackageInfo(srcDir, filename string) (*packageInfo, error) { 78 | considerTests := strings.HasSuffix(filename, "_test.go") 79 | 80 | // Handle file from stdin 81 | if _, err := os.Stat(filename); err != nil { 82 | if os.IsNotExist(err) { 83 | return &packageInfo{}, nil 84 | } 85 | return nil, err 86 | } 87 | 88 | fileBase := filepath.Base(filename) 89 | packageFileInfos, err := ioutil.ReadDir(srcDir) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | info := &packageInfo{Globals: make(map[string]bool)} 95 | for _, fi := range packageFileInfos { 96 | if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { 97 | continue 98 | } 99 | if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { 100 | continue 101 | } 102 | 103 | fileSet := token.NewFileSet() 104 | root, err := parser.ParseFile(fileSet, filepath.Join(srcDir, fi.Name()), nil, 0) 105 | if err != nil { 106 | continue 107 | } 108 | 109 | for _, decl := range root.Decls { 110 | genDecl, ok := decl.(*ast.GenDecl) 111 | if !ok { 112 | continue 113 | } 114 | 115 | for _, spec := range genDecl.Specs { 116 | valueSpec, ok := spec.(*ast.ValueSpec) 117 | if !ok { 118 | continue 119 | } 120 | info.Globals[valueSpec.Names[0].Name] = true 121 | } 122 | } 123 | } 124 | return info, nil 125 | } 126 | 127 | func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) { 128 | // refs are a set of possible package references currently unsatisfied by imports. 129 | // first key: either base package (e.g. "fmt") or renamed package 130 | // second key: referenced package symbol (e.g. "Println") 131 | refs := make(map[string]map[string]bool) 132 | 133 | // decls are the current package imports. key is base package or renamed package. 134 | decls := make(map[string]*ast.ImportSpec) 135 | 136 | abs, err := filepath.Abs(filename) 137 | if err != nil { 138 | return nil, err 139 | } 140 | srcDir := filepath.Dir(abs) 141 | if Debug { 142 | log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) 143 | } 144 | 145 | var packageInfo *packageInfo 146 | var loadedPackageInfo bool 147 | 148 | // collect potential uses of packages. 149 | var visitor visitFn 150 | visitor = visitFn(func(node ast.Node) ast.Visitor { 151 | if node == nil { 152 | return visitor 153 | } 154 | switch v := node.(type) { 155 | case *ast.ImportSpec: 156 | if v.Name != nil { 157 | decls[v.Name.Name] = v 158 | break 159 | } 160 | ipath := strings.Trim(v.Path.Value, `"`) 161 | if ipath == "C" { 162 | break 163 | } 164 | local := importPathToName(ipath, srcDir) 165 | decls[local] = v 166 | case *ast.SelectorExpr: 167 | xident, ok := v.X.(*ast.Ident) 168 | if !ok { 169 | break 170 | } 171 | if xident.Obj != nil { 172 | // if the parser can resolve it, it's not a package ref 173 | break 174 | } 175 | pkgName := xident.Name 176 | if refs[pkgName] == nil { 177 | refs[pkgName] = make(map[string]bool) 178 | } 179 | if !loadedPackageInfo { 180 | loadedPackageInfo = true 181 | packageInfo, _ = dirPackageInfo(srcDir, filename) 182 | } 183 | if decls[pkgName] == nil && (packageInfo == nil || !packageInfo.Globals[pkgName]) { 184 | refs[pkgName][v.Sel.Name] = true 185 | } 186 | } 187 | return visitor 188 | }) 189 | ast.Walk(visitor, f) 190 | 191 | // Nil out any unused ImportSpecs, to be removed in following passes 192 | unusedImport := map[string]string{} 193 | for pkg, is := range decls { 194 | if refs[pkg] == nil && pkg != "_" && pkg != "." { 195 | name := "" 196 | if is.Name != nil { 197 | name = is.Name.Name 198 | } 199 | unusedImport[strings.Trim(is.Path.Value, `"`)] = name 200 | } 201 | } 202 | for ipath, name := range unusedImport { 203 | if ipath == "C" { 204 | // Don't remove cgo stuff. 205 | continue 206 | } 207 | astutil.DeleteNamedImport(fset, f, name, ipath) 208 | } 209 | 210 | for pkgName, symbols := range refs { 211 | if len(symbols) == 0 { 212 | // skip over packages already imported 213 | delete(refs, pkgName) 214 | } 215 | } 216 | 217 | // Search for imports matching potential package references. 218 | searches := 0 219 | type result struct { 220 | ipath string // import path (if err == nil) 221 | name string // optional name to rename import as 222 | err error 223 | } 224 | results := make(chan result) 225 | for pkgName, symbols := range refs { 226 | go func(pkgName string, symbols map[string]bool) { 227 | ipath, rename, err := findImport(pkgName, symbols, filename) 228 | r := result{ipath: ipath, err: err} 229 | if rename { 230 | r.name = pkgName 231 | } 232 | results <- r 233 | }(pkgName, symbols) 234 | searches++ 235 | } 236 | for i := 0; i < searches; i++ { 237 | result := <-results 238 | if result.err != nil { 239 | return nil, result.err 240 | } 241 | if result.ipath != "" { 242 | if result.name != "" { 243 | astutil.AddNamedImport(fset, f, result.name, result.ipath) 244 | } else { 245 | astutil.AddImport(fset, f, result.ipath) 246 | } 247 | added = append(added, result.ipath) 248 | } 249 | } 250 | 251 | return added, nil 252 | } 253 | 254 | // importPathToName returns the package name for the given import path. 255 | var importPathToName func(importPath, srcDir string) (packageName string) = importPathToNameGoPath 256 | 257 | // importPathToNameBasic assumes the package name is the base of import path. 258 | func importPathToNameBasic(importPath, srcDir string) (packageName string) { 259 | return path.Base(importPath) 260 | } 261 | 262 | // importPathToNameGoPath finds out the actual package name, as declared in its .go files. 263 | // If there's a problem, it falls back to using importPathToNameBasic. 264 | func importPathToNameGoPath(importPath, srcDir string) (packageName string) { 265 | // Fast path for standard library without going to disk. 266 | if pkg, ok := stdImportPackage[importPath]; ok { 267 | return pkg 268 | } 269 | 270 | pkgName, err := importPathToNameGoPathParse(importPath, srcDir) 271 | if Debug { 272 | log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err) 273 | } 274 | if err == nil { 275 | return pkgName 276 | } 277 | return importPathToNameBasic(importPath, srcDir) 278 | } 279 | 280 | // importPathToNameGoPathParse is a faster version of build.Import if 281 | // the only thing desired is the package name. It uses build.FindOnly 282 | // to find the directory and then only parses one file in the package, 283 | // trusting that the files in the directory are consistent. 284 | func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) { 285 | buildPkg, err := build.Import(importPath, srcDir, build.FindOnly) 286 | if err != nil { 287 | return "", err 288 | } 289 | d, err := os.Open(buildPkg.Dir) 290 | if err != nil { 291 | return "", err 292 | } 293 | names, err := d.Readdirnames(-1) 294 | d.Close() 295 | if err != nil { 296 | return "", err 297 | } 298 | sort.Strings(names) // to have predictable behavior 299 | var lastErr error 300 | var nfile int 301 | for _, name := range names { 302 | if !strings.HasSuffix(name, ".go") { 303 | continue 304 | } 305 | if strings.HasSuffix(name, "_test.go") { 306 | continue 307 | } 308 | nfile++ 309 | fullFile := filepath.Join(buildPkg.Dir, name) 310 | 311 | fset := token.NewFileSet() 312 | f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) 313 | if err != nil { 314 | lastErr = err 315 | continue 316 | } 317 | pkgName := f.Name.Name 318 | if pkgName == "documentation" { 319 | // Special case from go/build.ImportDir, not 320 | // handled by ctx.MatchFile. 321 | continue 322 | } 323 | if pkgName == "main" { 324 | // Also skip package main, assuming it's a +build ignore generator or example. 325 | // Since you can't import a package main anyway, there's no harm here. 326 | continue 327 | } 328 | return pkgName, nil 329 | } 330 | if lastErr != nil { 331 | return "", lastErr 332 | } 333 | return "", fmt.Errorf("no importable package found in %d Go files", nfile) 334 | } 335 | 336 | var stdImportPackage = map[string]string{} // "net/http" => "http" 337 | 338 | func init() { 339 | // Nothing in the standard library has a package name not 340 | // matching its import base name. 341 | for _, pkg := range stdlib { 342 | if _, ok := stdImportPackage[pkg]; !ok { 343 | stdImportPackage[pkg] = path.Base(pkg) 344 | } 345 | } 346 | } 347 | 348 | // Directory-scanning state. 349 | var ( 350 | // scanGoRootOnce guards calling scanGoRoot (for $GOROOT) 351 | scanGoRootOnce sync.Once 352 | // scanGoPathOnce guards calling scanGoPath (for $GOPATH) 353 | scanGoPathOnce sync.Once 354 | 355 | // populateIgnoreOnce guards calling populateIgnore 356 | populateIgnoreOnce sync.Once 357 | ignoredDirs []os.FileInfo 358 | 359 | dirScanMu sync.RWMutex 360 | dirScan map[string]*pkg // abs dir path => *pkg 361 | ) 362 | 363 | type pkg struct { 364 | dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") 365 | importPath string // full pkg import path ("net/http", "foo/bar/vendor/a/b") 366 | importPathShort string // vendorless import path ("net/http", "a/b") 367 | } 368 | 369 | // byImportPathShortLength sorts by the short import path length, breaking ties on the 370 | // import string itself. 371 | type byImportPathShortLength []*pkg 372 | 373 | func (s byImportPathShortLength) Len() int { return len(s) } 374 | func (s byImportPathShortLength) Less(i, j int) bool { 375 | vi, vj := s[i].importPathShort, s[j].importPathShort 376 | return len(vi) < len(vj) || (len(vi) == len(vj) && vi < vj) 377 | 378 | } 379 | func (s byImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 380 | 381 | // gate is a semaphore for limiting concurrency. 382 | type gate chan struct{} 383 | 384 | func (g gate) enter() { g <- struct{}{} } 385 | func (g gate) leave() { <-g } 386 | 387 | var visitedSymlinks struct { 388 | sync.Mutex 389 | m map[string]struct{} 390 | } 391 | 392 | // guarded by populateIgnoreOnce; populates ignoredDirs. 393 | func populateIgnore() { 394 | for _, srcDir := range build.Default.SrcDirs() { 395 | if srcDir == filepath.Join(build.Default.GOROOT, "src") { 396 | continue 397 | } 398 | populateIgnoredDirs(srcDir) 399 | } 400 | } 401 | 402 | // populateIgnoredDirs reads an optional config file at /.goimportsignore 403 | // of relative directories to ignore when scanning for go files. 404 | // The provided path is one of the $GOPATH entries with "src" appended. 405 | func populateIgnoredDirs(path string) { 406 | file := filepath.Join(path, ".goimportsignore") 407 | slurp, err := ioutil.ReadFile(file) 408 | if Debug { 409 | if err != nil { 410 | log.Print(err) 411 | } else { 412 | log.Printf("Read %s", file) 413 | } 414 | } 415 | if err != nil { 416 | return 417 | } 418 | bs := bufio.NewScanner(bytes.NewReader(slurp)) 419 | for bs.Scan() { 420 | line := strings.TrimSpace(bs.Text()) 421 | if line == "" || strings.HasPrefix(line, "#") { 422 | continue 423 | } 424 | full := filepath.Join(path, line) 425 | if fi, err := os.Stat(full); err == nil { 426 | ignoredDirs = append(ignoredDirs, fi) 427 | if Debug { 428 | log.Printf("Directory added to ignore list: %s", full) 429 | } 430 | } else if Debug { 431 | log.Printf("Error statting entry in .goimportsignore: %v", err) 432 | } 433 | } 434 | } 435 | 436 | func skipDir(fi os.FileInfo) bool { 437 | for _, ignoredDir := range ignoredDirs { 438 | if os.SameFile(fi, ignoredDir) { 439 | return true 440 | } 441 | } 442 | return false 443 | } 444 | 445 | // shouldTraverse reports whether the symlink fi should, found in dir, 446 | // should be followed. It makes sure symlinks were never visited 447 | // before to avoid symlink loops. 448 | func shouldTraverse(dir string, fi os.FileInfo) bool { 449 | path := filepath.Join(dir, fi.Name()) 450 | target, err := filepath.EvalSymlinks(path) 451 | if err != nil { 452 | if !os.IsNotExist(err) { 453 | fmt.Fprintln(os.Stderr, err) 454 | } 455 | return false 456 | } 457 | ts, err := os.Stat(target) 458 | if err != nil { 459 | fmt.Fprintln(os.Stderr, err) 460 | return false 461 | } 462 | if !ts.IsDir() { 463 | return false 464 | } 465 | 466 | realParent, err := filepath.EvalSymlinks(dir) 467 | if err != nil { 468 | fmt.Fprint(os.Stderr, err) 469 | return false 470 | } 471 | realPath := filepath.Join(realParent, fi.Name()) 472 | visitedSymlinks.Lock() 473 | defer visitedSymlinks.Unlock() 474 | if visitedSymlinks.m == nil { 475 | visitedSymlinks.m = make(map[string]struct{}) 476 | } 477 | if _, ok := visitedSymlinks.m[realPath]; ok { 478 | return false 479 | } 480 | visitedSymlinks.m[realPath] = struct{}{} 481 | return true 482 | } 483 | 484 | var testHookScanDir = func(dir string) {} 485 | 486 | var scanGoRootDone = make(chan struct{}) // closed when scanGoRoot is done 487 | 488 | func scanGoRoot() { 489 | go func() { 490 | scanGoDirs(true) 491 | close(scanGoRootDone) 492 | }() 493 | } 494 | 495 | func scanGoPath() { scanGoDirs(false) } 496 | 497 | func scanGoDirs(goRoot bool) { 498 | if Debug { 499 | which := "$GOROOT" 500 | if !goRoot { 501 | which = "$GOPATH" 502 | } 503 | log.Printf("scanning " + which) 504 | defer log.Printf("scanned " + which) 505 | } 506 | dirScanMu.Lock() 507 | if dirScan == nil { 508 | dirScan = make(map[string]*pkg) 509 | } 510 | dirScanMu.Unlock() 511 | 512 | for _, srcDir := range build.Default.SrcDirs() { 513 | isGoroot := srcDir == filepath.Join(build.Default.GOROOT, "src") 514 | if isGoroot != goRoot { 515 | continue 516 | } 517 | testHookScanDir(srcDir) 518 | walkFn := func(path string, typ os.FileMode) error { 519 | dir := filepath.Dir(path) 520 | if typ.IsRegular() { 521 | if dir == srcDir { 522 | // Doesn't make sense to have regular files 523 | // directly in your $GOPATH/src or $GOROOT/src. 524 | return nil 525 | } 526 | if !strings.HasSuffix(path, ".go") { 527 | return nil 528 | } 529 | dirScanMu.Lock() 530 | if _, dup := dirScan[dir]; !dup { 531 | importpath := filepath.ToSlash(dir[len(srcDir)+len("/"):]) 532 | dirScan[dir] = &pkg{ 533 | importPath: importpath, 534 | importPathShort: vendorlessImportPath(importpath), 535 | dir: dir, 536 | } 537 | } 538 | dirScanMu.Unlock() 539 | return nil 540 | } 541 | if typ == os.ModeDir { 542 | base := filepath.Base(path) 543 | if base == "" || base[0] == '.' || base[0] == '_' || 544 | base == "testdata" || base == "node_modules" { 545 | return filepath.SkipDir 546 | } 547 | fi, err := os.Lstat(path) 548 | if err == nil && skipDir(fi) { 549 | if Debug { 550 | log.Printf("skipping directory %q under %s", fi.Name(), dir) 551 | } 552 | return filepath.SkipDir 553 | } 554 | return nil 555 | } 556 | if typ == os.ModeSymlink { 557 | base := filepath.Base(path) 558 | if strings.HasPrefix(base, ".#") { 559 | // Emacs noise. 560 | return nil 561 | } 562 | fi, err := os.Lstat(path) 563 | if err != nil { 564 | // Just ignore it. 565 | return nil 566 | } 567 | if shouldTraverse(dir, fi) { 568 | return traverseLink 569 | } 570 | } 571 | return nil 572 | } 573 | if err := fastWalk(srcDir, walkFn); err != nil { 574 | log.Printf("goimports: scanning directory %v: %v", srcDir, err) 575 | } 576 | } 577 | } 578 | 579 | // vendorlessImportPath returns the devendorized version of the provided import path. 580 | // e.g. "foo/bar/vendor/a/b" => "a/b" 581 | func vendorlessImportPath(ipath string) string { 582 | // Devendorize for use in import statement. 583 | if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { 584 | return ipath[i+len("/vendor/"):] 585 | } 586 | if strings.HasPrefix(ipath, "vendor/") { 587 | return ipath[len("vendor/"):] 588 | } 589 | return ipath 590 | } 591 | 592 | // loadExports returns the set of exported symbols in the package at dir. 593 | // It returns nil on error or if the package name in dir does not match expectPackage. 594 | var loadExports func(expectPackage, dir string) map[string]bool = loadExportsGoPath 595 | 596 | func loadExportsGoPath(expectPackage, dir string) map[string]bool { 597 | if Debug { 598 | log.Printf("loading exports in dir %s (seeking package %s)", dir, expectPackage) 599 | } 600 | exports := make(map[string]bool) 601 | 602 | ctx := build.Default 603 | 604 | // ReadDir is like ioutil.ReadDir, but only returns *.go files 605 | // and filters out _test.go files since they're not relevant 606 | // and only slow things down. 607 | ctx.ReadDir = func(dir string) (notTests []os.FileInfo, err error) { 608 | all, err := ioutil.ReadDir(dir) 609 | if err != nil { 610 | return nil, err 611 | } 612 | notTests = all[:0] 613 | for _, fi := range all { 614 | name := fi.Name() 615 | if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { 616 | notTests = append(notTests, fi) 617 | } 618 | } 619 | return notTests, nil 620 | } 621 | 622 | files, err := ctx.ReadDir(dir) 623 | if err != nil { 624 | log.Print(err) 625 | return nil 626 | } 627 | 628 | fset := token.NewFileSet() 629 | 630 | for _, fi := range files { 631 | match, err := ctx.MatchFile(dir, fi.Name()) 632 | if err != nil || !match { 633 | continue 634 | } 635 | fullFile := filepath.Join(dir, fi.Name()) 636 | f, err := parser.ParseFile(fset, fullFile, nil, 0) 637 | if err != nil { 638 | if Debug { 639 | log.Printf("Parsing %s: %v", fullFile, err) 640 | } 641 | return nil 642 | } 643 | pkgName := f.Name.Name 644 | if pkgName == "documentation" { 645 | // Special case from go/build.ImportDir, not 646 | // handled by ctx.MatchFile. 647 | continue 648 | } 649 | if pkgName != expectPackage { 650 | if Debug { 651 | log.Printf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName) 652 | } 653 | return nil 654 | } 655 | for name := range f.Scope.Objects { 656 | if ast.IsExported(name) { 657 | exports[name] = true 658 | } 659 | } 660 | } 661 | 662 | if Debug { 663 | exportList := make([]string, 0, len(exports)) 664 | for k := range exports { 665 | exportList = append(exportList, k) 666 | } 667 | sort.Strings(exportList) 668 | log.Printf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", ")) 669 | } 670 | return exports 671 | } 672 | 673 | // findImport searches for a package with the given symbols. 674 | // If no package is found, findImport returns ("", false, nil) 675 | // 676 | // This is declared as a variable rather than a function so goimports 677 | // can be easily extended by adding a file with an init function. 678 | // 679 | // The rename value tells goimports whether to use the package name as 680 | // a local qualifier in an import. For example, if findImports("pkg", 681 | // "X") returns ("foo/bar", rename=true), then goimports adds the 682 | // import line: 683 | // import pkg "foo/bar" 684 | // to satisfy uses of pkg.X in the file. 685 | var findImport func(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) = findImportGoPath 686 | 687 | // findImportGoPath is the normal implementation of findImport. 688 | // (Some companies have their own internally.) 689 | func findImportGoPath(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) { 690 | if inTests { 691 | testMu.RLock() 692 | defer testMu.RUnlock() 693 | } 694 | 695 | // Fast path for the standard library. 696 | // In the common case we hopefully never have to scan the GOPATH, which can 697 | // be slow with moving disks. 698 | if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok { 699 | return pkg, rename, nil 700 | } 701 | if pkgName == "rand" && symbols["Read"] { 702 | // Special-case rand.Read. 703 | // 704 | // If findImportStdlib didn't find it above, don't go 705 | // searching for it, lest it find and pick math/rand 706 | // in GOROOT (new as of Go 1.6) 707 | // 708 | // crypto/rand is the safer choice. 709 | return "", false, nil 710 | } 711 | 712 | // TODO(sameer): look at the import lines for other Go files in the 713 | // local directory, since the user is likely to import the same packages 714 | // in the current Go file. Return rename=true when the other Go files 715 | // use a renamed package that's also used in the current file. 716 | 717 | // Read all the $GOPATH/src/.goimportsignore files before scanning directories. 718 | populateIgnoreOnce.Do(populateIgnore) 719 | 720 | // Start scanning the $GOROOT asynchronously, then run the 721 | // GOPATH scan synchronously if needed, and then wait for the 722 | // $GOROOT to finish. 723 | // 724 | // TODO(bradfitz): run each $GOPATH entry async. But nobody 725 | // really has more than one anyway, so low priority. 726 | scanGoRootOnce.Do(scanGoRoot) // async 727 | if !fileInDir(filename, build.Default.GOROOT) { 728 | scanGoPathOnce.Do(scanGoPath) // blocking 729 | } 730 | <-scanGoRootDone 731 | 732 | // Find candidate packages, looking only at their directory names first. 733 | var candidates []*pkg 734 | for _, pkg := range dirScan { 735 | if pkgIsCandidate(filename, pkgName, pkg) { 736 | candidates = append(candidates, pkg) 737 | } 738 | } 739 | 740 | // Sort the candidates by their import package length, 741 | // assuming that shorter package names are better than long 742 | // ones. Note that this sorts by the de-vendored name, so 743 | // there's no "penalty" for vendoring. 744 | sort.Sort(byImportPathShortLength(candidates)) 745 | if Debug { 746 | for i, pkg := range candidates { 747 | log.Printf("%s candidate %d/%d: %v", pkgName, i+1, len(candidates), pkg.importPathShort) 748 | } 749 | } 750 | 751 | // Collect exports for packages with matching names. 752 | 753 | done := make(chan struct{}) // closed when we find the answer 754 | defer close(done) 755 | 756 | rescv := make([]chan *pkg, len(candidates)) 757 | for i := range candidates { 758 | rescv[i] = make(chan *pkg) 759 | } 760 | const maxConcurrentPackageImport = 4 761 | loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) 762 | 763 | go func() { 764 | for i, pkg := range candidates { 765 | select { 766 | case loadExportsSem <- struct{}{}: 767 | select { 768 | case <-done: 769 | default: 770 | } 771 | case <-done: 772 | return 773 | } 774 | pkg := pkg 775 | resc := rescv[i] 776 | go func() { 777 | if inTests { 778 | testMu.RLock() 779 | defer testMu.RUnlock() 780 | } 781 | defer func() { <-loadExportsSem }() 782 | exports := loadExports(pkgName, pkg.dir) 783 | 784 | // If it doesn't have the right 785 | // symbols, send nil to mean no match. 786 | for symbol := range symbols { 787 | if !exports[symbol] { 788 | pkg = nil 789 | break 790 | } 791 | } 792 | select { 793 | case resc <- pkg: 794 | case <-done: 795 | } 796 | }() 797 | } 798 | }() 799 | for _, resc := range rescv { 800 | pkg := <-resc 801 | if pkg == nil { 802 | continue 803 | } 804 | // If the package name in the source doesn't match the import path's base, 805 | // return true so the rewriter adds a name (import foo "github.com/bar/go-foo") 806 | needsRename := path.Base(pkg.importPath) != pkgName 807 | return pkg.importPathShort, needsRename, nil 808 | } 809 | return "", false, nil 810 | } 811 | 812 | // pkgIsCandidate reports whether pkg is a candidate for satisfying the 813 | // finding which package pkgIdent in the file named by filename is trying 814 | // to refer to. 815 | // 816 | // This check is purely lexical and is meant to be as fast as possible 817 | // because it's run over all $GOPATH directories to filter out poor 818 | // candidates in order to limit the CPU and I/O later parsing the 819 | // exports in candidate packages. 820 | // 821 | // filename is the file being formatted. 822 | // pkgIdent is the package being searched for, like "client" (if 823 | // searching for "client.New") 824 | func pkgIsCandidate(filename, pkgIdent string, pkg *pkg) bool { 825 | // Check "internal" and "vendor" visibility: 826 | if !canUse(filename, pkg.dir) { 827 | return false 828 | } 829 | 830 | // Speed optimization to minimize disk I/O: 831 | // the last two components on disk must contain the 832 | // package name somewhere. 833 | // 834 | // This permits mismatch naming like directory 835 | // "go-foo" being package "foo", or "pkg.v3" being "pkg", 836 | // or directory "google.golang.org/api/cloudbilling/v1" 837 | // being package "cloudbilling", but doesn't 838 | // permit a directory "foo" to be package 839 | // "bar", which is strongly discouraged 840 | // anyway. There's no reason goimports needs 841 | // to be slow just to accomodate that. 842 | lastTwo := lastTwoComponents(pkg.importPathShort) 843 | if strings.Contains(lastTwo, pkgIdent) { 844 | return true 845 | } 846 | if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { 847 | lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) 848 | if strings.Contains(lastTwo, pkgIdent) { 849 | return true 850 | } 851 | } 852 | 853 | return false 854 | } 855 | 856 | func hasHyphenOrUpperASCII(s string) bool { 857 | for i := 0; i < len(s); i++ { 858 | b := s[i] 859 | if b == '-' || ('A' <= b && b <= 'Z') { 860 | return true 861 | } 862 | } 863 | return false 864 | } 865 | 866 | func lowerASCIIAndRemoveHyphen(s string) (ret string) { 867 | buf := make([]byte, 0, len(s)) 868 | for i := 0; i < len(s); i++ { 869 | b := s[i] 870 | switch { 871 | case b == '-': 872 | continue 873 | case 'A' <= b && b <= 'Z': 874 | buf = append(buf, b+('a'-'A')) 875 | default: 876 | buf = append(buf, b) 877 | } 878 | } 879 | return string(buf) 880 | } 881 | 882 | // canUse reports whether the package in dir is usable from filename, 883 | // respecting the Go "internal" and "vendor" visibility rules. 884 | func canUse(filename, dir string) bool { 885 | // Fast path check, before any allocations. If it doesn't contain vendor 886 | // or internal, it's not tricky: 887 | // Note that this can false-negative on directories like "notinternal", 888 | // but we check it correctly below. This is just a fast path. 889 | if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { 890 | return true 891 | } 892 | 893 | dirSlash := filepath.ToSlash(dir) 894 | if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { 895 | return true 896 | } 897 | // Vendor or internal directory only visible from children of parent. 898 | // That means the path from the current directory to the target directory 899 | // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal 900 | // or bar/vendor or bar/internal. 901 | // After stripping all the leading ../, the only okay place to see vendor or internal 902 | // is at the very beginning of the path. 903 | absfile, err := filepath.Abs(filename) 904 | if err != nil { 905 | return false 906 | } 907 | absdir, err := filepath.Abs(dir) 908 | if err != nil { 909 | return false 910 | } 911 | rel, err := filepath.Rel(absfile, absdir) 912 | if err != nil { 913 | return false 914 | } 915 | relSlash := filepath.ToSlash(rel) 916 | if i := strings.LastIndex(relSlash, "../"); i >= 0 { 917 | relSlash = relSlash[i+len("../"):] 918 | } 919 | return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") 920 | } 921 | 922 | // lastTwoComponents returns at most the last two path components 923 | // of v, using either / or \ as the path separator. 924 | func lastTwoComponents(v string) string { 925 | nslash := 0 926 | for i := len(v) - 1; i >= 0; i-- { 927 | if v[i] == '/' || v[i] == '\\' { 928 | nslash++ 929 | if nslash == 2 { 930 | return v[i:] 931 | } 932 | } 933 | } 934 | return v 935 | } 936 | 937 | type visitFn func(node ast.Node) ast.Visitor 938 | 939 | func (fn visitFn) Visit(node ast.Node) ast.Visitor { 940 | return fn(node) 941 | } 942 | 943 | func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) { 944 | for symbol := range symbols { 945 | key := shortPkg + "." + symbol 946 | path := stdlib[key] 947 | if path == "" { 948 | if key == "rand.Read" { 949 | continue 950 | } 951 | return "", false, false 952 | } 953 | if importPath != "" && importPath != path { 954 | // Ambiguous. Symbols pointed to different things. 955 | return "", false, false 956 | } 957 | importPath = path 958 | } 959 | if importPath == "" && shortPkg == "rand" && symbols["Read"] { 960 | return "crypto/rand", false, true 961 | } 962 | return importPath, false, importPath != "" 963 | } 964 | 965 | // fileInDir reports whether the provided file path looks like 966 | // it's in dir. (without hitting the filesystem) 967 | func fileInDir(file, dir string) bool { 968 | rest := strings.TrimPrefix(file, dir) 969 | if len(rest) == len(file) { 970 | // dir is not a prefix of file. 971 | return false 972 | } 973 | // Check for boundary: either nothing (file == dir), or a slash. 974 | return len(rest) == 0 || rest[0] == '/' || rest[0] == '\\' 975 | } 976 | --------------------------------------------------------------------------------