-
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?
Changes from all commits
8e51c2d
dc3ea50
3806c52
683440e
5ddbec5
ea06599
ac1dbe0
5de25c8
98aa3b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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", | ||
"build_info_out_dirs": "Depset[File]: transitive closure of OutputGroupInfo files", | ||
"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 commentThe 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) |
||
}, | ||
) | ||
|
||
RustAnalyzerGroupInfo = provider( | ||
doc = "RustAnalyzerGroupInfo holds multiple RustAnalyzerInfos", | ||
fields = { | ||
"crate_specs": "Depset[File]: transitive closure of OutputGroupInfo files", | ||
"proc_macro_dylibs": "Depset[File]: transitive closure of OutputGroupInfo files", | ||
"build_info_out_dirs": "Depset[File]: transitive closure of OutputGroupInfo files", | ||
"deps": "List[String]: crate IDs of direct dependencies", | ||
}, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe 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) |
||
build_info_out_dirs = [base_info.build_info.out_dir] if base_info.build_info != None and base_info.build_info.out_dir != None else None | ||
|
||
# Recreate the provider with the spec file embedded in it. | ||
rust_analyzer_info = RustAnalyzerInfo( | ||
|
@@ -55,7 +57,9 @@ def write_rust_analyzer_spec_file(ctx, attrs, owner, base_info): | |
deps = base_info.deps, | ||
id = base_info.id, | ||
crate_specs = depset(direct = [crate_spec], transitive = [base_info.crate_specs]), | ||
proc_macro_dylib_path = base_info.proc_macro_dylib_path, | ||
proc_macro_dylibs = depset(direct = proc_macro_dylibs, transitive = [base_info.proc_macro_dylibs]), | ||
build_info_out_dirs = depset(direct = build_info_out_dirs, transitive = [base_info.build_info_out_dirs]), | ||
proc_macro_dylib = base_info.proc_macro_dylib, | ||
build_info = base_info.build_info, | ||
) | ||
|
||
|
@@ -101,23 +105,36 @@ def _rust_analyzer_aspect_impl(target, ctx): | |
# Gather required info from dependencies. | ||
label_to_id = {} # {Label of dependency => crate_id} | ||
crate_specs = [] # [depset of File - transitive crate_spec.json files] | ||
proc_macro_dylibs = [] # [depset of File - transitive crate_spec.json files] | ||
build_script_out_dirs = [] # [depset of File - transitive crate_spec.json files] | ||
attrs = ctx.rule.attr | ||
all_deps = getattr(attrs, "deps", []) + getattr(attrs, "proc_macro_deps", []) + \ | ||
[dep for dep in [getattr(attrs, "crate", None), getattr(attrs, "actual", None)] if dep != None] | ||
for dep in all_deps: | ||
if RustAnalyzerInfo in dep: | ||
label_to_id[dep.label] = dep[RustAnalyzerInfo].id | ||
crate_specs.append(dep[RustAnalyzerInfo].crate_specs) | ||
proc_macro_dylibs.append(dep[RustAnalyzerInfo].proc_macro_dylibs) | ||
build_script_out_dirs.append(dep[RustAnalyzerInfo].build_info_out_dirs) | ||
if RustAnalyzerGroupInfo in dep: | ||
for expanded_dep in dep[RustAnalyzerGroupInfo].deps: | ||
label_to_id[expanded_dep] = expanded_dep | ||
crate_specs.append(dep[RustAnalyzerGroupInfo].crate_specs) | ||
proc_macro_dylibs.append(dep[RustAnalyzerGroupInfo].proc_macro_dylibs) | ||
build_script_out_dirs.append(dep[RustAnalyzerGroupInfo].build_info_out_dirs) | ||
|
||
deps = label_to_id.values() | ||
crate_specs = depset(transitive = crate_specs) | ||
proc_macro_dylibs = depset(transitive = proc_macro_dylibs) | ||
build_script_out_dirs = depset(transitive = build_script_out_dirs) | ||
|
||
if rust_common.crate_group_info in target: | ||
return [RustAnalyzerGroupInfo(deps = deps, crate_specs = crate_specs)] | ||
return [RustAnalyzerGroupInfo( | ||
deps = deps, | ||
crate_specs = crate_specs, | ||
proc_macro_dylibs = proc_macro_dylibs, | ||
build_info_out_dirs = build_script_out_dirs, | ||
)] | ||
elif rust_common.crate_info in target: | ||
crate_info = target[rust_common.crate_info] | ||
elif rust_common.test_crate_info in target: | ||
|
@@ -134,6 +151,8 @@ def _rust_analyzer_aspect_impl(target, ctx): | |
# An arbitrary unique and stable identifier. | ||
crate_id = "ID-" + crate_info.root.path | ||
|
||
proc_macro_dylib = find_proc_macro_dylib(toolchain, target) | ||
|
||
rust_analyzer_info = write_rust_analyzer_spec_file(ctx, ctx.rule.attr, ctx.label, RustAnalyzerInfo( | ||
id = crate_id, | ||
aliases = aliases, | ||
|
@@ -142,23 +161,29 @@ def _rust_analyzer_aspect_impl(target, ctx): | |
env = crate_info.rustc_env, | ||
deps = deps, | ||
crate_specs = crate_specs, | ||
proc_macro_dylib_path = find_proc_macro_dylib_path(toolchain, target), | ||
proc_macro_dylibs = proc_macro_dylibs, | ||
build_info_out_dirs = build_script_out_dirs, | ||
proc_macro_dylib = proc_macro_dylib, | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. I think 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...) |
||
rust_analyzer_crate_spec = rust_analyzer_info.crate_specs, | ||
rust_analyzer_proc_macro_dylibs = rust_analyzer_info.proc_macro_dylibs, | ||
rust_analyzer_build_info_out_dirs = rust_analyzer_info.build_info_out_dirs, | ||
), | ||
] | ||
|
||
def find_proc_macro_dylib_path(toolchain, target): | ||
"""Find the proc_macro_dylib_path of target. Returns None if target crate is not type proc-macro. | ||
def find_proc_macro_dylib(toolchain, target): | ||
"""Find the proc_macro_dylib of target. Returns None if target crate is not type proc-macro. | ||
|
||
Args: | ||
toolchain: The current rust toolchain. | ||
target: The current target. | ||
Returns: | ||
(path): The path to the proc macro dylib, or None if this crate is not a proc-macro. | ||
(File): The path to the proc macro dylib, or None if this crate is not a proc-macro. | ||
""" | ||
if rust_common.crate_info in target: | ||
crate_info = target[rust_common.crate_info] | ||
|
@@ -174,7 +199,7 @@ def find_proc_macro_dylib_path(toolchain, target): | |
for action in target.actions: | ||
for output in action.outputs.to_list(): | ||
if output.extension == dylib_ext[1:]: | ||
return output.path | ||
return output | ||
|
||
# Failed to find the dylib path inside a proc-macro crate. | ||
# TODO: Should this be an error? | ||
|
@@ -188,7 +213,7 @@ rust_analyzer_aspect = aspect( | |
) | ||
|
||
# Paths in the generated JSON file begin with one of these placeholders. | ||
# The gen_rust_project driver will replace them with absolute paths. | ||
# The `rust-analyzer` driver will replace them with absolute paths. | ||
_WORKSPACE_TEMPLATE = "__WORKSPACE__/" | ||
_EXEC_ROOT_TEMPLATE = "__EXEC_ROOT__/" | ||
_OUTPUT_BASE_TEMPLATE = "__OUTPUT_BASE__/" | ||
|
@@ -211,6 +236,7 @@ def _create_single_crate(ctx, attrs, info): | |
crate["edition"] = info.crate.edition | ||
crate["env"] = {} | ||
crate["crate_type"] = info.crate.type | ||
crate["is_test"] = info.crate.is_test | ||
|
||
# Switch on external/ to determine if crates are in the workspace or remote. | ||
# TODO: Some folks may want to override this for vendored dependencies. | ||
|
@@ -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 commentThe 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 |
||
if not is_external and not is_generated: | ||
crate["build"] = { | ||
"label": ctx.label.package + ":" + ctx.label.name, | ||
"build_file": _WORKSPACE_TEMPLATE + ctx.build_file_path, | ||
} | ||
|
||
if is_generated: | ||
srcs = getattr(ctx.rule.files, "srcs", []) | ||
src_map = {src.short_path: src for src in srcs if src.is_source} | ||
|
@@ -259,8 +292,8 @@ def _create_single_crate(ctx, attrs, info): | |
crate["cfg"] = info.cfgs | ||
toolchain = find_toolchain(ctx) | ||
crate["target"] = (_EXEC_ROOT_TEMPLATE + toolchain.target_json.path) if toolchain.target_json else toolchain.target_flag_value | ||
if info.proc_macro_dylib_path != None: | ||
crate["proc_macro_dylib_path"] = _EXEC_ROOT_TEMPLATE + info.proc_macro_dylib_path | ||
if info.proc_macro_dylib != None: | ||
crate["proc_macro_dylib_path"] = _EXEC_ROOT_TEMPLATE + info.proc_macro_dylib.path | ||
return crate | ||
|
||
def _rust_analyzer_toolchain_impl(ctx): | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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?)