Fix //test/rustfmt/... on Windows#872
Conversation
`test_main.rs` was looking for runfiles by getting a path to the runfiles dir, and then walking the tree looking for files with a particular extension. This does not work where runfiles are manifest-based rather than directory-based, as is the case on Windows. This change fixes the issue by growing a `list_files` method on the `Runfiles` struct that higher-level code can use to enumerate all the runfiles, filtering out the files they're looking for. Since `find_runfiles_dir` was public purely for this one use case, it can become private; likely higher-level code shouldn't be using this function directly, but rather interacting with runfiles through a Runfiles object. Issues: bazelbuild#619
rustfmt_test should be finding its rustfmt binary and config via runfiles, but it is finding them by naively joining the supplied path with its current working directory. This works for directory-based runfiles, but not manifest-based runfiles. This change makes the discovery of rustfmt and the associated config runfiles-aware.
This change passes the files under test to rustfmt with a path decoded by runfiles, allowing it to work with manifest-based runfiles.
|
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here with What to do if you already signed the CLAIndividual signers
Corporate signers
ℹ️ Googlers: Go here for more info. |
|
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here with What to do if you already signed the CLAIndividual signers
Corporate signers
ℹ️ Googlers: Go here for more info. |
| }; | ||
| std::fs::metadata(&absolute_path).map(|_| absolute_path) | ||
| pub fn current_dir_name() -> PathBuf { | ||
| PathBuf::from(std::env::current_dir().unwrap().file_name().unwrap()) |
There was a problem hiding this comment.
General runfiles question:
Runfiles seem to be passed in/specified as e.g. external/rust_windows_x86_64/bin/rustfmt.exe, but sometimes (concretely, when using manifest-based runfiles) they're expected to be resolved with a workspace name prefix (rules_rust/external/rust_windows_x86_64/bin/rustfmt.exe, in this example, with a rules_rust prefix)
Where is that prefix supposed to come from? Is client code/the runfiles library supposed to assume the workspace name, or divine it somehow? This happens to coincide with the basename of the CWD for both runfiles implementations - hence this sketchy function here - but I'd like to better understand how runfiles are supposed to work r.e. this workspace prefix.
There was a problem hiding this comment.
This is a good question. I don't think the prefix is expected to be passed anywhere. Bazel uses a launcher to set some environment variables dictating the locations of runfiles related paths. My experience is that bazel suggests paths to be treated as relative paths from the runfiles directory which is also suggested to be a relative path from the binary. I say "suggest" here since Bazel produces outputs this way but it's obviously up to the author to maintain this if the outputs are distributed in some way.
... This very likely doesn't answer your question. Maybe rephrase and I can try to be more direct 😅
| pub fn from_slash(p: PathBuf) -> PathBuf { | ||
| let s = p | ||
| .to_string_lossy() | ||
| .chars() | ||
| .map(|c| match c { | ||
| '/' => std::path::MAIN_SEPARATOR, | ||
| c => c, | ||
| }) | ||
| .collect::<String>(); | ||
| PathBuf::from(s) |
There was a problem hiding this comment.
Essentially https://docs.rs/path-slash/0.1.4/src/path_slash/lib.rs.html#302; I would have also not liked to re-implement a directory walking function. Can this repo have third-party dependencies on these crates?
There was a problem hiding this comment.
There are ways to use 3rd party crates but for this particular tool I'd say it's not desirable since it'd require users to load the dependencies before the target can be used. Maybe there's an easy path forward there that I'm not thinking about but my current thought is that this target should remain free of 3rd party deps 😅
UebelAndre
left a comment
There was a problem hiding this comment.
Sorry for the delay, I like the changes here! 😄
From my end there's just one request for a test and I hope I was able to answer some of your questions.
| pub fn from_slash(p: PathBuf) -> PathBuf { | ||
| let s = p | ||
| .to_string_lossy() | ||
| .chars() | ||
| .map(|c| match c { | ||
| '/' => std::path::MAIN_SEPARATOR, | ||
| c => c, | ||
| }) | ||
| .collect::<String>(); | ||
| PathBuf::from(s) |
There was a problem hiding this comment.
There are ways to use 3rd party crates but for this particular tool I'd say it's not desirable since it'd require users to load the dependencies before the target can be used. Maybe there's an easy path forward there that I'm not thinking about but my current thought is that this target should remain free of 3rd party deps 😅
| }; | ||
| std::fs::metadata(&absolute_path).map(|_| absolute_path) | ||
| pub fn current_dir_name() -> PathBuf { | ||
| PathBuf::from(std::env::current_dir().unwrap().file_name().unwrap()) |
There was a problem hiding this comment.
This is a good question. I don't think the prefix is expected to be passed anywhere. Bazel uses a launcher to set some environment variables dictating the locations of runfiles related paths. My experience is that bazel suggests paths to be treated as relative paths from the runfiles directory which is also suggested to be a relative path from the binary. I say "suggest" here since Bazel produces outputs this way but it's obviously up to the author to maintain this if the outputs are distributed in some way.
... This very likely doesn't answer your question. Maybe rephrase and I can try to be more direct 😅
| /// Does not return directories. | ||
| /// The returned path may not be valid. The caller should check the path's | ||
| /// validity and that the path exists. | ||
| pub fn list_files(&self) -> Vec<PathBuf> { |
There was a problem hiding this comment.
Would you be willing to add a test for this? And perhaps add a few more sample text files to //tools/runfiles/data to make the test a bit more interesting?
There was a problem hiding this comment.
Hmm I actually would like to see if we could get away with not having this function at all. Other runfiles libraries didn't need it it seems (https://osscs.corp.google.com/bazel/bazel/+/master:tools/cpp/runfiles/runfiles_src.h, https://osscs.corp.google.com/bazel/bazel/+/master:tools/java/runfiles/Runfiles.java, https://osscs.corp.google.com/bazel/bazel/+/master:tools/bash/runfiles/runfiles.bash).
It seems the main motivation is to be able to find rustfmt manifests in the test data. Is there a reason why we cannot hardcode paths to manifests in the test?
There was a problem hiding this comment.
Unless a unique binary is compiled for each use of the rustfmt test binary, there isn't really a way to hard code the values. I think it'd be more desirable to continue to use a shared binary and locate manifests in runfiles than to compile unique binaries but if there's a well defined pattern for runfiles libraries then I'd happily respect that. If there is though, I'd question whether or not the implementations for each language could live all in the same place.
|
Hey, friendly ping on this PR 😄 Let me know if I can do something to help! |
|
Closing out in favor of #1375 |
This change aims to fix
//test/rustfmt/...tests under Windows.The root cause of test failures is, generally speaking, attempting to locate runfiles with logic that assumes directory-based runfiles, which is not available on Windows. This change attempts to find all files through the
Runfilesutility object so that it can work cross-platform, adding helper methods where necessary to facilitate higher-level logic.Fixes: #619