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""" 18Creates the next compatibility matrix. 19""" 20 21import argparse 22import os 23import pathlib 24import re 25import subprocess 26import textwrap 27 28 29def check_call(*args, **kwargs): 30 print(args) 31 subprocess.check_call(*args, **kwargs) 32 33 34def check_output(*args, **kwargs): 35 print(args) 36 return subprocess.check_output(*args, **kwargs) 37 38 39class Bump(object): 40 41 def __init__(self, cmdline_args): 42 self.top = pathlib.Path(os.environ["ANDROID_BUILD_TOP"]) 43 self.interfaces_dir = self.top / "hardware/interfaces" 44 45 self.current_level = cmdline_args.current_level 46 self.current_letter = cmdline_args.current_letter 47 self.current_version = cmdline_args.platform_version 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 self.device_module_name = "framework_compatibility_matrix.device.xml" 51 52 self.next_level = cmdline_args.next_level 53 self.next_letter = cmdline_args.next_letter 54 self.next_module_name = f"framework_compatibility_matrix.{self.next_level}.xml" 55 self.next_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.next_level}.xml" 56 57 def run(self): 58 self.bump_kernel_configs() 59 self.copy_matrix() 60 self.edit_android_bp() 61 self.bump_libvintf() 62 63 def bump_kernel_configs(self): 64 check_call([ 65 self.top / "kernel/configs/tools/bump.py", 66 self.current_letter.lower(), 67 self.next_letter.lower(), 68 ]) 69 70 def copy_matrix(self): 71 with open(self.current_xml) as f_current, open(self.next_xml, "w") as f_next: 72 f_next.write(f_current.read().replace(f"level=\"{self.current_level}\"", f"level=\"{self.next_level}\"")) 73 74 def edit_android_bp(self): 75 android_bp = self.interfaces_dir / "compatibility_matrices/Android.bp" 76 77 with open(android_bp, "r+") as f: 78 if self.next_module_name not in f.read(): 79 f.seek(0, 2) # end of file 80 f.write("\n") 81 f.write( 82 textwrap.dedent(f"""\ 83 vintf_compatibility_matrix {{ 84 name: "{self.next_module_name}", 85 }} 86 """)) 87 88 next_kernel_configs = check_output( 89 """grep -rh name: | sed -E 's/^.*"(.*)".*/\\1/g'""", 90 cwd=self.top / "kernel/configs" / 91 self.next_letter.lower(), 92 text=True, 93 shell=True, 94 ).splitlines() 95 print(next_kernel_configs) 96 97 check_call([ 98 "bpmodify", "-w", "-m", self.next_module_name, "-property", "stem", 99 "-str", self.next_xml.name, android_bp 100 ]) 101 102 check_call([ 103 "bpmodify", "-w", "-m", self.next_module_name, "-property", "srcs", 104 "-a", 105 self.next_xml.relative_to(android_bp.parent), android_bp 106 ]) 107 108 check_call([ 109 "bpmodify", "-w", "-m", self.next_module_name, "-property", 110 "kernel_configs", "-a", " ".join(next_kernel_configs), android_bp 111 ]) 112 113 # update the SYSTEM_MATRIX_DEPS variable and the phony module's 114 # product_variables entry. 115 lines = [] 116 with open(android_bp) as f: 117 for line in f: 118 if f" \"{self.device_module_name}\",\n" in line: 119 lines.append(f" \"{self.current_module_name}\",\n") 120 121 if f" \"{self.current_module_name}\",\n" in line: 122 lines.append(f" \"{self.next_module_name}\",\n") 123 else: 124 lines.append(line) 125 126 with open(android_bp, "w") as f: 127 f.write("".join(lines)) 128 129 def bump_libvintf(self): 130 if not self.current_version: 131 print("Skip libvintf update...") 132 return 133 try: 134 check_call(["grep", "-h", 135 f"{self.current_letter.upper()} = {self.current_level}", 136 "system/libvintf/include/vintf/Level.h"]) 137 except subprocess.CalledProcessError: 138 print("Adding new API level to libvintf") 139 add_lines_above("system/libvintf/analyze_matrix/analyze_matrix.cpp", 140 " case Level::UNSPECIFIED:", 141 textwrap.indent(textwrap.dedent(f"""\ 142 case Level::{self.current_letter.upper()}: 143 return "Android {self.current_version} ({self.current_letter.upper()})";"""), 144 " "*2)) 145 add_lines_above("system/libvintf/include/vintf/Level.h", 146 " // To add new values:", 147 f" {self.current_letter.upper()} = {self.current_level},") 148 add_lines_above("system/libvintf/include/vintf/Level.h", 149 " Level::UNSPECIFIED,", 150 f" Level::{self.current_letter.upper()},") 151 add_lines_above("system/libvintf/RuntimeInfo.cpp", 152 " // Add more levels above this line.", 153 textwrap.indent(textwrap.dedent(f"""\ 154 case {self.current_version}: {{ 155 ret = Level::{self.current_letter.upper()}; 156 }} break;"""), 157 " "*3)) 158 159 160def add_lines_above(file, pattern, lines): 161 with open(file, 'r+') as f: 162 text = f.read() 163 split_text = re.split(rf"\n{pattern}\n", text) 164 if len(split_text) != 2: 165 # Only one pattern must be found, otherwise the source must be 166 # changed unexpectedly. 167 raise Exception( 168 f'Pattern "{pattern}" not found or multiple patterns found in {file}') 169 f.seek(0) 170 f.write(f"\n{lines}\n{pattern}\n".join(split_text)) 171 f.truncate() 172 173 174def main(): 175 parser = argparse.ArgumentParser(description=__doc__) 176 parser.add_argument("current_level", 177 type=str, 178 help="VINTF level of the current version (e.g. 202404)") 179 parser.add_argument("next_level", 180 type=str, 181 help="VINTF level of the next version (e.g. 202504)") 182 parser.add_argument("current_letter", 183 type=str, 184 help="Letter of the API level of the current version (e.g. v)") 185 parser.add_argument("next_letter", 186 type=str, 187 help="Letter of the API level of the next version (e.g. w)") 188 parser.add_argument("platform_version", 189 type=str, 190 nargs="?", 191 help="Android release version number number (e.g. 15)") 192 cmdline_args = parser.parse_args() 193 194 Bump(cmdline_args).run() 195 196 197if __name__ == "__main__": 198 main() 199