forked from aosp-mirror/platform_development
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "Add a couple scripts to fixup version scripts."
- Loading branch information
Showing
2 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
#!/usr/bin/env python | ||
# | ||
# Copyright (C) 2016 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. | ||
# | ||
"""Builds a database of symbol version introductions.""" | ||
import argparse | ||
import json | ||
import logging | ||
import os | ||
|
||
|
||
THIS_DIR = os.path.realpath(os.path.dirname(__file__)) | ||
|
||
|
||
ALL_ARCHITECTURES = ( | ||
'arm', | ||
'arm64', | ||
'mips', | ||
'mips64', | ||
'x86', | ||
'x86_64', | ||
) | ||
|
||
|
||
def logger(): | ||
"""Returns the default logger for this module.""" | ||
return logging.getLogger(__name__) | ||
|
||
|
||
def get_platform_versions(): | ||
"""Returns a list of the platform versions we have data for.""" | ||
versions = [] | ||
platforms_dir = os.path.join(THIS_DIR, 'platforms') | ||
logger().debug('Getting platform versions from %s', platforms_dir) | ||
for name in os.listdir(platforms_dir): | ||
if name.startswith('android-'): | ||
versions.append(int(name.split('-')[1])) | ||
return versions | ||
|
||
|
||
def add_symbols(symbols, symbol_file_path, version, arch, is_var): | ||
"""Adds symbols from a file to the symbol dict.""" | ||
with open(symbol_file_path) as symbol_file: | ||
names = symbol_file.readlines() | ||
|
||
for name in names: | ||
name = name.strip() | ||
if not name: | ||
continue | ||
introduced_tag = 'introduced-' + arch | ||
if name in symbols: | ||
assert symbols[name]['is_var'] == is_var | ||
if introduced_tag in symbols[name]: | ||
continue | ||
symbols[name][introduced_tag] = version | ||
else: | ||
symbols[name] = {} | ||
symbols[name]['is_var'] = is_var | ||
symbols[name][introduced_tag] = version | ||
|
||
|
||
def build_symbol_db(lib_name): | ||
"""Returns a dict of symbols and their version information. | ||
Args: | ||
lib_name: Name of the library to return file mapping for. | ||
Returns: dict of symbol information in the following format: | ||
{ | ||
"symbol_name": { | ||
"is_var": "true", | ||
"introduced-arm": 9, | ||
"introduced-x86": 14, | ||
"introduced-mips": 16, | ||
"introduced-arm64": 21, | ||
"introduced-mips64": 21, | ||
"introduced-x86_64": 21, | ||
}, | ||
... | ||
} | ||
""" | ||
symbols = {} | ||
versions = sorted(get_platform_versions()) | ||
for version in versions: | ||
for arch in ALL_ARCHITECTURES: | ||
symbols_dir = os.path.join( | ||
THIS_DIR, 'platforms', 'android-' + str(version), | ||
'arch-' + arch, 'symbols') | ||
if not os.path.exists(symbols_dir): | ||
logger().debug('Skipping non-existent %s', symbols_dir) | ||
continue | ||
|
||
logger().info('Processing android-%d arch-%s', version, arch) | ||
|
||
funcs_file_name = lib_name + '.so.functions.txt' | ||
funcs_file = os.path.join(symbols_dir, funcs_file_name) | ||
if os.path.exists(funcs_file): | ||
add_symbols(symbols, funcs_file, version, arch, is_var='false') | ||
|
||
vars_file_name = lib_name + '.so.variables.txt' | ||
vars_file = os.path.join(symbols_dir, vars_file_name) | ||
if os.path.exists(funcs_file): | ||
add_symbols(symbols, vars_file, version, arch, is_var='true') | ||
return symbols | ||
|
||
|
||
def parse_args(): | ||
"""Returns parsed command line arguments.""" | ||
parser = argparse.ArgumentParser() | ||
|
||
parser.add_argument('-v', '--verbose', action='count', default=0) | ||
|
||
parser.add_argument( | ||
'library_name', metavar='LIBRARY_NAME', | ||
help='Name of the library to create a database for.') | ||
|
||
return parser.parse_args() | ||
|
||
|
||
def main(): | ||
"""Program entry point.""" | ||
args = parse_args() | ||
os.chdir(THIS_DIR) | ||
|
||
verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) | ||
verbosity = args.verbose | ||
if verbosity > 2: | ||
verbosity = 2 | ||
logging.basicConfig(level=verbose_map[verbosity]) | ||
|
||
symbol_db = build_symbol_db(args.library_name) | ||
with open(args.library_name + '.so.json', 'w') as db_file: | ||
json.dump(symbol_db, db_file, indent=4, separators=(',', ': '), | ||
sort_keys=True) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
#!/usr/bin/env python | ||
# | ||
# Copyright (C) 2016 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. | ||
# | ||
"""Annotates an existing version script with data for the NDK.""" | ||
import argparse | ||
import json | ||
import logging | ||
import os | ||
import sys | ||
|
||
|
||
ALL_ARCHITECTURES = ( | ||
'arm', | ||
'arm64', | ||
'mips', | ||
'mips64', | ||
'x86', | ||
'x86_64', | ||
) | ||
|
||
|
||
def logger(): | ||
"""Returns the default logger for this module.""" | ||
return logging.getLogger(__name__) | ||
|
||
|
||
def verify_version_script(lines, json_db): | ||
"""Checks that every symbol in the NDK is in the version script.""" | ||
symbols = dict(json_db) | ||
for line in lines: | ||
if ';' in line: | ||
name, _ = line.split(';') | ||
name = name.strip() | ||
|
||
if name in symbols: | ||
del symbols[name] | ||
if len(symbols) > 0: | ||
for symbol in symbols.keys(): | ||
logger().error( | ||
'NDK symbol not present in version script: {}'.format(symbol)) | ||
sys.exit(1) | ||
|
||
|
||
def was_always_present(db_entry, arches): | ||
"""Returns whether the symbol has always been present or not.""" | ||
for arch in arches: | ||
is_64 = arch.endswith('64') | ||
introduced_tag = 'introduced-' + arch | ||
if introduced_tag not in db_entry: | ||
return False | ||
if is_64 and db_entry[introduced_tag] != 21: | ||
return False | ||
elif not is_64 and db_entry[introduced_tag] != 9: | ||
return False | ||
# Else we have the symbol in this arch and was introduced in the first | ||
# version of it. | ||
return True | ||
|
||
|
||
def get_common_introduced(db_entry, arches): | ||
"""Returns the common introduction API level or None. | ||
If the symbol was introduced in the same API level for all architectures, | ||
return that API level. If the symbol is not present in all architectures or | ||
was introduced to them at different times, return None. | ||
""" | ||
introduced = None | ||
for arch in arches: | ||
introduced_tag = 'introduced-' + arch | ||
if introduced_tag not in db_entry: | ||
return None | ||
if introduced is None: | ||
introduced = db_entry[introduced_tag] | ||
elif db_entry[introduced_tag] != introduced: | ||
return None | ||
# Else we have the symbol in this arch and it's the same introduction | ||
# level. Keep going. | ||
return introduced | ||
|
||
|
||
def annotate_symbol(line, json_db): | ||
"""Returns the line with NDK data appended.""" | ||
name_part, rest = line.split(';') | ||
name = name_part.strip() | ||
if name not in json_db: | ||
return line | ||
|
||
rest = rest.rstrip() | ||
tags = [] | ||
db_entry = json_db[name] | ||
if db_entry['is_var'] == 'true': | ||
tags.append('var') | ||
|
||
arches = ALL_ARCHITECTURES | ||
if '#' in rest: | ||
had_tags = True | ||
# Current tags aren't necessarily arch tags. Check them before using | ||
# them. | ||
_, old_tags = rest.split('#') | ||
arch_tags = [] | ||
for tag in old_tags.strip().split(' '): | ||
if tag in ALL_ARCHITECTURES: | ||
arch_tags.append(tag) | ||
if len(arch_tags) > 0: | ||
arches = arch_tags | ||
else: | ||
had_tags = False | ||
|
||
always_present = was_always_present(db_entry, arches) | ||
common_introduced = get_common_introduced(db_entry, arches) | ||
if always_present: | ||
# No need to tag things that have always been there. | ||
pass | ||
elif common_introduced is not None: | ||
tags.append('introduced={}'.format(common_introduced)) | ||
else: | ||
for arch in ALL_ARCHITECTURES: | ||
introduced_tag = 'introduced-' + arch | ||
if introduced_tag not in db_entry: | ||
continue | ||
tags.append( | ||
'{}={}'.format(introduced_tag, db_entry[introduced_tag])) | ||
|
||
if tags: | ||
if not had_tags: | ||
rest += ' #' | ||
rest += ' ' + ' '.join(tags) | ||
return name_part + ';' + rest + '\n' | ||
|
||
|
||
def annotate_version_script(version_script, json_db, lines): | ||
"""Rewrites a version script with NDK annotations.""" | ||
for line in lines: | ||
# Lines contain a semicolon iff they contain a symbol name. | ||
if ';' in line: | ||
version_script.write(annotate_symbol(line, json_db)) | ||
else: | ||
version_script.write(line) | ||
|
||
|
||
def parse_args(): | ||
"""Returns parsed command line arguments.""" | ||
parser = argparse.ArgumentParser() | ||
|
||
parser.add_argument( | ||
'data_file', metavar='DATA_FILE', type=os.path.realpath, | ||
help='Path to JSON DB generated by build_symbol_db.py.') | ||
|
||
parser.add_argument( | ||
'version_script', metavar='VERSION_SCRIPT', type=os.path.realpath, | ||
help='Version script to be annotated.') | ||
|
||
parser.add_argument('-v', '--verbose', action='count', default=0) | ||
|
||
return parser.parse_args() | ||
|
||
|
||
def main(): | ||
"""Program entry point.""" | ||
args = parse_args() | ||
|
||
verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) | ||
verbosity = args.verbose | ||
if verbosity > 2: | ||
verbosity = 2 | ||
|
||
logging.basicConfig(level=verbose_map[verbosity]) | ||
with open(args.data_file) as json_db_file: | ||
json_db = json.load(json_db_file) | ||
|
||
with open(args.version_script, 'r') as version_script: | ||
file_data = version_script.readlines() | ||
verify_version_script(file_data, json_db) | ||
with open(args.version_script, 'w') as version_script: | ||
annotate_version_script(version_script, json_db, file_data) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |