Skip to content

Commit ceecf5a

Browse files
committed
Find runfiles in directories that are themselves runfiles
When a target has a runfile that is contained in a directory that is itself one of its runfiles, the runfile will be shadowed by the SymlinkEntry for the directory. While this still allows to resolve the file in the runfiles symlink tree, a manifest-based lookup will fail. This PR extends the lookup logic to also find runfiles contained within directories that are themselves runfiles. It does so by searching the manifest not only for the exact provided rlocation path, but also for all path prefixes. If a prefix is looked up successfully, the corresponding suffix is resolved relative to the looked up path. See bazelbuild/bazel#14336 for more context.
1 parent 388095b commit ceecf5a

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

manifest.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bufio"
1919
"fmt"
2020
"os"
21+
"path"
2122
"path/filepath"
2223
"strings"
2324
)
@@ -60,13 +61,22 @@ func (f ManifestFile) parse() (manifest, error) {
6061

6162
func (m manifest) path(s string) (string, error) {
6263
r, ok := m[s]
63-
if !ok {
64-
return "", os.ErrNotExist
65-
}
66-
if r == "" {
64+
if ok && r == "" {
6765
return "", ErrEmpty
6866
}
69-
return r, nil
67+
if ok {
68+
return r, nil
69+
}
70+
// If path references a runfile that lies under a directory that itself is a
71+
// runfile, then only the directory is listed in the manifest. Look up all
72+
// prefixes of path in the manifest.
73+
for prefix := s; prefix != ""; prefix, _ = path.Split(prefix) {
74+
prefix = strings.TrimSuffix(prefix, "/")
75+
if prefixMatch, ok := m[prefix]; ok {
76+
return prefixMatch + strings.TrimPrefix(s, prefix), nil
77+
}
78+
}
79+
return "", os.ErrNotExist
7080
}
7181

7282
const manifestFileVar = "RUNFILES_MANIFEST_FILE"

runfiles_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,30 @@ func TestRunfiles_empty(t *testing.T) {
101101
t.Errorf("Path for empty file: got error %q, want something that wraps %q", got, want)
102102
}
103103
}
104+
105+
func TestRunfiles_manifestWithDir(t *testing.T) {
106+
dir := t.TempDir()
107+
manifest := filepath.Join(dir, "manifest")
108+
if err := os.WriteFile(manifest, []byte("foo/dir path/to/foo/dir\n"), 0600); err != nil {
109+
t.Fatal(err)
110+
}
111+
r, err := runfiles.New(runfiles.ManifestFile(manifest))
112+
if err != nil {
113+
t.Fatal(err)
114+
}
115+
for rlocation, want := range map[string]string{
116+
"foo/dir": "path/to/foo/dir",
117+
"foo/dir/file": "path/to/foo/dir/file",
118+
"foo/dir/deeply/nested/file": "path/to/foo/dir/deeply/nested/file",
119+
} {
120+
t.Run(rlocation, func(t *testing.T) {
121+
got, err := r.Path(rlocation)
122+
if err != nil {
123+
t.Fatalf("Path failed: got unexpected error %q", err)
124+
}
125+
if got != want {
126+
t.Errorf("Path failed: got %q, want %q", got, want)
127+
}
128+
})
129+
}
130+
}

0 commit comments

Comments
 (0)