blob: a5767ae1cdf3020f45df16a5f11ccc30102e00e5 [file] [log] [blame]
#!/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()