mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
imports: extract fastWalk into new package internal/fastwalk
It is going to be used by a new tool. Moved to an internal package so it does not become a publicly supported api. Modified the tests so they don't depend on the fix_test infrastructure. Change-Id: Ib8ebef24dc23e180960af04aa3d06b5f41a7c02b Reviewed-on: https://go-review.googlesource.com/99678 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
1a83a0b548
commit
94b14834a2
@ -22,6 +22,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
"golang.org/x/tools/internal/fastwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Debug controls verbose logging.
|
// Debug controls verbose logging.
|
||||||
@ -545,16 +546,13 @@ func skipDir(fi os.FileInfo) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldTraverse reports whether the symlink fi should, found in dir,
|
// shouldTraverse reports whether the symlink fi, found in dir,
|
||||||
// should be followed. It makes sure symlinks were never visited
|
// should be followed. It makes sure symlinks were never visited
|
||||||
// before to avoid symlink loops.
|
// before to avoid symlink loops.
|
||||||
func shouldTraverse(dir string, fi os.FileInfo) bool {
|
func shouldTraverse(dir string, fi os.FileInfo) bool {
|
||||||
path := filepath.Join(dir, fi.Name())
|
path := filepath.Join(dir, fi.Name())
|
||||||
target, err := filepath.EvalSymlinks(path)
|
target, err := filepath.EvalSymlinks(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
ts, err := os.Stat(target)
|
ts, err := os.Stat(target)
|
||||||
@ -674,12 +672,12 @@ func scanGoDirs(goRoot bool) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if shouldTraverse(dir, fi) {
|
if shouldTraverse(dir, fi) {
|
||||||
return traverseLink
|
return fastwalk.TraverseLink
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fastWalk(srcDir, walkFn); err != nil {
|
if err := fastwalk.Walk(srcDir, walkFn); err != nil {
|
||||||
log.Printf("goimports: scanning directory %v: %v", srcDir, err)
|
log.Printf("goimports: scanning directory %v: %v", srcDir, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// A faster implementation of filepath.Walk.
|
// Package fastwalk provides a faster version of filepath.Walk for file system
|
||||||
//
|
// scanning tools.
|
||||||
// filepath.Walk's design necessarily calls os.Lstat on each file,
|
package fastwalk
|
||||||
// even if the caller needs less info. And goimports only need to know
|
|
||||||
// the type of each file. The kernel interface provides the type in
|
|
||||||
// the Readdir call but the standard library ignored it.
|
|
||||||
// fastwalk_unix.go contains a fork of the syscall routines.
|
|
||||||
//
|
|
||||||
// See golang.org/issue/16399
|
|
||||||
|
|
||||||
package imports
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -22,10 +14,22 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// traverseLink is a sentinel error for fastWalk, similar to filepath.SkipDir.
|
// TraverseLink is used as a return value from WalkFuncs to indicate that the
|
||||||
var traverseLink = errors.New("traverse symlink, assuming target is a directory")
|
// symlink named in the call may be traversed.
|
||||||
|
var TraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory")
|
||||||
|
|
||||||
// fastWalk walks the file tree rooted at root, calling walkFn for
|
// Walk is a faster implementation of filepath.Walk.
|
||||||
|
//
|
||||||
|
// filepath.Walk's design necessarily calls os.Lstat on each file,
|
||||||
|
// even if the caller needs less info.
|
||||||
|
// Many tools need only the type of each file.
|
||||||
|
// On some platforms, this information is provided directly by the readdir
|
||||||
|
// system call, avoiding the need to stat each file individually.
|
||||||
|
// fastwalk_unix.go contains a fork of the syscall routines.
|
||||||
|
//
|
||||||
|
// See golang.org/issue/16399
|
||||||
|
//
|
||||||
|
// Walk walks the file tree rooted at root, calling walkFn for
|
||||||
// each file or directory in the tree, including root.
|
// each file or directory in the tree, including root.
|
||||||
//
|
//
|
||||||
// If fastWalk returns filepath.SkipDir, the directory is skipped.
|
// If fastWalk returns filepath.SkipDir, the directory is skipped.
|
||||||
@ -36,10 +40,10 @@ var traverseLink = errors.New("traverse symlink, assuming target is a directory"
|
|||||||
// any permission bits.
|
// any permission bits.
|
||||||
// * multiple goroutines stat the filesystem concurrently. The provided
|
// * multiple goroutines stat the filesystem concurrently. The provided
|
||||||
// walkFn must be safe for concurrent use.
|
// walkFn must be safe for concurrent use.
|
||||||
// * fastWalk can follow symlinks if walkFn returns the traverseLink
|
// * fastWalk can follow symlinks if walkFn returns the TraverseLink
|
||||||
// sentinel error. It is the walkFn's responsibility to prevent
|
// sentinel error. It is the walkFn's responsibility to prevent
|
||||||
// fastWalk from going into symlink cycles.
|
// fastWalk from going into symlink cycles.
|
||||||
func fastWalk(root string, walkFn func(path string, typ os.FileMode) error) error {
|
func Walk(root string, walkFn func(path string, typ os.FileMode) error) error {
|
||||||
// TODO(bradfitz): make numWorkers configurable? We used a
|
// TODO(bradfitz): make numWorkers configurable? We used a
|
||||||
// minimum of 4 to give the kernel more info about multiple
|
// minimum of 4 to give the kernel more info about multiple
|
||||||
// things we want, in hopes its I/O scheduling can take
|
// things we want, in hopes its I/O scheduling can take
|
||||||
@ -158,7 +162,7 @@ func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error {
|
|||||||
|
|
||||||
err := w.fn(joined, typ)
|
err := w.fn(joined, typ)
|
||||||
if typ == os.ModeSymlink {
|
if typ == os.ModeSymlink {
|
||||||
if err == traverseLink {
|
if err == TraverseLink {
|
||||||
// Set callbackDone so we don't call it twice for both the
|
// Set callbackDone so we don't call it twice for both the
|
||||||
// symlink-as-symlink and the symlink-as-directory later:
|
// symlink-as-symlink and the symlink-as-directory later:
|
||||||
w.enqueue(walkItem{dir: joined, callbackDone: true})
|
w.enqueue(walkItem{dir: joined, callbackDone: true})
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// +build freebsd openbsd netbsd
|
// +build freebsd openbsd netbsd
|
||||||
|
|
||||||
package imports
|
package fastwalk
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
@ -5,7 +5,7 @@
|
|||||||
// +build linux darwin
|
// +build linux darwin
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
package imports
|
package fastwalk
|
||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
|
// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
|
||||||
|
|
||||||
package imports
|
package fastwalk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
@ -2,12 +2,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package imports
|
package fastwalk_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -16,6 +17,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/fastwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func formatFileModes(m map[string]os.FileMode) string {
|
func formatFileModes(m map[string]os.FileMode) string {
|
||||||
@ -32,30 +35,46 @@ func formatFileModes(m map[string]os.FileMode) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) {
|
func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) {
|
||||||
testConfig{
|
tempdir, err := ioutil.TempDir("", "test-fast-walk")
|
||||||
gopathFiles: files,
|
if err != nil {
|
||||||
}.test(t, func(t *goimportTest) {
|
t.Fatal(err)
|
||||||
got := map[string]os.FileMode{}
|
}
|
||||||
var mu sync.Mutex
|
defer os.RemoveAll(tempdir)
|
||||||
if err := fastWalk(t.gopath, func(path string, typ os.FileMode) error {
|
for path, contents := range files {
|
||||||
mu.Lock()
|
file := filepath.Join(tempdir, "/src", path)
|
||||||
defer mu.Unlock()
|
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
|
||||||
if !strings.HasPrefix(path, t.gopath) {
|
t.Fatal(err)
|
||||||
t.Fatalf("bogus prefix on %q, expect %q", path, t.gopath)
|
|
||||||
}
|
|
||||||
key := filepath.ToSlash(strings.TrimPrefix(path, t.gopath))
|
|
||||||
if old, dup := got[key]; dup {
|
|
||||||
t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ)
|
|
||||||
}
|
|
||||||
got[key] = typ
|
|
||||||
return callback(path, typ)
|
|
||||||
}); err != nil {
|
|
||||||
t.Fatalf("callback returned: %v", err)
|
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, want) {
|
var err error
|
||||||
t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want))
|
if strings.HasPrefix(contents, "LINK:") {
|
||||||
|
err = os.Symlink(strings.TrimPrefix(contents, "LINK:"), file)
|
||||||
|
} else {
|
||||||
|
err = ioutil.WriteFile(file, []byte(contents), 0644)
|
||||||
}
|
}
|
||||||
})
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
got := map[string]os.FileMode{}
|
||||||
|
var mu sync.Mutex
|
||||||
|
if err := fastwalk.Walk(tempdir, func(path string, typ os.FileMode) error {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
if !strings.HasPrefix(path, tempdir) {
|
||||||
|
t.Fatalf("bogus prefix on %q, expect %q", path, tempdir)
|
||||||
|
}
|
||||||
|
key := filepath.ToSlash(strings.TrimPrefix(path, tempdir))
|
||||||
|
if old, dup := got[key]; dup {
|
||||||
|
t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ)
|
||||||
|
}
|
||||||
|
got[key] = typ
|
||||||
|
return callback(path, typ)
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("callback returned: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFastWalk_Basic(t *testing.T) {
|
func TestFastWalk_Basic(t *testing.T) {
|
||||||
@ -140,7 +159,7 @@ func TestFastWalk_TraverseSymlink(t *testing.T) {
|
|||||||
},
|
},
|
||||||
func(path string, typ os.FileMode) error {
|
func(path string, typ os.FileMode) error {
|
||||||
if typ == os.ModeSymlink {
|
if typ == os.ModeSymlink {
|
||||||
return traverseLink
|
return fastwalk.TraverseLink
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -163,7 +182,7 @@ var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan
|
|||||||
func BenchmarkFastWalk(b *testing.B) {
|
func BenchmarkFastWalk(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := fastWalk(*benchDir, func(path string, typ os.FileMode) error { return nil })
|
err := fastwalk.Walk(*benchDir, func(path string, typ os.FileMode) error { return nil })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
@ -5,7 +5,7 @@
|
|||||||
// +build linux darwin freebsd openbsd netbsd
|
// +build linux darwin freebsd openbsd netbsd
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
package imports
|
package fastwalk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
Loading…
x
Reference in New Issue
Block a user