func resolvePackageSpec(ctx *build.Context, cwd string, src io.Reader, spec string) string { if strings.HasSuffix(spec, ".go") { d := path.Dir(spec) if !buildutil.IsAbsPath(ctx, d) { d = buildutil.JoinPath(ctx, cwd, d) } if bpkg, err := ctx.ImportDir(d, build.FindOnly); err == nil { return bpkg.ImportPath } } path := spec switch { case strings.HasPrefix(spec, "."): if bpkg, err := ctx.Import(spec, cwd, build.FindOnly); err == nil { path = bpkg.ImportPath } case strings.HasPrefix(spec, "/"): path = spec[1:] default: if p, ok := readImports(cwd, src)[spec]; ok { path = p } } return strings.TrimSuffix(path, "/") }
// parseFiles parses the Go source files within directory dir and // returns the ASTs of the ones that could be at least partially parsed, // along with a list of I/O and parse errors encountered. // // I/O is done via ctxt, which may specify a virtual file system. // displayPath is used to transform the filenames attached to the ASTs. // func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) { if displayPath == nil { displayPath = func(path string) string { return path } } var wg sync.WaitGroup n := len(files) parsed := make([]*ast.File, n) errors := make([]error, n) for i, file := range files { if !buildutil.IsAbsPath(ctxt, file) { file = buildutil.JoinPath(ctxt, dir, file) } wg.Add(1) go func(i int, file string) { ioLimit <- true // wait defer func() { wg.Done() <-ioLimit // signal }() var rd io.ReadCloser var err error if ctxt.OpenFile != nil { rd, err = ctxt.OpenFile(file) } else { rd, err = os.Open(file) } if err != nil { errors[i] = err // open failed return } // ParseFile may return both an AST and an error. parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode) rd.Close() }(i, file) } wg.Wait() // Eliminate nils, preserving order. var o int for _, f := range parsed { if f != nil { parsed[o] = f o++ } } parsed = parsed[:o] o = 0 for _, err := range errors { if err != nil { errors[o] = err o++ } } errors = errors[:o] return parsed, errors }
func (dir *DirSpec) buildContext() (*build.Context, error) { if dir.ctx != nil { return dir.ctx, nil } ctx := build.Default // copy if dir.VCS != "" && dir.Revision != "" { if dir.root == "" { cmd := exec.Command("git", "rev-parse", "--show-toplevel") cmd.Dir = dir.Path out, err := cmd.Output() if err != nil { return nil, err } dir.root = strings.TrimRight(string(out), "\n") } repo, err := vcs.Open(dir.VCS, dir.root) if err != nil { return nil, err } commit, err := repo.ResolveRevision(dir.Revision) if err != nil { return nil, err } fs, err := repo.FileSystem(commit) if err != nil { return nil, err } ctx.IsDir = func(path string) bool { if buildutil.IsAbsPath(&ctx, path) { if strings.HasPrefix(path, dir.root) { var err error path, err = filepath.Rel(dir.root, path) if err != nil { return false } } else { fi, err := os.Stat(path) return err == nil && fi.IsDir() } } fi, err := fs.Stat(path) return err == nil && fi.IsDir() } ctx.OpenFile = func(path string) (io.ReadCloser, error) { if buildutil.IsAbsPath(&ctx, path) { // the path maybe outside of repository (for standard libraries) if strings.HasPrefix(path, dir.root) { var err error path, err = filepath.Rel(dir.root, path) if err != nil { return nil, err } } else { return os.Open(path) } } return fs.Open(path) } ctx.ReadDir = func(path string) ([]os.FileInfo, error) { if filepath.IsAbs(path) { if strings.HasPrefix(path, dir.root) { var err error path, err = filepath.Rel(dir.root, path) if err != nil { return nil, err } } else { return ioutil.ReadDir(path) } } return fs.ReadDir(path) } } dir.ctx = &ctx return dir.ctx, nil }