Home | History | Annotate | Download | only in build
      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