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 }