github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/buildutil/util.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package buildutil
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/build"
    11  	"go/parser"
    12  	"go/token"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"runtime"
    19  	"strings"
    20  )
    21  
    22  // ParseFile behaves like parser.ParseFile,
    23  // but uses the build context's file system interface, if any.
    24  //
    25  // If file is not absolute (as defined by IsAbsPath), the (dir, file)
    26  // components are joined using JoinPath; dir must be absolute.
    27  //
    28  // The displayPath function, if provided, is used to transform the
    29  // filename that will be attached to the ASTs.
    30  //
    31  // TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
    32  //
    33  func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
    34  	if !IsAbsPath(ctxt, file) {
    35  		file = JoinPath(ctxt, dir, file)
    36  	}
    37  	rd, err := OpenFile(ctxt, file)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	defer rd.Close() // ignore error
    42  	if displayPath != nil {
    43  		file = displayPath(file)
    44  	}
    45  	return parser.ParseFile(fset, file, rd, mode)
    46  }
    47  
    48  // ContainingPackage returns the package containing filename.
    49  //
    50  // If filename is not absolute, it is interpreted relative to working directory dir.
    51  // All I/O is via the build context's file system interface, if any.
    52  //
    53  // The '...Files []string' fields of the resulting build.Package are not
    54  // populated (build.FindOnly mode).
    55  //
    56  // TODO(adonovan): call this from oracle when the tree thaws.
    57  //
    58  func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
    59  	if !IsAbsPath(ctxt, filename) {
    60  		filename = JoinPath(ctxt, dir, filename)
    61  	}
    62  
    63  	// We must not assume the file tree uses
    64  	// "/" always,
    65  	// `\` always,
    66  	// or os.PathSeparator (which varies by platform),
    67  	// but to make any progress, we are forced to assume that
    68  	// paths will not use `\` unless the PathSeparator
    69  	// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
    70  
    71  	dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
    72  
    73  	// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
    74  	for _, srcdir := range ctxt.SrcDirs() {
    75  		srcdirSlash := filepath.ToSlash(srcdir) + "/"
    76  		if dirHasPrefix(dirSlash, srcdirSlash) {
    77  			importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")]
    78  			return ctxt.Import(importPath, dir, build.FindOnly)
    79  		}
    80  	}
    81  
    82  	return nil, fmt.Errorf("can't find package containing %s", filename)
    83  }
    84  
    85  // dirHasPrefix tests whether the directory dir begins with prefix.
    86  func dirHasPrefix(dir, prefix string) bool {
    87  	if runtime.GOOS != "windows" {
    88  		return strings.HasPrefix(dir, prefix)
    89  	}
    90  	return len(dir) >= len(prefix) && strings.EqualFold(dir[:len(prefix)], prefix)
    91  }
    92  
    93  // StripVendor removes the "vendor" segment and all preceding ones
    94  // from a slash-segmented path.  (See go/build.AllowVendor.)
    95  func StripVendor(path string) string {
    96  	if i := strings.LastIndex(path, "/vendor/"); i >= 0 {
    97  		return path[i+len("/vendor/"):]
    98  	}
    99  	return strings.TrimPrefix(path, "vendor/")
   100  }
   101  
   102  // -- Effective methods of file system interface -------------------------
   103  
   104  // (go/build.Context defines these as methods, but does not export them.)
   105  
   106  // TODO(adonovan): HasSubdir?
   107  
   108  // FileExists returns true if the specified file exists,
   109  // using the build context's file system interface.
   110  func FileExists(ctxt *build.Context, path string) bool {
   111  	if ctxt.OpenFile != nil {
   112  		r, err := ctxt.OpenFile(path)
   113  		if err != nil {
   114  			return false
   115  		}
   116  		r.Close() // ignore error
   117  		return true
   118  	}
   119  	_, err := os.Stat(path)
   120  	return err == nil
   121  }
   122  
   123  // OpenFile behaves like os.Open,
   124  // but uses the build context's file system interface, if any.
   125  func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
   126  	if ctxt.OpenFile != nil {
   127  		return ctxt.OpenFile(path)
   128  	}
   129  	return os.Open(path)
   130  }
   131  
   132  // IsAbsPath behaves like filepath.IsAbs,
   133  // but uses the build context's file system interface, if any.
   134  func IsAbsPath(ctxt *build.Context, path string) bool {
   135  	if ctxt.IsAbsPath != nil {
   136  		return ctxt.IsAbsPath(path)
   137  	}
   138  	return filepath.IsAbs(path)
   139  }
   140  
   141  // JoinPath behaves like filepath.Join,
   142  // but uses the build context's file system interface, if any.
   143  func JoinPath(ctxt *build.Context, path ...string) string {
   144  	if ctxt.JoinPath != nil {
   145  		return ctxt.JoinPath(path...)
   146  	}
   147  	return filepath.Join(path...)
   148  }
   149  
   150  // IsDir behaves like os.Stat plus IsDir,
   151  // but uses the build context's file system interface, if any.
   152  func IsDir(ctxt *build.Context, path string) bool {
   153  	if ctxt.IsDir != nil {
   154  		return ctxt.IsDir(path)
   155  	}
   156  	fi, err := os.Stat(path)
   157  	return err == nil && fi.IsDir()
   158  }
   159  
   160  // ReadDir behaves like ioutil.ReadDir,
   161  // but uses the build context's file system interface, if any.
   162  func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
   163  	if ctxt.ReadDir != nil {
   164  		return ctxt.ReadDir(path)
   165  	}
   166  	return ioutil.ReadDir(path)
   167  }
   168  
   169  // SplitPathList behaves like filepath.SplitList,
   170  // but uses the build context's file system interface, if any.
   171  func SplitPathList(ctxt *build.Context, s string) []string {
   172  	if ctxt.SplitPathList != nil {
   173  		return ctxt.SplitPathList(s)
   174  	}
   175  	return filepath.SplitList(s)
   176  }