1
+ #!/usr/bin/env python3
2
+ """generate_rust_analyzer - Generates the `rust-project.json` file for `rust-analyzer`.
3
+ """
4
+
5
+ import argparse
6
+ import json
7
+ import logging
8
+ import pathlib
9
+ import sys
10
+
11
+ def generate_crates (srctree , objtree , sysroot_src , bindings_file ):
12
+ # Generate the configuration list.
13
+ cfg = []
14
+ with open (objtree / "include" / "generated" / "rustc_cfg" ) as fd :
15
+ for line in fd :
16
+ line = line .replace ("--cfg=" , "" )
17
+ line = line .replace ("\n " , "" )
18
+ cfg .append (line )
19
+
20
+ # Now fill the crates list -- dependencies need to come first.
21
+ #
22
+ # Avoid O(n^2) iterations by keeping a map of indexes.
23
+ crates = []
24
+ crates_indexes = {}
25
+
26
+ def append_crate (display_name , root_module , is_workspace_member , deps , cfg ):
27
+ crates_indexes [display_name ] = len (crates )
28
+ crates .append ({
29
+ "display_name" : display_name ,
30
+ "root_module" : str (root_module ),
31
+ "is_workspace_member" : is_workspace_member ,
32
+ "deps" : [{"crate" : crates_indexes [dep ], "name" : dep } for dep in deps ],
33
+ "cfg" : cfg ,
34
+ "edition" : "2018" ,
35
+ "env" : {
36
+ "RUST_MODFILE" : "This is only for rust-analyzer"
37
+ }
38
+ })
39
+
40
+ # First, the ones in `rust/` since they are a bit special.
41
+ append_crate (
42
+ "core" ,
43
+ sysroot_src / "core" / "src" / "lib.rs" ,
44
+ False ,
45
+ [],
46
+ [],
47
+ )
48
+
49
+ append_crate (
50
+ "compiler_builtins" ,
51
+ srctree / "rust" / "compiler_builtins.rs" ,
52
+ True ,
53
+ [],
54
+ [],
55
+ )
56
+
57
+ append_crate (
58
+ "alloc" ,
59
+ sysroot_src / "alloc" / "src" / "lib.rs" ,
60
+ False ,
61
+ ["core" , "compiler_builtins" ],
62
+ [],
63
+ )
64
+
65
+ append_crate (
66
+ "module" ,
67
+ srctree / "rust" / "module.rs" ,
68
+ True ,
69
+ [],
70
+ [],
71
+ )
72
+ crates [- 1 ]["proc_macro_dylib_path" ] = "rust/libmodule.so"
73
+
74
+ append_crate (
75
+ "kernel" ,
76
+ srctree / "rust" / "kernel" / "lib.rs" ,
77
+ True ,
78
+ ["core" , "alloc" , "module" ],
79
+ cfg ,
80
+ )
81
+ crates [- 1 ]["env" ]["RUST_BINDINGS_FILE" ] = str (bindings_file .resolve (True ))
82
+ crates [- 1 ]["source" ] = {
83
+ "include_dirs" : [
84
+ str (srctree / "rust" / "kernel" ),
85
+ str (objtree / "rust" )
86
+ ],
87
+ "exclude_dirs" : [],
88
+ }
89
+
90
+ # Then, the rest outside of `rust/`.
91
+ #
92
+ # We explicitly mention the top-level folders we want to cover.
93
+ for folder in ("samples" , "drivers" ):
94
+ for path in (srctree / folder ).rglob ("*.rs" ):
95
+ logging .info ("Checking %s" , path )
96
+ name = path .name .replace (".rs" , "" )
97
+
98
+ # Skip those that are not crate roots.
99
+ if f"{ name } .o" not in open (path .parent / "Makefile" ).read ():
100
+ continue
101
+
102
+ logging .info ("Adding %s" , name )
103
+ append_crate (
104
+ name ,
105
+ path ,
106
+ True ,
107
+ ["core" , "alloc" , "kernel" ],
108
+ cfg ,
109
+ )
110
+
111
+ return crates
112
+
113
+ def main ():
114
+ parser = argparse .ArgumentParser ()
115
+ parser .add_argument ('--verbose' , '-v' , action = 'store_true' )
116
+ parser .add_argument ("srctree" , type = pathlib .Path )
117
+ parser .add_argument ("objtree" , type = pathlib .Path )
118
+ parser .add_argument ("sysroot_src" , type = pathlib .Path )
119
+ parser .add_argument ("bindings_file" , type = pathlib .Path )
120
+ args = parser .parse_args ()
121
+
122
+ logging .basicConfig (
123
+ format = "[%(asctime)s] [%(levelname)s] %(message)s" ,
124
+ level = logging .INFO if args .verbose else logging .WARNING
125
+ )
126
+
127
+ rust_project = {
128
+ "crates" : generate_crates (args .srctree , args .objtree , args .sysroot_src , args .bindings_file ),
129
+ "sysroot_src" : str (args .sysroot_src ),
130
+ }
131
+
132
+ json .dump (rust_project , sys .stdout , sort_keys = True , indent = 4 )
133
+
134
+ if __name__ == "__main__" :
135
+ main ()
0 commit comments