• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""Creates the next compatibility matrix."""
18
19import argparse
20import os
21import pathlib
22import re
23import subprocess
24import textwrap
25
26
27def check_call(*args, **kwargs):
28  print(args)
29  subprocess.check_call(*args, **kwargs)
30
31
32def check_output(*args, **kwargs):
33  print(args)
34  return subprocess.check_output(*args, **kwargs)
35
36
37class Bump(object):
38
39  def __init__(self, cmdline_args):
40    self.top = pathlib.Path(os.environ["ANDROID_BUILD_TOP"])
41    self.interfaces_dir = self.top / "hardware/interfaces"
42
43    self.current_level = cmdline_args.current_level
44    self.current_letter = cmdline_args.current_letter
45    self.current_version = cmdline_args.platform_version
46    self.next_version = cmdline_args.next_platform_version
47    self.current_module_name = (
48        f"framework_compatibility_matrix.{self.current_level}.xml"
49    )
50    self.current_xml = (
51        self.interfaces_dir
52        / f"compatibility_matrices/compatibility_matrix.{self.current_level}.xml"
53    )
54    self.device_module_name = "framework_compatibility_matrix.device.xml"
55
56    self.next_level = cmdline_args.next_level
57    self.next_letter = cmdline_args.next_letter
58    self.current_sdk = cmdline_args.current_sdk
59    self.next_sdk = cmdline_args.next_sdk
60    self.next_module_name = (
61        f"framework_compatibility_matrix.{self.next_level}.xml"
62    )
63    self.next_xml = (
64        self.interfaces_dir
65        / f"compatibility_matrices/compatibility_matrix.{self.next_level}.xml"
66    )
67
68  def run(self):
69    self.bump_kernel_configs()
70    self.copy_matrix()
71    self.edit_android_bp()
72    self.bump_libvintf()
73    self.bump_libvts_vintf()
74    self.bump_cuttlefish()
75
76  def bump_kernel_configs(self):
77    check_call([
78        self.top / "kernel/configs/tools/bump.py",
79        self.current_letter.lower(),
80        self.next_letter.lower(),
81    ])
82
83  def copy_matrix(self):
84    with open(self.current_xml) as f_current, open(
85        self.next_xml, "w"
86    ) as f_next:
87      f_next.write(
88          f_current.read().replace(
89              f'level="{self.current_level}"', f'level="{self.next_level}"'
90          )
91      )
92
93  def edit_android_bp(self):
94    android_bp = self.interfaces_dir / "compatibility_matrices/Android.bp"
95
96    with open(android_bp, "r+") as f:
97      if self.next_module_name not in f.read():
98        f.seek(0, 2)  # end of file
99        f.write("\n")
100        f.write(textwrap.dedent(f"""\
101                        vintf_compatibility_matrix {{
102                            name: "{self.next_module_name}",
103                        }}
104                    """))
105
106    next_kernel_configs = check_output(
107        """grep -rh name: | sed -E 's/^.*"(.*)".*/\\1/g'""",
108        cwd=self.top / "kernel/configs" / self.next_letter.lower(),
109        text=True,
110        shell=True,
111    ).splitlines()
112    print(next_kernel_configs)
113
114    check_call([
115        "bpmodify",
116        "-w",
117        "-m",
118        self.next_module_name,
119        "-property",
120        "stem",
121        "-str",
122        self.next_xml.name,
123        android_bp,
124    ])
125
126    check_call([
127        "bpmodify",
128        "-w",
129        "-m",
130        self.next_module_name,
131        "-property",
132        "srcs",
133        "-a",
134        self.next_xml.relative_to(android_bp.parent),
135        android_bp,
136    ])
137
138    check_call([
139        "bpmodify",
140        "-w",
141        "-m",
142        self.next_module_name,
143        "-property",
144        "kernel_configs",
145        "-a",
146        " ".join(next_kernel_configs),
147        android_bp,
148    ])
149
150    # Replace the phony module's product_variables entry to add the new FCM
151    # to the development targets (trunk* configs).
152    lines = []
153    with open(android_bp) as f:
154      for line in f:
155        if f'                "{self.current_module_name}",\n' in line:
156          lines.append(f'                "{self.next_module_name}",\n')
157        else:
158          lines.append(line)
159
160    with open(android_bp, "w") as f:
161      f.write("".join(lines))
162
163  def bump_libvintf(self):
164    if not self.current_version:
165      print("Skip libvintf update...")
166      return
167    try:
168      check_call([
169          "grep",
170          "-h",
171          f"{self.next_letter.upper()} = {self.next_level}",
172          f"{self.top}/system/libvintf/include/vintf/Level.h",
173      ])
174    except subprocess.CalledProcessError:
175      print("Adding new API level to libvintf")
176      add_lines_above(
177          f"{self.top}/system/libvintf/analyze_matrix/analyze_matrix.cpp",
178          "        case Level::UNSPECIFIED:",
179          textwrap.indent(
180              textwrap.dedent(
181                  f"""\
182                                    case Level::{self.next_letter.upper()}:
183                                        return "Android {self.next_version} ({self.next_letter.upper()})";"""
184              ),
185              "    " * 2,
186          ),
187      )
188      add_lines_above(
189          f"{self.top}/system/libvintf/include/vintf/Level.h",
190          "    // To add new values:",
191          f"    {self.next_letter.upper()} = {self.next_level},",
192      )
193      add_lines_above(
194          f"{self.top}/system/libvintf/include/vintf/Level.h",
195          "        Level::UNSPECIFIED,",
196          f"        Level::{self.next_letter.upper()},",
197      )
198      add_lines_above(
199          f"{self.top}/system/libvintf/RuntimeInfo.cpp",
200          "            // Add more levels above this line.",
201          textwrap.indent(
202              textwrap.dedent(f"""\
203                                        case {self.next_version}: {{
204                                            ret = Level::{self.next_letter.upper()};
205                                        }} break;"""),
206              "    " * 3,
207          ),
208      )
209
210  def bump_libvts_vintf(self):
211    if not self.current_version:
212      print("Skip libvts_vintf update...")
213      return
214    try:
215      check_call([
216          "grep",
217          "-h",
218          f"{self.next_level}, Level::{self.next_letter.upper()}",
219          f"{self.top}/test/vts-testcase/hal/treble/vintf/libvts_vintf_test_common/common.cpp",
220      ])
221      print("libvts_vintf is already up-to-date")
222    except subprocess.CalledProcessError:
223      print("Adding new API level to libvts_vintf")
224      add_lines_below(
225          f"{self.top}/test/vts-testcase/hal/treble/vintf/libvts_vintf_test_common/common.cpp",
226          f"        {{{self.current_level},"
227          f" Level::{self.current_letter.upper()}}},",
228          f"        {{{self.next_level},"
229          f" Level::{self.next_letter.upper()}}},\n",
230      )
231
232  def bump_cuttlefish(self):
233    if not self.next_sdk:
234      print("Skip Cuttlefish update...")
235      return
236    cf_mk_file = f"{self.top}/device/google/cuttlefish/shared/device.mk"
237    try:
238      check_call([
239          "grep",
240          "-h",
241          f"PRODUCT_SHIPPING_API_LEVEL := {self.next_sdk}",
242          cf_mk_file,
243      ])
244      print("Cuttlefish is already up-to-date")
245    except subprocess.CalledProcessError:
246      print("Bumping Cuttlefish to the next SHIPPING_API_LEVEL")
247      final_lines = []
248      with open(cf_mk_file, "r+") as f:
249        for line in f:
250          if f"PRODUCT_SHIPPING_API_LEVEL := {self.current_sdk}" in line:
251            final_lines.append(
252                f"PRODUCT_SHIPPING_API_LEVEL := {self.next_sdk}\n"
253            )
254          elif line.startswith("PRODUCT_SHIPPING_API_LEVEL :="):
255            # this is the previous SDK level.
256            final_lines.append(
257                f"PRODUCT_SHIPPING_API_LEVEL := {self.current_sdk}\n"
258            )
259          else:
260            final_lines.append(line)
261        f.seek(0)
262        f.write("".join(final_lines))
263        f.truncate()
264    final_lines = []
265    with open(
266        f"{self.top}/device/google/cuttlefish/shared/config/previous_manifest.xml",
267        "r+",
268    ) as f:
269      for line in f:
270        if "target-level=" in line:
271          final_lines.append(
272              '<manifest version="1.0" type="device"'
273              f' target-level="{self.current_level}">\n'
274          )
275        else:
276          final_lines.append(line)
277      f.seek(0)
278      f.write("".join(final_lines))
279      f.truncate()
280
281    final_lines = []
282    with open(
283        f"{self.top}/device/google/cuttlefish/shared/config/manifest.xml", "r+"
284    ) as f:
285      for line in f:
286        if "target-level=" in line:
287          final_lines.append(
288              '<manifest version="1.0" type="device"'
289              f' target-level="{self.next_level}">\n'
290          )
291        else:
292          final_lines.append(line)
293      f.seek(0)
294      f.write("".join(final_lines))
295      f.truncate()
296
297
298def add_lines_above(file, pattern, lines):
299  with open(file, "r+") as f:
300    text = f.read()
301    split_text = re.split(rf"\n{pattern}\n", text)
302    if len(split_text) != 2:
303      # Only one pattern must be found, otherwise the source must be
304      # changed unexpectedly.
305      raise Exception(
306          f'Pattern "{pattern}" not found or multiple patterns found in {file}'
307      )
308    f.seek(0)
309    f.write(f"\n{lines}\n{pattern}\n".join(split_text))
310    f.truncate()
311
312
313def add_lines_below(file, pattern, lines):
314  final_lines = []
315  with open(file, "r+") as f:
316    for line in f:
317      final_lines.append(line)
318      if pattern in line:
319        final_lines.append(lines)
320    f.seek(0)
321    f.write("".join(final_lines))
322    f.truncate()
323
324
325def main():
326  parser = argparse.ArgumentParser(description=__doc__)
327  parser.add_argument(
328      "current_level",
329      type=str,
330      help="VINTF level of the current version (e.g. 202404)",
331  )
332  parser.add_argument(
333      "next_level",
334      type=str,
335      help="VINTF level of the next version (e.g. 202504)",
336  )
337  parser.add_argument(
338      "current_letter",
339      type=str,
340      help="Letter of the API level of the current version (e.g. b)",
341  )
342  parser.add_argument(
343      "next_letter",
344      type=str,
345      help="Letter of the API level of the next version (e.g. c)",
346  )
347  parser.add_argument(
348      "platform_version",
349      type=str,
350      nargs="?",
351      help="Current Android release version number (e.g. 16)",
352  )
353  parser.add_argument(
354      "next_platform_version",
355      type=str,
356      nargs="?",
357      help="Next Android release version number number (e.g. 17)",
358  )
359  parser.add_argument(
360      "current_sdk",
361      type=str,
362      nargs="?",
363      help="Version of the current SDK API level (e.g. 36)",
364  )
365  parser.add_argument(
366      "next_sdk",
367      type=str,
368      nargs="?",
369      help="Version of the next SDK API level(e.g. 37)",
370  )
371
372  cmdline_args = parser.parse_args()
373
374  Bump(cmdline_args).run()
375
376
377if __name__ == "__main__":
378  main()
379