Merge changes I0aee679a,I7fb380a3

* changes:
  Add systemapi as an APEX synonym for stub maps.
  Refactor tag handling code in stub generator.
This commit is contained in:
Treehugger Robot
2021-06-15 22:59:10 +00:00
committed by Gerrit Code Review
5 changed files with 285 additions and 182 deletions

View File

@@ -108,7 +108,14 @@ def parse_args() -> argparse.Namespace:
parser.add_argument( parser.add_argument(
'--llndk', action='store_true', help='Use the LLNDK variant.') '--llndk', action='store_true', help='Use the LLNDK variant.')
parser.add_argument( parser.add_argument(
'--apex', action='store_true', help='Use the APEX variant.') '--apex',
action='store_true',
help='Use the APEX variant. Note: equivalent to --system-api.')
parser.add_argument(
'--system-api',
action='store_true',
dest='apex',
help='Use the SystemAPI variant. Note: equivalent to --apex.')
parser.add_argument('--api-map', parser.add_argument('--api-map',
type=resolved_path, type=resolved_path,

View File

@@ -19,9 +19,10 @@ import io
import textwrap import textwrap
import unittest import unittest
import ndkstubgen
import symbolfile import symbolfile
from symbolfile import Arch, Tag from symbolfile import Arch, Tags
import ndkstubgen
# pylint: disable=missing-docstring # pylint: disable=missing-docstring
@@ -38,23 +39,25 @@ class GeneratorTest(unittest.TestCase):
version_file, symbol_list_file, version_file, symbol_list_file,
Arch('arm'), 9, False, False) Arch('arm'), 9, False, False)
version = symbolfile.Version('VERSION_PRIVATE', None, [], [ version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
symbolfile.Symbol('foo', []), symbolfile.Symbol('foo', Tags()),
]) ])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
version = symbolfile.Version('VERSION', None, [Tag('x86')], [ version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
symbolfile.Symbol('foo', []), [
]) symbolfile.Symbol('foo', Tags()),
])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [ version = symbolfile.Version('VERSION', None,
symbolfile.Symbol('foo', []), Tags.from_strs(['introduced=14']), [
]) symbolfile.Symbol('foo', Tags()),
])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
@@ -69,29 +72,29 @@ class GeneratorTest(unittest.TestCase):
version_file, symbol_list_file, version_file, symbol_list_file,
Arch('arm'), 9, False, False) Arch('arm'), 9, False, False)
version = symbolfile.Version('VERSION_1', None, [], [ version = symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', [Tag('x86')]), symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
]) ])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
version = symbolfile.Version('VERSION_1', None, [], [ version = symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', [Tag('introduced=14')]), symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
]) ])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
version = symbolfile.Version('VERSION_1', None, [], [ version = symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', [Tag('llndk')]), symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
]) ])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue()) self.assertEqual('', version_file.getvalue())
version = symbolfile.Version('VERSION_1', None, [], [ version = symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', [Tag('apex')]), symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
]) ])
generator.write_version(version) generator.write_version(version)
self.assertEqual('', src_file.getvalue()) self.assertEqual('', src_file.getvalue())
@@ -106,18 +109,17 @@ class GeneratorTest(unittest.TestCase):
Arch('arm'), 9, False, False) Arch('arm'), 9, False, False)
versions = [ versions = [
symbolfile.Version('VERSION_1', None, [], [ symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', []), symbolfile.Symbol('foo', Tags()),
symbolfile.Symbol('bar', [Tag('var')]), symbolfile.Symbol('bar', Tags.from_strs(['var'])),
symbolfile.Symbol('woodly', [Tag('weak')]), symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
symbolfile.Symbol('doodly', symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
[Tag('weak'), Tag('var')]),
]), ]),
symbolfile.Version('VERSION_2', 'VERSION_1', [], [ symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
symbolfile.Symbol('baz', []), symbolfile.Symbol('baz', Tags()),
]), ]),
symbolfile.Version('VERSION_3', 'VERSION_1', [], [ symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
symbolfile.Symbol('qux', [Tag('versioned=14')]), symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
]), ]),
] ]

View File

@@ -14,18 +14,22 @@
# limitations under the License. # limitations under the License.
# #
"""Parser for Android's version script information.""" """Parser for Android's version script information."""
from dataclasses import dataclass from __future__ import annotations
from dataclasses import dataclass, field
import logging import logging
import re import re
from typing import ( from typing import (
Dict, Dict,
Iterable, Iterable,
Iterator,
List, List,
Mapping, Mapping,
NewType, NewType,
Optional, Optional,
TextIO, TextIO,
Tuple, Tuple,
Union,
) )
@@ -51,12 +55,53 @@ def logger() -> logging.Logger:
return logging.getLogger(__name__) return logging.getLogger(__name__)
@dataclass
class Tags:
"""Container class for the tags attached to a symbol or version."""
tags: tuple[Tag, ...] = field(default_factory=tuple)
@classmethod
def from_strs(cls, strs: Iterable[str]) -> Tags:
"""Constructs tags from a collection of strings.
Does not decode API levels.
"""
return Tags(tuple(Tag(s) for s in strs))
def __contains__(self, tag: Union[Tag, str]) -> bool:
return tag in self.tags
def __iter__(self) -> Iterator[Tag]:
yield from self.tags
@property
def has_mode_tags(self) -> bool:
"""Returns True if any mode tags (apex, llndk, etc) are set."""
return self.has_apex_tags or self.has_llndk_tags
@property
def has_apex_tags(self) -> bool:
"""Returns True if any APEX tags are set."""
return 'apex' in self.tags or 'systemapi' in self.tags
@property
def has_llndk_tags(self) -> bool:
"""Returns True if any LL-NDK tags are set."""
return 'llndk' in self.tags
@property
def has_platform_only_tags(self) -> bool:
"""Returns True if any platform-only tags are set."""
return 'platform-only' in self.tags
@dataclass @dataclass
class Symbol: class Symbol:
"""A symbol definition from a symbol file.""" """A symbol definition from a symbol file."""
name: str name: str
tags: List[Tag] tags: Tags
@dataclass @dataclass
@@ -65,14 +110,22 @@ class Version:
name: str name: str
base: Optional[str] base: Optional[str]
tags: List[Tag] tags: Tags
symbols: List[Symbol] symbols: List[Symbol]
@property
def is_private(self) -> bool:
"""Returns True if this version block is private (platform only)."""
return self.name.endswith('_PRIVATE') or self.name.endswith('_PLATFORM')
def get_tags(line: str) -> List[Tag]:
def get_tags(line: str, api_map: ApiMap) -> Tags:
"""Returns a list of all tags on this line.""" """Returns a list of all tags on this line."""
_, _, all_tags = line.strip().partition('#') _, _, all_tags = line.strip().partition('#')
return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()] return Tags(tuple(
decode_api_level_tag(Tag(e), api_map)
for e in re.split(r'\s+', all_tags) if e.strip()
))
def is_api_level_tag(tag: Tag) -> bool: def is_api_level_tag(tag: Tag) -> bool:
@@ -104,24 +157,21 @@ def decode_api_level(api: str, api_map: ApiMap) -> int:
return api_map[api] return api_map[api]
def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]: def decode_api_level_tag(tag: Tag, api_map: ApiMap) -> Tag:
"""Decodes API level code names in a list of tags. """Decodes API level code name in a tag.
Raises: Raises:
ParseError: An unknown version name was found in a tag. ParseError: An unknown version name was found in a tag.
""" """
decoded_tags = list(tags) if not is_api_level_tag(tag):
for idx, tag in enumerate(tags): return tag
if not is_api_level_tag(tag):
continue
name, value = split_tag(tag)
try: name, value = split_tag(tag)
decoded = str(decode_api_level(value, api_map)) try:
decoded_tags[idx] = Tag('='.join([name, decoded])) decoded = str(decode_api_level(value, api_map))
except KeyError: return Tag(f'{name}={decoded}')
raise ParseError(f'Unknown version name in tag: {tag}') except KeyError as ex:
return decoded_tags raise ParseError(f'Unknown version name in tag: {tag}') from ex
def split_tag(tag: Tag) -> Tuple[str, str]: def split_tag(tag: Tag) -> Tuple[str, str]:
@@ -149,55 +199,52 @@ def get_tag_value(tag: Tag) -> str:
return split_tag(tag)[1] return split_tag(tag)[1]
def version_is_private(version: str) -> bool: def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
"""Returns True if the version name should be treated as private.""" apex: bool) -> bool:
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM') """Returns True if the tagged object should be omitted.
This defines the rules shared between version tagging and symbol tagging.
"""
# The apex and llndk tags will only exclude APIs from other modes. If in
# APEX or LLNDK mode and neither tag is provided, we fall back to the
# default behavior because all NDK symbols are implicitly available to APEX
# and LLNDK.
if tags.has_mode_tags:
if not apex and not llndk:
return True
if apex and not tags.has_apex_tags:
return True
if llndk and not tags.has_llndk_tags:
return True
if not symbol_in_arch(tags, arch):
return True
if not symbol_in_api(tags, arch, api):
return True
return False
def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool, def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool: apex: bool) -> bool:
"""Returns True if the version section should be ommitted. """Returns True if the version section should be omitted.
We want to omit any sections that do not have any symbols we'll have in the We want to omit any sections that do not have any symbols we'll have in the
stub library. Sections that contain entirely future symbols or only symbols stub library. Sections that contain entirely future symbols or only symbols
for certain architectures. for certain architectures.
""" """
if version_is_private(version.name): if version.is_private:
return True return True
if 'platform-only' in version.tags: if version.tags.has_platform_only_tags:
return True return True
return _should_omit_tags(version.tags, arch, api, llndk, apex)
no_llndk_no_apex = ('llndk' not in version.tags
and 'apex' not in version.tags)
keep = no_llndk_no_apex or \
('llndk' in version.tags and llndk) or \
('apex' in version.tags and apex)
if not keep:
return True
if not symbol_in_arch(version.tags, arch):
return True
if not symbol_in_api(version.tags, arch, api):
return True
return False
def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool, def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool: apex: bool) -> bool:
"""Returns True if the symbol should be omitted.""" """Returns True if the symbol should be omitted."""
no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
keep = no_llndk_no_apex or \
('llndk' in symbol.tags and llndk) or \
('apex' in symbol.tags and apex)
if not keep:
return True
if not symbol_in_arch(symbol.tags, arch):
return True
if not symbol_in_api(symbol.tags, arch, api):
return True
return False
def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool: def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
"""Returns true if the symbol is present for the given architecture.""" """Returns true if the symbol is present for the given architecture."""
has_arch_tags = False has_arch_tags = False
for tag in tags: for tag in tags:
@@ -325,8 +372,7 @@ class SymbolFileParser:
"""Parses a single version section and returns a Version object.""" """Parses a single version section and returns a Version object."""
assert self.current_line is not None assert self.current_line is not None
name = self.current_line.split('{')[0].strip() name = self.current_line.split('{')[0].strip()
tags = get_tags(self.current_line) tags = get_tags(self.current_line, self.api_map)
tags = decode_api_level_tags(tags, self.api_map)
symbols: List[Symbol] = [] symbols: List[Symbol] = []
global_scope = True global_scope = True
cpp_symbols = False cpp_symbols = False
@@ -373,8 +419,7 @@ class SymbolFileParser:
'Wildcard global symbols are not permitted.') 'Wildcard global symbols are not permitted.')
# Line is now in the format "<symbol-name>; # tags" # Line is now in the format "<symbol-name>; # tags"
name, _, _ = self.current_line.strip().partition(';') name, _, _ = self.current_line.strip().partition(';')
tags = get_tags(self.current_line) tags = get_tags(self.current_line, self.api_map)
tags = decode_api_level_tags(tags, self.api_map)
return Symbol(name, tags) return Symbol(name, tags)
def next_line(self) -> str: def next_line(self) -> str:

View File

@@ -19,7 +19,7 @@ import textwrap
import unittest import unittest
import symbolfile import symbolfile
from symbolfile import Arch, Tag from symbolfile import Arch, Tag, Tags, Version
# pylint: disable=missing-docstring # pylint: disable=missing-docstring
@@ -35,12 +35,14 @@ class DecodeApiLevelTest(unittest.TestCase):
class TagsTest(unittest.TestCase): class TagsTest(unittest.TestCase):
def test_get_tags_no_tags(self) -> None: def test_get_tags_no_tags(self) -> None:
self.assertEqual([], symbolfile.get_tags('')) self.assertEqual(Tags(), symbolfile.get_tags('', {}))
self.assertEqual([], symbolfile.get_tags('foo bar baz')) self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
def test_get_tags(self) -> None: def test_get_tags(self) -> None:
self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar')) self.assertEqual(Tags.from_strs(['foo', 'bar']),
self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz')) symbolfile.get_tags('# foo bar', {}))
self.assertEqual(Tags.from_strs(['bar', 'baz']),
symbolfile.get_tags('foo # bar baz', {}))
def test_split_tag(self) -> None: def test_split_tag(self) -> None:
self.assertTupleEqual(('foo', 'bar'), self.assertTupleEqual(('foo', 'bar'),
@@ -77,12 +79,14 @@ class TagsTest(unittest.TestCase):
} }
tags = [ tags = [
Tag('introduced=9'), symbolfile.decode_api_level_tag(t, api_map) for t in (
Tag('introduced-arm=14'), Tag('introduced=9'),
Tag('versioned=16'), Tag('introduced-arm=14'),
Tag('arm'), Tag('versioned=16'),
Tag('introduced=O'), Tag('arm'),
Tag('introduced=P'), Tag('introduced=O'),
Tag('introduced=P'),
)
] ]
expected_tags = [ expected_tags = [
Tag('introduced=9'), Tag('introduced=9'),
@@ -92,33 +96,37 @@ class TagsTest(unittest.TestCase):
Tag('introduced=9000'), Tag('introduced=9000'),
Tag('introduced=9001'), Tag('introduced=9001'),
] ]
self.assertListEqual( self.assertListEqual(expected_tags, tags)
expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
with self.assertRaises(symbolfile.ParseError): with self.assertRaises(symbolfile.ParseError):
symbolfile.decode_api_level_tags([Tag('introduced=O')], {}) symbolfile.decode_api_level_tag(Tag('introduced=O'), {})
class PrivateVersionTest(unittest.TestCase): class PrivateVersionTest(unittest.TestCase):
def test_version_is_private(self) -> None: def test_version_is_private(self) -> None:
self.assertFalse(symbolfile.version_is_private('foo')) def mock_version(name: str) -> Version:
self.assertFalse(symbolfile.version_is_private('PRIVATE')) return Version(name, base=None, tags=Tags(), symbols=[])
self.assertFalse(symbolfile.version_is_private('PLATFORM'))
self.assertFalse(symbolfile.version_is_private('foo_private'))
self.assertFalse(symbolfile.version_is_private('foo_platform'))
self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
self.assertTrue(symbolfile.version_is_private('foo_PRIVATE')) self.assertFalse(mock_version('foo').is_private)
self.assertTrue(symbolfile.version_is_private('foo_PLATFORM')) self.assertFalse(mock_version('PRIVATE').is_private)
self.assertFalse(mock_version('PLATFORM').is_private)
self.assertFalse(mock_version('foo_private').is_private)
self.assertFalse(mock_version('foo_platform').is_private)
self.assertFalse(mock_version('foo_PRIVATE_').is_private)
self.assertFalse(mock_version('foo_PLATFORM_').is_private)
self.assertTrue(mock_version('foo_PRIVATE').is_private)
self.assertTrue(mock_version('foo_PLATFORM').is_private)
class SymbolPresenceTest(unittest.TestCase): class SymbolPresenceTest(unittest.TestCase):
def test_symbol_in_arch(self) -> None: def test_symbol_in_arch(self) -> None:
self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm'))) self.assertTrue(symbolfile.symbol_in_arch(Tags(), Arch('arm')))
self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm'))) self.assertTrue(
symbolfile.symbol_in_arch(Tags.from_strs(['arm']), Arch('arm')))
self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm'))) self.assertFalse(
symbolfile.symbol_in_arch(Tags.from_strs(['x86']), Arch('arm')))
def test_symbol_in_api(self) -> None: def test_symbol_in_api(self) -> None:
self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9)) self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
@@ -197,81 +205,99 @@ class OmitVersionTest(unittest.TestCase):
def test_omit_private(self) -> None: def test_omit_private(self) -> None:
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
False)) False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'), symbolfile.Version('foo_PRIVATE', None, Tags(), []),
9, False, False)) Arch('arm'), 9, False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'), symbolfile.Version('foo_PLATFORM', None, Tags(), []),
9, False, False)) Arch('arm'), 9, False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('platform-only')], []), symbolfile.Version('foo', None,
Tags.from_strs(['platform-only']), []),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
def test_omit_llndk(self) -> None: def test_omit_llndk(self) -> None:
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('llndk')], []), symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True, symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
False)) True, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('llndk')], []), symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, True, False)) Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None: def test_omit_apex(self) -> None:
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('apex')], []), symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
True)) False, True))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('apex')], []), symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, True)) Arch('arm'), 9, False, True))
def test_omit_systemapi(self) -> None:
self.assertTrue(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
[]), Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
False, True))
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
[]), Arch('arm'), 9, False, True))
def test_omit_arch(self) -> None: def test_omit_arch(self) -> None:
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
False)) False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'), symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
9, False, False))
def test_omit_api(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
False))
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('introduced=9')], []),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_version( symbolfile.should_omit_version(
symbolfile.Version('foo', None, [Tag('introduced=14')], []), symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
Arch('arm'), 9, False, False))
def test_omit_api(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
False, False))
self.assertFalse(
symbolfile.should_omit_version(
symbolfile.Version('foo', None,
Tags.from_strs(['introduced=9']), []),
Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
symbolfile.Version('foo', None,
Tags.from_strs(['introduced=14']), []),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
@@ -279,58 +305,72 @@ class OmitSymbolTest(unittest.TestCase):
def test_omit_llndk(self) -> None: def test_omit_llndk(self) -> None:
self.assertTrue( self.assertTrue(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
False, False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, True, False)) Arch('arm'), 9, True, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True, symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
False)) Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None: def test_omit_apex(self) -> None:
self.assertTrue( self.assertTrue(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False, symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, True)) Arch('arm'), 9, False, True))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False, symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
True)) Arch('arm'), 9, False, True))
def test_omit_systemapi(self) -> None:
self.assertTrue(
symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, True))
self.assertFalse(
symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
Arch('arm'), 9, False, True))
def test_omit_arch(self) -> None: def test_omit_arch(self) -> None:
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False, symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
False)) 9, False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False, symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
False)) 9, False, False))
def test_omit_api(self) -> None: def test_omit_api(self) -> None:
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False)) Arch('arm'), 9, False, False))
self.assertFalse( self.assertFalse(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'), symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
9, False, False)) Arch('arm'), 9, False, False))
self.assertTrue( self.assertTrue(
symbolfile.should_omit_symbol( symbolfile.should_omit_symbol(
symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'), symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
9, False, False)) Arch('arm'), 9, False, False))
class SymbolFileParseTest(unittest.TestCase): class SymbolFileParseTest(unittest.TestCase):
@@ -376,11 +416,11 @@ class SymbolFileParseTest(unittest.TestCase):
version = parser.parse_version() version = parser.parse_version()
self.assertEqual('VERSION_1', version.name) self.assertEqual('VERSION_1', version.name)
self.assertIsNone(version.base) self.assertIsNone(version.base)
self.assertEqual(['foo', 'bar'], version.tags) self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
expected_symbols = [ expected_symbols = [
symbolfile.Symbol('baz', []), symbolfile.Symbol('baz', Tags()),
symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]), symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
] ]
self.assertEqual(expected_symbols, version.symbols) self.assertEqual(expected_symbols, version.symbols)
@@ -388,7 +428,7 @@ class SymbolFileParseTest(unittest.TestCase):
version = parser.parse_version() version = parser.parse_version()
self.assertEqual('VERSION_2', version.name) self.assertEqual('VERSION_2', version.name)
self.assertEqual('VERSION_1', version.base) self.assertEqual('VERSION_1', version.base)
self.assertEqual([], version.tags) self.assertEqual(Tags(), version.tags)
def test_parse_version_eof(self) -> None: def test_parse_version_eof(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\ input_file = io.StringIO(textwrap.dedent("""\
@@ -423,12 +463,12 @@ class SymbolFileParseTest(unittest.TestCase):
parser.next_line() parser.next_line()
symbol = parser.parse_symbol() symbol = parser.parse_symbol()
self.assertEqual('foo', symbol.name) self.assertEqual('foo', symbol.name)
self.assertEqual([], symbol.tags) self.assertEqual(Tags(), symbol.tags)
parser.next_line() parser.next_line()
symbol = parser.parse_symbol() symbol = parser.parse_symbol()
self.assertEqual('bar', symbol.name) self.assertEqual('bar', symbol.name)
self.assertEqual(['baz', 'qux'], symbol.tags) self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
def test_wildcard_symbol_global(self) -> None: def test_wildcard_symbol_global(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\ input_file = io.StringIO(textwrap.dedent("""\
@@ -497,14 +537,15 @@ class SymbolFileParseTest(unittest.TestCase):
versions = parser.parse() versions = parser.parse()
expected = [ expected = [
symbolfile.Version('VERSION_1', None, [], [ symbolfile.Version('VERSION_1', None, Tags(), [
symbolfile.Symbol('foo', []), symbolfile.Symbol('foo', Tags()),
symbolfile.Symbol('bar', [Tag('baz')]), symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
]),
symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
symbolfile.Symbol('woodly', []),
symbolfile.Symbol('doodly', [Tag('asdf')]),
]), ]),
symbolfile.Version(
'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
symbolfile.Symbol('woodly', Tags()),
symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
]),
] ]
self.assertEqual(expected, versions) self.assertEqual(expected, versions)
@@ -527,10 +568,10 @@ class SymbolFileParseTest(unittest.TestCase):
self.assertIsNone(version.base) self.assertIsNone(version.base)
expected_symbols = [ expected_symbols = [
symbolfile.Symbol('foo', []), symbolfile.Symbol('foo', Tags()),
symbolfile.Symbol('bar', [Tag('llndk')]), symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]), symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
symbolfile.Symbol('qux', [Tag('apex')]), symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
] ]
self.assertEqual(expected_symbols, version.symbols) self.assertEqual(expected_symbols, version.symbols)

View File

@@ -70,9 +70,11 @@ the same line. The supported tags are:
### apex ### apex
Indicates that the version or symbol is to be exposed in the APEX stubs rather Indicates that the version or symbol is to be exposed by an APEX rather than the
than the NDK. May be used in combination with `llndk` if the symbol is exposed NDK. For APIs exposed by the platform *for* APEX, use `systemapi`.
to both APEX and the LL-NDK.
May be used in combination with `llndk` if the symbol is exposed to both APEX
and the LL-NDK.
### future ### future
@@ -144,6 +146,12 @@ for use by the NDK, LL-NDK, or APEX (similar to Java's `@SystemAPI`). It is
preferable to keep such APIs in an entirely separate library to protect them preferable to keep such APIs in an entirely separate library to protect them
from access via `dlsym`, but this is not always possible. from access via `dlsym`, but this is not always possible.
### systemapi
This is a synonym of the `apex` tag. It should be used to clarify that the API
is an API exposed by the system for an APEX, whereas `apex` should be used for
APIs exposed by an APEX to the platform or another APEX.
### var ### var
Used to define a public global variable. By default all symbols are exposed as Used to define a public global variable. By default all symbols are exposed as