kleaf: support ddk_library in compile_commands.json

Currently, ddk_library targets explicitly skip compdb generation,
and kernel_module does not propagate CompileCommandsInfo from
dependencies (which are represented as KernelModuleDepInfo structs
in kernel_module_deps).

This change:
1. Removes internal_compdb = "skip" from ddk_library.
2. Adds the compile_commands.json target to the generated Makefiles
   for libraries, allowing Kbuild to generate the commands.
3. Adds compile_commands_info field to KernelModuleDepInfo to
   propagate this information from libraries to modules that depend on
   them.
4. Updates kernel_module implementation to collect these transitive
   compile commands from both legacy targets and DDK dependency
   structs.

This ensures that running kernel_compile_commands on a module will
correctly include compile commands for its dependent libraries.

Bug: 394411899
Test: tools/bazel run //build/kernel/kleaf/tests/ddk_examples/ddk_library:mymod_compile_commands
Test: tools/bazel run //build/kernel/kleaf/tests/integration_test -- DdkWorkspaceSetupTest.test_compile_commands_reachable
Assisted-by: Antigravity:Gemini
Change-Id: I2c0f3c7ed88840c5616a2089e447760fe2006adf
diff --git a/kleaf/impl/common_providers.bzl b/kleaf/impl/common_providers.bzl
index fe83f72..d8323dd 100644
--- a/kleaf/impl/common_providers.bzl
+++ b/kleaf/impl/common_providers.bzl
@@ -451,6 +451,7 @@
         "kernel_module_setup_info": "`KernelModuleSetupInfo`",
         "module_symvers_info": "`ModuleSymversInfo`",
         "kernel_module_info": "`KernelModuleInfo`",
+        "compile_commands_info": "`CompileCommandsInfo`",
     },
 )
 
diff --git a/kleaf/impl/ddk/ddk_library.bzl b/kleaf/impl/ddk/ddk_library.bzl
index 729c69a..7f5d932 100644
--- a/kleaf/impl/ddk/ddk_library.bzl
+++ b/kleaf/impl/ddk/ddk_library.bzl
@@ -137,7 +137,6 @@
         internal_ddk_config = name + "_config",
         internal_is_ddk_library = True,
         internal_extra_make_goals = ["kleaf-objects"],
-        internal_compdb = "skip",
         internal_modules_install = False,
         internal_mnemonic = "DDK library",
         # Don't ask Kbuild to stamp it
diff --git a/kleaf/impl/ddk/gen_makefiles.py b/kleaf/impl/ddk/gen_makefiles.py
index 7abbb65..eeecbac 100755
--- a/kleaf/impl/ddk/gen_makefiles.py
+++ b/kleaf/impl/ddk/gen_makefiles.py
@@ -142,6 +142,11 @@
             \t$(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) \\
             \t    KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)"       \\
             \t    {objects}
+
+            compile_commands.json:
+            \t$(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) \\
+            \t    KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)"       \\
+            \t    $(@)
             """)
     else:
         content += textwrap.dedent("""\
diff --git a/kleaf/impl/kernel_module.bzl b/kleaf/impl/kernel_module.bzl
index 99e2dce..975e21c 100644
--- a/kleaf/impl/kernel_module.bzl
+++ b/kleaf/impl/kernel_module.bzl
@@ -797,6 +797,8 @@
         transitive = [dep.kernel_module_info.transitive_files for dep in kernel_module_deps],
     )
 
+    transitive_compile_commands = [dep.compile_commands_info.infos for dep in kernel_module_deps]
+
     return [
         # Sync list of infos with kernel_module_group.
         DefaultInfo(
@@ -840,10 +842,13 @@
             directories = depset([grab_cmd_step.cmd_dir]),
         ),
         CompileCommandsInfo(
-            infos = depset([CompileCommandsSingleInfo(
-                compile_commands_with_vars = compile_commands_step.compile_commands_with_vars,
-                compile_commands_common_out_dir = compile_commands_step.compile_commands_common_out_dir,
-            )]),
+            infos = depset(
+                [CompileCommandsSingleInfo(
+                    compile_commands_with_vars = compile_commands_step.compile_commands_with_vars,
+                    compile_commands_common_out_dir = compile_commands_step.compile_commands_common_out_dir,
+                )] if compile_commands_step.compile_commands_with_vars else [],
+                transitive = transitive_compile_commands,
+            ),
         ),
         ModuleSymversFileInfo(
             module_symvers = depset([module_symvers]) if module_symvers else depset(),
diff --git a/kleaf/impl/utils.bzl b/kleaf/impl/utils.bzl
index a150509..2bda23d 100644
--- a/kleaf/impl/utils.bzl
+++ b/kleaf/impl/utils.bzl
@@ -22,6 +22,7 @@
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load(
     ":common_providers.bzl",
+    "CompileCommandsInfo",
     "DdkConfigInfo",
     "DdkHeadersInfo",
     "DdkLibraryInfo",
@@ -567,6 +568,7 @@
         kernel_module_setup_info = kernel_module[KernelModuleSetupInfo],
         kernel_module_info = kernel_module[KernelModuleInfo],
         module_symvers_info = kernel_module[ModuleSymversInfo],
+        compile_commands_info = kernel_module[CompileCommandsInfo],
     )
 
 # Cross compiler name is not always the same as the linux arch
diff --git a/kleaf/tests/ddk_examples/ddk_library/BUILD.bazel b/kleaf/tests/ddk_examples/ddk_library/BUILD.bazel
index 5ba390f..c4b1bb8 100644
--- a/kleaf/tests/ddk_examples/ddk_library/BUILD.bazel
+++ b/kleaf/tests/ddk_examples/ddk_library/BUILD.bazel
@@ -1,6 +1,7 @@
 load(
     "@kleaf//build/kernel/kleaf:kernel.bzl",
     "ddk_module",
+    "kernel_compile_commands",
 )
 
 ddk_module(
@@ -22,3 +23,8 @@
         "//build/kernel/kleaf/tests/ddk_examples/ddk_library/libfoo",
     ],
 )
+
+kernel_compile_commands(
+    name = "mymod_compile_commands",
+    deps = [":mymod"],
+)
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel
index d3408a3..2f4a347 100644
--- a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/BUILD.bazel
@@ -1,5 +1,6 @@
 load(
     "@kleaf//build/kernel/kleaf:kernel.bzl",
+    "ddk_library",
     "ddk_module",
     "kernel_compile_commands",
     "kernel_config_devicetree_library",
@@ -10,11 +11,19 @@
 load("@rules_devicetree//devicetree:dtb.bzl", "dtb")
 
 # Test building no-op DDK module against kernel from sources
+ddk_library(
+    name = "mylib",
+    srcs = ["mylib.c"],
+    hdrs = ["mylib.h"],
+    kernel_build = "//tests:kernel",
+)
+
 ddk_module(
     name = "out_of_tree",
     srcs = ["mydriver.c"],
     out = "out_of_tree.ko",
     kernel_build = "//tests:kernel",
+    deps = [":mylib"],
 )
 
 kernel_modules_install(
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c
index c1b826e..d834d11 100644
--- a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mydriver.c
@@ -24,4 +24,16 @@
 MODULE_DESCRIPTION("A test module for DDK testing purposes");
 MODULE_AUTHOR("Yifan Hong <elsk@google.com>");
 MODULE_LICENSE("GPL v2");
+#include "mylib.h"
+#include <linux/init.h>
 
+static int __init mydriver_init(void) {
+    mylib_function();
+    return 0;
+}
+
+static void __exit mydriver_exit(void) {
+}
+
+module_init(mydriver_init);
+module_exit(mydriver_exit);
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.c b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.c
new file mode 100644
index 0000000..9e09f61
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.c
@@ -0,0 +1,8 @@
+#include "mylib.h"
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+void mylib_function(void) {
+    pr_info("Hello from mylib\n");
+}
+EXPORT_SYMBOL_GPL(mylib_function);
diff --git a/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.h b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.h
new file mode 100644
index 0000000..a84ef30
--- /dev/null
+++ b/kleaf/tests/integration_test/ddk_workspace_test/out_of_tree/mylib.h
@@ -0,0 +1 @@
+void mylib_function(void);
diff --git a/kleaf/tests/integration_test/integration_test.py b/kleaf/tests/integration_test/integration_test.py
index a552d86..be03197 100644
--- a/kleaf/tests/integration_test/integration_test.py
+++ b/kleaf/tests/integration_test/integration_test.py
@@ -829,6 +829,14 @@
             f"found {staged_modinfos}",
         )
 
+        with open(json_file) as f:
+            commands = json.load(f)
+        files = [entry["file"] for entry in commands]
+        self.assertTrue(any(f.endswith("out_of_tree/mydriver.c") for f in files),
+                        f"mydriver.c not found in {json_file}")
+        self.assertTrue(any(f.endswith("out_of_tree/mylib.c") for f in files),
+                        f"mylib.c not found in {json_file}")
+
 
 
     def _run_test_setup_with_local_prebuilts(