kleaf: mixed kernel_build modinst fix up kernel_release.

Before kernel_build executes make modules_install, manually
overwrite kernel.release to be the one from base_kernel. This
ensures the directory name under /lib/modules is the same
as uname.

After kernel_build executes make modules_install, checks that
Kbuild did not rebuild kernel.release to be the scmversion
of in-tree modules.

Impact:
- If both the base_kernel and in-tree modules are built from
  the same tree (same git metadata, same BUILD_NUMBER, etc.),
  then this change has no effect because kernel.release file is
  the same for base_kernel and device kernel_build.
- If the base_kernel's and in-tree modules (device kernel_build's
  KERNEL_DIR) are built from different trees (e.g. by using prebuilts),
  before this change, there should be a build error because multiple
  directories exist below /lib/modules. After this change, the
  modules are merged into the same directory.

Test: manual by building a Pixel device against prebuilts,
      which are built on ci.android.com with a different scmversion.
Bug: 284198523
Change-Id: Iee21231897e66b3b1279989ccc4136377ff1cec1
diff --git a/kleaf/impl/kernel_build.bzl b/kleaf/impl/kernel_build.bzl
index 6c2eb97..2d65399 100644
--- a/kleaf/impl/kernel_build.bzl
+++ b/kleaf/impl/kernel_build.bzl
@@ -1117,7 +1117,40 @@
     module_strip_flag = "INSTALL_MOD_STRIP="
     if ctx.attr.strip_modules:
         module_strip_flag += "1"
-    cmd = """
+
+    base_kernel = base_kernel_utils.get_base_kernel(ctx)
+
+    cmd = ""
+    inputs = []
+    tools = []
+
+    if base_kernel:
+        cmd += """
+          # Check that base_kernel has the same KMI as the current kernel_build
+            (
+                base_release=$(cat {base_kernel_release_file})
+                base_kmi=$({get_kmi_string} --keep_sublevel ${{base_release}})
+                my_release=$(cat ${{OUT_DIR}}/include/config/kernel.release)
+                my_kmi=$({get_kmi_string} --keep_sublevel ${{my_release}})
+                if [[ "${{base_kmi}}" != "${{my_kmi}}" ]]; then
+                    echo "ERROR: KMI or sublevel mismatch before running make modules_install:" >&2
+                    echo "  {label}: ${{my_kmi}} (from ${{my_release}})" >&2
+                    echo "  {base_kernel_label}: ${{base_kmi}} (from ${{base_release}})" >&2
+                fi
+            )
+
+          # Fix up kernel.release to be the one from {base_kernel_label} before installing modules
+            cp -L {base_kernel_release_file} ${{OUT_DIR}}/include/config/kernel.release
+        """.format(
+            get_kmi_string = ctx.executable._get_kmi_string.path,
+            label = ctx.label,
+            base_kernel_label = base_kernel.label,
+            base_kernel_release_file = base_kernel[KernelBuildUnameInfo].kernel_release.path,
+        )
+        inputs.append(base_kernel[KernelBuildUnameInfo].kernel_release)
+        tools.append(ctx.executable._get_kmi_string)
+
+    cmd += """
          # Set variables and create dirs for modules
            mkdir -p {modules_staging_dir}
          # Install modules
@@ -1134,9 +1167,20 @@
         make_goals = ctx.attr.config[KernelEnvMakeGoalsInfo].make_goals,
     )
 
+    if base_kernel:
+        cmd += """
+          # Check that `make modules_install` does not revert include/config/kernel.release
+            if diff -q {base_kernel_release} ${{OUT_DIR}}/include/config/kernel.release; then
+                echo "ERROR: make modules_install modifies include/config/kernel.release." >&2
+                echo "   This is not expected; please file a bug!" >&2
+            fi
+        """.format(
+            base_kernel_release = base_kernel[KernelBuildUnameInfo].kernel_release.path,
+        )
+
     return struct(
-        inputs = [],
-        tools = [],
+        inputs = inputs,
+        tools = tools,
         cmd = cmd,
         outputs = [],
     )
@@ -1752,6 +1796,11 @@
             executable = True,
             cfg = "exec",
         ),
+        "_get_kmi_string": attr.label(
+            default = "//build/kernel/kleaf/impl:get_kmi_string",
+            executable = True,
+            cfg = "exec",
+        ),
         "_hermetic_tools": attr.label(default = "//build/kernel:hermetic-tools", providers = [HermeticToolsInfo]),
         "_debug_print_scripts": attr.label(default = "//build/kernel/kleaf:debug_print_scripts"),
         "_config_is_local": attr.label(default = "//build/kernel/kleaf:config_local"),
diff --git a/kleaf/impl/kernel_filegroup.bzl b/kleaf/impl/kernel_filegroup.bzl
index a1cabbe..201921b 100644
--- a/kleaf/impl/kernel_filegroup.bzl
+++ b/kleaf/impl/kernel_filegroup.bzl
@@ -74,10 +74,8 @@
         name = "gki-info.txt",
         files = ctx.files.gki_artifacts,
         what = "{} gki_artifacts".format(ctx.label),
+        required = True,
     )
-    if gki_info == None:
-        # For tests
-        return None
     kernel_release = ctx.actions.declare_file("{}/kernel.release".format(ctx.label.name))
     command = ctx.attr._hermetic_tools[HermeticToolsInfo].setup + """
         kernel_release=$(cat {gki_info} | sed -nE 's/^kernel_release=(.*)$/\\1/p')
diff --git a/kleaf/tests/kernel_build_test/kernel_toolchain_test.bzl b/kleaf/tests/kernel_build_test/kernel_toolchain_test.bzl
index 58c74fd..c381d09 100644
--- a/kleaf/tests/kernel_build_test/kernel_toolchain_test.bzl
+++ b/kleaf/tests/kernel_build_test/kernel_toolchain_test.bzl
@@ -85,6 +85,15 @@
         )
 
         write_file(
+            name = filegroup_name + "_gki_info",
+            out = filegroup_name + "_gki_info/gki-info.txt",
+            content = [
+                "KERNEL_RELEASE=99.98.97",
+                "",
+            ],
+        )
+
+        write_file(
             name = filegroup_name + "_toolchain_version",
             out = filegroup_name + "/toolchain_version",
             content = [clang_version],
@@ -98,6 +107,7 @@
                 filegroup_name + "_staging_archive",
             ],
             module_outs_file = filegroup_name + "_module_outs_file",
+            gki_artifacts = filegroup_name + "_gki_info",
             tags = ["manual"],
         )
 
diff --git a/kleaf/tests/merged_kernel_uapi_headers_test/order_test.bzl b/kleaf/tests/merged_kernel_uapi_headers_test/order_test.bzl
index f598e03..2f4cba6 100644
--- a/kleaf/tests/merged_kernel_uapi_headers_test/order_test.bzl
+++ b/kleaf/tests/merged_kernel_uapi_headers_test/order_test.bzl
@@ -12,9 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Test ordering of unpacking UAPI header archives.
+"""Test ordering of unpacking UAPI header archives."""
 
 load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
 load("//build/kernel/kleaf/impl:common_providers.bzl", "KernelBuildUapiInfo", "KernelModuleInfo")
 load("//build/kernel/kleaf/impl:constants.bzl", "TOOLCHAIN_VERSION_FILENAME")
 load("//build/kernel/kleaf/impl:kernel_build.bzl", "kernel_build")
@@ -30,6 +31,7 @@
         file_name = target.label.name + "/kernel-uapi-headers.tar.gz_staging"
     else:
         asserts.true(env, False, "Unrecognized target {}".format(target.label.name))
+        return None
 
     for index, command in enumerate(commands):
         if file_name in command:
@@ -39,6 +41,7 @@
         file_name,
         "\n".join(commands),
     ))
+    return None
 
 def _assert_acending(env, lst, commands_text):
     for prev_item, next_item in zip(lst, lst[1:]):
@@ -90,6 +93,11 @@
     )
 
 def order_test(name):
+    """Test ordering of unpacking UAPI header archives.
+
+    Args:
+        name: name of test
+    """
     tests = []
 
     kernel_build(
@@ -113,6 +121,15 @@
         tags = ["manual"],
     )
 
+    write_file(
+        name = name + "_gki_info",
+        out = name + "_gki_info/gki-info.txt",
+        content = [
+            "KERNEL_RELEASE=99.98.97",
+            "",
+        ],
+    )
+
     kernel_filegroup(
         name = name + "_fg",
         srcs = [name + "_base"],
@@ -122,6 +139,7 @@
         ],
         kernel_uapi_headers = name + "_base_uapi_headers",
         module_outs_file = name + "_module_outs_file",
+        gki_artifacts = name + "_gki_info",
         tags = ["manual"],
     )