generate_rust_analyzer.py 5.0 KB

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