-
Notifications
You must be signed in to change notification settings - Fork 440
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
rust-analyzer
discoverConfig integration
#3073
base: main
Are you sure you want to change the base?
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
d8ef7e4
to
80a45c1
Compare
267aa69
to
ea06599
Compare
@sam-mccall I noticed you've been working on things adjacent to this in other recent PRs. Just adding you here for visibility reasons. Any advice you can offer regarding the PR is more than welcome. |
Thanks! Yes I had a draft version running locally that I'd planned to clean up and send, but I'm happy you got there first. This PR combines a few logically-separate changes. I get it - there are a bunch of details that all need to be right for this to work end-to-end. (I ran into some overlapping-but-different set of these myself). I'll try to understand them all and provide high-level comments first, and then let you figure out whether to split them out and land the more "obvious" stuff first. (e.g. having a separate output-base and injecting blaze configuration are useful, but different users will have different needs here). (I did spot this last friday and started to review, but have been unwell this week...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great stuff, really thorough job! Some parts of the protocol (logging & runnables) were totally new to me.
High level:
- starlark changes (proc macros & build scripts) are good. Related: rust_analyzer: generate more reqired sources #3031 includes some other generated files.
- A separate binary for
discover
sounds good to me (I had this as a flag ongen_rust_project
, but it's messy) - this is background infra that should "do what I mean", as such we want fewer flags and more detection.
- some things we will eventually want should inform flags/detection:
- become a standalone binary (not invoked through
blaze run
) - automatically select an
output_base
- become a standalone binary (not invoked through
- a
BlazeSpec
(argv0, workspace, execroot, ...) would be a useful abstraction, replacingConfig
and long param lists - clearer split between main (config + protocol) and library (build+query+describe) would make the code easier to follow.
Let me know what you think - as I said I'm not an owner here. I can nag one to get you a stamp :-) but also feel free to get a second opinion instead.
If you're interested, this could be a few PRs - might go quicker, but totally up to you:
- proc macros + build scripts
- refactor current bin+lib to make lib reusable
- add discover tool
- runnables
build_info = build_info, | ||
)) | ||
|
||
return [ | ||
rust_analyzer_info, | ||
OutputGroupInfo(rust_analyzer_crate_spec = rust_analyzer_info.crate_specs), | ||
OutputGroupInfo( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rust_analyzer_build_info_out_dirs
is really about ensuring generated sources are available, and should be named as such: rust_analyzer_srcs
. See https://github.com/bazelbuild/rules_rust/pull/3031/files. These don't need to be distinct output groups as from the caller's point of view it doesn't make sense to want one without the other.
Having three output groups does make sense to me though: crate specs are the bare minimum and I can imagine producing the rest asynchronously, and proc macros are optional (we use a toolchain that doesn't even have the proc-macro-srv...)
"deps": "List[String]: IDs of direct dependency crates", | ||
"env": "Dict[String: String]: Environment variables, used for the `env!` macro", | ||
"id": "String: Arbitrary unique ID for this crate", | ||
"proc_macro_dylib_path": "File: compiled shared library output of proc-macro rule", | ||
"proc_macro_dylib": "File: compiled shared library output of proc-macro rule", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "if this is a proc-macro target, the shared-library output"?
(wasn't clear to me which macro, and "rule" is wrong here I think)
@@ -162,17 +162,21 @@ RustAnalyzerInfo = provider( | |||
"cfgs": "List[String]: features or other compilation `--cfg` settings", | |||
"crate": "CrateInfo: Crate information.", | |||
"crate_specs": "Depset[File]: transitive closure of OutputGroupInfo files", | |||
"proc_macro_dylibs": "Depset[File]: transitive closure of OutputGroupInfo files", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should have clear names and comments (the comment on crate_specs is bad; maybe fix while here?)
@@ -45,6 +45,8 @@ def write_rust_analyzer_spec_file(ctx, attrs, owner, base_info): | |||
RustAnalyzerInfo: Info with the embedded spec file. | |||
""" | |||
crate_spec = ctx.actions.declare_file("{}.rust_analyzer_crate_spec.json".format(owner.name)) | |||
proc_macro_dylibs = [base_info.proc_macro_dylib] if base_info.proc_macro_dylib else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is just to write the file, everything with the RustAnalyzerInfo should be a pure passthrough.
(This function is ugly and there's probably a way to get rid of it, but failing that it should at least stay trivial)
@@ -221,6 +247,13 @@ def _create_single_crate(ctx, attrs, info): | |||
crate["root_module"] = path_prefix + info.crate.root.path | |||
crate["source"] = {"exclude_dirs": [], "include_dirs": []} | |||
|
|||
# Store build system related info only for local crates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment echoes the code, can you say why instead?
To me it's not obvious why, but I guess it's to prevent blaze test
runnables from showing up for other crates? But is "local" the right choice there, rather than "top-level" i.e. targets from the selected blaze package?
@@ -108,6 +223,31 @@ pub fn generate_rust_project( | |||
sysroot: Some(sysroot.into()), | |||
sysroot_src: Some(sysroot_src.into()), | |||
crates: Vec::new(), | |||
runnables: vec![ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) runnables
is not documented in the rust-project.json
spec, we should fix that.
@@ -108,6 +223,31 @@ pub fn generate_rust_project( | |||
sysroot: Some(sysroot.into()), | |||
sysroot_src: Some(sysroot_src.into()), | |||
crates: Vec::new(), | |||
runnables: vec![ | |||
Runnable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the intended use of build
?
If it's to get diagnostics (which RunnableKind::Check
suggests), it's not clear to me if we should pass --config
, --output_base
etc as we're a tool, or not pass them as we're acting as a simple proxy for the user.
I'm fine with either answer but let's say why in a comment.
Part of this is: are we expecting --config
to be something like --config=generate_simplified_sources_for_rust_analyzer
(should be ignored) or --config=macos
(should be passed)?
.replace("__OUTPUT_BASE__", output_base) | ||
.replace("__WORKSPACE__", workspace); | ||
let rust_project_content = serde_json::to_string_pretty(rust_project)?; | ||
let rust_project_content = normalize_project_string( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the layering is unclear: here you're calling normalize_project_string
within the rust_project
module, but in the other case (discover
) the caller is expected to invoke it.
This normalization is messy :-(
As-is, I think the cleanest solution is to consistently call the normalize function from the top level, right before doing the IO (and not have the IO buried inside libraries).
.env_remove("BAZELISK_SKIP_WRAPPER") | ||
.env_remove("BUILD_WORKING_DIRECTORY") | ||
.env_remove("BUILD_WORKSPACE_DIRECTORY") | ||
.arg(format!("--output_base={output_base}")) | ||
.arg("build") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(no action needed) When we're running in the background, we want to be as forgiving as possible if the user's current state is broken. So eventually we should have --nocheck_visibility
here and possibly elsewhere.
@@ -1,62 +1,153 @@ | |||
mod aquery; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general the layering/responsibility split between main, this lib, and its submodules feels pretty unclear. (This is true before your changes, but it's more load-bearing after them!)
I'd suggest main should be responsible for flag parsing, protocol (serialization, deserialization, io), and most things that are inherently tool-specific. I don't see a clear reason that we want discover_rust_project
and write_rust_project
as different library entry points.
Adds a target that can be used for project auto-discovery by using the
discoverConfig
settings as described in therust-analyzer
user manual.Unlike the
gen_rust_project
target, this can be used for dynamic project discovery, and passing{arg}
todiscoverConfig.command
can split big repositories into multiple, smaller workspaces thatrust-analyzer
switches between as needed. Large repositories can make it OOM.At amo, we've used a similar implementation for a while with great success, which is why we figured we might upstream it. The changes also include two additional output groups to ensure that proc-macros and build script targets are built, as
rust-analyzer
depends on these to provide complete IDE support.Additionally, the PR makes use of the
output_base
value inbazel
invocations. We found it helpful to have tools such asrust-analyzer
andclippy
run on a separate bazel server than the one used for building. And aconfig_group
argument was added to provide the ability to provide a config group tobazel
invocations.An attempt to get codelens actions to work was done as well, particularly around tests and binaries. They seem to work, but I'm not 100% sure whether the approach taken is the right one.
Closes #2755 .