diff --git a/manifest.go b/manifest.go index c871357..509ed9f 100644 --- a/manifest.go +++ b/manifest.go @@ -59,6 +59,32 @@ func (f ManifestFile) parse() (manifest, error) { } func (m manifest) path(s string) (string, error) { + r, err := m.pathExact(s) + if err == nil || err == ErrEmpty { + return r, err + } + // If path references a runfile that lies under a directory that itself is a + // runfile, then only the directory is listed in the manifest. Look up all + // prefixes of path in the manifest. + prefixEnd := len(s) + for { + prefixEnd = strings.LastIndex(s[0:prefixEnd-1], "/") + if prefixEnd == -1 { + break + } + prefixMatch, err := m.pathExact(s[0:prefixEnd]) + if err == ErrEmpty { + return "", ErrEmpty + } + if err == nil { + suffixPathSegments := strings.Split(s[prefixEnd+1:], "/") + return filepath.Join(append([]string{prefixMatch}, suffixPathSegments...)...), nil + } + } + return "", os.ErrNotExist +} + +func (m manifest) pathExact(s string) (string, error) { r, ok := m[s] if !ok { return "", os.ErrNotExist diff --git a/runfiles_test.go b/runfiles_test.go index 1aba1e7..4ccc9cd 100644 --- a/runfiles_test.go +++ b/runfiles_test.go @@ -19,6 +19,7 @@ import ( "fmt" "os" "os/exec" + "path" "path/filepath" "testing" @@ -101,3 +102,31 @@ func TestRunfiles_empty(t *testing.T) { t.Errorf("Path for empty file: got error %q, want something that wraps %q", got, want) } } + +var manifestWithDirTestCases = map[string]string { + "foo/dir": "path/to/foo/dir", + "foo/dir/file": path.Join("path/to/foo/dir", "file"), + "foo/dir/deeply/nested/file": path.Join("path/to/foo/dir", "deeply", "nested", "file"), +} + +func TestRunfiles_manifestWithDir(t *testing.T) { + dir := t.TempDir() + manifest := filepath.Join(dir, "manifest") + if err := os.WriteFile(manifest, []byte("foo/dir path/to/foo/dir\n"), 0600); err != nil { + t.Fatal(err) + } + r, err := runfiles.New(runfiles.ManifestFile(manifest), runfiles.ProgramName("/invalid"), runfiles.Directory("/invalid")) + if err != nil { + t.Fatal(err) + } + for rlocation, want := range manifestWithDirTestCases { + got, err := r.Path(rlocation) + if err != nil { + t.Errorf("Path for %q: got unexpected error %q", rlocation, err) + continue + } + if got != want { + t.Errorf("Path for %q: got %q, want %q", rlocation, got, want) + } + } +}