/ prelude / cxx / comp_db.bzl
comp_db.bzl
  1  # prelude/cxx/comp_db.bzl
  2  #
  3  # Compilation database support for IDE integration.
  4  # Generates compile_commands.json for clangd, ccls, etc.
  5  #
  6  # Extracted from buck2-prelude/cxx/comp_db.bzl (76 lines)
  7  #
  8  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  9  # PRELUDE ARCHAEOLOGY
 10  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 11  #
 12  # The compilation database is essential for IDE tooling:
 13  #   - clangd uses it for code navigation, completion, diagnostics
 14  #   - ccls, cquery use it similarly
 15  #   - IDE plugins consume compile_commands.json
 16  #
 17  # The upstream implementation:
 18  #   - CxxCompilationDbInfo provider: exposes compilation commands
 19  #   - make_compilation_db_info: creates provider from compile commands
 20  #   - create_compilation_database: generates compile_commands.json
 21  #
 22  # What's worth keeping:
 23  #   - Provider definition (for integration)
 24  #   - Database generation logic (essential for IDEs)
 25  #
 26  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 27  
 28  load("@straylight_prelude//:paths.bzl", "paths")
 29  load("@straylight_prelude//cxx:cxx_toolchain_types.bzl", "CxxPlatformInfo", "CxxToolchainInfo")
 30  load("@straylight_prelude//utils:argfile.bzl", "at_argfile")
 31  load(
 32      "@straylight_prelude//cxx:compile_types.bzl",
 33      "CxxSrcCompileCommand",
 34  )
 35  load("@straylight_prelude//cxx:cxx_context.bzl", "get_cxx_toolchain_info")
 36  
 37  # Provider that exposes the compilation database information.
 38  # Used by IDE tools to understand how to compile each source file.
 39  CxxCompilationDbInfo = provider(fields = {
 40      # A map of the file (an Artifact) to its corresponding CxxSrcCompileCommand
 41      "info": provider_field(typing.Any, default = None),
 42      # Platform for this compilation database
 43      "platform": provider_field(typing.Any, default = None),
 44      # Toolchain for this compilation database
 45      "toolchain": provider_field(typing.Any, default = None),
 46  })
 47  
 48  def make_compilation_db_info(
 49          src_compile_cmds: list[CxxSrcCompileCommand],
 50          toolchainInfo: CxxToolchainInfo,
 51          platformInfo: CxxPlatformInfo) -> CxxCompilationDbInfo:
 52      """Create a CxxCompilationDbInfo from a list of compile commands."""
 53      info = {}
 54      for src_compile_cmd in src_compile_cmds:
 55          info.update({src_compile_cmd.src: src_compile_cmd})
 56  
 57      return CxxCompilationDbInfo(
 58          info = info,
 59          toolchain = toolchainInfo,
 60          platform = platformInfo,
 61      )
 62  
 63  def create_compilation_database(
 64          ctx: AnalysisContext,
 65          src_compile_cmds: list[CxxSrcCompileCommand],
 66          identifier: str) -> DefaultInfo:
 67      """
 68      Generate a compile_commands.json for the given source compile commands.
 69      
 70      This is the standard format consumed by clangd, ccls, and other tools.
 71      """
 72      mk_comp_db = get_cxx_toolchain_info(ctx).internal_tools.make_comp_db
 73  
 74      # Generate the per-source compilation DB entries
 75      entries = {}
 76      other_outputs = []
 77  
 78      for src_compile_cmd in src_compile_cmds:
 79          cdb_path = paths.join(identifier, "__comp_db__", src_compile_cmd.src.short_path + ".comp_db.json")
 80          if cdb_path not in entries:
 81              entry = ctx.actions.declare_output(cdb_path)
 82              cmd = cmd_args(
 83                  mk_comp_db,
 84                  "gen",
 85                  cmd_args(entry.as_output(), format = "--output={}"),
 86                  src_compile_cmd.src.basename,
 87                  cmd_args(src_compile_cmd.src, parent = 1),
 88                  "--",
 89                  src_compile_cmd.cxx_compile_cmd.base_compile_cmd,
 90                  src_compile_cmd.cxx_compile_cmd.argsfile.cmd_form,
 91                  src_compile_cmd.args,
 92              )
 93              entry_identifier = paths.join(identifier, src_compile_cmd.src.short_path)
 94              ctx.actions.run(cmd, category = "cxx_compilation_database", identifier = entry_identifier)
 95  
 96              # Add all inputs the command uses to runtime files
 97              other_outputs.append(cmd)
 98              entries[cdb_path] = entry
 99  
100      # Merge all entries into the actual compilation DB
101      db = ctx.actions.declare_output(paths.join(identifier, "compile_commands.json"))
102      cmd = cmd_args(mk_comp_db)
103      cmd.add("merge")
104      cmd.add(cmd_args(db.as_output(), format = "--output={}"))
105      cmd.add(at_argfile(
106          actions = ctx.actions,
107          name = identifier + ".cxx_comp_db_argsfile",
108          args = entries.values(),
109      ))
110  
111      ctx.actions.run(cmd, category = "cxx_compilation_database_merge", identifier = identifier)
112  
113      return DefaultInfo(default_output = db, other_outputs = other_outputs)