generate_rust_analyzer.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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, core_edition):
  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, edition="2021"):
  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": edition,
  41. "env": {
  42. "RUST_MODFILE": "This is only for rust-analyzer"
  43. }
  44. })
  45. def append_sysroot_crate(
  46. display_name,
  47. deps,
  48. cfg=[],
  49. edition="2021",
  50. ):
  51. append_crate(
  52. display_name,
  53. sysroot_src / display_name / "src" / "lib.rs",
  54. deps,
  55. cfg,
  56. is_workspace_member=False,
  57. edition=edition,
  58. )
  59. # NB: sysroot crates reexport items from one another so setting up our transitive dependencies
  60. # here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth
  61. # for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`.
  62. append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []), edition=core_edition)
  63. append_sysroot_crate("alloc", ["core"])
  64. append_sysroot_crate("std", ["alloc", "core"])
  65. append_sysroot_crate("proc_macro", ["core", "std"])
  66. append_crate(
  67. "compiler_builtins",
  68. srctree / "rust" / "compiler_builtins.rs",
  69. [],
  70. )
  71. append_crate(
  72. "macros",
  73. srctree / "rust" / "macros" / "lib.rs",
  74. ["std", "proc_macro"],
  75. is_proc_macro=True,
  76. )
  77. crates[-1]["proc_macro_dylib_path"] = f"{objtree}/rust/libmacros.so"
  78. append_crate(
  79. "build_error",
  80. srctree / "rust" / "build_error.rs",
  81. ["core", "compiler_builtins"],
  82. )
  83. append_crate(
  84. "ffi",
  85. srctree / "rust" / "ffi.rs",
  86. ["core", "compiler_builtins"],
  87. )
  88. def append_crate_with_generated(
  89. display_name,
  90. deps,
  91. ):
  92. append_crate(
  93. display_name,
  94. srctree / "rust"/ display_name / "lib.rs",
  95. deps,
  96. cfg=cfg,
  97. )
  98. crates[-1]["env"]["OBJTREE"] = str(objtree.resolve(True))
  99. crates[-1]["source"] = {
  100. "include_dirs": [
  101. str(srctree / "rust" / display_name),
  102. str(objtree / "rust")
  103. ],
  104. "exclude_dirs": [],
  105. }
  106. append_crate_with_generated("bindings", ["core", "ffi"])
  107. append_crate_with_generated("uapi", ["core", "ffi"])
  108. append_crate_with_generated("kernel", ["core", "macros", "build_error", "ffi", "bindings", "uapi"])
  109. def is_root_crate(build_file, target):
  110. try:
  111. return f"{target}.o" in open(build_file).read()
  112. except FileNotFoundError:
  113. return False
  114. # Then, the rest outside of `rust/`.
  115. #
  116. # We explicitly mention the top-level folders we want to cover.
  117. extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers"))
  118. if external_src is not None:
  119. extra_dirs = [external_src]
  120. for folder in extra_dirs:
  121. for path in folder.rglob("*.rs"):
  122. logging.info("Checking %s", path)
  123. name = path.name.replace(".rs", "")
  124. # Skip those that are not crate roots.
  125. if not is_root_crate(path.parent / "Makefile", name) and \
  126. not is_root_crate(path.parent / "Kbuild", name):
  127. continue
  128. logging.info("Adding %s", name)
  129. append_crate(
  130. name,
  131. path,
  132. ["core", "kernel"],
  133. cfg=cfg,
  134. )
  135. return crates
  136. def main():
  137. parser = argparse.ArgumentParser()
  138. parser.add_argument('--verbose', '-v', action='store_true')
  139. parser.add_argument('--cfgs', action='append', default=[])
  140. parser.add_argument("core_edition")
  141. parser.add_argument("srctree", type=pathlib.Path)
  142. parser.add_argument("objtree", type=pathlib.Path)
  143. parser.add_argument("sysroot", type=pathlib.Path)
  144. parser.add_argument("sysroot_src", type=pathlib.Path)
  145. parser.add_argument("exttree", type=pathlib.Path, nargs="?")
  146. args = parser.parse_args()
  147. logging.basicConfig(
  148. format="[%(asctime)s] [%(levelname)s] %(message)s",
  149. level=logging.INFO if args.verbose else logging.WARNING
  150. )
  151. # Making sure that the `sysroot` and `sysroot_src` belong to the same toolchain.
  152. assert args.sysroot in args.sysroot_src.parents
  153. rust_project = {
  154. "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.core_edition),
  155. "sysroot": str(args.sysroot),
  156. }
  157. json.dump(rust_project, sys.stdout, sort_keys=True, indent=4)
  158. if __name__ == "__main__":
  159. main()