From 3191bbae17db7ba9d176ba6a5c7cfb12a9b719b9 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 5 Nov 2014 12:34:34 -0500 Subject: [PATCH] go/buildutil: call ReadDir via the build.Context's file system interface LGTM=gri R=gri, crawshaw CC=golang-codereviews https://golang.org/cl/171920043 --- go/buildutil/allpackages.go | 15 +++++------ go/buildutil/util.go | 51 +++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/go/buildutil/allpackages.go b/go/buildutil/allpackages.go index 7ca3e585..1da5560e 100644 --- a/go/buildutil/allpackages.go +++ b/go/buildutil/allpackages.go @@ -4,11 +4,13 @@ // Package buildutil provides utilities related to the go/build // package in the standard library. +// +// All I/O is done via the build.Context file system interface, which must +// be concurrency-safe. package buildutil import ( "go/build" - "io/ioutil" "os" "path/filepath" "sort" @@ -23,7 +25,7 @@ import ( // The result may include import paths for directories that contain no // *.go files, such as "archive" (in $GOROOT/src). // -// All I/O is via the build.Context virtual file system, +// All I/O is done via the build.Context file system interface, // which must be concurrency-safe. // func AllPackages(ctxt *build.Context) []string { @@ -45,7 +47,7 @@ func AllPackages(ctxt *build.Context) []string { // If the package directory exists but could not be read, the second // argument to the found function provides the error. // -// The found function and the build.Context virtual file system +// The found function and the build.Context file system interface // accessors must be concurrency safe. // func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { @@ -66,11 +68,6 @@ func ForEachPackage(ctxt *build.Context, found func(importPath string, err error } func allPackages(ctxt *build.Context, sema chan bool, root string, found func(string, error)) { - ReadDir := ctxt.ReadDir - if ReadDir == nil { - ReadDir = ioutil.ReadDir - } - root = filepath.Clean(root) + string(os.PathSeparator) var wg sync.WaitGroup @@ -92,7 +89,7 @@ func allPackages(ctxt *build.Context, sema chan bool, root string, found func(st } sema <- true - files, err := ReadDir(dir) + files, err := ReadDir(ctxt, dir) <-sema if pkg != "" || err != nil { found(pkg, err) diff --git a/go/buildutil/util.go b/go/buildutil/util.go index f4dca6a2..60eeae25 100644 --- a/go/buildutil/util.go +++ b/go/buildutil/util.go @@ -11,6 +11,7 @@ import ( "go/parser" "go/token" "io" + "io/ioutil" "os" "path" "path/filepath" @@ -18,7 +19,7 @@ import ( ) // ParseFile behaves like parser.ParseFile, -// but uses the build context's virtual file system, if any. +// but uses the build context's file system interface, if any. // // If file is not absolute (as defined by IsAbsPath), the (dir, file) // components are joined using JoinPath; dir must be absolute. @@ -46,7 +47,7 @@ func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string // ContainingPackage returns the package containing filename. // // If filename is not absolute, it is interpreted relative to working directory dir. -// All I/O is via the build context's virtual file system, if any. +// All I/O is via the build context's file system interface, if any. // // The '...Files []string' fields of the resulting build.Package are not // populated (build.FindOnly mode). @@ -58,12 +59,12 @@ func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Packag filename = JoinPath(ctxt, dir, filename) } - // We must not assume the virtualized file tree uses + // We must not assume the file tree uses // "/" always, // `\` always, // or os.PathSeparator (which varies by platform), // but to make any progress, we are forced to assume that - // virtualized paths will not use `\` unless the PathSeparator + // paths will not use `\` unless the PathSeparator // is also `\`, thus we can rely on filepath.ToSlash for some sanity. dirSlash := path.Dir(filepath.ToSlash(filename)) + "/" @@ -80,10 +81,14 @@ func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Packag return nil, fmt.Errorf("can't find package containing %s", filename) } -// -- Effective methods of virtual file system ------------------------- +// -- Effective methods of file system interface ------------------------- + +// (go/build.Context defines these as methods, but does not export them.) + +// TODO(adonovan): HasSubdir? // FileExists returns true if the specified file exists, -// using the build context's virtual file system. +// using the build context's file system interface. func FileExists(ctxt *build.Context, path string) bool { if ctxt.OpenFile != nil { r, err := ctxt.OpenFile(path) @@ -98,7 +103,7 @@ func FileExists(ctxt *build.Context, path string) bool { } // OpenFile behaves like os.Open, -// but uses the build context's virtual file system, if any. +// but uses the build context's file system interface, if any. func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) { if ctxt.OpenFile != nil { return ctxt.OpenFile(path) @@ -107,7 +112,7 @@ func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) { } // IsAbsPath behaves like filepath.IsAbs, -// but uses the build context's virtual file system, if any. +// but uses the build context's file system interface, if any. func IsAbsPath(ctxt *build.Context, path string) bool { if ctxt.IsAbsPath != nil { return ctxt.IsAbsPath(path) @@ -116,7 +121,7 @@ func IsAbsPath(ctxt *build.Context, path string) bool { } // JoinPath behaves like filepath.Join, -// but uses the build context's virtual file system. +// but uses the build context's file system interface, if any. func JoinPath(ctxt *build.Context, path ...string) string { if ctxt.JoinPath != nil { return ctxt.JoinPath(path...) @@ -124,4 +129,30 @@ func JoinPath(ctxt *build.Context, path ...string) string { return filepath.Join(path...) } -// TODO(adonovan): SplitPathList, IsDir, HasSubdir, ReadDir? +// IsDir behaves like os.Stat plus IsDir, +// but uses the build context's file system interface, if any. +func IsDir(ctxt *build.Context, path string) bool { + if ctxt.IsDir != nil { + return ctxt.IsDir(path) + } + fi, err := os.Stat(path) + return err == nil && fi.IsDir() +} + +// ReadDir behaves like ioutil.ReadDir, +// but uses the build context's file system interface, if any. +func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) { + if ctxt.ReadDir != nil { + return ctxt.ReadDir(path) + } + return ioutil.ReadDir(path) +} + +// SplitPathList behaves like filepath.SplitList, +// but uses the build context's file system interface, if any. +func SplitPathList(ctxt *build.Context, s string) []string { + if ctxt.SplitPathList != nil { + return ctxt.SplitPathList(s) + } + return filepath.SplitList(s) +}