Use libabigail to track NDK ABIs.
The local diffing behavior is currently flagged off so we can land
this in stages.
Test: pytest cc
Test: treehugger
Test: development/tools/update_ndk_abi.sh
Test: m ndk
Bug: http://b/156513478
Change-Id: Iccb314411bc74ea3ddfea8b85b0539709295f65a
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 86bf6ff..2387e69 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -18,7 +18,7 @@
import argparse
import json
import logging
-import os
+from pathlib import Path
import sys
from typing import Iterable, TextIO
@@ -28,10 +28,12 @@
class Generator:
"""Output generator that writes stub source files and version scripts."""
- def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
- api: int, llndk: bool, apex: bool) -> None:
+ def __init__(self, src_file: TextIO, version_script: TextIO,
+ symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
+ apex: bool) -> None:
self.src_file = src_file
self.version_script = version_script
+ self.symbol_list = symbol_list
self.arch = arch
self.api = api
self.llndk = llndk
@@ -39,6 +41,7 @@
def write(self, versions: Iterable[Version]) -> None:
"""Writes all symbol data to the output files."""
+ self.symbol_list.write('[abi_symbol_list]\n')
for version in versions:
self.write_version(version)
@@ -76,11 +79,11 @@
weak = '__attribute__((weak)) '
if 'var' in symbol.tags:
- self.src_file.write('{}int {} = 0;\n'.format(
- weak, symbol.name))
+ self.src_file.write(f'{weak}int {symbol.name} = 0;\n')
else:
- self.src_file.write('{}void {}() {{}}\n'.format(
- weak, symbol.name))
+ self.src_file.write(f'{weak}void {symbol.name}() {{}}\n')
+
+ self.symbol_list.write(f'{symbol.name}\n')
if not version_empty and section_versioned:
base = '' if version.base is None else ' ' + version.base
@@ -91,6 +94,10 @@
"""Parses and returns command line arguments."""
parser = argparse.ArgumentParser()
+ def resolved_path(raw: str) -> Path:
+ """Returns a resolved Path for the given string."""
+ return Path(raw).resolve()
+
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument(
@@ -103,26 +110,23 @@
parser.add_argument(
'--apex', action='store_true', help='Use the APEX variant.')
- # https://github.com/python/mypy/issues/1317
- # mypy has issues with using os.path.realpath as an argument here.
- parser.add_argument(
- '--api-map',
- type=os.path.realpath, # type: ignore
- required=True,
- help='Path to the API level map JSON file.')
+ parser.add_argument('--api-map',
+ type=resolved_path,
+ required=True,
+ help='Path to the API level map JSON file.')
- parser.add_argument(
- 'symbol_file',
- type=os.path.realpath, # type: ignore
- help='Path to symbol file.')
- parser.add_argument(
- 'stub_src',
- type=os.path.realpath, # type: ignore
- help='Path to output stub source file.')
- parser.add_argument(
- 'version_script',
- type=os.path.realpath, # type: ignore
- help='Path to output version script.')
+ parser.add_argument('symbol_file',
+ type=resolved_path,
+ help='Path to symbol file.')
+ parser.add_argument('stub_src',
+ type=resolved_path,
+ help='Path to output stub source file.')
+ parser.add_argument('version_script',
+ type=resolved_path,
+ help='Path to output version script.')
+ parser.add_argument('symbol_list',
+ type=resolved_path,
+ help='Path to output abigail symbol list.')
return parser.parse_args()
@@ -131,7 +135,7 @@
"""Program entry point."""
args = parse_args()
- with open(args.api_map) as map_file:
+ with args.api_map.open() as map_file:
api_map = json.load(map_file)
api = symbolfile.decode_api_level(args.api, api_map)
@@ -141,19 +145,20 @@
verbosity = 2
logging.basicConfig(level=verbose_map[verbosity])
- with open(args.symbol_file) as symbol_file:
+ with args.symbol_file.open() as symbol_file:
try:
versions = symbolfile.SymbolFileParser(symbol_file, api_map,
args.arch, api, args.llndk,
args.apex).parse()
except symbolfile.MultiplyDefinedSymbolError as ex:
- sys.exit('{}: error: {}'.format(args.symbol_file, ex))
+ sys.exit(f'{args.symbol_file}: error: {ex}')
- with open(args.stub_src, 'w') as src_file:
- with open(args.version_script, 'w') as version_file:
- generator = Generator(src_file, version_file, args.arch, api,
- args.llndk, args.apex)
- generator.write(versions)
+ with args.stub_src.open('w') as src_file:
+ with args.version_script.open('w') as version_script:
+ with args.symbol_list.open('w') as symbol_list:
+ generator = Generator(src_file, version_script, symbol_list,
+ args.arch, api, args.llndk, args.apex)
+ generator.write(versions)
if __name__ == '__main__':
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 6d2c9d6..3dbab61 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -33,8 +33,10 @@
# OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9, False, False)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9, False, False)
version = symbolfile.Version('VERSION_PRIVATE', None, [], [
symbolfile.Symbol('foo', []),
@@ -62,8 +64,10 @@
# SymbolPresenceTest.
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9, False, False)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9, False, False)
version = symbolfile.Version('VERSION_1', None, [], [
symbolfile.Symbol('foo', [Tag('x86')]),
@@ -96,8 +100,10 @@
def test_write(self) -> None:
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9, False, False)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9, False, False)
versions = [
symbolfile.Version('VERSION_1', None, [], [
@@ -141,6 +147,17 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ expected_allowlist = textwrap.dedent("""\
+ [abi_symbol_list]
+ foo
+ bar
+ woodly
+ doodly
+ baz
+ qux
+ """)
+ self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
class IntegrationTest(unittest.TestCase):
def test_integration(self) -> None:
@@ -186,8 +203,10 @@
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9, False, False)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9, False, False)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -215,6 +234,16 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ expected_allowlist = textwrap.dedent("""\
+ [abi_symbol_list]
+ foo
+ baz
+ qux
+ wibble
+ wobble
+ """)
+ self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
def test_integration_future_api(self) -> None:
api_map = {
'O': 9000,
@@ -238,8 +267,10 @@
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9001, False, False)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9001, False, False)
generator.write(versions)
expected_src = textwrap.dedent("""\
@@ -257,6 +288,13 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ expected_allowlist = textwrap.dedent("""\
+ [abi_symbol_list]
+ foo
+ bar
+ """)
+ self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
def test_multiple_definition(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
VERSION_1 {
@@ -336,8 +374,10 @@
src_file = io.StringIO()
version_file = io.StringIO()
- generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
- 9, False, True)
+ symbol_list_file = io.StringIO()
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file,
+ Arch('arm'), 9, False, True)
generator.write(versions)
expected_src = textwrap.dedent("""\