Skip to content

Commit a3aff78

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 a3aff78

File tree

5 files changed

+204
-40
lines changed

5 files changed

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

0 commit comments

Comments
 (0)