18c4555ccSMiguel Ojeda#!/usr/bin/env python3 28c4555ccSMiguel Ojeda# SPDX-License-Identifier: GPL-2.0 38c4555ccSMiguel Ojeda"""generate_rust_analyzer - Generates the `rust-project.json` file for `rust-analyzer`. 48c4555ccSMiguel Ojeda""" 58c4555ccSMiguel Ojeda 68c4555ccSMiguel Ojedaimport argparse 78c4555ccSMiguel Ojedaimport json 88c4555ccSMiguel Ojedaimport logging 949a9ef76SVinay Varmaimport os 108c4555ccSMiguel Ojedaimport pathlib 118c4555ccSMiguel Ojedaimport sys 128c4555ccSMiguel Ojeda 13*4f353e0dSMartin Rodriguez Reboredodef args_crates_cfgs(cfgs): 14*4f353e0dSMartin Rodriguez Reboredo crates_cfgs = {} 15*4f353e0dSMartin Rodriguez Reboredo for cfg in cfgs: 16*4f353e0dSMartin Rodriguez Reboredo crate, vals = cfg.split("=", 1) 17*4f353e0dSMartin Rodriguez Reboredo crates_cfgs[crate] = vals.replace("--cfg", "").split() 18*4f353e0dSMartin Rodriguez Reboredo 19*4f353e0dSMartin Rodriguez Reboredo return crates_cfgs 20*4f353e0dSMartin Rodriguez Reboredo 21*4f353e0dSMartin Rodriguez Reboredodef generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): 228c4555ccSMiguel Ojeda # Generate the configuration list. 238c4555ccSMiguel Ojeda cfg = [] 248c4555ccSMiguel Ojeda with open(objtree / "include" / "generated" / "rustc_cfg") as fd: 258c4555ccSMiguel Ojeda for line in fd: 268c4555ccSMiguel Ojeda line = line.replace("--cfg=", "") 278c4555ccSMiguel Ojeda line = line.replace("\n", "") 288c4555ccSMiguel Ojeda cfg.append(line) 298c4555ccSMiguel Ojeda 308c4555ccSMiguel Ojeda # Now fill the crates list -- dependencies need to come first. 318c4555ccSMiguel Ojeda # 328c4555ccSMiguel Ojeda # Avoid O(n^2) iterations by keeping a map of indexes. 338c4555ccSMiguel Ojeda crates = [] 348c4555ccSMiguel Ojeda crates_indexes = {} 35*4f353e0dSMartin Rodriguez Reboredo crates_cfgs = args_crates_cfgs(cfgs) 368c4555ccSMiguel Ojeda 378c4555ccSMiguel Ojeda def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False): 388c4555ccSMiguel Ojeda crates_indexes[display_name] = len(crates) 398c4555ccSMiguel Ojeda crates.append({ 408c4555ccSMiguel Ojeda "display_name": display_name, 418c4555ccSMiguel Ojeda "root_module": str(root_module), 428c4555ccSMiguel Ojeda "is_workspace_member": is_workspace_member, 438c4555ccSMiguel Ojeda "is_proc_macro": is_proc_macro, 448c4555ccSMiguel Ojeda "deps": [{"crate": crates_indexes[dep], "name": dep} for dep in deps], 458c4555ccSMiguel Ojeda "cfg": cfg, 468c4555ccSMiguel Ojeda "edition": "2021", 478c4555ccSMiguel Ojeda "env": { 488c4555ccSMiguel Ojeda "RUST_MODFILE": "This is only for rust-analyzer" 498c4555ccSMiguel Ojeda } 508c4555ccSMiguel Ojeda }) 518c4555ccSMiguel Ojeda 528c4555ccSMiguel Ojeda # First, the ones in `rust/` since they are a bit special. 538c4555ccSMiguel Ojeda append_crate( 548c4555ccSMiguel Ojeda "core", 558c4555ccSMiguel Ojeda sysroot_src / "core" / "src" / "lib.rs", 568c4555ccSMiguel Ojeda [], 57*4f353e0dSMartin Rodriguez Reboredo cfg=crates_cfgs.get("core", []), 588c4555ccSMiguel Ojeda is_workspace_member=False, 598c4555ccSMiguel Ojeda ) 608c4555ccSMiguel Ojeda 618c4555ccSMiguel Ojeda append_crate( 628c4555ccSMiguel Ojeda "compiler_builtins", 638c4555ccSMiguel Ojeda srctree / "rust" / "compiler_builtins.rs", 648c4555ccSMiguel Ojeda [], 658c4555ccSMiguel Ojeda ) 668c4555ccSMiguel Ojeda 678c4555ccSMiguel Ojeda append_crate( 688c4555ccSMiguel Ojeda "alloc", 698c4555ccSMiguel Ojeda srctree / "rust" / "alloc" / "lib.rs", 708c4555ccSMiguel Ojeda ["core", "compiler_builtins"], 71*4f353e0dSMartin Rodriguez Reboredo cfg=crates_cfgs.get("alloc", []), 728c4555ccSMiguel Ojeda ) 738c4555ccSMiguel Ojeda 748c4555ccSMiguel Ojeda append_crate( 758c4555ccSMiguel Ojeda "macros", 768c4555ccSMiguel Ojeda srctree / "rust" / "macros" / "lib.rs", 778c4555ccSMiguel Ojeda [], 788c4555ccSMiguel Ojeda is_proc_macro=True, 798c4555ccSMiguel Ojeda ) 8049a9ef76SVinay Varma crates[-1]["proc_macro_dylib_path"] = f"{objtree}/rust/libmacros.so" 818c4555ccSMiguel Ojeda 828c4555ccSMiguel Ojeda append_crate( 83ecaa6ddfSGary Guo "build_error", 84ecaa6ddfSGary Guo srctree / "rust" / "build_error.rs", 85ecaa6ddfSGary Guo ["core", "compiler_builtins"], 86ecaa6ddfSGary Guo ) 87ecaa6ddfSGary Guo 88ecaa6ddfSGary Guo append_crate( 898c4555ccSMiguel Ojeda "bindings", 908c4555ccSMiguel Ojeda srctree / "rust"/ "bindings" / "lib.rs", 918c4555ccSMiguel Ojeda ["core"], 928c4555ccSMiguel Ojeda cfg=cfg, 938c4555ccSMiguel Ojeda ) 948c4555ccSMiguel Ojeda crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True)) 958c4555ccSMiguel Ojeda 968c4555ccSMiguel Ojeda append_crate( 978c4555ccSMiguel Ojeda "kernel", 988c4555ccSMiguel Ojeda srctree / "rust" / "kernel" / "lib.rs", 99ecaa6ddfSGary Guo ["core", "alloc", "macros", "build_error", "bindings"], 1008c4555ccSMiguel Ojeda cfg=cfg, 1018c4555ccSMiguel Ojeda ) 1028c4555ccSMiguel Ojeda crates[-1]["source"] = { 1038c4555ccSMiguel Ojeda "include_dirs": [ 1048c4555ccSMiguel Ojeda str(srctree / "rust" / "kernel"), 1058c4555ccSMiguel Ojeda str(objtree / "rust") 1068c4555ccSMiguel Ojeda ], 1078c4555ccSMiguel Ojeda "exclude_dirs": [], 1088c4555ccSMiguel Ojeda } 1098c4555ccSMiguel Ojeda 11049a9ef76SVinay Varma def is_root_crate(build_file, target): 11149a9ef76SVinay Varma try: 11249a9ef76SVinay Varma return f"{target}.o" in open(build_file).read() 11349a9ef76SVinay Varma except FileNotFoundError: 11449a9ef76SVinay Varma return False 11549a9ef76SVinay Varma 1168c4555ccSMiguel Ojeda # Then, the rest outside of `rust/`. 1178c4555ccSMiguel Ojeda # 1188c4555ccSMiguel Ojeda # We explicitly mention the top-level folders we want to cover. 11949a9ef76SVinay Varma extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers")) 12049a9ef76SVinay Varma if external_src is not None: 12149a9ef76SVinay Varma extra_dirs = [external_src] 12249a9ef76SVinay Varma for folder in extra_dirs: 12349a9ef76SVinay Varma for path in folder.rglob("*.rs"): 1248c4555ccSMiguel Ojeda logging.info("Checking %s", path) 1258c4555ccSMiguel Ojeda name = path.name.replace(".rs", "") 1268c4555ccSMiguel Ojeda 1278c4555ccSMiguel Ojeda # Skip those that are not crate roots. 12849a9ef76SVinay Varma if not is_root_crate(path.parent / "Makefile", name) and \ 12949a9ef76SVinay Varma not is_root_crate(path.parent / "Kbuild", name): 1305c7548d5SAsahi Lina continue 1318c4555ccSMiguel Ojeda 1328c4555ccSMiguel Ojeda logging.info("Adding %s", name) 1338c4555ccSMiguel Ojeda append_crate( 1348c4555ccSMiguel Ojeda name, 1358c4555ccSMiguel Ojeda path, 1368c4555ccSMiguel Ojeda ["core", "alloc", "kernel"], 1378c4555ccSMiguel Ojeda cfg=cfg, 1388c4555ccSMiguel Ojeda ) 1398c4555ccSMiguel Ojeda 1408c4555ccSMiguel Ojeda return crates 1418c4555ccSMiguel Ojeda 1428c4555ccSMiguel Ojedadef main(): 1438c4555ccSMiguel Ojeda parser = argparse.ArgumentParser() 1448c4555ccSMiguel Ojeda parser.add_argument('--verbose', '-v', action='store_true') 145*4f353e0dSMartin Rodriguez Reboredo parser.add_argument('--cfgs', action='append', default=[]) 1468c4555ccSMiguel Ojeda parser.add_argument("srctree", type=pathlib.Path) 1478c4555ccSMiguel Ojeda parser.add_argument("objtree", type=pathlib.Path) 1488c4555ccSMiguel Ojeda parser.add_argument("sysroot_src", type=pathlib.Path) 14949a9ef76SVinay Varma parser.add_argument("exttree", type=pathlib.Path, nargs="?") 1508c4555ccSMiguel Ojeda args = parser.parse_args() 1518c4555ccSMiguel Ojeda 1528c4555ccSMiguel Ojeda logging.basicConfig( 1538c4555ccSMiguel Ojeda format="[%(asctime)s] [%(levelname)s] %(message)s", 1548c4555ccSMiguel Ojeda level=logging.INFO if args.verbose else logging.WARNING 1558c4555ccSMiguel Ojeda ) 1568c4555ccSMiguel Ojeda 1578c4555ccSMiguel Ojeda rust_project = { 158*4f353e0dSMartin Rodriguez Reboredo "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs), 1598c4555ccSMiguel Ojeda "sysroot_src": str(args.sysroot_src), 1608c4555ccSMiguel Ojeda } 1618c4555ccSMiguel Ojeda 1628c4555ccSMiguel Ojeda json.dump(rust_project, sys.stdout, sort_keys=True, indent=4) 1638c4555ccSMiguel Ojeda 1648c4555ccSMiguel Ojedaif __name__ == "__main__": 1658c4555ccSMiguel Ojeda main() 166