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:
@@ -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,
|
||||||
|
@@ -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'])),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -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:
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user