diff --git a/go/runfiles/fs.go b/go/runfiles/fs.go index 0383b3d5c..27c0dbf89 100644 --- a/go/runfiles/fs.go +++ b/go/runfiles/fs.go @@ -55,7 +55,7 @@ func (r *Runfiles) Open(name string) (fs.File, error) { return f, nil } // Return a special file for a repo dir that knows its apparent name. - return &repoDir{f.(fs.ReadDirFile), split[0]}, nil + return &repoDirFile{f.(fs.ReadDirFile), split[0]}, nil } type rootDir struct { @@ -107,7 +107,7 @@ func (r *rootDir) initEntries() error { if apparent, ok := canonicalToApparentName[e.Name()]; ok && e.IsDir() { // A repo directory that is visible to the current source repo is materialized // under its apparent name. - r.entries = append(r.entries, repoDirEntry{apparent, r.rf}) + r.entries = append(r.entries, repoDirEntry{e, apparent}) } else if _, ok := allCanonicalNames[e.Name()]; ok && e.IsDir() { // Skip repo directory that isn't visible to the current source repo. } else { @@ -123,12 +123,12 @@ func (r *rootDir) initEntries() error { return nil } -type repoDir struct { +type repoDirFile struct { fs.ReadDirFile name string } -func (r repoDir) Stat() (fs.FileInfo, error) { +func (r repoDirFile) Stat() (fs.FileInfo, error) { info, err := r.ReadDirFile.Stat() if err != nil { return nil, err @@ -136,22 +136,26 @@ func (r repoDir) Stat() (fs.FileInfo, error) { return repoDirFileInfo{info, r.name}, nil } -type repoDirFileInfo struct { - fs.FileInfo +type repoDirEntry struct { + fs.DirEntry name string } -func (r repoDirFileInfo) Name() string { return r.name } +func (r repoDirEntry) Name() string { return r.name } +func (r repoDirEntry) Info() (fs.FileInfo, error) { + info, err := r.DirEntry.Info() + if err != nil { + return nil, err + } + return repoDirFileInfo{info, r.name}, nil +} -type repoDirEntry struct { +type repoDirFileInfo struct { + fs.FileInfo name string - rf *Runfiles } -func (r repoDirEntry) Name() string { return r.name } -func (r repoDirEntry) IsDir() bool { return true } -func (r repoDirEntry) Type() fs.FileMode { return fs.ModeDir } -func (r repoDirEntry) Info() (fs.FileInfo, error) { return fs.Stat(r.rf, r.name) } +func (r repoDirFileInfo) Name() string { return r.name } type emptyFile string diff --git a/go/runfiles/manifest.go b/go/runfiles/manifest.go index 4ef34da58..25b10b8fb 100644 --- a/go/runfiles/manifest.go +++ b/go/runfiles/manifest.go @@ -125,6 +125,9 @@ func (m *manifest) open(name string) (fs.File, error) { } } + // At this point the file is not directly listed in the manifest (or + // contained in a directory that is). We lazily build a trie to allow + // efficient listing of the contents of intermediate directories. m.initTrie() dir := m.trie @@ -187,12 +190,16 @@ func (m *manifestReadDirFile) ReadDir(n int) ([]fs.DirEntry, error) { } entries := m.entries[:n] m.entries = m.entries[n:] + var dirEntries []fs.DirEntry for _, e := range entries { var info fs.FileInfo if e.path == "" { + // The entry corresponds to a directory that is a prefix of some + // manifest entry. We represent it as a read-only directory. info = dirFileInfo(e.name) } else { + // The entry corresponds to a real file in the manifest. var err error info, err = os.Stat(e.path) if err != nil { diff --git a/go/runfiles/runfiles.go b/go/runfiles/runfiles.go index f2385b085..e602be655 100644 --- a/go/runfiles/runfiles.go +++ b/go/runfiles/runfiles.go @@ -77,6 +77,11 @@ const noSourceRepoSentinel = "_not_a_valid_repository_name" // // See section “Runfiles discovery” in // https://docs.google.com/document/d/e/2PACX-1vSDIrFnFvEYhKsCMdGdD40wZRBX3m3aZ5HhVj4CtHPmiXKDCxioTUbYsDydjKtFDAzER5eg7OjJWs3V/pub. +// +// The returned object implements fs.FS regardless of the type of runfiles +// that backs it. This is the preferred way to interact with runfiles in a +// platform-agnostic way. For example, to find all runfiles beneath a +// directory, use fs.Glob. func New(opts ...Option) (*Runfiles, error) { var o options o.sourceRepo = noSourceRepoSentinel diff --git a/tests/runfiles/testfs/testfs.go b/tests/runfiles/testfs/testfs.go index cb6ac6cce..f297eb73a 100644 --- a/tests/runfiles/testfs/testfs.go +++ b/tests/runfiles/testfs/testfs.go @@ -1,10 +1,14 @@ +// Taken from +// https://github.com/golang/go/blob/e8ee1dc4f9e2632ba1018610d1a1187743ae397f/src/testing/fstest/testfs.go +// +// Modifications: +// - fixed https://github.com/golang/go/issues/50401 by statting a dir entry to determine whether it +// is a symlink that resolves to a directory +// // Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.16 -// +build go1.16 - package testfs import (