Generate compat files and modules with scripts

The steps have been done by hand, which is highly errorprone.

Bug: 207344718
Test: run the script manually
Change-Id: I9deb367b0cbd8d357147f83964bc214cd00266f7
diff --git a/tools/sepolicy_generate_compat.py b/tools/sepolicy_generate_compat.py
index ce974f9..ca5ae91 100644
--- a/tools/sepolicy_generate_compat.py
+++ b/tools/sepolicy_generate_compat.py
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from pathlib import Path
 import argparse
 import glob
 import logging
@@ -190,6 +191,122 @@
     return versioned_type.removesuffix(old_suffix) + new_suffix
 
 
+def create_target_compat_modules(bp_path, target_ver):
+    """ Creates compat modules to Android.bp.
+
+    Args:
+      bp_path: string, path to Android.bp
+      target_ver: string, api version to generate
+    """
+
+    module_template = """
+se_build_files {{
+    name: "{ver}.board.compat.map",
+    srcs: ["compat/{ver}/{ver}.cil"],
+}}
+
+se_build_files {{
+    name: "{ver}.board.compat.cil",
+    srcs: ["compat/{ver}/{ver}.compat.cil"],
+}}
+
+se_build_files {{
+    name: "{ver}.board.ignore.map",
+    srcs: ["compat/{ver}/{ver}.ignore.cil"],
+}}
+
+se_cil_compat_map {{
+    name: "plat_{ver}.cil",
+    stem: "{ver}.cil",
+    bottom_half: [":{ver}.board.compat.map{{.plat_private}}"],
+}}
+
+se_cil_compat_map {{
+    name: "system_ext_{ver}.cil",
+    stem: "{ver}.cil",
+    bottom_half: [":{ver}.board.compat.map{{.system_ext_private}}"],
+    system_ext_specific: true,
+}}
+
+se_cil_compat_map {{
+    name: "product_{ver}.cil",
+    stem: "{ver}.cil",
+    bottom_half: [":{ver}.board.compat.map{{.product_private}}"],
+    product_specific: true,
+}}
+
+se_cil_compat_map {{
+    name: "{ver}.ignore.cil",
+    bottom_half: [":{ver}.board.ignore.map{{.plat_private}}"],
+}}
+
+se_cil_compat_map {{
+    name: "system_ext_{ver}.ignore.cil",
+    stem: "{ver}.ignore.cil",
+    bottom_half: [":{ver}.board.ignore.map{{.system_ext_private}}"],
+    system_ext_specific: true,
+}}
+
+se_cil_compat_map {{
+    name: "product_{ver}.ignore.cil",
+    stem: "{ver}.ignore.cil",
+    bottom_half: [":{ver}.board.ignore.map{{.product_private}}"],
+    product_specific: true,
+}}
+
+se_compat_cil {{
+    name: "{ver}.compat.cil",
+    srcs: [":{ver}.board.compat.cil{{.plat_private}}"],
+}}
+
+se_compat_cil {{
+    name: "system_ext_{ver}.compat.cil",
+    stem: "{ver}.compat.cil",
+    srcs: [":{ver}.board.compat.cil{{.system_ext_private}}"],
+    system_ext_specific: true,
+}}
+"""
+
+    with open(bp_path, 'a') as f:
+        f.write(module_template.format(ver=target_ver))
+
+
+def patch_top_half_of_latest_compat_modules(bp_path, latest_ver, target_ver):
+    """ Adds top_half property to latest compat modules in Android.bp.
+
+    Args:
+      bp_path: string, path to Android.bp
+      latest_ver: string, previous api version
+      target_ver: string, api version to generate
+    """
+
+    modules_to_patch = [
+        "plat_{ver}.cil",
+        "system_ext_{ver}.cil",
+        "product_{ver}.cil",
+        "{ver}.ignore.cil",
+        "system_ext_{ver}.ignore.cil",
+        "product_{ver}.ignore.cil",
+    ]
+
+    for module in modules_to_patch:
+        # set latest_ver module's top_half property to target_ver
+        # e.g.
+        #
+        # se_cil_compat_map {
+        #    name: "plat_33.0.cil",
+        #    top_half: "plat_34.0.cil", <== this
+        #    ...
+        # }
+        check_run([
+            "bpmodify",
+            "-m", module.format(ver=latest_ver),
+            "-property", "top_half",
+            "-str", module.format(ver=target_ver),
+            "-w",
+            bp_path
+        ])
+
 def get_args():
     parser = argparse.ArgumentParser()
     parser.add_argument(
@@ -236,6 +353,26 @@
         build_top = get_android_build_top()
         sepolicy_path = os.path.join(build_top, 'system', 'sepolicy')
 
+        # Step 0. Create a placeholder files and compat modules
+        # These are needed to build base policy files below.
+        compat_bp_path = os.path.join(sepolicy_path, 'compat', 'Android.bp')
+        create_target_compat_modules(compat_bp_path, args.target_version)
+        patch_top_half_of_latest_compat_modules(compat_bp_path, args.latest_version,
+            args.target_version)
+
+        target_compat_path = os.path.join(sepolicy_path, 'private', 'compat',
+                                          args.target_version)
+        target_mapping_file = os.path.join(target_compat_path,
+                                           args.target_version + '.cil')
+        target_compat_file = os.path.join(target_compat_path,
+                                          args.target_version + '.compat.cil')
+        target_ignore_file = os.path.join(target_compat_path,
+                                          args.target_version + '.ignore.cil')
+        Path(target_compat_path).mkdir(parents=True, exist_ok=True)
+        Path(target_mapping_file).touch()
+        Path(target_compat_file).touch()
+        Path(target_ignore_file).touch()
+
         # Step 1. Download system/etc/selinux/mapping/{ver}.cil, and remove types/typeattributes
         mapping_file = download_mapping_file(
             args.branch, args.build, args.target_version, destination=temp_dir)
@@ -341,15 +478,6 @@
             sys.exit(error_msg)
 
         # Step 5. Write to system/sepolicy/private/compat
-        target_compat_path = os.path.join(sepolicy_path, 'private', 'compat',
-                                          args.target_version)
-        target_mapping_file = os.path.join(target_compat_path,
-                                           args.target_version + '.cil')
-        target_compat_file = os.path.join(target_compat_path,
-                                          args.target_version + '.compat.cil')
-        target_ignore_file = os.path.join(target_compat_path,
-                                          args.target_version + '.ignore.cil')
-
         with open(target_mapping_file, 'w') as f:
             logging.info('writing %s' % target_mapping_file)
             if removed_types: