| #!/usr/bin/env python |
| # |
| # 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 argparse |
| import os |
| import subprocess |
| import sys |
| |
| import extract_lsdump |
| |
| |
| def _ExecuteCommand(cmd, **kwargs): |
| """Executes a command and returns stdout. |
| |
| Args: |
| cmd: A list of strings, the command to execute. |
| **kwargs: The arguments passed to subprocess.Popen. |
| |
| Returns: |
| A string, the stdout. |
| """ |
| proc = subprocess.Popen( |
| cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) |
| stdout, stderr = proc.communicate() |
| if proc.returncode: |
| sys.exit("Command failed: %s\nstdout=%s\nstderr=%s" % ( |
| cmd, stdout, stderr)) |
| if stderr: |
| print("Warning: cmd=%s\nstdout=%s\nstderr=%s" % (cmd, stdout, stderr)) |
| return stdout.strip() |
| |
| |
| def GetBuildVariables(build_top_dir, abs_path, vars): |
| """Gets values of variables from build config. |
| |
| Args: |
| build_top_dir: The path to root directory of Android source. |
| abs_path: A boolean, whether to convert the values to absolute paths. |
| vars: A list of strings, the names of the variables. |
| |
| Returns: |
| A list of strings which are the values of the variables. |
| """ |
| cmd = ["build/soong/soong_ui.bash", "--dumpvars-mode", |
| ("--abs-vars" if abs_path else "--vars"), " ".join(vars)] |
| stdout = _ExecuteCommand(cmd, cwd=build_top_dir) |
| print(stdout) |
| return [line.split("=", 1)[1].strip("'") for line in stdout.splitlines()] |
| |
| |
| def _LoadLibraryNamesFromTxt(vndk_lib_list_file): |
| """Loads VNDK and VNDK-SP library names from a VNDK library list. |
| |
| Args: |
| vndk_lib_list_file: A file object of |
| build/make/target/product/vndk/current.txt |
| |
| Returns: |
| A list of strings, the VNDK and VNDK-SP library names with vndk/vndk-sp |
| directory prefixes. |
| """ |
| tags = ( |
| ("VNDK-core: ", len("VNDK-core: "), False), |
| ("VNDK-SP: ", len("VNDK-SP: "), False), |
| ("VNDK-private: ", len("VNDK-private: "), True), |
| ("VNDK-SP-private: ", len("VNDK-SP-private: "), True), |
| ) |
| lib_names = set() |
| lib_names_exclude = set() |
| for line in vndk_lib_list_file: |
| for tag, tag_len, is_exclude in tags: |
| if line.startswith(tag): |
| lib_name = line[tag_len:].strip() |
| if is_exclude: |
| lib_names_exclude.add(lib_name) |
| else: |
| lib_names.add(lib_name) |
| return sorted(lib_names - lib_names_exclude) |
| |
| |
| def _LoadLibraryNames(file_names): |
| """Loads library names from files. |
| |
| Each element in the input list can be a .so file or a .txt file. The |
| returned list consists of: |
| - The .so file names in the input list. |
| - The libraries tagged with VNDK-core or VNDK-SP in the .txt file. |
| |
| Args: |
| file_names: A list of strings, the library or text file names. |
| |
| Returns: |
| A list of strings, the library names (probably with vndk/vndk-sp |
| directory prefixes). |
| """ |
| lib_names = [] |
| for file_name in file_names: |
| if file_name.endswith(".so"): |
| lib_names.append(file_name) |
| else: |
| with open(file_name, "r") as txt_file: |
| lib_names.extend(_LoadLibraryNamesFromTxt(txt_file)) |
| return lib_names |
| |
| |
| def DumpAbi(output_dir, lib_names, lsdump_path): |
| """Generates ABI dumps from library lsdumps. |
| |
| Args: |
| output_dir: The output directory of dump files. |
| lib_names: The names of the libraries to dump. |
| lsdump_path: The path to the directory containing lsdumps. |
| |
| Returns: |
| A list of strings, the libraries whose ABI dump fails to be created. |
| """ |
| missing_dumps = [] |
| for lib_name in lib_names: |
| dump_path = os.path.join(output_dir, lib_name + '.abi.dump') |
| lib_lsdump_path = os.path.join(lsdump_path, lib_name + '.lsdump') |
| if os.path.isfile(lib_lsdump_path + '.gz'): |
| lib_lsdump_path += '.gz' |
| |
| print(lib_lsdump_path) |
| try: |
| extract_lsdump.ParseLsdumpFile(lib_lsdump_path, dump_path) |
| except extract_lsdump.LsdumpError as e: |
| missing_dumps.append(lib_name) |
| print(e) |
| else: |
| print('Output: ' + dump_path) |
| print('') |
| return missing_dumps |
| |
| |
| def _GetTargetArchDir(target_arch, target_arch_variant): |
| if target_arch == target_arch_variant: |
| return target_arch |
| return '{}_{}'.format(target_arch, target_arch_variant) |
| |
| |
| def _GetAbiBitnessFromArch(target_arch): |
| arch_bitness = { |
| 'arm': '32', |
| 'arm64': '64', |
| 'x86': '32', |
| 'x86_64': '64', |
| } |
| return arch_bitness[target_arch] |
| |
| |
| def main(): |
| # Parse arguments |
| description = ( |
| 'Generates VTS VNDK ABI test abidumps from lsdump. ' |
| 'Option values are read from build variables if no value is given. ' |
| 'If none of the options are specified, then abidumps for target second ' |
| 'arch are also generated.' |
| ) |
| arg_parser = argparse.ArgumentParser(description=description) |
| arg_parser.add_argument("file", nargs="*", |
| help="the libraries to dump. Each file can be " |
| ".so or .txt. The text file can be found at " |
| "build/make/target/product/vndk/current.txt.") |
| arg_parser.add_argument("--output", "-o", action="store", |
| help="output directory for ABI reference dump. " |
| "Default value is PLATFORM_VNDK_VERSION.") |
| arg_parser.add_argument('--platform-vndk-version', |
| help='platform VNDK version. ' |
| 'Default value is PLATFORM_VNDK_VERSION.') |
| arg_parser.add_argument('--binder-bitness', |
| choices=['32', '64'], |
| help='bitness of binder interface. ' |
| 'Default value is 32 if BINDER32BIT is set ' |
| 'else is 64.') |
| arg_parser.add_argument('--target-main-arch', |
| choices=['arm', 'arm64', 'x86', 'x86_64'], |
| help='main CPU arch of the device. ' |
| 'Default value is TARGET_ARCH.') |
| arg_parser.add_argument('--target-arch', |
| choices=['arm', 'arm64', 'x86', 'x86_64'], |
| help='CPU arch of the libraries to dump. ' |
| 'Default value is TARGET_ARCH.') |
| arg_parser.add_argument('--target-arch-variant', |
| help='CPU arch variant of the libraries to dump. ' |
| 'Default value is TARGET_ARCH_VARIANT.') |
| |
| args = arg_parser.parse_args() |
| |
| build_top_dir = os.getenv("ANDROID_BUILD_TOP") |
| if not build_top_dir: |
| sys.exit("env var ANDROID_BUILD_TOP is not set") |
| |
| # If some options are not specified, read build variables as default values. |
| if not all([args.platform_vndk_version, |
| args.binder_bitness, |
| args.target_main_arch, |
| args.target_arch, |
| args.target_arch_variant]): |
| [platform_vndk_version, |
| binder_32_bit, |
| target_arch, |
| target_arch_variant, |
| target_2nd_arch, |
| target_2nd_arch_variant] = GetBuildVariables( |
| build_top_dir, |
| False, |
| ['PLATFORM_VNDK_VERSION', |
| 'BINDER32BIT', |
| 'TARGET_ARCH', |
| 'TARGET_ARCH_VARIANT', |
| 'TARGET_2ND_ARCH', |
| 'TARGET_2ND_ARCH_VARIANT'] |
| ) |
| target_main_arch = target_arch |
| binder_bitness = '32' if binder_32_bit else '64' |
| |
| if args.platform_vndk_version: |
| platform_vndk_version = args.platform_vndk_version |
| |
| if args.binder_bitness: |
| binder_bitness = args.binder_bitness |
| |
| if args.target_main_arch: |
| target_main_arch = args.target_main_arch |
| |
| if args.target_arch: |
| target_arch = args.target_arch |
| |
| if args.target_arch_variant: |
| target_arch_variant = args.target_arch_variant |
| |
| dump_targets = [(platform_vndk_version, |
| binder_bitness, |
| target_main_arch, |
| target_arch, |
| target_arch_variant)] |
| |
| # If all options are not specified, then also create dump for 2nd arch. |
| if not any([args.platform_vndk_version, |
| args.binder_bitness, |
| args.target_main_arch, |
| args.target_arch, |
| args.target_arch_variant]): |
| dump_targets.append((platform_vndk_version, |
| binder_bitness, |
| target_main_arch, |
| target_2nd_arch, |
| target_2nd_arch_variant)) |
| |
| for target_tuple in dump_targets: |
| (platform_vndk_version, |
| binder_bitness, |
| target_main_arch, |
| target_arch, |
| target_arch_variant) = target_tuple |
| |
| # Determine abi_bitness from target architecture |
| abi_bitness = _GetAbiBitnessFromArch(target_arch) |
| |
| # Generate ABI dump from lsdump in TOP/prebuilts/abi-dumps |
| lsdump_path = os.path.join( |
| build_top_dir, |
| 'prebuilts', |
| 'abi-dumps', |
| 'vndk', |
| platform_vndk_version, |
| binder_bitness, |
| _GetTargetArchDir(target_arch, target_arch_variant), |
| 'source-based') |
| if not os.path.exists(lsdump_path): |
| print('Warning: lsdump path does not exist: ' + lsdump_path) |
| print('No abidump created.') |
| continue |
| |
| output_dir = os.path.join( |
| args.output if args.output else platform_vndk_version, |
| 'binder' + binder_bitness, |
| target_main_arch, |
| 'lib64' if abi_bitness == '64' else 'lib') |
| print("OUTPUT_DIR=" + output_dir) |
| |
| lib_names = _LoadLibraryNames(args.file) |
| |
| missing_dumps = DumpAbi(output_dir, lib_names, lsdump_path) |
| |
| if missing_dumps: |
| print('Warning: Fails to create ABI dumps for libraries:') |
| for lib_name in missing_dumps: |
| print(lib_name) |
| |
| |
| if __name__ == "__main__": |
| main() |