Yifan Hong | ace13de | 2023-04-28 15:54:04 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (C) 2023 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | # |
| 17 | """ |
| 18 | Creates the next compatibility matrix. |
| 19 | |
| 20 | Requires libvintf Level.h to be updated before executing this script. |
| 21 | """ |
| 22 | |
| 23 | import argparse |
| 24 | import os |
| 25 | import pathlib |
| 26 | import shutil |
| 27 | import subprocess |
| 28 | import textwrap |
| 29 | |
| 30 | |
| 31 | def check_call(*args, **kwargs): |
| 32 | print(args) |
| 33 | subprocess.check_call(*args, **kwargs) |
| 34 | |
| 35 | |
| 36 | def check_output(*args, **kwargs): |
| 37 | print(args) |
| 38 | return subprocess.check_output(*args, **kwargs) |
| 39 | |
| 40 | |
| 41 | class Bump(object): |
| 42 | |
| 43 | def __init__(self, cmdline_args): |
| 44 | self.top = pathlib.Path(os.environ["ANDROID_BUILD_TOP"]) |
| 45 | self.interfaces_dir = self.top / "hardware/interfaces" |
| 46 | |
| 47 | self.current_level = cmdline_args.current |
| 48 | self.current_module_name = f"framework_compatibility_matrix.{self.current_level}.xml" |
| 49 | self.current_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.current_level}.xml" |
| 50 | |
| 51 | self.next_level = cmdline_args.next |
| 52 | self.next_module_name = f"framework_compatibility_matrix.{self.next_level}.xml" |
| 53 | self.next_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.next_level}.xml" |
| 54 | |
| 55 | self.level_to_letter = self.get_level_to_letter_mapping() |
| 56 | print("Found level mapping in libvintf Level.h:", self.level_to_letter) |
| 57 | |
| 58 | def run(self): |
| 59 | self.bump_kernel_configs() |
| 60 | self.copy_matrix() |
| 61 | self.edit_android_bp() |
| 62 | self.edit_android_mk() |
| 63 | |
| 64 | def get_level_to_letter_mapping(self): |
| 65 | levels_file = self.top / "system/libvintf/include/vintf/Level.h" |
| 66 | with open(levels_file) as f: |
| 67 | lines = f.readlines() |
| 68 | pairs = [ |
| 69 | line.split("=", maxsplit=2) for line in lines if "=" in line |
| 70 | ] |
| 71 | return { |
| 72 | level.strip().removesuffix(","): letter.strip() |
| 73 | for letter, level in pairs |
| 74 | } |
| 75 | |
| 76 | def bump_kernel_configs(self): |
| 77 | check_call([ |
| 78 | self.top / "kernel/configs/tools/bump.py", |
| 79 | self.level_to_letter[self.current_level].lower(), |
| 80 | self.level_to_letter[self.next_level].lower(), |
| 81 | ]) |
| 82 | |
| 83 | def copy_matrix(self): |
| 84 | shutil.copyfile(self.current_xml, self.next_xml) |
| 85 | |
| 86 | def edit_android_bp(self): |
| 87 | android_bp = self.interfaces_dir / "compatibility_matrices/Android.bp" |
| 88 | |
| 89 | with open(android_bp, "r+") as f: |
| 90 | if self.next_module_name not in f.read(): |
| 91 | f.seek(0, 2) # end of file |
| 92 | f.write("\n") |
| 93 | f.write( |
| 94 | textwrap.dedent(f"""\ |
| 95 | vintf_compatibility_matrix {{ |
| 96 | name: "{self.next_module_name}", |
| 97 | }} |
| 98 | """)) |
| 99 | |
| 100 | next_kernel_configs = check_output( |
| 101 | """grep -rh name: | sed -E 's/^.*"(.*)".*/\\1/g'""", |
| 102 | cwd=self.top / "kernel/configs" / |
| 103 | self.level_to_letter[self.next_level].lower(), |
| 104 | text=True, |
| 105 | shell=True, |
| 106 | ).splitlines() |
| 107 | print(next_kernel_configs) |
| 108 | |
| 109 | check_call([ |
| 110 | "bpmodify", "-w", "-m", self.next_module_name, "-property", "stem", |
| 111 | "-str", self.next_xml.name, android_bp |
| 112 | ]) |
| 113 | |
| 114 | check_call([ |
| 115 | "bpmodify", "-w", "-m", self.next_module_name, "-property", "srcs", |
| 116 | "-a", |
| 117 | self.next_xml.relative_to(android_bp.parent), android_bp |
| 118 | ]) |
| 119 | |
| 120 | check_call([ |
| 121 | "bpmodify", "-w", "-m", self.next_module_name, "-property", |
| 122 | "kernel_configs", "-a", " ".join(next_kernel_configs), android_bp |
| 123 | ]) |
| 124 | |
| 125 | def edit_android_mk(self): |
| 126 | android_mk = self.interfaces_dir / "compatibility_matrices/Android.mk" |
| 127 | with open(android_mk) as f: |
| 128 | if self.next_module_name in f.read(): |
| 129 | return |
| 130 | f.seek(0) |
| 131 | lines = f.readlines() |
| 132 | current_module_line_number = None |
| 133 | for line_number, line in enumerate(lines): |
| 134 | if self.current_module_name in line: |
| 135 | current_module_line_number = line_number |
| 136 | break |
| 137 | assert current_module_line_number is not None |
| 138 | lines.insert(current_module_line_number + 1, |
| 139 | f" {self.next_module_name} \\\n") |
| 140 | with open(android_mk, "w") as f: |
| 141 | f.write("".join(lines)) |
| 142 | |
| 143 | |
| 144 | def main(): |
| 145 | parser = argparse.ArgumentParser(description=__doc__) |
| 146 | parser.add_argument("current", |
| 147 | type=str, |
| 148 | help="VINTF level of the current version (e.g. 9)") |
| 149 | parser.add_argument("next", |
| 150 | type=str, |
| 151 | help="VINTF level of the next version (e.g. 10)") |
| 152 | cmdline_args = parser.parse_args() |
| 153 | |
| 154 | Bump(cmdline_args).run() |
| 155 | |
| 156 | |
| 157 | if __name__ == "__main__": |
| 158 | main() |