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 shutil
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_module_name = f"framework_compatibility_matrix.{self.current_level}.xml"
48        self.current_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.current_level}.xml"
49        self.device_module_name = "framework_compatibility_matrix.device.xml"
50
51        self.next_level = cmdline_args.next_level
52        self.next_letter = cmdline_args.next_letter
53        self.next_module_name = f"framework_compatibility_matrix.{self.next_level}.xml"
54        self.next_xml = self.interfaces_dir / f"compatibility_matrices/compatibility_matrix.{self.next_level}.xml"
55
56    def run(self):
57        self.bump_kernel_configs()
58        self.copy_matrix()
59        self.edit_android_bp()
60        self.edit_android_mk()
61
62    def bump_kernel_configs(self):
63        check_call([
64            self.top / "kernel/configs/tools/bump.py",
65            self.current_letter,
66            self.next_letter,
67        ])
68
69    def copy_matrix(self):
70        with open(self.current_xml) as f_current, open(self.next_xml, "w") as f_next:
71            f_next.write(f_current.read().replace(f"level=\"{self.current_level}\"", f"level=\"{self.next_level}\""))
72
73    def edit_android_bp(self):
74        android_bp = self.interfaces_dir / "compatibility_matrices/Android.bp"
75
76        with open(android_bp, "r+") as f:
77            if self.next_module_name not in f.read():
78                f.seek(0, 2)  # end of file
79                f.write("\n")
80                f.write(
81                    textwrap.dedent(f"""\
82                        vintf_compatibility_matrix {{
83                            name: "{self.next_module_name}",
84                        }}
85                    """))
86
87        next_kernel_configs = check_output(
88            """grep -rh name: | sed -E 's/^.*"(.*)".*/\\1/g'""",
89            cwd=self.top / "kernel/configs" /
90            self.next_letter,
91            text=True,
92            shell=True,
93        ).splitlines()
94        print(next_kernel_configs)
95
96        check_call([
97            "bpmodify", "-w", "-m", self.next_module_name, "-property", "stem",
98            "-str", self.next_xml.name, android_bp
99        ])
100
101        check_call([
102            "bpmodify", "-w", "-m", self.next_module_name, "-property", "srcs",
103            "-a",
104            self.next_xml.relative_to(android_bp.parent), android_bp
105        ])
106
107        check_call([
108            "bpmodify", "-w", "-m", self.next_module_name, "-property",
109            "kernel_configs", "-a", " ".join(next_kernel_configs), android_bp
110        ])
111
112    def edit_android_mk(self):
113        android_mk = self.interfaces_dir / "compatibility_matrices/Android.mk"
114        lines = []
115        with open(android_mk) as f:
116            if self.next_module_name in f.read():
117                return
118            f.seek(0)
119            for line in f:
120              if f"    {self.device_module_name} \\\n" in line:
121                  lines.append(f"    {self.current_module_name} \\\n")
122
123              if self.current_module_name in line:
124                  lines.append(f"    {self.next_module_name} \\\n")
125              else:
126                  lines.append(line)
127
128        with open(android_mk, "w") as f:
129            f.write("".join(lines))
130
131
132def main():
133    parser = argparse.ArgumentParser(description=__doc__)
134    parser.add_argument("current_level",
135                        type=str,
136                        help="VINTF level of the current version (e.g. 9)")
137    parser.add_argument("next_level",
138                        type=str,
139                        help="VINTF level of the next version (e.g. 10)")
140    parser.add_argument("current_letter",
141                        type=str,
142                        help="Letter of the API level of the current version (e.g. v)")
143    parser.add_argument("next_letter",
144                        type=str,
145                        help="Letter of the API level of the next version (e.g. w)")
146    cmdline_args = parser.parse_args()
147
148    Bump(cmdline_args).run()
149
150
151if __name__ == "__main__":
152    main()
153