blob: f4001dfd6a3f2ab5b6a76cd194bb6ad544d89992 [file] [log] [blame]
#!/usr/bin/env python3.4
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import re
import shutil
import subprocess
import sys
import tempfile
from utils.const import Constant
ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP')
if not ANDROID_BUILD_TOP:
print 'Run "lunch" command first.'
sys.exit(1)
# TODO(trong): use proper packaging without referencing modules from source.
TEST_VTS_DIR = os.path.join(ANDROID_BUILD_TOP, 'test', 'vts')
sys.path.append(TEST_VTS_DIR)
from proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
from google.protobuf import text_format
import build_rule_gen_utils as utils
class VtsSpecParser(object):
"""Provides an API to generate a parse .vts spec files."""
def __init__(self,
package_root=Constant.HAL_PACKAGE_PREFIX,
path_root=Constant.HAL_INTERFACE_PATH):
"""VtsSpecParser constructor.
For every unique pair of (hal name, hal version) available under
path_root, generates .vts files using hidl-gen.
Args:
tmp_dir: string, temporary directory to which to write .vts files.
"""
self._cache = set()
self._tmp_dir = tempfile.mkdtemp()
self._package_root = package_root
self._path_root = path_root
hal_list = self.HalNamesAndVersions()
def __del__(self):
"""VtsSpecParser destructor.
Removes all temporary files that were generated.
"""
print "Removing temp files."
if os.path.exists(self._tmp_dir):
shutil.rmtree(self._tmp_dir)
def ImportedPackagesList(self, hal_name, hal_version):
"""Returns a list of imported packages.
Args:
hal_name: string, name of the hal, e.g. 'vibrator'.
hal_version: string, version of the hal, e.g '7.4'
Returns:
list of strings. For example,
['android.hardware.vibrator@1.3', 'android.hidl.base@1.7']
"""
self.GenerateVtsSpecs(hal_name, hal_version)
vts_spec_protos = self.VtsSpecProtos(hal_name, hal_version)
imported_packages = set()
for vts_spec in vts_spec_protos:
for package in getattr(vts_spec, 'import', []):
package = package.split('::')[0]
imported_packages.add(package)
# Exclude the current package and packages with no corresponding libs.
exclude_packages = [
"android.hidl.base@1.0", "android.hidl.manager@1.0",
'%s.%s@%s' % (self._package_root, hal_name, hal_version)
]
return sorted(list(set(imported_packages) - set(exclude_packages)))
def GenerateVtsSpecs(self, hal_name, hal_version):
"""Generates VTS specs.
Uses hidl-gen to generate .vts files under a tmp directory.
Args:
hal_name: string, name of the hal, e.g. 'vibrator'.
hal_version: string, version of the hal, e.g '7.4'
tmp_dir: string, location to which to write tmp files.
"""
if (hal_name, hal_version) in self._cache:
return
hidl_gen_cmd = (
'hidl-gen -o {TEMP_DIR} -L vts -r {PACKAGE_ROOT}:{PATH_ROOT} '
'{PACKAGE_ROOT}.{HAL_NAME}@{HAL_VERSION}').format(
TEMP_DIR=self._tmp_dir,
PACKAGE_ROOT=self._package_root,
PATH_ROOT=self._path_root,
HAL_NAME=hal_name,
HAL_VERSION=hal_version)
subprocess.call(hidl_gen_cmd, shell=True)
self._cache.add((hal_name, hal_version))
def HalNamesAndVersions(self):
"""Returns a list of hals and versions under hal interface directory.
Returns:
List of tuples of strings containing hal names and hal versions.
For example, [('vibrator', '1.3'), ('sensors', '1.7')]
"""
full_path_root = os.path.join(ANDROID_BUILD_TOP, self._path_root)
result = set()
# Walk through ANDROID_BUILD_TOP/self._path_root and heuristically
# figure out all the HAL names and versions in the source tree.
for base, dirs, files in os.walk(full_path_root):
has_hals = any(f.endswith('.hal') for f in files)
if not has_hals:
continue
hal_dir = os.path.relpath(base, full_path_root)
# Find the first occurance of version in directory path.
match = re.search("(\d+)\.(\d+)", hal_dir)
if match and 'example' not in hal_dir:
hal_version = match.group(0)
# Name of the hal preceds hal version in the directory path.
hal_dir = hal_dir[:match.end()]
hal_name = os.path.dirname(hal_dir).replace('/', '.')
result.add((hal_name, hal_version))
return sorted(result)
def VtsSpecNames(self, hal_name, hal_version):
"""Returns list of .vts file names for given hal name and version.
hal_name: string, name of the hal, e.g. 'vibrator'.
hal_version: string, version of the hal, e.g '7.4'
Returns:
list of string, .vts files for given hal name and version,
e.g. ['Vibrator.vts', 'types.vts']
"""
self.GenerateVtsSpecs(hal_name, hal_version)
vts_spec_dir = os.path.join(self._tmp_dir,
self._package_root.replace('.', '/'),
utils.HalNameDir(hal_name), hal_version)
vts_spec_names = filter(lambda x: x.endswith('.vts'),
os.listdir(vts_spec_dir))
return sorted(vts_spec_names)
def VtsSpecProtos(self, hal_name, hal_version):
"""Returns list of .vts protos for given hal name and version.
hal_name: string, name of the hal, e.g. 'vibrator'.
hal_version: string, version of the hal, e.g '7.4'
Returns:
list of ComponentSpecificationMessages
"""
self.GenerateVtsSpecs(hal_name, hal_version)
vts_spec_dir = os.path.join(self._tmp_dir,
self._package_root.replace('.', '/'),
utils.HalNameDir(hal_name), hal_version)
vts_spec_protos = []
for vts_spec in self.VtsSpecNames(hal_name, hal_version):
spec_proto = CompSpecMsg.ComponentSpecificationMessage()
vts_spec_path = os.path.join(vts_spec_dir, vts_spec)
with open(vts_spec_path, 'r') as spec_file:
spec_string = spec_file.read()
text_format.Merge(spec_string, spec_proto)
vts_spec_protos.append(spec_proto)
return vts_spec_protos