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 18import os 19import re 20import shutil 21import subprocess 22import sys 23import tempfile 24 25from utils.const import Constant 26 27ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP') 28if 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. 33TEST_VTS_DIR = os.path.join(ANDROID_BUILD_TOP, 'test', 'vts') 34sys.path.append(TEST_VTS_DIR) 35from proto import ComponentSpecificationMessage_pb2 as CompSpecMsg 36from google.protobuf import text_format 37import build_rule_gen_utils as utils 38 39 40class 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@1.3', 'android.hidl.base@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@1.0", "android.hidl.manager@1.0", 92 ] 93 94 return sorted(list(set(imported_packages) - set(exclude_packages))) 95 96 def GenerateVtsSpecs(self, hal_name, hal_version): 97 """Generates VTS specs. 98 99 Uses hidl-gen to generate .vts files under a tmp directory. 100 101 Args: 102 hal_name: string, name of the hal, e.g. 'vibrator'. 103 hal_version: string, version of the hal, e.g '7.4' 104 tmp_dir: string, location to which to write tmp files. 105 """ 106 if (hal_name, hal_version) in self._cache: 107 return 108 hidl_gen_cmd = ( 109 'hidl-gen -o {TEMP_DIR} -L vts -r {PACKAGE_ROOT}:{PATH_ROOT} ' 110 '{PACKAGE_ROOT}.{HAL_NAME}@{HAL_VERSION}').format( 111 TEMP_DIR=self._tmp_dir, 112 PACKAGE_ROOT=self._package_root, 113 PATH_ROOT=self._path_root, 114 HAL_NAME=hal_name, 115 HAL_VERSION=hal_version) 116 subprocess.call(hidl_gen_cmd, shell=True) 117 self._cache.add((hal_name, hal_version)) 118 119 def HalNamesAndVersions(self): 120 """Returns a list of hals and versions under hal interface directory. 121 122 Returns: 123 List of tuples of strings containing hal names and hal versions. 124 For example, [('vibrator', '1.3'), ('sensors', '1.7')] 125 """ 126 full_path_root = os.path.join(ANDROID_BUILD_TOP, self._path_root) 127 result = set() 128 # Walk through ANDROID_BUILD_TOP/self._path_root and heuristically 129 # figure out all the HAL names and versions in the source tree. 130 for base, dirs, files in os.walk(full_path_root): 131 has_hals = any(f.endswith('.hal') for f in files) 132 if not has_hals: 133 continue 134 135 hal_dir = os.path.relpath(base, full_path_root) 136 # Find the first occurance of version in directory path. 137 match = re.search("(\d+)\.(\d+)", hal_dir) 138 if match and 'example' not in hal_dir: 139 hal_version = match.group(0) 140 # Name of the hal preceds hal version in the directory path. 141 hal_dir = hal_dir[:match.end()] 142 hal_name = os.path.dirname(hal_dir).replace('/', '.') 143 result.add((hal_name, hal_version)) 144 return sorted(result) 145 146 def VtsSpecNames(self, hal_name, hal_version): 147 """Returns list of .vts file names for given hal name and version. 148 149 hal_name: string, name of the hal, e.g. 'vibrator'. 150 hal_version: string, version of the hal, e.g '7.4' 151 152 Returns: 153 list of string, .vts files for given hal name and version, 154 e.g. ['Vibrator.vts', 'types.vts'] 155 """ 156 self.GenerateVtsSpecs(hal_name, hal_version) 157 vts_spec_dir = os.path.join(self._tmp_dir, 158 self._package_root.replace('.', '/'), 159 utils.HalNameDir(hal_name), hal_version) 160 vts_spec_names = filter(lambda x: x.endswith('.vts'), 161 os.listdir(vts_spec_dir)) 162 return sorted(vts_spec_names) 163 164 def VtsSpecProtos(self, hal_name, hal_version): 165 """Returns list of .vts protos for given hal name and version. 166 167 hal_name: string, name of the hal, e.g. 'vibrator'. 168 hal_version: string, version of the hal, e.g '7.4' 169 170 Returns: 171 list of ComponentSpecificationMessages 172 """ 173 self.GenerateVtsSpecs(hal_name, hal_version) 174 vts_spec_dir = os.path.join(self._tmp_dir, 175 self._package_root.replace('.', '/'), 176 utils.HalNameDir(hal_name), hal_version) 177 vts_spec_protos = [] 178 for vts_spec in self.VtsSpecNames(hal_name, hal_version): 179 spec_proto = CompSpecMsg.ComponentSpecificationMessage() 180 vts_spec_path = os.path.join(vts_spec_dir, vts_spec) 181 with open(vts_spec_path, 'r') as spec_file: 182 spec_string = spec_file.read() 183 text_format.Merge(spec_string, spec_proto) 184 185 vts_spec_protos.append(spec_proto) 186 return vts_spec_protos 187