// 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 }
// FileExists returns true if the specified file exists, // using the build context's file system interface. func FileExists(ctxt *build.Context, path string) bool { if ctxt.OpenFile != nil { r, err := ctxt.OpenFile(path) if err != nil { return false } r.Close() // ignore error return true } _, err := os.Stat(path) return err == nil }
// Setup configures a *build.Context to use the given VFS // as its filesystem. func Setup(ctx *build.Context, fs vfs.VFS) { ctx.JoinPath = path.Join ctx.SplitPathList = filepath.SplitList ctx.IsAbsPath = func(p string) bool { return p != "" && p[0] == '/' } ctx.IsDir = func(p string) bool { stat, err := fs.Stat(p) return err == nil && stat.IsDir() } ctx.HasSubdir = func(root, dir string) (string, bool) { root = path.Clean(root) if !strings.HasSuffix(root, separator) { root += separator } dir = path.Clean(dir) if !strings.HasPrefix(dir, root) { return "", false } return dir[len(root):], true } ctx.ReadDir = fs.ReadDir ctx.OpenFile = func(p string) (io.ReadCloser, error) { return fs.Open(p) } }
// parseFiles parses the Go source files files within directory dir // and returns their ASTs, or the first parse error if any. // // 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 } } isAbs := filepath.IsAbs if ctxt.IsAbsPath != nil { isAbs = ctxt.IsAbsPath } joinPath := filepath.Join if ctxt.JoinPath != nil { joinPath = ctxt.JoinPath } var wg sync.WaitGroup n := len(files) parsed := make([]*ast.File, n) errors := make([]error, n) for i, file := range files { if !isAbs(file) { file = joinPath(dir, file) } wg.Add(1) go func(i int, file string) { defer wg.Done() var rd io.ReadCloser var err error if ctxt.OpenFile != nil { rd, err = ctxt.OpenFile(file) } else { rd, err = os.Open(file) } defer rd.Close() if err != nil { errors[i] = err return } parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode) }(i, file) } wg.Wait() for _, err := range errors { if err != nil { return nil, err } } return parsed, nil }
func (w *Walker) setMemoryContext(ctxt *build.Context) { ctxt.JoinPath = path.Join ctxt.IsAbsPath = path.IsAbs ctxt.IsDir = func(path string) bool { panic("unexpected") } ctxt.HasSubdir = func(root, dir string) (rel string, ok bool) { panic("unexpected") } ctxt.ReadDir = func(dir string) (fi []os.FileInfo, err error) { return w.readDir(dir) } ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) { return w.openFile(path) } }
func (pkg *pkg) parseFile(ctx *build.Context, name string) (*ast.File, error) { f, err := ctx.OpenFile(ctx.JoinPath(pkg.Build.Dir, name)) if err != nil { return nil, err } defer f.Close() p, err := ioutil.ReadAll(f) if err != nil { return nil, err } // overwrite //line comments for _, m := range linePat.FindAllIndex(p, -1) { for i := m[0] + 2; i < m[1]; i++ { p[i] = ' ' } } return parser.ParseFile(pkg.FSet, name, p, parser.ParseComments) }
// Import returns details about the package in the directory. func (dir *Directory) Import(ctx *build.Context, mode build.ImportMode) (*build.Package, error) { safeCopy := *ctx ctx = &safeCopy ctx.JoinPath = path.Join ctx.IsAbsPath = path.IsAbs ctx.SplitPathList = func(list string) []string { return strings.Split(list, ":") } ctx.IsDir = func(path string) bool { return false } ctx.HasSubdir = func(root, dir string) (rel string, ok bool) { return "", false } ctx.ReadDir = dir.readDir ctx.OpenFile = dir.openFile return ctx.ImportDir(".", mode) }
// OpenFile behaves like os.Open, // but uses the build context's file system interface, if any. func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) { if ctxt.OpenFile != nil { return ctxt.OpenFile(path) } return os.Open(path) }
func getPackage(pkgpath string) (*gobuild.Package, error) { var ctx gobuild.Context = gobuild.Default ctx.GOARCH = GOARCH ctx.GOOS = GOOS ctx.BuildTags = append(ctx.BuildTags[:], "llgo") //ctx.Compiler = "llgo" // Attempt to find an overlay package path, // which we'll use in ReadDir below. overlayentries := make(map[string]bool) overlaypkgpath := llgoPkgPrefix + pkgpath overlaypkg, err := ctx.Import(overlaypkgpath, "", gobuild.FindOnly) if err != nil { overlaypkg = nil } // ReadDir is overridden to return a fake ".s" // file for each ".ll" file in the directory. ctx.ReadDir = func(dir string) (fi []os.FileInfo, err error) { fi, err = ioutil.ReadDir(dir) if err != nil { return nil, err } entries := make(map[string]os.FileInfo) for _, info := range fi { entries[info.Name()] = info } // Overlay all files in the overlay package dir. // If we find any .ll files, replace the suffix // with .s. if overlaypkg != nil { fi, err = ioutil.ReadDir(overlaypkg.Dir) } if err == nil { // Check for .ll files in the overlay dir if // we have one, else in the standard package dir. for _, info := range fi { name := info.Name() if strings.HasSuffix(name, ".ll") { name = name[:len(name)-3] + ".s" info = &renamedFileInfo{info, name} } overlayentries[name] = true entries[name] = info } } fi = make([]os.FileInfo, 0, len(entries)) for _, info := range entries { fi = append(fi, info) } return fi, nil } // OpenFile is overridden to return the contents // of the ".ll" file found in ReadDir above. The // returned ReadCloser is wrapped to transform // LLVM IR comments to use "//", as expected by // go/build when looking for build tags. ctx.OpenFile = func(path string) (io.ReadCloser, error) { base := filepath.Base(path) overlay := overlayentries[base] if overlay { if overlaypkg != nil { path = filepath.Join(overlaypkg.Dir, base) } if strings.HasSuffix(path, ".s") { path := path[:len(path)-2] + ".ll" var r io.ReadCloser var err error r, err = os.Open(path) if err == nil { r = build.NewLLVMIRReader(r) } return r, err } } return os.Open(path) } pkg, err := ctx.Import(pkgpath, "", 0) if err != nil { return nil, err } else { if overlaypkg == nil { overlaypkg = pkg } for i, filename := range pkg.GoFiles { pkgdir := pkg.Dir if overlayentries[filename] { pkgdir = overlaypkg.Dir } pkg.GoFiles[i] = path.Join(pkgdir, filename) } for i, filename := range pkg.CFiles { pkgdir := pkg.Dir if overlayentries[filename] { pkgdir = overlaypkg.Dir } pkg.CFiles[i] = path.Join(pkgdir, filename) } for i, filename := range pkg.SFiles { pkgdir := pkg.Dir if overlayentries[filename] { pkgdir = overlaypkg.Dir filename = filename[:len(filename)-2] + ".ll" } else { err := fmt.Errorf("No matching .ll file for %q", filename) return nil, err } pkg.SFiles[i] = path.Join(pkgdir, filename) } } return pkg, nil }