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