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 110730422bSTamir Dubersteinimport subprocess 128c4555ccSMiguel Ojedaimport sys 138c4555ccSMiguel Ojeda 144f353e0dSMartin Rodriguez Reboredodef args_crates_cfgs(cfgs): 154f353e0dSMartin Rodriguez Reboredo crates_cfgs = {} 164f353e0dSMartin Rodriguez Reboredo for cfg in cfgs: 174f353e0dSMartin Rodriguez Reboredo crate, vals = cfg.split("=", 1) 184f353e0dSMartin Rodriguez Reboredo crates_cfgs[crate] = vals.replace("--cfg", "").split() 194f353e0dSMartin Rodriguez Reboredo 204f353e0dSMartin Rodriguez Reboredo return crates_cfgs 214f353e0dSMartin Rodriguez Reboredo 224f353e0dSMartin Rodriguez Reboredodef generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): 238c4555ccSMiguel Ojeda # Generate the configuration list. 248c4555ccSMiguel Ojeda cfg = [] 258c4555ccSMiguel Ojeda with open(objtree / "include" / "generated" / "rustc_cfg") as fd: 268c4555ccSMiguel Ojeda for line in fd: 278c4555ccSMiguel Ojeda line = line.replace("--cfg=", "") 288c4555ccSMiguel Ojeda line = line.replace("\n", "") 298c4555ccSMiguel Ojeda cfg.append(line) 308c4555ccSMiguel Ojeda 318c4555ccSMiguel Ojeda # Now fill the crates list -- dependencies need to come first. 328c4555ccSMiguel Ojeda # 338c4555ccSMiguel Ojeda # Avoid O(n^2) iterations by keeping a map of indexes. 348c4555ccSMiguel Ojeda crates = [] 358c4555ccSMiguel Ojeda crates_indexes = {} 364f353e0dSMartin Rodriguez Reboredo crates_cfgs = args_crates_cfgs(cfgs) 378c4555ccSMiguel Ojeda 388c4555ccSMiguel Ojeda def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False): 390730422bSTamir Duberstein crate = { 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 } 500730422bSTamir Duberstein } 510730422bSTamir Duberstein if is_proc_macro: 520730422bSTamir Duberstein proc_macro_dylib_name = subprocess.check_output( 530730422bSTamir Duberstein [os.environ["RUSTC"], "--print", "file-names", "--crate-name", display_name, "--crate-type", "proc-macro", "-"], 540730422bSTamir Duberstein stdin=subprocess.DEVNULL, 550730422bSTamir Duberstein ).decode('utf-8').strip() 560730422bSTamir Duberstein crate["proc_macro_dylib_path"] = f"{objtree}/rust/{proc_macro_dylib_name}" 570730422bSTamir Duberstein crates_indexes[display_name] = len(crates) 580730422bSTamir Duberstein crates.append(crate) 598c4555ccSMiguel Ojeda 602e0f91abSTamir Duberstein def append_sysroot_crate( 612e0f91abSTamir Duberstein display_name, 622e0f91abSTamir Duberstein deps, 632e0f91abSTamir Duberstein cfg=[], 642e0f91abSTamir Duberstein ): 658c4555ccSMiguel Ojeda append_crate( 662e0f91abSTamir Duberstein display_name, 672e0f91abSTamir Duberstein sysroot_src / display_name / "src" / "lib.rs", 682e0f91abSTamir Duberstein deps, 692e0f91abSTamir Duberstein cfg, 708c4555ccSMiguel Ojeda is_workspace_member=False, 718c4555ccSMiguel Ojeda ) 728c4555ccSMiguel Ojeda 732e0f91abSTamir Duberstein # NB: sysroot crates reexport items from one another so setting up our transitive dependencies 742e0f91abSTamir Duberstein # here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth 752e0f91abSTamir Duberstein # for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`. 762e0f91abSTamir Duberstein append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", [])) 772e0f91abSTamir Duberstein append_sysroot_crate("alloc", ["core"]) 782e0f91abSTamir Duberstein append_sysroot_crate("std", ["alloc", "core"]) 792e0f91abSTamir Duberstein append_sysroot_crate("proc_macro", ["core", "std"]) 802e0f91abSTamir Duberstein 818c4555ccSMiguel Ojeda append_crate( 828c4555ccSMiguel Ojeda "compiler_builtins", 838c4555ccSMiguel Ojeda srctree / "rust" / "compiler_builtins.rs", 848c4555ccSMiguel Ojeda [], 858c4555ccSMiguel Ojeda ) 868c4555ccSMiguel Ojeda 878c4555ccSMiguel Ojeda append_crate( 888c4555ccSMiguel Ojeda "macros", 898c4555ccSMiguel Ojeda srctree / "rust" / "macros" / "lib.rs", 902e0f91abSTamir Duberstein ["std", "proc_macro"], 918c4555ccSMiguel Ojeda is_proc_macro=True, 928c4555ccSMiguel Ojeda ) 938c4555ccSMiguel Ojeda 948c4555ccSMiguel Ojeda append_crate( 95ecaa6ddfSGary Guo "build_error", 96ecaa6ddfSGary Guo srctree / "rust" / "build_error.rs", 97ecaa6ddfSGary Guo ["core", "compiler_builtins"], 98ecaa6ddfSGary Guo ) 99ecaa6ddfSGary Guo 100ecaa6ddfSGary Guo append_crate( 101d7659accSMiguel Ojeda "pin_init_internal", 102dbd5058bSBenno Lossin srctree / "rust" / "pin-init" / "internal" / "src" / "lib.rs", 103d7659accSMiguel Ojeda [], 104d7659accSMiguel Ojeda cfg=["kernel"], 105d7659accSMiguel Ojeda is_proc_macro=True, 106d7659accSMiguel Ojeda ) 107d7659accSMiguel Ojeda 108d7659accSMiguel Ojeda append_crate( 109d7659accSMiguel Ojeda "pin_init", 110dbd5058bSBenno Lossin srctree / "rust" / "pin-init" / "src" / "lib.rs", 111d7659accSMiguel Ojeda ["core", "pin_init_internal", "macros"], 112d7659accSMiguel Ojeda cfg=["kernel"], 113d7659accSMiguel Ojeda ) 114d7659accSMiguel Ojeda 115*05a2b001SLukas Fischer append_crate( 116*05a2b001SLukas Fischer "ffi", 117*05a2b001SLukas Fischer srctree / "rust" / "ffi.rs", 118*05a2b001SLukas Fischer ["core", "compiler_builtins"], 119*05a2b001SLukas Fischer ) 120*05a2b001SLukas Fischer 121d1f92805STamir Duberstein def append_crate_with_generated( 122d1f92805STamir Duberstein display_name, 123d1f92805STamir Duberstein deps, 124d1f92805STamir Duberstein ): 1258c4555ccSMiguel Ojeda append_crate( 126d1f92805STamir Duberstein display_name, 127d1f92805STamir Duberstein srctree / "rust"/ display_name / "lib.rs", 128d1f92805STamir Duberstein deps, 1298c4555ccSMiguel Ojeda cfg=cfg, 1308c4555ccSMiguel Ojeda ) 1318c4555ccSMiguel Ojeda crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True)) 1328c4555ccSMiguel Ojeda crates[-1]["source"] = { 1338c4555ccSMiguel Ojeda "include_dirs": [ 134d1f92805STamir Duberstein str(srctree / "rust" / display_name), 1358c4555ccSMiguel Ojeda str(objtree / "rust") 1368c4555ccSMiguel Ojeda ], 1378c4555ccSMiguel Ojeda "exclude_dirs": [], 1388c4555ccSMiguel Ojeda } 1398c4555ccSMiguel Ojeda 140*05a2b001SLukas Fischer append_crate_with_generated("bindings", ["core", "ffi"]) 141*05a2b001SLukas Fischer append_crate_with_generated("uapi", ["core", "ffi"]) 142*05a2b001SLukas Fischer append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "ffi", "bindings", "uapi"]) 143d1f92805STamir Duberstein 14449a9ef76SVinay Varma def is_root_crate(build_file, target): 14549a9ef76SVinay Varma try: 14649a9ef76SVinay Varma return f"{target}.o" in open(build_file).read() 14749a9ef76SVinay Varma except FileNotFoundError: 14849a9ef76SVinay Varma return False 14949a9ef76SVinay Varma 1508c4555ccSMiguel Ojeda # Then, the rest outside of `rust/`. 1518c4555ccSMiguel Ojeda # 1528c4555ccSMiguel Ojeda # We explicitly mention the top-level folders we want to cover. 15349a9ef76SVinay Varma extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers")) 15449a9ef76SVinay Varma if external_src is not None: 15549a9ef76SVinay Varma extra_dirs = [external_src] 15649a9ef76SVinay Varma for folder in extra_dirs: 15749a9ef76SVinay Varma for path in folder.rglob("*.rs"): 1588c4555ccSMiguel Ojeda logging.info("Checking %s", path) 1598c4555ccSMiguel Ojeda name = path.name.replace(".rs", "") 1608c4555ccSMiguel Ojeda 1618c4555ccSMiguel Ojeda # Skip those that are not crate roots. 16249a9ef76SVinay Varma if not is_root_crate(path.parent / "Makefile", name) and \ 16349a9ef76SVinay Varma not is_root_crate(path.parent / "Kbuild", name): 1645c7548d5SAsahi Lina continue 1658c4555ccSMiguel Ojeda 1668c4555ccSMiguel Ojeda logging.info("Adding %s", name) 1678c4555ccSMiguel Ojeda append_crate( 1688c4555ccSMiguel Ojeda name, 1698c4555ccSMiguel Ojeda path, 170392e34b6SDanilo Krummrich ["core", "kernel"], 1718c4555ccSMiguel Ojeda cfg=cfg, 1728c4555ccSMiguel Ojeda ) 1738c4555ccSMiguel Ojeda 1748c4555ccSMiguel Ojeda return crates 1758c4555ccSMiguel Ojeda 1768c4555ccSMiguel Ojedadef main(): 1778c4555ccSMiguel Ojeda parser = argparse.ArgumentParser() 1788c4555ccSMiguel Ojeda parser.add_argument('--verbose', '-v', action='store_true') 1794f353e0dSMartin Rodriguez Reboredo parser.add_argument('--cfgs', action='append', default=[]) 1808c4555ccSMiguel Ojeda parser.add_argument("srctree", type=pathlib.Path) 1818c4555ccSMiguel Ojeda parser.add_argument("objtree", type=pathlib.Path) 182fe992163SSarthak Singh parser.add_argument("sysroot", type=pathlib.Path) 1838c4555ccSMiguel Ojeda parser.add_argument("sysroot_src", type=pathlib.Path) 18449a9ef76SVinay Varma parser.add_argument("exttree", type=pathlib.Path, nargs="?") 1858c4555ccSMiguel Ojeda args = parser.parse_args() 1868c4555ccSMiguel Ojeda 1878c4555ccSMiguel Ojeda logging.basicConfig( 1888c4555ccSMiguel Ojeda format="[%(asctime)s] [%(levelname)s] %(message)s", 1898c4555ccSMiguel Ojeda level=logging.INFO if args.verbose else logging.WARNING 1908c4555ccSMiguel Ojeda ) 1918c4555ccSMiguel Ojeda 192fe992163SSarthak Singh # Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain. 193fe992163SSarthak Singh assert args.sysroot in args.sysroot_src.parents 194fe992163SSarthak Singh 1958c4555ccSMiguel Ojeda rust_project = { 1964f353e0dSMartin Rodriguez Reboredo "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs), 197fe992163SSarthak Singh "sysroot": str(args.sysroot), 1988c4555ccSMiguel Ojeda } 1998c4555ccSMiguel Ojeda 2008c4555ccSMiguel Ojeda json.dump(rust_project, sys.stdout, sort_keys=True, indent=4) 2018c4555ccSMiguel Ojeda 2028c4555ccSMiguel Ojedaif __name__ == "__main__": 2038c4555ccSMiguel Ojeda main() 204