Home | History | Annotate | Download | only in build
      1 #
      2 # Copyright (C) 2017 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 #
     16 
     17 import os
     18 import shutil
     19 import tempfile
     20 import zipfile
     21 
     22 from host_controller import common
     23 from vts.runners.host import utils
     24 
     25 
     26 class BuildProvider(object):
     27     """The base class for build provider.
     28 
     29     Attributes:
     30         _IMAGE_FILE_EXTENSIONS: a list of strings which are common image file
     31                                 extensions.
     32         _BASIC_IMAGE_FILE_NAMES: a list of strings which are the image names in
     33                                  an artifact zip.
     34         _CONFIG_FILE_EXTENSION: string, the config file extension.
     35         _additional_files: a dict containing additionally fetched files that
     36                            custom features may need. The key is the path
     37                            relative to temporary directory and the value is the
     38                            full path.
     39         _configs: dict where the key is config type and value is the config file
     40                   path.
     41         _device_images: dict where the key is image file name and value is the
     42                         path.
     43         _test_suites: dict where the key is test suite type and value is the
     44                       test suite package file path.
     45         _tmp_dirpath: string, the temp dir path created to keep artifacts.
     46     """
     47     _CONFIG_FILE_EXTENSION = ".zip"
     48     _IMAGE_FILE_EXTENSIONS = [".img", ".bin"]
     49     _BASIC_IMAGE_FILE_NAMES = ["boot.img", "system.img", "vendor.img"]
     50 
     51     def __init__(self):
     52         self._additional_files = {}
     53         self._device_images = {}
     54         self._test_suites = {}
     55         self._configs = {}
     56         tempdir_base = os.path.join(os.getcwd(), "tmp")
     57         if not os.path.exists(tempdir_base):
     58             os.mkdir(tempdir_base)
     59         self._tmp_dirpath = tempfile.mkdtemp(dir=tempdir_base)
     60 
     61     def __del__(self):
     62         """Deletes the temp dir if still set."""
     63         if self._tmp_dirpath:
     64             shutil.rmtree(self._tmp_dirpath)
     65             self._tmp_dirpath = None
     66 
     67     @property
     68     def tmp_dirpath(self):
     69         return self._tmp_dirpath
     70 
     71     def CreateNewTmpDir(self):
     72         return tempfile.mkdtemp(dir=self._tmp_dirpath)
     73 
     74     def SetDeviceImage(self, name, path):
     75         """Sets device image `path` for the specified `name`."""
     76         self._device_images[name] = path
     77 
     78     def _IsFullDeviceImage(self, namelist):
     79         """Returns true if given namelist list has all common device images."""
     80         for image_file in self._BASIC_IMAGE_FILE_NAMES:
     81             if image_file not in namelist:
     82                 return False
     83         return True
     84 
     85     def _IsImageFile(self, file_path):
     86         """Returns whether a file is an image.
     87 
     88         Args:
     89             file_path: string, the file path.
     90 
     91         Returns:
     92             boolean, whether the file is an image.
     93         """
     94         return any(file_path.endswith(ext)
     95                    for ext in self._IMAGE_FILE_EXTENSIONS)
     96 
     97     def SetDeviceImageZip(self, path):
     98         """Sets device image(s) using files in a given zip file.
     99 
    100         It extracts image files inside the given zip file and selects
    101         known Android image files.
    102 
    103         Args:
    104             path: string, the path to a zip file.
    105         """
    106         dest_path = path + ".dir"
    107         with zipfile.ZipFile(path, 'r') as zip_ref:
    108             if self._IsFullDeviceImage(zip_ref.namelist()):
    109                 self.SetDeviceImage(common.FULL_ZIPFILE, path)
    110             else:
    111                 zip_ref.extractall(dest_path)
    112                 self.SetFetchedDirectory(dest_path)
    113 
    114     def GetDeviceImage(self, name=None):
    115         """Returns device image info."""
    116         if name is None:
    117             return self._device_images
    118         return self._device_images[name]
    119 
    120     def SetTestSuitePackage(self, type, path):
    121         """Sets test suite package `path` for the specified `type`.
    122 
    123         Args:
    124             type: string, test suite type such as 'vts' or 'cts'.
    125             path: string, the path of a file. if a file is a zip file,
    126                   it's unziped and its main binary is set.
    127         """
    128         if path.endswith("android-vts.zip"):
    129             dest_path = os.path.join(self.tmp_dirpath, "android-vts")
    130             with zipfile.ZipFile(path, 'r') as zip_ref:
    131                 zip_ref.extractall(dest_path)
    132                 bin_path = os.path.join(dest_path, "android-vts",
    133                                         "tools", "vts-tradefed")
    134                 os.chmod(bin_path, 0766)
    135                 path = bin_path
    136         else:
    137             print("unsupported zip file %s" % path)
    138         self._test_suites[type] = path
    139 
    140     def GetTestSuitePackage(self, type=None):
    141         """Returns test suite package info."""
    142         if type is None:
    143             return self._test_suites
    144         return self._test_suites[type]
    145 
    146     def SetConfigPackage(self, config_type, path):
    147         """Sets test suite package `path` for the specified `type`.
    148 
    149         All valid config files have .zip extension.
    150 
    151         Args:
    152             config_type: string, config type such as 'prod' or 'test'.
    153             path: string, the path of a config file.
    154         """
    155         if path.endswith(self._CONFIG_FILE_EXTENSION):
    156             dest_path = os.path.join(
    157                 self.tmp_dirpath, os.path.basename(path) + ".dir")
    158             with zipfile.ZipFile(path, 'r') as zip_ref:
    159                 zip_ref.extractall(dest_path)
    160                 path = dest_path
    161         else:
    162             print("unsupported config package file %s" % path)
    163         self._configs[config_type] = path
    164 
    165     def GetConfigPackage(self, config_type=None):
    166         """Returns config package info."""
    167         if config_type is None:
    168             return self._configs
    169         return self._configs[config_type]
    170 
    171     def SetAdditionalFile(self, rel_path, full_path):
    172         """Sets the key and value of additionally fetched files.
    173 
    174         Args:
    175             rel_path: the file path relative to temporary directory.
    176             abs_path: the file path that this process can access.
    177         """
    178         self._additional_files[rel_path] = full_path
    179 
    180     def GetAdditionalFile(self, rel_path=None):
    181         """Returns the paths to fetched files."""
    182         if rel_path is None:
    183             return self._additional_files
    184         return self._additional_files[rel_path]
    185 
    186     def SetFetchedDirectory(self, dir_path, root_path=None):
    187         """Adds every file in a directory to one of the dictionaries.
    188 
    189         This method follows symlink to file, but skips symlink to directory.
    190 
    191         Args:
    192             dir_path: string, the directory to find files in.
    193             root_path: string, the temporary directory that dir_path is in.
    194                        The default value is dir_path.
    195         """
    196         for dir_name, file_name in utils.iterate_files(dir_path):
    197             full_path = os.path.join(dir_name, file_name)
    198             self.SetFetchedFile(full_path,
    199                                 (root_path if root_path else dir_path))
    200 
    201     def SetFetchedFile(self, file_path, root_dir=None):
    202         """Adds a file to one of the dictionaries.
    203 
    204         Args:
    205             file_path: string, the path to the file.
    206             root_dir: string, the temporary directory that file_path is in.
    207                       The default value is file_path if file_path is a
    208                       directory. Otherwise, the default value is file_path's
    209                       parent directory.
    210         """
    211         file_name = os.path.basename(file_path)
    212         if os.path.isdir(file_path):
    213             self.SetFetchedDirectory(file_path, root_dir)
    214         elif self._IsImageFile(file_path):
    215             self.SetDeviceImage(file_name, file_path)
    216         elif file_name == "android-vts.zip":
    217             self.SetTestSuitePackage("vts", file_path)
    218         elif file_name.startswith("vti-global-config"):
    219             self.SetConfigPackage(
    220                 "prod" if "prod" in file_name else "test", file_path)
    221         elif file_path.endswith(".zip"):
    222             self.SetDeviceImageZip(file_path)
    223         else:
    224             rel_path = (os.path.relpath(file_path, root_dir) if root_dir else
    225                         os.path.basename(file_path))
    226             self.SetAdditionalFile(rel_path, file_path)
    227 
    228     def PrintDeviceImageInfo(self):
    229         """Prints device image info."""
    230         print("%s" % self.GetDeviceImage())
    231 
    232     def PrintGetTestSuitePackageInfo(self):
    233         """Prints test suite package info."""
    234         print("%s" % self.GetTestSuitePackage())
    235