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