├── vendor ├── github.com │ └── mattn │ │ └── go-zglob │ │ ├── .travis.yml │ │ ├── fastwalk │ │ ├── fastwalk_dirent_ino.go │ │ ├── fastwalk_dirent_fileno.go │ │ ├── fastwalk_portable.go │ │ ├── fastwalk_unix.go │ │ └── fastwalk.go │ │ ├── README.md │ │ ├── LICENSE │ │ └── zglob.go └── modules.txt ├── go.mod ├── go.sum ├── README.md └── main.go /vendor/github.com/mattn/go-zglob/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - tip 4 | sudo: false 5 | script: 6 | - go test 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/goware/modvendor 2 | 3 | require github.com/mattn/go-zglob v0.0.2-0.20191112051448-a8912a37f9e7 4 | 5 | go 1.14 6 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/mattn/go-zglob v0.0.2-0.20191112051448-a8912a37f9e7 2 | ## explicit 3 | github.com/mattn/go-zglob 4 | github.com/mattn/go-zglob/fastwalk 5 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/fastwalk/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 fastwalk 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Ino) 13 | } 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/fastwalk/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 fastwalk 8 | 9 | import "syscall" 10 | 11 | func direntInode(dirent *syscall.Dirent) uint64 { 12 | return uint64(dirent.Fileno) 13 | } 14 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/README.md: -------------------------------------------------------------------------------- 1 | # go-zglob 2 | 3 | [![Build Status](https://travis-ci.org/mattn/go-zglob.svg)](https://travis-ci.org/mattn/go-zglob) 4 | 5 | zglob 6 | 7 | ## Usage 8 | 9 | ```go 10 | matches, err := zglob.Glob(`./foo/b*/**/z*.txt`) 11 | ``` 12 | 13 | ## Installation 14 | 15 | ``` 16 | $ go get github.com/mattn/go-zglob 17 | ``` 18 | 19 | ## License 20 | 21 | MIT 22 | 23 | ## Author 24 | 25 | Yasuhiro Matsumoto (a.k.a mattn) 26 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53 h1:tGfIHhDghvEnneeRhODvGYOt305TPwingKt6p90F4MU= 2 | github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 3 | github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY= 4 | github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 5 | github.com/mattn/go-zglob v0.0.2-0.20191112051448-a8912a37f9e7 h1:6HgbBMgs3hI9y1/MYG0r9j6daUubUskZNsEW4fkWR/k= 6 | github.com/mattn/go-zglob v0.0.2-0.20191112051448-a8912a37f9e7/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | modvendor 2 | ========= 3 | 4 | Simple tool to copy additional module files into a local ./vendor folder. This 5 | tool should be run after `go mod vendor`. 6 | 7 | `go get -u github.com/goware/modvendor` 8 | 9 | ## Usage 10 | 11 | ``` 12 | $ GO111MODULE=on go mod vendor 13 | $ modvendor -copy="**/*.c **/*.h **/*.proto" -v 14 | ``` 15 | 16 | If you have additional directories that you wish to copy which are not specified 17 | under `./vendor/modules.txt`, use the `-include` flag with multiple values separated 18 | by commas, e.g.: 19 | 20 | ``` 21 | $ GO111MODULE=on go mod vendor 22 | $ modvendor -copy="**/*.c **/*.h **/*.proto" -v -include="github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/rpc,github.com/prometheus/client_model" 23 | ``` 24 | 25 | ## LICENSE 26 | 27 | MIT 28 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/fastwalk/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 fastwalk 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 nil 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 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Yasuhiro Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/fastwalk/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 fastwalk 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 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/zglob.go: -------------------------------------------------------------------------------- 1 | package zglob 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "regexp" 8 | "runtime" 9 | "strings" 10 | "sync" 11 | 12 | "github.com/mattn/go-zglob/fastwalk" 13 | ) 14 | 15 | var ( 16 | envre = regexp.MustCompile(`^(\$[a-zA-Z][a-zA-Z0-9_]+|\$\([a-zA-Z][a-zA-Z0-9_]+\))$`) 17 | mu sync.Mutex 18 | ) 19 | 20 | type zenv struct { 21 | dre *regexp.Regexp 22 | fre *regexp.Regexp 23 | pattern string 24 | root string 25 | } 26 | 27 | func New(pattern string) (*zenv, error) { 28 | globmask := "" 29 | root := "" 30 | for n, i := range strings.Split(filepath.ToSlash(pattern), "/") { 31 | if root == "" && strings.Index(i, "*") != -1 { 32 | if globmask == "" { 33 | root = "." 34 | } else { 35 | root = filepath.ToSlash(globmask) 36 | } 37 | } 38 | if n == 0 && i == "~" { 39 | if runtime.GOOS == "windows" { 40 | i = os.Getenv("USERPROFILE") 41 | } else { 42 | i = os.Getenv("HOME") 43 | } 44 | } 45 | if envre.MatchString(i) { 46 | i = strings.Trim(strings.Trim(os.Getenv(i[1:]), "()"), `"`) 47 | } 48 | 49 | globmask = filepath.Join(globmask, i) 50 | if n == 0 { 51 | if runtime.GOOS == "windows" && filepath.VolumeName(i) != "" { 52 | globmask = i + "/" 53 | } else if len(globmask) == 0 { 54 | globmask = "/" 55 | } 56 | } 57 | } 58 | if root == "" { 59 | return &zenv{ 60 | dre: nil, 61 | fre: nil, 62 | pattern: pattern, 63 | root: "", 64 | }, nil 65 | } 66 | if globmask == "" { 67 | globmask = "." 68 | } 69 | globmask = filepath.ToSlash(filepath.Clean(globmask)) 70 | 71 | cc := []rune(globmask) 72 | dirmask := "" 73 | filemask := "" 74 | for i := 0; i < len(cc); i++ { 75 | if cc[i] == '*' { 76 | if i < len(cc)-2 && cc[i+1] == '*' && cc[i+2] == '/' { 77 | filemask += "(.*/)?" 78 | if dirmask == "" { 79 | dirmask = filemask 80 | } 81 | i += 2 82 | } else { 83 | filemask += "[^/]*" 84 | } 85 | } else { 86 | c := cc[i] 87 | if c == '/' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 255 < c { 88 | filemask += string(c) 89 | } else { 90 | filemask += fmt.Sprintf("[\\x%02X]", c) 91 | } 92 | if c == '/' && dirmask == "" && strings.Index(filemask, "*") != -1 { 93 | dirmask = filemask 94 | } 95 | } 96 | } 97 | if dirmask == "" { 98 | dirmask = filemask 99 | } 100 | if len(filemask) > 0 && filemask[len(filemask)-1] == '/' { 101 | if root == "" { 102 | root = filemask 103 | } 104 | filemask += "[^/]*" 105 | } 106 | if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 107 | dirmask = "(?i:" + dirmask + ")" 108 | filemask = "(?i:" + filemask + ")" 109 | } 110 | return &zenv{ 111 | dre: regexp.MustCompile("^" + dirmask), 112 | fre: regexp.MustCompile("^" + filemask + "$"), 113 | pattern: pattern, 114 | root: filepath.Clean(root), 115 | }, nil 116 | } 117 | 118 | func Glob(pattern string) ([]string, error) { 119 | return glob(pattern, false) 120 | } 121 | 122 | func GlobFollowSymlinks(pattern string) ([]string, error) { 123 | return glob(pattern, true) 124 | } 125 | 126 | func glob(pattern string, followSymlinks bool) ([]string, error) { 127 | zenv, err := New(pattern) 128 | if err != nil { 129 | return nil, err 130 | } 131 | if zenv.root == "" { 132 | _, err := os.Stat(pattern) 133 | if err != nil { 134 | return nil, os.ErrNotExist 135 | } 136 | return []string{pattern}, nil 137 | } 138 | relative := !filepath.IsAbs(pattern) 139 | matches := []string{} 140 | 141 | fastwalk.FastWalk(zenv.root, func(path string, info os.FileMode) error { 142 | if zenv.root == "." && len(zenv.root) < len(path) { 143 | path = path[len(zenv.root)+1:] 144 | } 145 | path = filepath.ToSlash(path) 146 | 147 | if followSymlinks && info == os.ModeSymlink { 148 | followedPath, err := filepath.EvalSymlinks(path) 149 | if err == nil { 150 | fi, err := os.Lstat(followedPath) 151 | if err == nil && fi.IsDir() { 152 | return fastwalk.TraverseLink 153 | } 154 | } 155 | } 156 | 157 | if info.IsDir() { 158 | if path == "." || len(path) <= len(zenv.root) { 159 | return nil 160 | } 161 | if zenv.fre.MatchString(path) { 162 | mu.Lock() 163 | matches = append(matches, path) 164 | mu.Unlock() 165 | return nil 166 | } 167 | if !zenv.dre.MatchString(path + "/") { 168 | return filepath.SkipDir 169 | } 170 | } 171 | 172 | if zenv.fre.MatchString(path) { 173 | if relative && filepath.IsAbs(path) { 174 | path = path[len(zenv.root)+1:] 175 | } 176 | mu.Lock() 177 | matches = append(matches, path) 178 | mu.Unlock() 179 | } 180 | return nil 181 | }) 182 | return matches, nil 183 | } 184 | 185 | func Match(pattern, name string) (matched bool, err error) { 186 | zenv, err := New(pattern) 187 | if err != nil { 188 | return false, err 189 | } 190 | return zenv.Match(name), nil 191 | } 192 | 193 | func (z *zenv) Match(name string) bool { 194 | if z.root == "" { 195 | return z.pattern == name 196 | } 197 | 198 | name = filepath.ToSlash(name) 199 | 200 | if name == "." || len(name) <= len(z.root) { 201 | return false 202 | } 203 | 204 | if z.fre.MatchString(name) { 205 | return true 206 | } 207 | return false 208 | } 209 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-zglob/fastwalk/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 fastwalk 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | "path/filepath" 21 | "runtime" 22 | "sync" 23 | ) 24 | 25 | // TraverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir. 26 | var TraverseLink = errors.New("traverse symlink, assuming target is a directory") 27 | 28 | // FastWalk walks the file tree rooted at root, calling walkFn for 29 | // each file or directory in the tree, including root. 30 | // 31 | // If fastWalk returns filepath.SkipDir, the directory is skipped. 32 | // 33 | // Unlike filepath.Walk: 34 | // * file stat calls must be done by the user. 35 | // The only provided metadata is the file type, which does not include 36 | // any permission bits. 37 | // * multiple goroutines stat the filesystem concurrently. The provided 38 | // walkFn must be safe for concurrent use. 39 | // * fastWalk can follow symlinks if walkFn returns the TraverseLink 40 | // sentinel error. It is the walkFn's responsibility to prevent 41 | // fastWalk from going into symlink cycles. 42 | func FastWalk(root string, walkFn func(path string, typ os.FileMode) error) error { 43 | // Check if "root" is actually a file, not a directory. 44 | stat, err := os.Stat(root) 45 | if err != nil { 46 | return err 47 | } 48 | if !stat.IsDir() { 49 | // If it is, just directly pass it to walkFn and return. 50 | return walkFn(root, stat.Mode()) 51 | } 52 | 53 | // TODO(bradfitz): make numWorkers configurable? We used a 54 | // minimum of 4 to give the kernel more info about multiple 55 | // things we want, in hopes its I/O scheduling can take 56 | // advantage of that. Hopefully most are in cache. Maybe 4 is 57 | // even too low of a minimum. Profile more. 58 | numWorkers := 4 59 | if n := runtime.NumCPU(); n > numWorkers { 60 | numWorkers = n 61 | } 62 | w := &walker{ 63 | fn: walkFn, 64 | enqueuec: make(chan walkItem, numWorkers), // buffered for performance 65 | workc: make(chan walkItem, numWorkers), // buffered for performance 66 | donec: make(chan struct{}), 67 | 68 | // buffered for correctness & not leaking goroutines: 69 | resc: make(chan error, numWorkers), 70 | } 71 | 72 | // TODO(bradfitz): start the workers as needed? maybe not worth it. 73 | var wg sync.WaitGroup 74 | for i := 0; i < numWorkers; i++ { 75 | wg.Add(1) 76 | go w.doWork(&wg) 77 | } 78 | 79 | todo := []walkItem{{dir: root}} 80 | out := 0 81 | for { 82 | workc := w.workc 83 | var workItem walkItem 84 | if len(todo) == 0 { 85 | workc = nil 86 | } else { 87 | workItem = todo[len(todo)-1] 88 | } 89 | select { 90 | case workc <- workItem: 91 | todo = todo[:len(todo)-1] 92 | out++ 93 | case it := <-w.enqueuec: 94 | todo = append(todo, it) 95 | case err := <-w.resc: 96 | if err != nil { 97 | // Signal to the workers to close. 98 | close(w.donec) 99 | 100 | // Drain the results channel from the other workers which 101 | // haven't returned yet. 102 | go func() { 103 | for { 104 | select { 105 | case _, ok := <-w.resc: 106 | if !ok { 107 | return 108 | } 109 | } 110 | } 111 | }() 112 | 113 | wg.Wait() 114 | return err 115 | } 116 | 117 | out-- 118 | if out == 0 && len(todo) == 0 { 119 | // It's safe to quit here, as long as the buffered 120 | // enqueue channel isn't also readable, which might 121 | // happen if the worker sends both another unit of 122 | // work and its result before the other select was 123 | // scheduled and both w.resc and w.enqueuec were 124 | // readable. 125 | select { 126 | case it := <-w.enqueuec: 127 | todo = append(todo, it) 128 | default: 129 | // Signal to the workers to close, and wait for all of 130 | // them to return. 131 | close(w.donec) 132 | wg.Wait() 133 | return nil 134 | } 135 | } 136 | } 137 | } 138 | } 139 | 140 | // doWork reads directories as instructed (via workc) and runs the 141 | // user's callback function. 142 | func (w *walker) doWork(wg *sync.WaitGroup) { 143 | for { 144 | select { 145 | case <-w.donec: 146 | wg.Done() 147 | return 148 | case it := <-w.workc: 149 | w.resc <- w.walk(it.dir, !it.callbackDone) 150 | } 151 | } 152 | } 153 | 154 | type walker struct { 155 | fn func(path string, typ os.FileMode) error 156 | 157 | donec chan struct{} // closed on fastWalk's return 158 | workc chan walkItem // to workers 159 | enqueuec chan walkItem // from workers 160 | resc chan error // from workers 161 | } 162 | 163 | type walkItem struct { 164 | dir string 165 | callbackDone bool // callback already called; don't do it again 166 | } 167 | 168 | func (w *walker) enqueue(it walkItem) { 169 | select { 170 | case w.enqueuec <- it: 171 | case <-w.donec: 172 | } 173 | } 174 | 175 | func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error { 176 | joined := dirName + string(os.PathSeparator) + baseName 177 | if typ == os.ModeDir { 178 | w.enqueue(walkItem{dir: joined}) 179 | return nil 180 | } 181 | 182 | err := w.fn(joined, typ) 183 | if typ == os.ModeSymlink { 184 | if err == TraverseLink { 185 | // Set callbackDone so we don't call it twice for both the 186 | // symlink-as-symlink and the symlink-as-directory later: 187 | w.enqueue(walkItem{dir: joined, callbackDone: true}) 188 | return nil 189 | } 190 | if err == filepath.SkipDir { 191 | // Permit SkipDir on symlinks too. 192 | return nil 193 | } 194 | } 195 | return err 196 | } 197 | func (w *walker) walk(root string, runUserCallback bool) error { 198 | if runUserCallback { 199 | err := w.fn(root, os.ModeDir) 200 | if err == filepath.SkipDir { 201 | return nil 202 | } 203 | if err != nil { 204 | return err 205 | } 206 | } 207 | 208 | return readDir(root, w.onDirEnt) 209 | } 210 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "unicode" 12 | 13 | zglob "github.com/mattn/go-zglob" 14 | ) 15 | 16 | var ( 17 | flags = flag.NewFlagSet("modvendor", flag.ExitOnError) 18 | copyPatFlag = flags.String("copy", "", "copy files matching glob pattern to ./vendor/ (ie. modvendor -copy=\"**/*.c **/*.h **/*.proto\")") 19 | verboseFlag = flags.Bool("v", false, "verbose output") 20 | includeFlag = flags.String( 21 | "include", 22 | "", 23 | `specifies additional directories to copy into ./vendor/ which are not specified in ./vendor/modules.txt. Multiple directories can be included by comma separation e.g. -include:github.com/a/b/dir1,github.com/a/b/dir1/dir2`) 24 | ) 25 | 26 | type Mod struct { 27 | ImportPath string 28 | SourcePath string 29 | Version string 30 | SourceVersion string 31 | Dir string // full path, $GOPATH/pkg/mod/ 32 | Pkgs []string // sub-pkg import paths 33 | VendorList map[string]bool // files to vendor 34 | } 35 | 36 | func main() { 37 | flags.Parse(os.Args[1:]) 38 | 39 | // Ensure go.mod file exists and we're running from the project root, 40 | // and that ./vendor/modules.txt file exists. 41 | cwd, err := os.Getwd() 42 | if err != nil { 43 | fmt.Println(err) 44 | os.Exit(1) 45 | } 46 | if _, err := os.Stat(filepath.Join(cwd, "go.mod")); os.IsNotExist(err) { 47 | fmt.Println("Whoops, cannot find `go.mod` file") 48 | os.Exit(1) 49 | } 50 | modtxtPath := filepath.Join(cwd, "vendor", "modules.txt") 51 | if _, err := os.Stat(modtxtPath); os.IsNotExist(err) { 52 | fmt.Println("Whoops, cannot find vendor/modules.txt, first run `go mod vendor` and try again") 53 | os.Exit(1) 54 | } 55 | 56 | // Prepare vendor copy patterns 57 | copyPat := strings.Split(strings.TrimSpace(*copyPatFlag), " ") 58 | if len(copyPat) == 0 { 59 | fmt.Println("Whoops, -copy argument is empty, nothing to copy.") 60 | os.Exit(1) 61 | } 62 | additionalDirsToInclude := strings.Split(*includeFlag, ",") 63 | 64 | // Parse/process modules.txt file of pkgs 65 | f, _ := os.Open(modtxtPath) 66 | defer f.Close() 67 | scanner := bufio.NewScanner(f) 68 | scanner.Split(bufio.ScanLines) 69 | 70 | var mod *Mod 71 | modules := []*Mod{} 72 | 73 | for scanner.Scan() { 74 | line := scanner.Text() 75 | 76 | if line[0] == 35 { 77 | s := strings.Split(line, " ") 78 | if (len(s) != 6 && len(s) != 3) || s[1] == "explicit" { 79 | continue 80 | } 81 | 82 | mod = &Mod{ 83 | ImportPath: s[1], 84 | Version: s[2], 85 | } 86 | if s[2] == "=>" { 87 | // issue https://github.com/golang/go/issues/33848 added these, 88 | // see comments. I think we can get away with ignoring them. 89 | continue 90 | } 91 | // Handle "replace" in module file if any 92 | if len(s) > 3 && s[3] == "=>" { 93 | mod.SourcePath = s[4] 94 | 95 | // Handle replaces with a relative target. For example: 96 | // "replace github.com/status-im/status-go/protocol => ./protocol" 97 | if strings.HasPrefix(s[4], ".") || strings.HasPrefix(s[4], "/") { 98 | mod.Dir, err = filepath.Abs(s[4]) 99 | if err != nil { 100 | fmt.Printf("invalid relative path: %v", err) 101 | os.Exit(1) 102 | } 103 | } else { 104 | mod.SourceVersion = s[5] 105 | mod.Dir = pkgModPath(mod.SourcePath, mod.SourceVersion) 106 | } 107 | } else { 108 | mod.Dir = pkgModPath(mod.ImportPath, mod.Version) 109 | } 110 | 111 | if _, err := os.Stat(mod.Dir); os.IsNotExist(err) { 112 | fmt.Printf("Error! %q module path does not exist, check $GOPATH/pkg/mod\n", mod.Dir) 113 | os.Exit(1) 114 | } 115 | 116 | // Build list of files to module path source to project vendor folder 117 | mod.VendorList = buildModVendorList(copyPat, mod) 118 | // Append directories we need to also include which may not be in vendor/modules.txt. 119 | for _, dir := range additionalDirsToInclude { 120 | if strings.HasPrefix(dir, mod.ImportPath) { 121 | mod.Pkgs = append(mod.Pkgs, dir) 122 | } 123 | } 124 | 125 | modules = append(modules, mod) 126 | 127 | continue 128 | } 129 | 130 | mod.Pkgs = append(mod.Pkgs, line) 131 | } 132 | 133 | // Filter out files not part of the mod.Pkgs 134 | for _, mod := range modules { 135 | if len(mod.VendorList) == 0 { 136 | continue 137 | } 138 | for vendorFile, _ := range mod.VendorList { 139 | for _, subpkg := range mod.Pkgs { 140 | path := filepath.Join(mod.Dir, importPathIntersect(mod.ImportPath, subpkg)) 141 | 142 | x := strings.Index(vendorFile, path) 143 | if x == 0 { 144 | mod.VendorList[vendorFile] = true 145 | } 146 | } 147 | } 148 | for vendorFile, toggle := range mod.VendorList { 149 | if !toggle { 150 | delete(mod.VendorList, vendorFile) 151 | } 152 | } 153 | } 154 | 155 | // Copy mod vendor list files to ./vendor/ 156 | for _, mod := range modules { 157 | for vendorFile := range mod.VendorList { 158 | x := strings.Index(vendorFile, mod.Dir) 159 | if x < 0 { 160 | fmt.Println("Error! vendor file doesn't belong to mod, strange.") 161 | os.Exit(1) 162 | } 163 | 164 | localPath := fmt.Sprintf("%s%s", mod.ImportPath, vendorFile[len(mod.Dir):]) 165 | localFile := fmt.Sprintf("./vendor/%s", localPath) 166 | 167 | if *verboseFlag { 168 | fmt.Printf("vendoring %s\n", localPath) 169 | } 170 | 171 | os.MkdirAll(filepath.Dir(localFile), os.ModePerm) 172 | if _, err := copyFile(vendorFile, localFile); err != nil { 173 | fmt.Printf("Error! %s - unable to copy file %s\n", err.Error(), vendorFile) 174 | os.Exit(1) 175 | } 176 | } 177 | } 178 | } 179 | 180 | func buildModVendorList(copyPat []string, mod *Mod) map[string]bool { 181 | vendorList := map[string]bool{} 182 | 183 | for _, pat := range copyPat { 184 | matches, err := zglob.Glob(filepath.Join(mod.Dir, pat)) 185 | if err != nil { 186 | fmt.Println("Error! glob match failure:", err) 187 | os.Exit(1) 188 | } 189 | 190 | for _, m := range matches { 191 | vendorList[m] = false 192 | } 193 | } 194 | 195 | return vendorList 196 | } 197 | 198 | func importPathIntersect(basePath, pkgPath string) string { 199 | if strings.Index(pkgPath, basePath) != 0 { 200 | return "" 201 | } 202 | return pkgPath[len(basePath):] 203 | } 204 | 205 | func normString(str string) (normStr string) { 206 | for _, char := range str { 207 | if unicode.IsUpper(char) { 208 | normStr += "!" + string(unicode.ToLower(char)) 209 | } else { 210 | normStr += string(char) 211 | } 212 | } 213 | return 214 | } 215 | 216 | func pkgModPath(importPath, version string) string { 217 | goPath := os.Getenv("GOPATH") 218 | if goPath == "" { 219 | // the default GOPATH for go v1.11 220 | goPath = filepath.Join(os.Getenv("HOME"), "go") 221 | } 222 | 223 | normPath := normString(importPath) 224 | normVersion := normString(version) 225 | 226 | return filepath.Join(goPath, "pkg", "mod", fmt.Sprintf("%s@%s", normPath, normVersion)) 227 | } 228 | 229 | func copyFile(src, dst string) (int64, error) { 230 | srcStat, err := os.Stat(src) 231 | if err != nil { 232 | return 0, err 233 | } 234 | 235 | if !srcStat.Mode().IsRegular() { 236 | return 0, fmt.Errorf("%s is not a regular file", src) 237 | } 238 | 239 | srcFile, err := os.Open(src) 240 | if err != nil { 241 | return 0, err 242 | } 243 | defer srcFile.Close() 244 | 245 | dstFile, err := os.Create(dst) 246 | if err != nil { 247 | return 0, err 248 | } 249 | defer dstFile.Close() 250 | 251 | return io.Copy(dstFile, srcFile) 252 | } 253 | --------------------------------------------------------------------------------