kleaf: Build compile_commands.json from Kbuild.

Add a build_compile_commands bool_setting that, when
it is set from transitions:
- Add compile_commands.json to MAKE_GOALS
- Collect necessary files from OUT_DIR that may be useful later
  (namely generated headers and sources)
- Sanitize $OUT_DIR/compile_commands.json by replacing the
  values of ROOT_DIR and OUT_DIR so it can be replaced with
  real values within different environments. The new file is
  called compile_commands_with_vars.json.

Test: build kythe and compile_commands
Bug: 201801372
Bug: 255429314
Bug: 258447707

Change-Id: Idd98e93554561e334ec740633e048c33ee4ce93b
diff --git a/kleaf/impl/BUILD.bazel b/kleaf/impl/BUILD.bazel
index 4632406..0561242 100644
--- a/kleaf/impl/BUILD.bazel
+++ b/kleaf/impl/BUILD.bazel
@@ -33,6 +33,7 @@
         "abi/trim_nonlisted_kmi_utils.bzl",
         "btf.bzl",
         "common_providers.bzl",
+        "compile_commands_utils.bzl",
         "constants.bzl",
         "ddk/ddk_headers.bzl",
         "ddk/ddk_module.bzl",
@@ -99,6 +100,15 @@
     visibility = ["//visibility:public"],
 )
 
+# If true, also build compile_commands.json and collect necessary files in $OUT_DIR that
+# may be used in compile_commands.json (namely generated source files).
+bool_setting(
+    name = "build_compile_commands",
+    build_setting_default = False,
+    # All kernel_* from different packages can see this
+    visibility = ["//visibility:public"],
+)
+
 # Whether trimming is actually enabled.
 bool_setting(
     name = "trim_nonlisted_kmi_setting",
diff --git a/kleaf/impl/common_providers.bzl b/kleaf/impl/common_providers.bzl
index 257ed24..8018d54 100644
--- a/kleaf/impl/common_providers.bzl
+++ b/kleaf/impl/common_providers.bzl
@@ -53,6 +53,8 @@
             [Default outputs](https://docs.bazel.build/versions/main/skylark/rules.html#default-outputs)
             of the rule specified by `base_kernel`""",
         "interceptor_output": "`interceptor` log. See [`interceptor`](https://android.googlesource.com/kernel/tools/interceptor/) project.",
+        "compile_commands_with_vars": "A file that can be transformed into `compile_commands.json`.",
+        "compile_commands_out_dir": "A subset of `$OUT_DIR` for `compile_commands.json`.",
         "kernel_release": "The file `kernel.release`.",
     },
 )
diff --git a/kleaf/impl/compile_commands_utils.bzl b/kleaf/impl/compile_commands_utils.bzl
new file mode 100644
index 0000000..122742d
--- /dev/null
+++ b/kleaf/impl/compile_commands_utils.bzl
@@ -0,0 +1,84 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utility functions for building compile_commands.json."""
+
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
+
+def _compile_commands_config_settings_raw():
+    """Attributes of rules that supports `compile_commands.json`."""
+    return {
+        "_build_compile_commands": "//build/kernel/kleaf/impl:build_compile_commands",
+    }
+
+def _kernel_build_step(ctx):
+    """Returns a step for grabbing required files for `compile_commands.json`
+
+    Args:
+        ctx: `kernel_build` ctx
+
+    Returns:
+        A struct with these fields:
+        * inputs
+        * tools
+        * outputs
+        * compile_commands_out_dir
+        * compile_commands_with_vars
+    """
+    cmd = ""
+    compile_commands_with_vars = None
+    out_dir = None
+    outputs = []
+    if ctx.attr._build_compile_commands[BuildSettingInfo].value:
+        out_dir = ctx.actions.declare_directory("{name}/compile_commands_out_dir".format(name = ctx.label.name))
+        compile_commands_with_vars = ctx.actions.declare_file(
+            "{name}/compile_commands_with_vars.json".format(name = ctx.label.name),
+        )
+        outputs += [out_dir, compile_commands_with_vars]
+        cmd = """
+            rsync -a --prune-empty-dirs \\
+                --include '*/' \\
+                --include '*.c' \\
+                --include '*.h' \\
+                --exclude '*' ${{OUT_DIR}}/ {out_dir}/
+            sed -e "s:${{OUT_DIR}}:\\${{OUT_DIR}}:g;s:${{ROOT_DIR}}:\\${{ROOT_DIR}}:g" \\
+                ${{OUT_DIR}}/compile_commands.json > {compile_commands_with_vars}
+        """.format(
+            out_dir = out_dir.path,
+            compile_commands_with_vars = compile_commands_with_vars.path,
+        )
+    return struct(
+        inputs = [],
+        tools = [],
+        cmd = cmd,
+        outputs = outputs,
+        compile_commands_with_vars = compile_commands_with_vars,
+        compile_commands_out_dir = out_dir,
+    )
+
+def _additional_make_goals(ctx):
+    """Returns a list of additional `MAKE_GOALS`.
+
+    Args:
+        ctx: ctx
+    """
+    if ctx.attr._build_compile_commands[BuildSettingInfo].value:
+        return ["compile_commands.json"]
+    return []
+
+compile_commands_utils = struct(
+    kernel_build_step = _kernel_build_step,
+    config_settings_raw = _compile_commands_config_settings_raw,
+    additional_make_goals = _additional_make_goals,
+)
diff --git a/kleaf/impl/kernel_build.bzl b/kleaf/impl/kernel_build.bzl
index 53c1982..40f3aa3 100644
--- a/kleaf/impl/kernel_build.bzl
+++ b/kleaf/impl/kernel_build.bzl
@@ -43,6 +43,7 @@
     "KernelImagesInfo",
     "KernelUnstrippedModulesInfo",
 )
+load(":compile_commands_utils.bzl", "compile_commands_utils")
 load(
     ":constants.bzl",
     "MODULES_STAGING_ARCHIVE",
@@ -1009,6 +1010,7 @@
     grab_symtypes_step = _get_grab_symtypes_step(ctx)
     grab_gcno_step = _get_grab_gcno_step(ctx)
     grab_cmd_step = get_grab_cmd_step(ctx, "${OUT_DIR}")
+    compile_commands_step = compile_commands_utils.kernel_build_step(ctx)
     grab_gdb_scripts_step = kgdb.get_grab_gdb_scripts_step(ctx)
     check_remaining_modules_step = _get_check_remaining_modules_step(
         ctx = ctx,
@@ -1024,6 +1026,7 @@
         grab_symtypes_step,
         grab_gcno_step,
         grab_cmd_step,
+        compile_commands_step,
         grab_gdb_scripts_step,
         check_remaining_modules_step,
     )
@@ -1066,6 +1069,8 @@
            {grab_gcno_step_cmd}
          # Grab *.cmd
            {grab_cmd_cmd}
+         # Grab files for compile_commands.json
+           {compile_commands_step}
          # Grab GDB scripts
            {grab_gdb_scripts_cmd}
          # Grab in-tree modules
@@ -1090,6 +1095,7 @@
         grab_symtypes_cmd = grab_symtypes_step.cmd,
         grab_gcno_step_cmd = grab_gcno_step.cmd,
         grab_cmd_cmd = grab_cmd_step.cmd,
+        compile_commands_step = compile_commands_step.cmd,
         grab_gdb_scripts_cmd = grab_gdb_scripts_step.cmd,
         check_remaining_modules_cmd = check_remaining_modules_step.cmd,
         modules_staging_dir = modules_staging_dir,
@@ -1145,6 +1151,8 @@
         unstripped_dir = grab_unstripped_modules_step.unstripped_dir,
         ruledir = ruledir,
         cmd_dir = grab_cmd_step.cmd_dir,
+        compile_commands_with_vars = compile_commands_step.compile_commands_with_vars,
+        compile_commands_out_dir = compile_commands_step.compile_commands_out_dir,
     )
 
 def _create_infos(
@@ -1191,6 +1199,8 @@
         outs = all_output_files["outs"].values(),
         base_kernel_files = kbuild_mixed_tree_ret.base_kernel_files,
         interceptor_output = main_action_ret.interceptor_output,
+        compile_commands_with_vars = main_action_ret.compile_commands_with_vars,
+        compile_commands_out_dir = main_action_ret.compile_commands_out_dir,
         kernel_release = all_output_files["internal_outs"]["include/config/kernel.release"],
     )
 
diff --git a/kleaf/impl/kernel_config_settings.bzl b/kleaf/impl/kernel_config_settings.bzl
index eff0b64..c5fa1aa 100644
--- a/kleaf/impl/kernel_config_settings.bzl
+++ b/kleaf/impl/kernel_config_settings.bzl
@@ -35,6 +35,7 @@
 load(":abi/base_kernel_utils.bzl", "base_kernel_utils")
 load(":abi/force_add_vmlinux_utils.bzl", "force_add_vmlinux_utils")
 load(":abi/trim_nonlisted_kmi_utils.bzl", "trim_nonlisted_kmi_utils")
+load(":compile_commands_utils.bzl", "compile_commands_utils")
 load(":kgdb.bzl", "kgdb")
 
 def _kernel_build_config_settings_raw():
@@ -43,6 +44,7 @@
         force_add_vmlinux_utils.config_settings_raw(),
         base_kernel_utils.config_settings_raw(),
         kgdb.config_settings_raw(),
+        compile_commands_utils.config_settings_raw(),
         {
             "_use_kmi_symbol_list_strict_mode": "//build/kernel/kleaf:kmi_symbol_list_strict_mode",
             "_gcov": "//build/kernel/kleaf:gcov",
@@ -78,6 +80,7 @@
         _kernel_config_config_settings_raw(),
         force_add_vmlinux_utils.config_settings_raw(),
         kgdb.config_settings_raw(),
+        compile_commands_utils.config_settings_raw(),
         {
             "_kbuild_symtypes_flag": "//build/kernel/kleaf:kbuild_symtypes",
         },
diff --git a/kleaf/impl/kernel_env.bzl b/kleaf/impl/kernel_env.bzl
index e04998c..a3d8895 100644
--- a/kleaf/impl/kernel_env.bzl
+++ b/kleaf/impl/kernel_env.bzl
@@ -26,6 +26,7 @@
     "KernelEnvAttrInfo",
     "KernelEnvInfo",
 )
+load(":compile_commands_utils.bzl", "compile_commands_utils")
 load(":debug.bzl", "debug")
 load(":kernel_config_settings.bzl", "kernel_config_settings")
 load(":kernel_dtstree.bzl", "DtstreeInfo")
@@ -136,6 +137,7 @@
 
     additional_make_goals = force_add_vmlinux_utils.additional_make_goals(ctx)
     additional_make_goals += kgdb.additional_make_goals(ctx)
+    additional_make_goals += compile_commands_utils.additional_make_goals(ctx)
 
     command += """
         # create a build environment