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
This commit is contained in:
Alan Donovan 2014-11-05 12:34:34 -05:00
parent ef648ad89b
commit 3191bbae17
2 changed files with 47 additions and 19 deletions

View File

@ -4,11 +4,13 @@
// Package buildutil provides utilities related to the go/build // Package buildutil provides utilities related to the go/build
// package in the standard library. // package in the standard library.
//
// All I/O is done via the build.Context file system interface, which must
// be concurrency-safe.
package buildutil package buildutil
import ( import (
"go/build" "go/build"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -23,7 +25,7 @@ import (
// The result may include import paths for directories that contain no // The result may include import paths for directories that contain no
// *.go files, such as "archive" (in $GOROOT/src). // *.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. // which must be concurrency-safe.
// //
func AllPackages(ctxt *build.Context) []string { 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 // If the package directory exists but could not be read, the second
// argument to the found function provides the error. // 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. // accessors must be concurrency safe.
// //
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) { 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)) { 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) root = filepath.Clean(root) + string(os.PathSeparator)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -92,7 +89,7 @@ func allPackages(ctxt *build.Context, sema chan bool, root string, found func(st
} }
sema <- true sema <- true
files, err := ReadDir(dir) files, err := ReadDir(ctxt, dir)
<-sema <-sema
if pkg != "" || err != nil { if pkg != "" || err != nil {
found(pkg, err) found(pkg, err)

View File

@ -11,6 +11,7 @@ import (
"go/parser" "go/parser"
"go/token" "go/token"
"io" "io"
"io/ioutil"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -18,7 +19,7 @@ import (
) )
// ParseFile behaves like parser.ParseFile, // 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) // If file is not absolute (as defined by IsAbsPath), the (dir, file)
// components are joined using JoinPath; dir must be absolute. // 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. // ContainingPackage returns the package containing filename.
// //
// If filename is not absolute, it is interpreted relative to working directory dir. // 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 // The '...Files []string' fields of the resulting build.Package are not
// populated (build.FindOnly mode). // populated (build.FindOnly mode).
@ -58,12 +59,12 @@ func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Packag
filename = JoinPath(ctxt, dir, filename) filename = JoinPath(ctxt, dir, filename)
} }
// We must not assume the virtualized file tree uses // We must not assume the file tree uses
// "/" always, // "/" always,
// `\` always, // `\` always,
// or os.PathSeparator (which varies by platform), // or os.PathSeparator (which varies by platform),
// but to make any progress, we are forced to assume that // 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. // is also `\`, thus we can rely on filepath.ToSlash for some sanity.
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/" 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) 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, // 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 { func FileExists(ctxt *build.Context, path string) bool {
if ctxt.OpenFile != nil { if ctxt.OpenFile != nil {
r, err := ctxt.OpenFile(path) r, err := ctxt.OpenFile(path)
@ -98,7 +103,7 @@ func FileExists(ctxt *build.Context, path string) bool {
} }
// OpenFile behaves like os.Open, // 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) { func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
if ctxt.OpenFile != nil { if ctxt.OpenFile != nil {
return ctxt.OpenFile(path) return ctxt.OpenFile(path)
@ -107,7 +112,7 @@ func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
} }
// IsAbsPath behaves like filepath.IsAbs, // 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 { func IsAbsPath(ctxt *build.Context, path string) bool {
if ctxt.IsAbsPath != nil { if ctxt.IsAbsPath != nil {
return ctxt.IsAbsPath(path) return ctxt.IsAbsPath(path)
@ -116,7 +121,7 @@ func IsAbsPath(ctxt *build.Context, path string) bool {
} }
// JoinPath behaves like filepath.Join, // 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 { func JoinPath(ctxt *build.Context, path ...string) string {
if ctxt.JoinPath != nil { if ctxt.JoinPath != nil {
return ctxt.JoinPath(path...) return ctxt.JoinPath(path...)
@ -124,4 +129,30 @@ func JoinPath(ctxt *build.Context, path ...string) string {
return filepath.Join(path...) 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)
}