GoPLS Viewer

Home|gopls/go/buildutil/util.go
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
5package buildutil
6
7import (
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    "strings"
19)
20
21// ParseFile behaves like parser.ParseFile,
22// but uses the build context's file system interface, if any.
23//
24// If file is not absolute (as defined by IsAbsPath), the (dir, file)
25// components are joined using JoinPath; dir must be absolute.
26//
27// The displayPath function, if provided, is used to transform the
28// filename that will be attached to the ASTs.
29//
30// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
31func ParseFile(fset *token.FileSetctxt *build.ContextdisplayPath func(stringstringdir stringfile stringmode parser.Mode) (*ast.Fileerror) {
32    if !IsAbsPath(ctxtfile) {
33        file = JoinPath(ctxtdirfile)
34    }
35    rderr := OpenFile(ctxtfile)
36    if err != nil {
37        return nilerr
38    }
39    defer rd.Close() // ignore error
40    if displayPath != nil {
41        file = displayPath(file)
42    }
43    return parser.ParseFile(fsetfilerdmode)
44}
45
46// ContainingPackage returns the package containing filename.
47//
48// If filename is not absolute, it is interpreted relative to working directory dir.
49// All I/O is via the build context's file system interface, if any.
50//
51// The '...Files []string' fields of the resulting build.Package are not
52// populated (build.FindOnly mode).
53func ContainingPackage(ctxt *build.Contextdirfilename string) (*build.Packageerror) {
54    if !IsAbsPath(ctxtfilename) {
55        filename = JoinPath(ctxtdirfilename)
56    }
57
58    // We must not assume the file tree uses
59    // "/" always,
60    // `\` always,
61    // or os.PathSeparator (which varies by platform),
62    // but to make any progress, we are forced to assume that
63    // paths will not use `\` unless the PathSeparator
64    // is also `\`, thus we can rely on filepath.ToSlash for some sanity.
65
66    dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
67
68    // We assume that no source root (GOPATH[i] or GOROOT) contains any other.
69    for _srcdir := range ctxt.SrcDirs() {
70        srcdirSlash := filepath.ToSlash(srcdir) + "/"
71        if importPathok := HasSubdir(ctxtsrcdirSlashdirSlash); ok {
72            return ctxt.Import(importPathdirbuild.FindOnly)
73        }
74    }
75
76    return nilfmt.Errorf("can't find package containing %s"filename)
77}
78
79// -- Effective methods of file system interface -------------------------
80
81// (go/build.Context defines these as methods, but does not export them.)
82
83// HasSubdir calls ctxt.HasSubdir (if not nil) or else uses
84// the local file system to answer the question.
85func HasSubdir(ctxt *build.Contextrootdir string) (rel stringok bool) {
86    if f := ctxt.HasSubdirf != nil {
87        return f(rootdir)
88    }
89
90    // Try using paths we received.
91    if relok = hasSubdir(rootdir); ok {
92        return
93    }
94
95    // Try expanding symlinks and comparing
96    // expanded against unexpanded and
97    // expanded against expanded.
98    rootSym_ := filepath.EvalSymlinks(root)
99    dirSym_ := filepath.EvalSymlinks(dir)
100
101    if relok = hasSubdir(rootSymdir); ok {
102        return
103    }
104    if relok = hasSubdir(rootdirSym); ok {
105        return
106    }
107    return hasSubdir(rootSymdirSym)
108}
109
110func hasSubdir(rootdir string) (rel stringok bool) {
111    const sep = string(filepath.Separator)
112    root = filepath.Clean(root)
113    if !strings.HasSuffix(rootsep) {
114        root += sep
115    }
116
117    dir = filepath.Clean(dir)
118    if !strings.HasPrefix(dirroot) {
119        return ""false
120    }
121
122    return filepath.ToSlash(dir[len(root):]), true
123}
124
125// FileExists returns true if the specified file exists,
126// using the build context's file system interface.
127func FileExists(ctxt *build.Contextpath stringbool {
128    if ctxt.OpenFile != nil {
129        rerr := ctxt.OpenFile(path)
130        if err != nil {
131            return false
132        }
133        r.Close() // ignore error
134        return true
135    }
136    _err := os.Stat(path)
137    return err == nil
138}
139
140// OpenFile behaves like os.Open,
141// but uses the build context's file system interface, if any.
142func OpenFile(ctxt *build.Contextpath string) (io.ReadClosererror) {
143    if ctxt.OpenFile != nil {
144        return ctxt.OpenFile(path)
145    }
146    return os.Open(path)
147}
148
149// IsAbsPath behaves like filepath.IsAbs,
150// but uses the build context's file system interface, if any.
151func IsAbsPath(ctxt *build.Contextpath stringbool {
152    if ctxt.IsAbsPath != nil {
153        return ctxt.IsAbsPath(path)
154    }
155    return filepath.IsAbs(path)
156}
157
158// JoinPath behaves like filepath.Join,
159// but uses the build context's file system interface, if any.
160func JoinPath(ctxt *build.Contextpath ...stringstring {
161    if ctxt.JoinPath != nil {
162        return ctxt.JoinPath(path...)
163    }
164    return filepath.Join(path...)
165}
166
167// IsDir behaves like os.Stat plus IsDir,
168// but uses the build context's file system interface, if any.
169func IsDir(ctxt *build.Contextpath stringbool {
170    if ctxt.IsDir != nil {
171        return ctxt.IsDir(path)
172    }
173    fierr := os.Stat(path)
174    return err == nil && fi.IsDir()
175}
176
177// ReadDir behaves like ioutil.ReadDir,
178// but uses the build context's file system interface, if any.
179func ReadDir(ctxt *build.Contextpath string) ([]os.FileInfoerror) {
180    if ctxt.ReadDir != nil {
181        return ctxt.ReadDir(path)
182    }
183    return ioutil.ReadDir(path)
184}
185
186// SplitPathList behaves like filepath.SplitList,
187// but uses the build context's file system interface, if any.
188func SplitPathList(ctxt *build.Contexts string) []string {
189    if ctxt.SplitPathList != nil {
190        return ctxt.SplitPathList(s)
191    }
192    return filepath.SplitList(s)
193}
194
195// sameFile returns true if x and y have the same basename and denote
196// the same file.
197func sameFile(xy stringbool {
198    if path.Clean(x) == path.Clean(y) {
199        return true
200    }
201    if filepath.Base(x) == filepath.Base(y) { // (optimisation)
202        if xierr := os.Stat(x); err == nil {
203            if yierr := os.Stat(y); err == nil {
204                return os.SameFile(xiyi)
205            }
206        }
207    }
208    return false
209}
210
MembersX
HasSubdir.f
SplitPathList
sameFile.x
sameFile.BlockStmt.xi
HasSubdir.rootSym
hasSubdir.ok
FileExists.err
ReadDir.ctxt
ast
parser
ParseFile.err
ContainingPackage.RangeStmt_2123.BlockStmt.importPath
OpenFile
sameFile
ParseFile.fset
ParseFile.dir
hasSubdir
FileExists.BlockStmt.err
sameFile.BlockStmt.err
ContainingPackage
ContainingPackage.dir
HasSubdir.dirSym
JoinPath
ContainingPackage.RangeStmt_2123.BlockStmt.ok
sameFile.BlockStmt.BlockStmt.err
IsAbsPath.ctxt
IsDir
ParseFile.ctxt
ContainingPackage.RangeStmt_2123.srcdir
HasSubdir
HasSubdir.rel
sameFile.BlockStmt.BlockStmt.yi
ParseFile.mode
HasSubdir.root
hasSubdir.dir
FileExists.path
IsDir.err
ReadDir.path
FileExists
FileExists._
IsAbsPath.path
IsDir.fi
ParseFile.displayPath
FileExists.BlockStmt.r
SplitPathList.ctxt
token
IsAbsPath
JoinPath.path
ReadDir
ParseFile.rd
HasSubdir._
FileExists.ctxt
sameFile.y
IsDir.path
HasSubdir.ok
hasSubdir.root
OpenFile.ctxt
OpenFile.path
SplitPathList.s
JoinPath.ctxt
ContainingPackage.ctxt
HasSubdir.ctxt
HasSubdir.dir
hasSubdir.rel
ParseFile.file
ContainingPackage.filename
IsDir.ctxt
ParseFile
Members
X