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 17import collections 18import json 19import logging 20import os 21import re 22import zipfile 23 24try: 25 from importlib import resources 26except ImportError: 27 resources = None 28 29# The tags in VNDK list: 30# Low-level NDK libraries that can be used by framework and vendor modules. 31LL_NDK = "LLNDK" 32 33# Same-process HAL implementation in vendor partition. 34SP_HAL = "SP-HAL" 35 36# Framework libraries that can be used by vendor modules except same-process HAL 37# and its dependencies in vendor partition. 38VNDK = "VNDK-core" 39 40# VNDK dependencies that vendor modules cannot directly access. 41VNDK_PRIVATE = "VNDK-core-private" 42 43# Same-process HAL dependencies in framework. 44VNDK_SP = "VNDK-SP" 45 46# VNDK-SP dependencies that vendor modules cannot directly access. 47VNDK_SP_PRIVATE = "VNDK-SP-private" 48 49# The tuples of (ABI name, bitness, arch name, legacy name ...). The legacy 50# name is for VNDK 32 and older versions. 64-bit comes before 32-bit in order 51# to sequentially search for longest prefix. 52_ABI_LIST = ( 53 ("arm64", 64, "arm64", "arm64_armv8-a"), 54 ("arm64", 32, "arm_arm64", "arm_armv8-a"), 55 ("arm", 32, "arm", "arm_armv7-a-neon"), 56 ("x86_64", 64, "x86_64"), 57 ("x86_64", 32, "x86_x86_64"), 58 ("x86", 32, "x86"), 59) 60 61# The data directory. 62_GOLDEN_DIR = os.path.join("vts", "testcases", "vndk", "golden") 63 64# The data package. 65_RESOURCE_PACKAGE = "vts.testcases.vndk" 66 67# The name of the zip file containing ABI dumps. 68_ABI_DUMP_ZIP_NAME = "abi_dump.zip" 69 70# Regular expression prefix for library name patterns. 71_REGEX_PREFIX = "[regex]" 72 73 74class AbiDumpResource: 75 """The class for loading ABI dumps from the zip in resources.""" 76 77 def __init__(self): 78 self._resource = None 79 self.zip_file = None 80 81 def __enter__(self): 82 self._resource = resources.files(_RESOURCE_PACKAGE).joinpath( 83 _ABI_DUMP_ZIP_NAME).open("rb") 84 self.zip_file = zipfile.ZipFile(self._resource, "r") 85 return self 86 87 def __exit__(self, exc_type, exc_val, traceback): 88 if self._resource: 89 self._resource.close() 90 if self.zip_file: 91 self.zip_file.close() 92 93 94def GetAbiDumpPathsFromResources(version, binder_bitness, abi_name, abi_bitness): 95 """Returns the VNDK dump paths in resources. 96 97 Args: 98 version: A string, the VNDK version. 99 binder_bitness: A string or an integer, 32 or 64. 100 abi_name: A string, the ABI of the library dump. 101 abi_bitness: A string or an integer, 32 or 64. 102 103 Returns: 104 A dict of {library name: dump resource path}. For example, 105 {"libbase.so": "R/64/arm64_armv8-a/source-based/libbase.so.lsdump"}. 106 If there is no dump for the version and ABI, this function returns an 107 empty dict. 108 """ 109 if not resources: 110 logging.error("Could not import resources module.") 111 return dict() 112 113 abi_bitness = int(abi_bitness) 114 try: 115 arch_names = next(x[2:] for x in _ABI_LIST if 116 abi_name.startswith(x[0]) and x[1] == abi_bitness) 117 except StopIteration: 118 logging.warning("Unknown %d-bit ABI %s.", abi_bitness, abi_name) 119 return dict() 120 121 # The separator in zipped path is always "/". 122 dump_dirs = ["/".join((version, str(binder_bitness), arch_name, 123 "source-based")) + "/" 124 for arch_name in arch_names] 125 ext = ".lsdump" 126 127 dump_paths = dict() 128 129 with AbiDumpResource() as dump_resource: 130 for path in dump_resource.zip_file.namelist(): 131 for dump_dir in dump_dirs: 132 if path.startswith(dump_dir) and path.endswith(ext): 133 lib_name = path[len(dump_dir):-len(ext)] 134 dump_paths[lib_name] = path 135 break 136 137 return dump_paths 138 139 140def _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_file, 141 change_history_file=None): 142 """Load VNDK libraries from the file to the specified tuple. 143 144 Args: 145 vndk_lists: The output tuple of lists containing library names. 146 tags: Strings, the tags of the libraries to find. 147 vndk_lib_list_file: The file object containing the VNDK library list. 148 change_history_file: The file object containing the VNDK list change 149 history. It adds the vndk files that are removed. 150 """ 151 def ReadTagAndFile(line): 152 # Ignore comments. 153 if line.startswith('#'): 154 return None, None 155 156 # Split columns. 157 cells = line.split(': ', 1) 158 if len(cells) < 2: 159 return None, None 160 return cells[0].strip(), cells[1].strip() 161 162 lib_sets = collections.defaultdict(set) 163 164 # Load VNDK tags from the list. 165 for line in vndk_lib_list_file: 166 tag, lib_name = ReadTagAndFile(line) 167 if not tag: 168 continue 169 lib_sets[tag].add(lib_name) 170 171 if change_history_file: 172 for line in change_history_file: 173 tag, lib_name = ReadTagAndFile(line) 174 if not tag: 175 continue 176 177 # In the history file, tag has '+' prefix if the file is added and 178 # '-' prefix if removed. 179 # To relax the test, include the removed files to the list. 180 if tag[0] != '-': 181 continue 182 tag = tag[1:] 183 lib_sets[tag].add(lib_name) 184 185 # Compute VNDK-core-private and VNDK-SP-private. 186 private = lib_sets.get('VNDK-private', set()) 187 188 lib_sets[VNDK_PRIVATE].update(lib_sets[VNDK] & private) 189 lib_sets[VNDK_SP_PRIVATE].update(lib_sets[VNDK_SP] & private) 190 191 lib_sets[LL_NDK].difference_update(private) 192 lib_sets[VNDK].difference_update(private) 193 lib_sets[VNDK_SP].difference_update(private) 194 195 # Update the output entries. 196 for index, tag in enumerate(tags): 197 for lib_name in lib_sets.get(tag, tuple()): 198 if lib_name.startswith(_REGEX_PREFIX): 199 lib_name = lib_name[len(_REGEX_PREFIX):] 200 vndk_lists[index].append(lib_name) 201 202 203def LoadVndkLibraryListsFromResources(version, *tags): 204 """Find the VNDK libraries with specific tags in resources. 205 206 Args: 207 version: A string, the VNDK version. 208 *tags: Strings, the tags of the libraries to find. 209 210 Returns: 211 A tuple of lists containing library names. Each list corresponds to 212 one tag in the argument. For SP-HAL, the returned names are regular 213 expressions. 214 None if the VNDK list for the version is not found. 215 """ 216 if not resources: 217 logging.error("Could not import resources module.") 218 return None 219 220 # VNDK 35 will not be frozen. 221 version_str = (version if re.match("\\d+", version) and int(version) <= 34 222 else "current") 223 vndk_lib_list_name = version_str + ".txt" 224 vndk_lib_list = resources.files(_RESOURCE_PACKAGE).joinpath( 225 vndk_lib_list_name) 226 vndk_lib_list_history_name = version_str + "_history.txt" 227 vndk_lib_list_history = resources.files(_RESOURCE_PACKAGE).joinpath( 228 vndk_lib_list_history_name) 229 vndk_lib_extra_list_name = "vndk-lib-extra-list-" + version_str + ".txt" 230 vndk_lib_extra_list = resources.files(_RESOURCE_PACKAGE).joinpath( 231 vndk_lib_extra_list_name) 232 233 if not vndk_lib_list.is_file(): 234 logging.warning("Cannot load %s.", vndk_lib_list_name) 235 return None 236 237 if not vndk_lib_extra_list.is_file(): 238 logging.warning("Cannot load %s.", vndk_lib_extra_list_name) 239 return None 240 241 vndk_lists = tuple([] for x in tags) 242 243 with vndk_lib_list.open("r") as f: 244 if vndk_lib_list_history.is_file(): 245 with vndk_lib_list_history.open("r") as history: 246 _LoadVndkLibraryListsFile(vndk_lists, tags, f, history) 247 else: 248 _LoadVndkLibraryListsFile(vndk_lists, tags, f) 249 with vndk_lib_extra_list.open("r") as f: 250 _LoadVndkLibraryListsFile(vndk_lists, tags, f) 251 return vndk_lists 252