summaryrefslogtreecommitdiff
path: root/scripts/list_hal_vts.py
blob: 1fb51a5c9eb43ecd7143d5a2ddb9612e53686e9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/python3

"""
List VTS tests for each HAL by parsing module-info.json.

Example usage:

  # First, build modules-info.json
  m -j "${ANDROID_PRODUCT_OUT#$ANDROID_BUILD_TOP/}/module-info.json"

  # List with pretty-printed JSON. *IDL packages without a VTS module will show up
  # as keys with empty lists.
  ./list_hals_vts.py | python3 -m json.tool

  # List with CSV. *IDL packages without a VTS module will show up as a line with
  # empty value in the VTS module column.
  ./list_hals_vts.py --csv
"""

import argparse
import collections
import csv
import io
import json
import os
import logging
import pathlib
import re
import sys

PATH_PACKAGE_PATTERN = re.compile(
  r'^hardware/interfaces/(?P<path>(?:\w+/)*?)(?:aidl|(?P<version>\d+\.\d+))/.*')


class CriticalHandler(logging.StreamHandler):
  def emit(self, record):
    super(CriticalHandler, self).emit(record)
    if record.levelno >= logging.CRITICAL:
      sys.exit(1)


logger = logging.getLogger(__name__)
logger.addHandler(CriticalHandler())


def default_json():
  out = os.environ.get('ANDROID_PRODUCT_OUT')
  if not out: return None
  return os.path.join(out, 'module-info.json')


def infer_package(path):
  """
  Infer package from a relative path from build top where a VTS module lives.

  :param path: a path like 'hardware/interfaces/vibrator/aidl/vts'
  :return: The inferred *IDL package, e.g. 'android.hardware.vibrator'

  >>> infer_package('hardware/interfaces/automotive/sv/1.0/vts/functional')
  'android.hardware.automotive.sv@1.0'
  >>> infer_package('hardware/interfaces/vibrator/aidl/vts')
  'android.hardware.vibrator'
  """
  mo = re.match(PATH_PACKAGE_PATTERN, path)
  if not mo: return None
  package = 'android.hardware.' + ('.'.join(pathlib.Path(mo.group('path')).parts))
  if mo.group('version'):
    package += '@' + mo.group('version')
  return package


def load_modules_info(json_file):
  """
  :param json_file: The path to modules-info.json
  :return: a dictionary, where the keys are inferred *IDL package names, and
           values are a list of VTS modules with that inferred package name.
  """
  with open(json_file) as fp:
    root = json.load(fp)
    ret = collections.defaultdict(list)
    for module_name, module_info in root.items():
      if 'vts' not in module_info.get('compatibility_suites', []):
        continue
      for path in module_info.get('path', []):
        inferred_package = infer_package(path)
        if not inferred_package:
          continue
        ret[inferred_package].append(module_name)
    return ret


def add_missing_idl(vts_modules):
  top = os.environ.get("ANDROID_BUILD_TOP")
  interfaces = None
  if top:
    interfaces = os.path.join(top, "hardware", "interfaces")
  else:
    logger.warning("Missing ANDROID_BUILD_TOP")
    interfaces = "hardware/interfaces"
  if not os.path.isdir(interfaces):
    logger.error("Not adding missing *IDL modules because missing hardware/interfaces dir")
    return
  assert not interfaces.endswith(os.path.sep)
  for root, dirs, files in os.walk(interfaces):
    for dir in dirs:
      full_dir = os.path.join(root, dir)
      assert full_dir.startswith(interfaces)
      top_rel_dir = os.path.join('hardware', 'interfaces', full_dir[len(interfaces) + 1:])
      inferred_package = infer_package(top_rel_dir)
      if inferred_package is None:
        continue
      if inferred_package not in vts_modules:
        vts_modules[inferred_package] = []


def main():
  parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
  parser.add_argument('json', metavar='module-info.json', default=default_json(), nargs='?')
  parser.add_argument('--csv', action='store_true', help='Print CSV. If not specified print JSON.')
  args = parser.parse_args()
  if not args.json:
    logger.critical('No module-info.json is specified or found.')
  vts_modules = load_modules_info(args.json)
  add_missing_idl(vts_modules)

  if args.csv:
    out = io.StringIO()
    writer = csv.writer(out, )
    writer.writerow(["package", "vts_module"])
    for package, modules in vts_modules.items():
      if not modules:
        writer.writerow([package, ""])
      for module in modules:
        writer.writerow([package, module])
    result = out.getvalue()
  else:
    result = json.dumps(vts_modules)

  print(result)


if __name__ == '__main__':
  main()