Skip to content

Commit af95907

Browse files
committed
add rust-analyzer make target
This target creates ${objtree}/rust-project.json which can be read by rust-analyzer to help with autocompletion for the kernel crate. The raw bindings do not work yet, as rust-analyzer seems to have a problem with the include macro. Signed-off-by: Finn Behrens <[email protected]>
1 parent 2222bec commit af95907

File tree

5 files changed

+186
-40
lines changed

5 files changed

+186
-40
lines changed

Documentation/rust/quick-start.rst

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,8 @@ definition, and other features.
155155
``rust-analyzer`` will need to be
156156
`configured <https://rust-analyzer.github.io/manual.html#non-cargo-based-projects>`_
157157
to work with the kernel by adding a ``rust-project.json`` file in the root folder.
158-
The example ``Documentation/rust/rust-project.json`` can
159-
be used after updating ``sysroot_src`` and including the relevant modules.
160-
The path to ``sysroot_src`` is given by::
161-
162-
$(rustc --print sysroot)/lib/rustlib/src/rust/library
158+
A ``rust-project.json`` can be generated by building the Make target ``rust-analyzer``,
159+
which will create a ``rust-project.json`` in the root of the output directory.
163160

164161

165162
Configuration

Documentation/rust/rust-project.json

Lines changed: 0 additions & 35 deletions
This file was deleted.

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,8 @@ help:
17381738
@echo ' is formatted, printing a diff otherwise.'
17391739
@echo ' rustdoc - Generate Rust documentation'
17401740
@echo ' (requires kernel .config)'
1741+
@echo ' rust-analyzer - Generate rust-project.json rust-analyzer support file'
1742+
@echo ' (requires kernel .config)'
17411743
@echo ''
17421744
@$(if $(dtstree), \
17431745
echo 'Devicetree:'; \
@@ -1830,6 +1832,10 @@ rustfmt:
18301832
rustfmtcheck:
18311833
find -name '*.rs' | xargs $(RUSTFMT) --check
18321834

1835+
# IDE support targets
1836+
PHONY += rust-analyzer
1837+
rust-analyzer: prepare0
1838+
$(Q)$(MAKE) $(build)=rust $@
18331839

18341840
# Misc
18351841
# ---------------------------------------------------------------------------

rust/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,6 @@ $(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
150150
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
151151
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
152152
$(call if_changed_dep,rustc_library)
153+
154+
rust-analyzer:
155+
$(Q)$(srctree)/scripts/generate_rust_analyzer.py $(srctree) $(objtree) $(RUST_LIB_SRC) $(abspath $(objtree)/rust/bindings_generated.rs) > $(objtree)/rust-project.json

scripts/generate_rust_analyzer.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env python3
2+
import sys
3+
import os
4+
from os import path
5+
import json
6+
7+
if (len(sys.argv) != 5):
8+
print("Not enough arguments")
9+
sys.exit(-1)
10+
11+
srctree = sys.argv[1]
12+
objtree = sys.argv[2]
13+
lib_src = sys.argv[3]
14+
bindgen_file = sys.argv[4]
15+
16+
crate_list = []
17+
18+
def find(list, name):
19+
pos = -1
20+
for x in list:
21+
pos += 1
22+
if x['display_name'] == name:
23+
return pos
24+
25+
def generate_deps(source, deps):
26+
ret = []
27+
for x in deps:
28+
ret.append({"crate": find(source, x), "name": x})
29+
return ret
30+
31+
def generate_cfgs():
32+
ret = []
33+
with open( objtree + "/include/generated/rustc_cfg") as f:
34+
for line in f:
35+
line = line.replace('--cfg=', '')
36+
line = line.replace('\n', '')
37+
ret.append(line)
38+
return ret
39+
40+
def get_default_deps(source = crate_list):
41+
return generate_deps(source, ["core", "alloc", "kernel"])
42+
43+
def generate_crate(name, module, member = True, deps = get_default_deps(), cfg = generate_cfgs(), extra = {}, edition = "2018"):
44+
return {
45+
"display_name": name,
46+
"root_module": module,
47+
"edition": edition,
48+
"is_workspace_member": member,
49+
"cfg": cfg,
50+
"deps": deps,
51+
"env": {
52+
"RUST_MODFILE": "This is only for rust-analyzer"
53+
}
54+
}
55+
56+
def generate_kernel(srctree = srctree, objtree = objtree, lib_src = lib_src, bindgen_file = bindgen_file):
57+
ret = []
58+
59+
# core
60+
ret.append(
61+
generate_crate(
62+
name="core",
63+
module=lib_src + "/core/src/lib.rs",
64+
member=False,
65+
deps=[],
66+
cfg=[]
67+
)
68+
)
69+
70+
# compiler_builtins
71+
ret.append(
72+
generate_crate(
73+
name="compiler_builtins",
74+
module=srctree + "/rust/compiler_builtins.rs",
75+
deps=[],
76+
cfg=[]
77+
)
78+
)
79+
80+
# alloc
81+
ret.append(
82+
generate_crate(
83+
name="alloc",
84+
module=lib_src + "/alloc/src/lib.rs",
85+
member=False,
86+
deps=generate_deps(ret, ["core", "compiler_builtins"]),
87+
cfg=[]
88+
)
89+
)
90+
91+
# module
92+
module = generate_crate(
93+
name="module",
94+
module=srctree + "/rust/module.rs",
95+
deps=[],
96+
cfg=[],
97+
)
98+
module["proc_macro_dylib_path"] = "rust/libmodule.so"
99+
ret.append(
100+
module
101+
)
102+
103+
# kernel
104+
kernel = generate_crate(
105+
name="kernel",
106+
module=srctree + "/rust/kernel/lib.rs",
107+
deps=generate_deps(ret, ["core", "alloc", "module"]),
108+
)
109+
kernel["env"]["RUST_BINDINGS_FILE"] = bindgen_file
110+
kernel["source"] = {
111+
"include_dirs": [ srctree + "/rust/kernel", objtree + "/rust" ],
112+
"exclude_dirs": []
113+
}
114+
ret.append(kernel)
115+
116+
return ret
117+
118+
def does_file_contains(filepath, search):
119+
with open (filepath) as f:
120+
for line in f:
121+
if search in line:
122+
return True
123+
return False
124+
125+
def check_and_generate(filepath, source=crate_list):
126+
file = path.basename(filepath)
127+
makefile = path.dirname(filepath) + "/Makefile"
128+
name = file.replace('.rs', '')
129+
objname = name + ".o"
130+
print("checking " + objname, file=sys.stderr)
131+
if does_file_contains(makefile, objname):
132+
print("building crate " + name, file=sys.stderr)
133+
return generate_crate(
134+
name=name,
135+
module=filepath,
136+
deps=get_default_deps(source=source)
137+
)
138+
139+
140+
def find_rs(tree):
141+
ret = []
142+
for root, dirs, files in os.walk(tree):
143+
for file in files:
144+
if file.endswith(".rs"):
145+
ret.append(path.join(root, file))
146+
return ret
147+
148+
def generate_samples(srctree=srctree, source=crate_list):
149+
ret = []
150+
for x in find_rs(srctree + "/samples/rust"):
151+
sample = check_and_generate(x, source=source)
152+
if sample:
153+
ret.append(sample)
154+
return ret
155+
156+
def generate_drivers(srctree=srctree, source=crate_list):
157+
ret = []
158+
for x in find_rs(srctree + "/drivers"):
159+
driver = check_and_generate(x, source=source)
160+
if driver:
161+
ret.append(driver)
162+
return ret
163+
164+
165+
166+
if __name__ == "__main__":
167+
crate_list = crate_list + generate_kernel()
168+
crate_list = crate_list + generate_samples(srctree=srctree, source=crate_list)
169+
crate_list = crate_list + generate_drivers(srctree=srctree, source=crate_list)
170+
171+
rust_project = {
172+
"crates": crate_list,
173+
"sysroot_src": lib_src
174+
}
175+
print(json.dumps(rust_project))

0 commit comments

Comments
 (0)