Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting a runfiles directory with runfiles.Rlocation instead of bazel.Runfile #3830

Closed
bcspragu opened this issue Jan 15, 2024 · 3 comments · Fixed by #3969
Closed

Getting a runfiles directory with runfiles.Rlocation instead of bazel.Runfile #3830

bcspragu opened this issue Jan 15, 2024 · 3 comments · Fixed by #3969

Comments

@bcspragu
Copy link
Contributor

bcspragu commented Jan 15, 2024

What version of rules_go are you using?

v0.45.0

What version of gazelle are you using?

v0.35.0

What version of Bazel are you using?

6.1.2

Does this issue reproduce with the latest releases of all the above?

rules_go and gazelle are at the latest, Bazel isn't but I don't think it should affect this

What operating system and processor architecture are you using?

Linux, x86-64

What did you do?

I noticed that bazel.Runfile was deprecated and recommended the runfiles package as a replacement.

The problem I have is that runfiles.Rlocation, which I believe is the replacement, doesn't work for directories as far as I can tell.

What did you expect to see?

I expected bazel.Runfile("some/directory") and runfiles.Rlocation("some/directory") to return the same result.

What did you see instead?

Instead, I get the error runfile some/directory: file does not exist.

I'm able to work around this with:

p, err := runfiles.Rlocation("__main__/some/directory/a_file_I_know_exists")
if err != nil { ... }
p = filepath.Dir(p)
// p is now what bazel.Runfile("some/directory") used to return

I'm not sure if I'm just using the wrong helper/missing something, but this workaround isn't great, as a_file_I_know_exists is a pretty hacky and specific construct, and I also am not sure where __main__ comes from (was a shot in the dark based on printing a runfiles.Runfiles)

@bcspragu bcspragu changed the title Getting a runfiles directory with runfiles.RLocation instead of bazel.Runfile Getting a runfiles directory with runfiles.Rlocation instead of bazel.Runfile Jan 15, 2024
@fmeum
Copy link
Member

fmeum commented Jan 16, 2024

Instead of __main__, you can also use the name of your main workspace or module (depending on whether you use Bzlmod).

Bazel's runfiles layout doesn't guarantee that the files under the logical runfiles directory __main__/some/directory all lie in the same physical directory. bazel.Runfiles used certain heuristics that may be non-hermetic and also don't work cross-platform.

The proper way to make your use case work would be to add an implementation of fs.FS to the runfiles instance. Help and contributions in this area would definitely be appreciated.

In the meantime, you can use a helper function that accepts both a runfiles path to a directory and the relative path of a file inside it and returns the actual path of the directory. This is what I'm currently using myself.

@bcspragu
Copy link
Contributor Author

bcspragu commented Jan 16, 2024

The proper way to make your use case work would be to add an implementation of fs.FS to the runfiles instance. Help and contributions in this area would definitely be appreciated.

I'm happy to contribute something here, though I don't understand what it means to add an impl of fs.FS in this context. I see that *runfiles.Runfiles implements fs.FS, but under the hood, that's just calling r.Rlocation. I see the Directory implementation of path, which seems relevant, but I'm not sure how this all fits together.

Bazel's runfiles layout doesn't guarantee that the files under the logical runfiles directory main/some/directory all lie in the same physical directory.

That's odd, but I guess it doesn't matter as long as there are at least links in the right location. My use case here seems like a pretty standard happy path. I have a filegroup of some glob(['*.ext']) in a directory, and I pass that filegroup in as a data attribute to something that wants to look at all those *.ext, without referencing individual ones directly. Am I missing a more direct route to getting the path of the directory containing those files in Bazel's runfiles?

@fmeum
Copy link
Member

fmeum commented Jan 20, 2024

I see that *runfiles.Runfiles implements fs.FS, but under the hood, that's just calling r.Rlocation.

Yep, that's exactly the problem: A fully featured fs.FS implementation would also implement https://pkg.go.dev/io/fs#ReadDirFS and provide logic that doesn't just go through r.Rlocation. For Directory, this would be straightforward, but for Manifest, the other supported runfiles mode, it would require more effort.

Am I missing a more direct route to getting the path of the directory containing those files in Bazel's runfiles?

No, there isn't, but it's not a simple omission. Since Bazel supports both directory-based and manifest-based runfiles and the latter are not realized on disk, there may not be a single filesystem path that contains all those files. The fs.FS abstraction would help here, as would using runfiles.New to explicitly force a Directory implementation if you know at you are only going to support Unix systems with --enable_runfiles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants