kleaf: abi: Add rule to invoke stgdiff directly.

* This is so `*_abi_diff_stg` can be invoked.

* Cleanup of previous XML support will be done as
follow ups.

* This is not integrated to kernel_abi_dist yet.

Bug: 261722616
Change-Id: Ib27299efa2ae597d3bb225ea5624825e5c318780
Signed-off-by: Ulises Mendez Martinez <umendez@google.com>
diff --git a/kleaf/common_kernels.bzl b/kleaf/common_kernels.bzl
index ca1f778..7ba5313c 100644
--- a/kleaf/common_kernels.bzl
+++ b/kleaf/common_kernels.bzl
@@ -101,6 +101,7 @@
 # Subset of _TARGET_CONFIG_VALID_KEYS for kernel_abi.
 _KERNEL_ABI_VALID_KEYS = [
     "abi_definition",
+    "abi_definition_stg",
     "kmi_enforced",
 ]
 
@@ -131,6 +132,8 @@
     aarch64_trim_and_check = bool(aarch64_kmi_symbol_list) or len(aarch64_additional_kmi_symbol_lists) > 0
     aarch64_abi_definition = native.glob(["android/abi_gki_aarch64.xml"])
     aarch64_abi_definition = aarch64_abi_definition[0] if aarch64_abi_definition else None
+    aarch64_abi_definition_stg = native.glob(["android/abi_gki_aarch64.stg"])
+    aarch64_abi_definition_stg = aarch64_abi_definition_stg[0] if aarch64_abi_definition_stg else None
 
     # Common configs for aarch64 and aarch64_debug
     aarch64_common = {
@@ -139,6 +142,7 @@
         "kmi_symbol_list": aarch64_kmi_symbol_list,
         "additional_kmi_symbol_lists": aarch64_additional_kmi_symbol_lists,
         "abi_definition": aarch64_abi_definition,
+        "abi_definition_stg": aarch64_abi_definition_stg,
         "kmi_enforced": bool(aarch64_abi_definition),
         # Assume BUILD_GKI_ARTIFACTS=1
         "build_gki_artifacts": True,
diff --git a/kleaf/impl/BUILD.bazel b/kleaf/impl/BUILD.bazel
index 7f2a7ac..aa4699d 100644
--- a/kleaf/impl/BUILD.bazel
+++ b/kleaf/impl/BUILD.bazel
@@ -24,6 +24,7 @@
         "abi/abi_diff.bzl",
         "abi/abi_dump.bzl",
         "abi/abi_prop.bzl",
+        "abi/abi_stgdiff.bzl",
         "abi/abi_transitions.bzl",
         "abi/base_kernel_utils.bzl",
         "abi/extracted_symbols.bzl",
diff --git a/kleaf/impl/abi/abi_dump.bzl b/kleaf/impl/abi/abi_dump.bzl
index 8369cd5..c382a73 100644
--- a/kleaf/impl/abi/abi_dump.bzl
+++ b/kleaf/impl/abi/abi_dump.bzl
@@ -48,7 +48,7 @@
         ])),
         OutputGroupInfo(
             abi_out_file = depset([abi_out_file]),
-            stg_abi_out_file = depset([abi_out_file_stg]),
+            abi_out_file_stg = depset([abi_out_file_stg]),
         ),
     ]
 
diff --git a/kleaf/impl/abi/abi_stgdiff.bzl b/kleaf/impl/abi/abi_stgdiff.bzl
new file mode 100644
index 0000000..c80a5d4
--- /dev/null
+++ b/kleaf/impl/abi/abi_stgdiff.bzl
@@ -0,0 +1,148 @@
+# Copyright (C) 2023 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.
+
+"""
+Run `stgdiff` tool.
+"""
+
+load("//build/kernel/kleaf:hermetic_tools.bzl", "HermeticToolsInfo")
+load(":debug.bzl", "debug")
+
+STGDIFF_FORMATS = ["plain", "flat", "small", "short", "viz"]
+STGDIFF_CHANGE_CODE = 4
+
+def _stgdiff_impl(ctx):
+    inputs = [
+        ctx.file._stgdiff,
+        ctx.file.baseline,
+        ctx.file.new,
+    ]
+    inputs += ctx.attr._hermetic_tools[HermeticToolsInfo].deps
+
+    output_dir = ctx.actions.declare_directory("{}/abi_stgdiff".format(ctx.attr.name))
+    error_msg_file = ctx.actions.declare_file("{}/error_msg_file.txt".format(ctx.attr.name))
+    exit_code_file = ctx.actions.declare_file("{}/exit_code_file.txt".format(ctx.attr.name))
+
+    default_outputs = [output_dir]
+    command_outputs = default_outputs + [
+        error_msg_file,
+        exit_code_file,
+    ]
+    basename = "{output_dir}/abi.stg_report".format(output_dir = output_dir.path)
+    short_report = basename + ".short"
+    outputs = " ".join(["--format {ext} --output {basename}.{ext}".format(
+        basename = basename,
+        ext = ext,
+    ) for ext in STGDIFF_FORMATS])
+
+    command = ctx.attr._hermetic_tools[HermeticToolsInfo].setup + """
+        set +e
+        {stgdiff}  --stg {new} {baseline} {outputs} > {error_msg_file}
+        rc=$?
+        set -e
+        echo $rc > {exit_code_file}
+        if [[ $rc == 0 ]]; then
+            echo "INFO: $(cat {error_msg_file})"
+        elif [[ $rc == {change_code} ]]; then
+            echo "INFO: ABI DIFFERENCES HAVE BEEN DETECTED!"
+            echo "INFO: $(cat {short_report})"
+        else
+            echo "ERROR: $(cat {error_msg_file})" >&2
+            echo "INFO: exit code is not checked. 'tools/bazel run {label}' to check the exit code." >&2
+        fi
+    """.format(
+        stgdiff = ctx.file._stgdiff.path,
+        baseline = ctx.file.baseline.path,
+        new = ctx.file.new.path,
+        output_dir = output_dir.path,
+        exit_code_file = exit_code_file.path,
+        error_msg_file = error_msg_file.path,
+        short_report = short_report,
+        outputs = outputs,
+        label = ctx.label,
+        change_code = STGDIFF_CHANGE_CODE,
+    )
+
+    debug.print_scripts(ctx, command)
+    ctx.actions.run_shell(
+        inputs = inputs,
+        outputs = command_outputs,
+        command = command,
+        mnemonic = "KernelDiffAbiStg",
+        progress_message = "[stg] Comparing Kernel ABI {}".format(ctx.label),
+    )
+
+    script = ctx.actions.declare_file("{}/print_results.sh".format(ctx.attr.name))
+
+    # TODO(b/265020068) Remove duplicate code here.
+    short_report = "{output_dir}/abi.stg_report.short".format(output_dir = output_dir.short_path)
+    script_content = """#!/bin/bash -e
+        rc=$(cat {exit_code_file})
+        if [[ $rc == 0 ]]; then
+            echo "INFO: $(cat {error_msg_file})"
+        elif [[ $rc == 4 ]]; then
+            echo "INFO: ABI DIFFERENCES HAVE BEEN DETECTED!"
+            echo "INFO: $(cat {short_report})"
+        else
+            echo "ERROR: $(cat {error_msg_file})" >&2
+        fi
+""".format(
+        exit_code_file = exit_code_file.short_path,
+        error_msg_file = error_msg_file.short_path,
+        short_report = short_report,
+    )
+    if ctx.attr.kmi_enforced:
+        script_content += """
+            exit $rc
+        """
+    else:
+        script_content += """
+            if [[ $rc != 0 ]]; then
+                echo "WARN: KMI is not enforced, return code of stgdiff is not checked" >&2
+            fi
+        """
+    ctx.actions.write(script, script_content, is_executable = True)
+
+    return [
+        DefaultInfo(
+            files = depset(default_outputs),
+            executable = script,
+            runfiles = ctx.runfiles(files = command_outputs),
+        ),
+        OutputGroupInfo(
+            executable = depset([script]),
+        ),
+    ]
+
+stgdiff = rule(
+    implementation = _stgdiff_impl,
+    doc = "Run `stgdiff`",
+    attrs = {
+        "baseline": attr.label(allow_single_file = True),
+        "new": attr.label(allow_single_file = True),
+        "kmi_enforced": attr.bool(),
+        "_hermetic_tools": attr.label(
+            default = "//build/kernel:hermetic-tools",
+            providers = [HermeticToolsInfo],
+        ),
+        "_stgdiff": attr.label(
+            default = "//prebuilts/kernel-build-tools:linux-x86/bin/stgdiff",
+            allow_single_file = True,
+            cfg = "exec",
+            executable = True,
+        ),
+        "_debug_print_scripts": attr.label(default = "//build/kernel/kleaf:debug_print_scripts"),
+    },
+    executable = True,
+)
diff --git a/kleaf/impl/abi/kernel_build_abi.bzl b/kleaf/impl/abi/kernel_build_abi.bzl
index e66e4ea..b6baf1e 100644
--- a/kleaf/impl/abi/kernel_build_abi.bzl
+++ b/kleaf/impl/abi/kernel_build_abi.bzl
@@ -17,6 +17,7 @@
 load("//build/bazel_common_rules/exec:exec.bzl", "exec")
 load("//build/kernel/kleaf:update_source_file.bzl", "update_source_file")
 load(":abi/abi_diff.bzl", "abi_diff")
+load(":abi/abi_stgdiff.bzl", "stgdiff")
 load(":abi/abi_dump.bzl", "abi_dump")
 load(":abi/abi_prop.bzl", "abi_prop")
 load(":abi/extracted_symbols.bzl", "extracted_symbols")
@@ -32,6 +33,7 @@
         kernel_modules = None,
         module_grouping = None,
         abi_definition = None,
+        abi_definition_stg = None,
         kmi_enforced = None,
         unstripped_modules_archive = None,
         kmi_symbol_list_add_only = None,
@@ -75,6 +77,7 @@
       kernel_modules: See [`kernel_abi.kernel_modules`](#kernel_abi-kernel_modules)
       module_grouping: See [`kernel_abi.module_grouping`](#kernel_abi-module_grouping)
       abi_definition: See [`kernel_abi.abi_definition`](#kernel_abi-abi_definition)
+      abi_definition_stg: See [`kernel_abi.abi_definition_stg`](#kernel_abi-abi_definition_stg)
       kmi_enforced: See [`kernel_abi.kmi_enforced`](#kernel_abi-kmi_enforced)
       unstripped_modules_archive: See [`kernel_abi.unstripped_modules_archive`](#kernel_abi-unstripped_modules_archive)
       kmi_symbol_list_add_only: See [`kernel_abi.kmi_symbol_list_add_only`](#kernel_abi-kmi_symbol_list_add_only)
@@ -122,6 +125,7 @@
             kernel_modules = kernel_modules,
             module_grouping = module_grouping,
             abi_definition = abi_definition,
+            abi_definition_stg = abi_definition_stg,
             kmi_enforced = kmi_enforced,
             unstripped_modules_archive = unstripped_modules_archive,
             kmi_symbol_list_add_only = kmi_symbol_list_add_only,
@@ -140,6 +144,7 @@
         module_grouping = module_grouping,
         kmi_symbol_list_add_only = kmi_symbol_list_add_only,
         abi_definition = abi_definition,
+        abi_definition_stg = abi_definition_stg,
         kmi_enforced = kmi_enforced,
         unstripped_modules_archive = unstripped_modules_archive,
         # common attributes
@@ -166,6 +171,7 @@
         kernel_modules = None,
         module_grouping = None,
         abi_definition = None,
+        abi_definition_stg = None,
         kmi_enforced = None,
         unstripped_modules_archive = None,
         kmi_symbol_list_add_only = None,
@@ -239,6 +245,7 @@
         list will simply be a sorted list of symbols used by all the kernel
         modules.
       abi_definition: Location of the ABI definition.
+      abi_definition_stg: Location of the ABI definition in STG format.
       kmi_enforced: This is an indicative option to signal that KMI is enforced.
         If set to `True`, KMI checking tools respects it and
         reacts to it by failing if KMI differences are detected.
@@ -286,6 +293,7 @@
             module_grouping = module_grouping,
             kmi_symbol_list_add_only = kmi_symbol_list_add_only,
             abi_definition = abi_definition,
+            abi_definition_stg = abi_definition_stg,
             kmi_enforced = kmi_enforced,
             unstripped_modules_archive = unstripped_modules_archive,
             abi_dump_target = name + "_dump",
@@ -322,6 +330,11 @@
         script = "",
         **private_kwargs
     )
+    exec(
+        name = name + "_diff_executable_stg",
+        script = "",
+        **private_kwargs
+    )
 
 def _define_abi_targets(
         name,
@@ -330,6 +343,7 @@
         module_grouping,
         kmi_symbol_list_add_only,
         abi_definition,
+        abi_definition_stg,
         kmi_enforced,
         unstripped_modules_archive,
         abi_dump_target,
@@ -371,10 +385,10 @@
         dst = name + "_src_kmi_symbol_list",
         **private_kwargs
     )
-
     default_outputs += _define_abi_definition_targets(
         name = name,
         abi_definition = abi_definition,
+        abi_definition_stg = abi_definition_stg,
         kmi_enforced = kmi_enforced,
         kmi_symbol_list = name + "_src_kmi_symbol_list",
         **private_kwargs
@@ -399,6 +413,7 @@
 def _define_abi_definition_targets(
         name,
         abi_definition,
+        abi_definition_stg,
         kmi_enforced,
         kmi_symbol_list,
         **kwargs):
@@ -408,6 +423,9 @@
 
     Defines `{name}_diff_executable`.
     """
+
+    default_outputs = []
+
     if not abi_definition:
         # For kernel_abi_dist to use when abi_definition is empty.
         exec(
@@ -415,111 +433,132 @@
             script = "",
             **kwargs
         )
-        return []
+        default_outputs.append(name + "_diff_executable")
+    else:
+        native.filegroup(
+            name = name + "_out_file",
+            srcs = [name + "_dump"],
+            output_group = "abi_out_file",
+            **kwargs
+        )
 
-    default_outputs = []
+        abi_diff(
+            name = name + "_diff",
+            baseline = abi_definition,
+            new = name + "_out_file",
+            kmi_enforced = kmi_enforced,
+            **kwargs
+        )
+        default_outputs.append(name + "_diff")
 
-    native.filegroup(
-        name = name + "_out_file",
-        srcs = [name + "_dump"],
-        output_group = "abi_out_file",
-        **kwargs
-    )
+        # The default outputs of _diff does not contain the executable,
+        # but the reports. Use this filegroup to select the executable
+        # so rootpath in _update works.
+        native.filegroup(
+            name = name + "_diff_executable",
+            srcs = [name + "_diff"],
+            output_group = "executable",
+            **kwargs
+        )
 
-    abi_diff(
-        name = name + "_diff",
-        baseline = abi_definition,
-        new = name + "_out_file",
-        kmi_enforced = kmi_enforced,
-        **kwargs
-    )
-    default_outputs.append(name + "_diff")
+        native.filegroup(
+            name = name + "_diff_git_message",
+            srcs = [name + "_diff"],
+            output_group = "git_message",
+            **kwargs
+        )
 
-    # The default outputs of _diff does not contain the executable,
-    # but the reports. Use this filegroup to select the executable
-    # so rootpath in _update works.
-    native.filegroup(
-        name = name + "_diff_executable",
-        srcs = [name + "_diff"],
-        output_group = "executable",
-        **kwargs
-    )
+        update_source_file(
+            name = name + "_update_definition",
+            src = name + "_out_file",
+            dst = abi_definition,
+            **kwargs
+        )
 
-    native.filegroup(
-        name = name + "_diff_git_message",
-        srcs = [name + "_diff"],
-        output_group = "git_message",
-        **kwargs
-    )
+        exec(
+            name = name + "_nodiff_update",
+            data = [
+                name + "_extracted_symbols",
+                name + "_update_definition",
+                kmi_symbol_list,
+            ],
+            script = """
+                # Ensure that symbol list is updated
+                    if ! diff -q $(rootpath {src_symbol_list}) $(rootpath {dst_symbol_list}); then
+                    echo "ERROR: symbol list must be updated before updating ABI definition. To update, execute 'tools/bazel run //{package}:{update_symbol_list_label}'." >&2
+                    exit 1
+                    fi
+                # Update abi_definition
+                    $(rootpath {update_definition})
+                """.format(
+                src_symbol_list = name + "_extracted_symbols",
+                dst_symbol_list = kmi_symbol_list,
+                package = native.package_name(),
+                update_symbol_list_label = name + "_update_symbol_list",
+                update_definition = name + "_update_definition",
+            ),
+            **kwargs
+        )
 
-    update_source_file(
-        name = name + "_update_definition",
-        src = name + "_out_file",
-        dst = abi_definition,
-        **kwargs
-    )
+        exec(
+            name = name + "_update",
+            data = [
+                abi_definition,
+                name + "_diff_git_message",
+                name + "_diff_executable",
+                name + "_nodiff_update",
+            ],
+            script = """
+                # Update abi_definition
+                    $(rootpath {nodiff_update})
+                # Create git commit if requested
+                    if [[ $1 == "--commit" ]]; then
+                        real_abi_def="$(realpath $(rootpath {abi_definition}))"
+                        git -C $(dirname ${{real_abi_def}}) add $(basename ${{real_abi_def}})
+                        git -C $(dirname ${{real_abi_def}}) commit -F $(realpath $(rootpath {git_message}))
+                    fi
+                # Check return code of diff_abi and kmi_enforced
+                    set +e
+                    $(rootpath {diff})
+                    rc=$?
+                    set -e
+                # Prompt for editing the commit message
+                    if [[ $1 == "--commit" ]]; then
+                        echo
+                        echo "INFO: git commit created. Execute the following to edit the commit message:"
+                        echo "        git -C $(dirname $(rootpath {abi_definition})) commit --amend"
+                    fi
+                    exit $rc
+                """.format(
+                diff = name + "_diff_executable",
+                nodiff_update = name + "_nodiff_update",
+                abi_definition = abi_definition,
+                git_message = name + "_diff_git_message",
+            ),
+            **kwargs
+        )
 
-    exec(
-        name = name + "_nodiff_update",
-        data = [
-            name + "_extracted_symbols",
-            name + "_update_definition",
-            kmi_symbol_list,
-        ],
-        script = """
-              # Ensure that symbol list is updated
-                if ! diff -q $(rootpath {src_symbol_list}) $(rootpath {dst_symbol_list}); then
-                  echo "ERROR: symbol list must be updated before updating ABI definition. To update, execute 'tools/bazel run //{package}:{update_symbol_list_label}'." >&2
-                  exit 1
-                fi
-              # Update abi_definition
-                $(rootpath {update_definition})
-            """.format(
-            src_symbol_list = name + "_extracted_symbols",
-            dst_symbol_list = kmi_symbol_list,
-            package = native.package_name(),
-            update_symbol_list_label = name + "_update_symbol_list",
-            update_definition = name + "_update_definition",
-        ),
-        **kwargs
-    )
-
-    exec(
-        name = name + "_update",
-        data = [
-            abi_definition,
-            name + "_diff_git_message",
-            name + "_diff_executable",
-            name + "_nodiff_update",
-        ],
-        script = """
-              # Update abi_definition
-                $(rootpath {nodiff_update})
-              # Create git commit if requested
-                if [[ $1 == "--commit" ]]; then
-                    real_abi_def="$(realpath $(rootpath {abi_definition}))"
-                    git -C $(dirname ${{real_abi_def}}) add $(basename ${{real_abi_def}})
-                    git -C $(dirname ${{real_abi_def}}) commit -F $(realpath $(rootpath {git_message}))
-                fi
-              # Check return code of diff_abi and kmi_enforced
-                set +e
-                $(rootpath {diff})
-                rc=$?
-                set -e
-              # Prompt for editing the commit message
-                if [[ $1 == "--commit" ]]; then
-                    echo
-                    echo "INFO: git commit created. Execute the following to edit the commit message:"
-                    echo "        git -C $(dirname $(rootpath {abi_definition})) commit --amend"
-                fi
-                exit $rc
-            """.format(
-            diff = name + "_diff_executable",
-            nodiff_update = name + "_nodiff_update",
-            abi_definition = abi_definition,
-            git_message = name + "_diff_git_message",
-        ),
-        **kwargs
-    )
+    if not abi_definition_stg:
+        # For kernel_abi_dist to use when abi_definition is empty.
+        exec(
+            name = name + "_diff_executable_stg",
+            script = "",
+            **kwargs
+        )
+        default_outputs.append(name + "_diff_executable_stg")
+    else:
+        native.filegroup(
+            name = name + "_out_file_stg",
+            srcs = [name + "_dump"],
+            output_group = "abi_out_file_stg",
+            **kwargs
+        )
+        stgdiff(
+            name = name + "_diff_stg",
+            baseline = abi_definition_stg,
+            new = name + "_out_file_stg",
+            kmi_enforced = kmi_enforced,
+        )
+        default_outputs.append(name + "_diff_stg")
 
     return default_outputs