1 #!/usr/bin/env python3.4 2 # 3 # Copyright (C) 2017 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 import os 19 import re 20 import shutil 21 import subprocess 22 import sys 23 import tempfile 24 25 from utils.const import Constant 26 27 ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP') 28 if not ANDROID_BUILD_TOP: 29 print 'Run "lunch" command first.' 30 sys.exit(1) 31 32 # TODO(trong): use proper packaging without referencing modules from source. 33 TEST_VTS_DIR = os.path.join(ANDROID_BUILD_TOP, 'test', 'vts') 34 sys.path.append(TEST_VTS_DIR) 35 from proto import ComponentSpecificationMessage_pb2 as CompSpecMsg 36 from google.protobuf import text_format 37 import build_rule_gen_utils as utils 38 39 40 class VtsSpecParser(object): 41 """Provides an API to generate a parse .vts spec files.""" 42 43 def __init__(self, 44 package_root=Constant.HAL_PACKAGE_PREFIX, 45 path_root=Constant.HAL_INTERFACE_PATH): 46 """VtsSpecParser constructor. 47 48 For every unique pair of (hal name, hal version) available under 49 path_root, generates .vts files using hidl-gen. 50 51 Args: 52 tmp_dir: string, temporary directory to which to write .vts files. 53 """ 54 self._cache = set() 55 self._tmp_dir = tempfile.mkdtemp() 56 self._package_root = package_root 57 self._path_root = path_root 58 hal_list = self.HalNamesAndVersions() 59 60 def __del__(self): 61 """VtsSpecParser destructor. 62 63 Removes all temporary files that were generated. 64 """ 65 print "Removing temp files." 66 if os.path.exists(self._tmp_dir): 67 shutil.rmtree(self._tmp_dir) 68 69 def ImportedPackagesList(self, hal_name, hal_version): 70 """Returns a list of imported packages. 71 72 Args: 73 hal_name: string, name of the hal, e.g. 'vibrator'. 74 hal_version: string, version of the hal, e.g '7.4' 75 76 Returns: 77 list of strings. For example, 78 ['android.hardware.vibrator (at] 1.3', 'android.hidl.base (at] 1.7'] 79 """ 80 self.GenerateVtsSpecs(hal_name, hal_version) 81 vts_spec_protos = self.VtsSpecProtos(hal_name, hal_version) 82 83 imported_packages = set() 84 for vts_spec in vts_spec_protos: 85 for package in getattr(vts_spec, 'import', []): 86 package = package.split('::')[0] 87 imported_packages.add(package) 88 89 # Exclude the current package and packages with no corresponding libs. 90 exclude_packages = [ 91 "android.hidl.base (at] 1.0", "android.hidl.manager (at] 1.0", 92 '%s.%s@%s' % (self._package_root, hal_name, hal_version) 93 ] 94 95 return sorted(list(set(imported_packages) - set(exclude_packages))) 96 97 def GenerateVtsSpecs(self, hal_name, hal_version): 98 """Generates VTS specs. 99 100 Uses hidl-gen to generate .vts files under a tmp directory. 101 102 Args: 103 hal_name: string, name of the hal, e.g. 'vibrator'. 104 hal_version: string, version of the hal, e.g '7.4' 105 tmp_dir: string, location to which to write tmp files. 106 """ 107 if (hal_name, hal_version) in self._cache: 108 return 109 hidl_gen_cmd = ( 110 'hidl-gen -o {TEMP_DIR} -L vts -r {PACKAGE_ROOT}:{PATH_ROOT} ' 111 '{PACKAGE_ROOT}.{HAL_NAME}@{HAL_VERSION}').format( 112 TEMP_DIR=self._tmp_dir, 113 PACKAGE_ROOT=self._package_root, 114 PATH_ROOT=self._path_root, 115 HAL_NAME=hal_name, 116 HAL_VERSION=hal_version) 117 subprocess.call(hidl_gen_cmd, shell=True) 118 self._cache.add((hal_name, hal_version)) 119 120 def HalNamesAndVersions(self): 121 """Returns a list of hals and versions under hal interface directory. 122 123 Returns: 124 List of tuples of strings containing hal names and hal versions. 125 For example, [('vibrator', '1.3'), ('sensors', '1.7')] 126 """ 127 full_path_root = os.path.join(ANDROID_BUILD_TOP, self._path_root) 128 result = set() 129 # Walk through ANDROID_BUILD_TOP/self._path_root and heuristically 130 # figure out all the HAL names and versions in the source tree. 131 for base, dirs, files in os.walk(full_path_root): 132 has_hals = any(f.endswith('.hal') for f in files) 133 if not has_hals: 134 continue 135 136 hal_dir = os.path.relpath(base, full_path_root) 137 # Find the first occurance of version in directory path. 138 match = re.search("(\d+)\.(\d+)", hal_dir) 139 if match and 'example' not in hal_dir: 140 hal_version = match.group(0) 141 # Name of the hal preceds hal version in the directory path. 142 hal_dir = hal_dir[:match.end()] 143 hal_name = os.path.dirname(hal_dir).replace('/', '.') 144 result.add((hal_name, hal_version)) 145 return sorted(result) 146 147 def VtsSpecNames(self, hal_name, hal_version): 148 """Returns list of .vts file names for given hal name and version. 149 150 hal_name: string, name of the hal, e.g. 'vibrator'. 151 hal_version: string, version of the hal, e.g '7.4' 152 153 Returns: 154 list of string, .vts files for given hal name and version, 155 e.g. ['Vibrator.vts', 'types.vts'] 156 """ 157 self.GenerateVtsSpecs(hal_name, hal_version) 158 vts_spec_dir = os.path.join(self._tmp_dir, 159 self._package_root.replace('.', '/'), 160 utils.HalNameDir(hal_name), hal_version) 161 vts_spec_names = filter(lambda x: x.endswith('.vts'), 162 os.listdir(vts_spec_dir)) 163 return sorted(vts_spec_names) 164 165 def VtsSpecProtos(self, hal_name, hal_version): 166 """Returns list of .vts protos for given hal name and version. 167 168 hal_name: string, name of the hal, e.g. 'vibrator'. 169 hal_version: string, version of the hal, e.g '7.4' 170 171 Returns: 172 list of ComponentSpecificationMessages 173 """ 174 self.GenerateVtsSpecs(hal_name, hal_version) 175 vts_spec_dir = os.path.join(self._tmp_dir, 176 self._package_root.replace('.', '/'), 177 utils.HalNameDir(hal_name), hal_version) 178 vts_spec_protos = [] 179 for vts_spec in self.VtsSpecNames(hal_name, hal_version): 180 spec_proto = CompSpecMsg.ComponentSpecificationMessage() 181 vts_spec_path = os.path.join(vts_spec_dir, vts_spec) 182 with open(vts_spec_path, 'r') as spec_file: 183 spec_string = spec_file.read() 184 text_format.Merge(spec_string, spec_proto) 185 186 vts_spec_protos.append(spec_proto) 187 return vts_spec_protos 188