mirror of https://github.com/k3s-io/k3s
118 lines
3.0 KiB
Go
118 lines
3.0 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/build"
|
|
"os"
|
|
"path"
|
|
"sort"
|
|
)
|
|
|
|
// VisitFunc is a function called by WalkPkg to examine a single package.
|
|
type VisitFunc func(importPath string, absPath string) error
|
|
|
|
// ErrSkipPkg can be returned by a VisitFunc to indicate that the package in
|
|
// question should not be walked any further.
|
|
var ErrSkipPkg = fmt.Errorf("package skipped")
|
|
|
|
// WalkPkg recursively visits all packages under pkgName. This is similar
|
|
// to filepath.Walk, except that it follows symlinks. A package is always
|
|
// visited before the children of that package. If visit returns ErrSkipPkg,
|
|
// pkgName will not be walked.
|
|
func WalkPkg(pkgName string, visit VisitFunc) error {
|
|
// Visit the package itself.
|
|
pkg, err := findPackage(pkgName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := visit(pkg.ImportPath, pkg.Dir); err == ErrSkipPkg {
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Read all of the child dirents and find sub-packages.
|
|
infos, err := readDirInfos(pkg.Dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, info := range infos {
|
|
if !info.IsDir() {
|
|
continue
|
|
}
|
|
name := info.Name()
|
|
if name[0] == '_' || (len(name) > 1 && name[0] == '.') || name == "testdata" {
|
|
continue
|
|
}
|
|
// Don't use path.Join() because it drops leading `./` via path.Clean().
|
|
err := WalkPkg(pkgName+"/"+name, visit)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// findPackage finds a Go package.
|
|
func findPackage(pkgName string) (*build.Package, error) {
|
|
debug("find", pkgName)
|
|
pkg, err := build.Import(pkgName, getwd(), build.FindOnly)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
// readDirInfos returns a list of os.FileInfo structures for the dirents under
|
|
// dirPath. The result list is sorted by name. This is very similar to
|
|
// ioutil.ReadDir, except that it follows symlinks.
|
|
func readDirInfos(dirPath string) ([]os.FileInfo, error) {
|
|
names, err := readDirNames(dirPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Strings(names)
|
|
|
|
infos := make([]os.FileInfo, 0, len(names))
|
|
for _, n := range names {
|
|
info, err := os.Stat(path.Join(dirPath, n))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
infos = append(infos, info)
|
|
}
|
|
return infos, nil
|
|
}
|
|
|
|
// readDirNames returns a list of all dirents in dirPath. The result list is
|
|
// not sorted or filtered.
|
|
func readDirNames(dirPath string) ([]string, error) {
|
|
d, err := os.Open(dirPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer d.Close()
|
|
|
|
names, err := d.Readdirnames(-1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return names, nil
|
|
}
|