forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/golang.org/x/tools/go/buildutil/util.go

356 lines
5.9 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
// Copyright 2014 The Go Authors. All rights reserved.
2024-05-14 13:07:09 +00:00
// Use of this source code is governed by a BSD-style
2024-05-14 13:07:09 +00:00
// license that can be found in the LICENSE file.
package buildutil
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
)
// ParseFile behaves like parser.ParseFile,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// If file is not absolute (as defined by IsAbsPath), the (dir, file)
2024-05-14 13:07:09 +00:00
// components are joined using JoinPath; dir must be absolute.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The displayPath function, if provided, is used to transform the
2024-05-14 13:07:09 +00:00
// filename that will be attached to the ASTs.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
2024-05-14 13:07:09 +00:00
func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
2024-05-14 13:07:09 +00:00
if !IsAbsPath(ctxt, file) {
2024-05-14 13:07:09 +00:00
file = JoinPath(ctxt, dir, file)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
rd, err := OpenFile(ctxt, file)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return nil, err
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
defer rd.Close() // ignore error
2024-05-14 13:07:09 +00:00
if displayPath != nil {
2024-05-14 13:07:09 +00:00
file = displayPath(file)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return parser.ParseFile(fset, file, rd, mode)
2024-05-14 13:07:09 +00:00
}
// ContainingPackage returns the package containing filename.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// If filename is not absolute, it is interpreted relative to working directory dir.
2024-05-14 13:07:09 +00:00
// All I/O is via the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
//
2024-05-14 13:07:09 +00:00
// The '...Files []string' fields of the resulting build.Package are not
2024-05-14 13:07:09 +00:00
// populated (build.FindOnly mode).
2024-05-14 13:07:09 +00:00
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
2024-05-14 13:07:09 +00:00
if !IsAbsPath(ctxt, filename) {
2024-05-14 13:07:09 +00:00
filename = JoinPath(ctxt, dir, filename)
2024-05-14 13:07:09 +00:00
}
// We must not assume the file tree uses
2024-05-14 13:07:09 +00:00
// "/" always,
2024-05-14 13:07:09 +00:00
// `\` always,
2024-05-14 13:07:09 +00:00
// or os.PathSeparator (which varies by platform),
2024-05-14 13:07:09 +00:00
// but to make any progress, we are forced to assume that
2024-05-14 13:07:09 +00:00
// paths will not use `\` unless the PathSeparator
2024-05-14 13:07:09 +00:00
// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
2024-05-14 13:07:09 +00:00
for _, srcdir := range ctxt.SrcDirs() {
2024-05-14 13:07:09 +00:00
srcdirSlash := filepath.ToSlash(srcdir) + "/"
2024-05-14 13:07:09 +00:00
if importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
2024-05-14 13:07:09 +00:00
return ctxt.Import(importPath, dir, build.FindOnly)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
return nil, fmt.Errorf("can't find package containing %s", filename)
2024-05-14 13:07:09 +00:00
}
// -- Effective methods of file system interface -------------------------
// (go/build.Context defines these as methods, but does not export them.)
// HasSubdir calls ctxt.HasSubdir (if not nil) or else uses
2024-05-14 13:07:09 +00:00
// the local file system to answer the question.
2024-05-14 13:07:09 +00:00
func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
2024-05-14 13:07:09 +00:00
if f := ctxt.HasSubdir; f != nil {
2024-05-14 13:07:09 +00:00
return f(root, dir)
2024-05-14 13:07:09 +00:00
}
// Try using paths we received.
2024-05-14 13:07:09 +00:00
if rel, ok = hasSubdir(root, dir); ok {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
// Try expanding symlinks and comparing
2024-05-14 13:07:09 +00:00
// expanded against unexpanded and
2024-05-14 13:07:09 +00:00
// expanded against expanded.
2024-05-14 13:07:09 +00:00
rootSym, _ := filepath.EvalSymlinks(root)
2024-05-14 13:07:09 +00:00
dirSym, _ := filepath.EvalSymlinks(dir)
if rel, ok = hasSubdir(rootSym, dir); ok {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if rel, ok = hasSubdir(root, dirSym); ok {
2024-05-14 13:07:09 +00:00
return
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return hasSubdir(rootSym, dirSym)
2024-05-14 13:07:09 +00:00
}
func hasSubdir(root, dir string) (rel string, ok bool) {
2024-05-14 13:07:09 +00:00
const sep = string(filepath.Separator)
2024-05-14 13:07:09 +00:00
root = filepath.Clean(root)
2024-05-14 13:07:09 +00:00
if !strings.HasSuffix(root, sep) {
2024-05-14 13:07:09 +00:00
root += sep
2024-05-14 13:07:09 +00:00
}
dir = filepath.Clean(dir)
2024-05-14 13:07:09 +00:00
if !strings.HasPrefix(dir, root) {
2024-05-14 13:07:09 +00:00
return "", false
2024-05-14 13:07:09 +00:00
}
return filepath.ToSlash(dir[len(root):]), true
2024-05-14 13:07:09 +00:00
}
// FileExists returns true if the specified file exists,
2024-05-14 13:07:09 +00:00
// using the build context's file system interface.
2024-05-14 13:07:09 +00:00
func FileExists(ctxt *build.Context, path string) bool {
2024-05-14 13:07:09 +00:00
if ctxt.OpenFile != nil {
2024-05-14 13:07:09 +00:00
r, err := ctxt.OpenFile(path)
2024-05-14 13:07:09 +00:00
if err != nil {
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
r.Close() // ignore error
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
_, err := os.Stat(path)
2024-05-14 13:07:09 +00:00
return err == nil
2024-05-14 13:07:09 +00:00
}
// OpenFile behaves like os.Open,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
2024-05-14 13:07:09 +00:00
if ctxt.OpenFile != nil {
2024-05-14 13:07:09 +00:00
return ctxt.OpenFile(path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return os.Open(path)
2024-05-14 13:07:09 +00:00
}
// IsAbsPath behaves like filepath.IsAbs,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func IsAbsPath(ctxt *build.Context, path string) bool {
2024-05-14 13:07:09 +00:00
if ctxt.IsAbsPath != nil {
2024-05-14 13:07:09 +00:00
return ctxt.IsAbsPath(path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return filepath.IsAbs(path)
2024-05-14 13:07:09 +00:00
}
// JoinPath behaves like filepath.Join,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func JoinPath(ctxt *build.Context, path ...string) string {
2024-05-14 13:07:09 +00:00
if ctxt.JoinPath != nil {
2024-05-14 13:07:09 +00:00
return ctxt.JoinPath(path...)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return filepath.Join(path...)
2024-05-14 13:07:09 +00:00
}
// IsDir behaves like os.Stat plus IsDir,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func IsDir(ctxt *build.Context, path string) bool {
2024-05-14 13:07:09 +00:00
if ctxt.IsDir != nil {
2024-05-14 13:07:09 +00:00
return ctxt.IsDir(path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
fi, err := os.Stat(path)
2024-05-14 13:07:09 +00:00
return err == nil && fi.IsDir()
2024-05-14 13:07:09 +00:00
}
// ReadDir behaves like ioutil.ReadDir,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
2024-05-14 13:07:09 +00:00
if ctxt.ReadDir != nil {
2024-05-14 13:07:09 +00:00
return ctxt.ReadDir(path)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return ioutil.ReadDir(path)
2024-05-14 13:07:09 +00:00
}
// SplitPathList behaves like filepath.SplitList,
2024-05-14 13:07:09 +00:00
// but uses the build context's file system interface, if any.
2024-05-14 13:07:09 +00:00
func SplitPathList(ctxt *build.Context, s string) []string {
2024-05-14 13:07:09 +00:00
if ctxt.SplitPathList != nil {
2024-05-14 13:07:09 +00:00
return ctxt.SplitPathList(s)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return filepath.SplitList(s)
2024-05-14 13:07:09 +00:00
}
// sameFile returns true if x and y have the same basename and denote
2024-05-14 13:07:09 +00:00
// the same file.
2024-05-14 13:07:09 +00:00
func sameFile(x, y string) bool {
2024-05-14 13:07:09 +00:00
if path.Clean(x) == path.Clean(y) {
2024-05-14 13:07:09 +00:00
return true
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
2024-05-14 13:07:09 +00:00
if xi, err := os.Stat(x); err == nil {
2024-05-14 13:07:09 +00:00
if yi, err := os.Stat(y); err == nil {
2024-05-14 13:07:09 +00:00
return os.SameFile(xi, yi)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return false
2024-05-14 13:07:09 +00:00
}