Add tag for "versioned=API".
This adds the `versioned=API` tag. This should be a very uncommonly needed tag, and is really only needed to fix versioning mistakes that are already out in the wild. For example, some of libc's __aeabi_* functions were originally placed in the private version, but that was incorrect. They are now in LIBC_N, but when building against any version prior to N we need the symbol to be unversioned (otherwise it won't resolve on M where it is private). Test: make ndk Change-Id: I0cd2f80cf4b32356356914cf7ff4119e67f15032
This commit is contained in:
@@ -71,6 +71,19 @@ def get_tags(line):
|
|||||||
return re.split(r'\s+', all_tags)
|
return re.split(r'\s+', all_tags)
|
||||||
|
|
||||||
|
|
||||||
|
def get_tag_value(tag):
|
||||||
|
"""Returns the value of a key/value tag.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Tag is not a key/value type tag.
|
||||||
|
|
||||||
|
Returns: Value part of tag as a string.
|
||||||
|
"""
|
||||||
|
if '=' not in tag:
|
||||||
|
raise ValueError('Not a key/value tag: ' + tag)
|
||||||
|
return tag.partition('=')[2]
|
||||||
|
|
||||||
|
|
||||||
def version_is_private(version):
|
def version_is_private(version):
|
||||||
"""Returns True if the version name should be treated as private."""
|
"""Returns True if the version name should be treated as private."""
|
||||||
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
|
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
|
||||||
@@ -87,7 +100,7 @@ def should_omit_version(name, tags, arch, api):
|
|||||||
return True
|
return True
|
||||||
if not symbol_in_arch(tags, arch):
|
if not symbol_in_arch(tags, arch):
|
||||||
return True
|
return True
|
||||||
if not symbol_in_version(tags, arch, api):
|
if not symbol_in_api(tags, arch, api):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -178,8 +191,8 @@ def symbol_in_arch(tags, arch):
|
|||||||
return not has_arch_tags
|
return not has_arch_tags
|
||||||
|
|
||||||
|
|
||||||
def symbol_in_version(tags, arch, version):
|
def symbol_in_api(tags, arch, api):
|
||||||
"""Returns true if the symbol is present for the given version."""
|
"""Returns true if the symbol is present for the given API level."""
|
||||||
introduced_tag = None
|
introduced_tag = None
|
||||||
arch_specific = False
|
arch_specific = False
|
||||||
for tag in tags:
|
for tag in tags:
|
||||||
@@ -191,7 +204,7 @@ def symbol_in_version(tags, arch, version):
|
|||||||
arch_specific = True
|
arch_specific = True
|
||||||
elif tag == 'future':
|
elif tag == 'future':
|
||||||
# This symbol is not in any released API level.
|
# This symbol is not in any released API level.
|
||||||
# TODO(danalbert): These need to be emitted for version == current.
|
# TODO(danalbert): These need to be emitted for api == current.
|
||||||
# That's not a construct we have yet, so just skip it for now.
|
# That's not a construct we have yet, so just skip it for now.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -200,9 +213,28 @@ def symbol_in_version(tags, arch, version):
|
|||||||
# available.
|
# available.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# The tag is a key=value pair, and we only care about the value now.
|
return api >= int(get_tag_value(introduced_tag))
|
||||||
_, _, version_str = introduced_tag.partition('=')
|
|
||||||
return version >= int(version_str)
|
|
||||||
|
def symbol_versioned_in_api(tags, api):
|
||||||
|
"""Returns true if the symbol should be versioned for the given API.
|
||||||
|
|
||||||
|
This models the `versioned=API` tag. This should be a very uncommonly
|
||||||
|
needed tag, and is really only needed to fix versioning mistakes that are
|
||||||
|
already out in the wild.
|
||||||
|
|
||||||
|
For example, some of libc's __aeabi_* functions were originally placed in
|
||||||
|
the private version, but that was incorrect. They are now in LIBC_N, but
|
||||||
|
when building against any version prior to N we need the symbol to be
|
||||||
|
unversioned (otherwise it won't resolve on M where it is private).
|
||||||
|
"""
|
||||||
|
for tag in tags:
|
||||||
|
if tag.startswith('versioned='):
|
||||||
|
return api >= int(get_tag_value(tag))
|
||||||
|
# If there is no "versioned" tag, the tag has been versioned for as long as
|
||||||
|
# it was introduced.
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def handle_global_scope(scope, line, src_file, version_file, arch, api):
|
def handle_global_scope(scope, line, src_file, version_file, arch, api):
|
||||||
@@ -221,18 +253,20 @@ def handle_global_scope(scope, line, src_file, version_file, arch, api):
|
|||||||
|
|
||||||
# Line is now in the format "<symbol-name>; # tags"
|
# Line is now in the format "<symbol-name>; # tags"
|
||||||
# Tags are whitespace separated.
|
# Tags are whitespace separated.
|
||||||
symbol_name, _, rest = line.strip().partition(';')
|
symbol_name, _, _ = line.strip().partition(';')
|
||||||
tags = get_tags(line)
|
tags = get_tags(line)
|
||||||
|
|
||||||
if not symbol_in_arch(tags, arch):
|
if not symbol_in_arch(tags, arch):
|
||||||
return
|
return
|
||||||
if not symbol_in_version(tags, arch, api):
|
if not symbol_in_api(tags, arch, api):
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'var' in tags:
|
if 'var' in tags:
|
||||||
src_file.write('int {} = 0;\n'.format(symbol_name))
|
src_file.write('int {} = 0;\n'.format(symbol_name))
|
||||||
else:
|
else:
|
||||||
src_file.write('void {}() {{}}\n'.format(symbol_name))
|
src_file.write('void {}() {{}}\n'.format(symbol_name))
|
||||||
|
|
||||||
|
if symbol_versioned_in_api(tags, api):
|
||||||
version_file.write(line)
|
version_file.write(line)
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user