1#!/usr/bin/env python 2# 3# Copyright (C) 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Generates source for stub shared libraries for the NDK.""" 18import argparse 19import json 20import logging 21import os 22import sys 23from typing import Iterable, TextIO 24 25import symbolfile 26from symbolfile import Arch, Version 27 28 29class Generator: 30 """Output generator that writes stub source files and version scripts.""" 31 def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch, 32 api: int, llndk: bool, apex: bool) -> None: 33 self.src_file = src_file 34 self.version_script = version_script 35 self.arch = arch 36 self.api = api 37 self.llndk = llndk 38 self.apex = apex 39 40 def write(self, versions: Iterable[Version]) -> None: 41 """Writes all symbol data to the output files.""" 42 for version in versions: 43 self.write_version(version) 44 45 def write_version(self, version: Version) -> None: 46 """Writes a single version block's data to the output files.""" 47 if symbolfile.should_omit_version(version, self.arch, self.api, 48 self.llndk, self.apex): 49 return 50 51 section_versioned = symbolfile.symbol_versioned_in_api( 52 version.tags, self.api) 53 version_empty = True 54 pruned_symbols = [] 55 for symbol in version.symbols: 56 if symbolfile.should_omit_symbol(symbol, self.arch, self.api, 57 self.llndk, self.apex): 58 continue 59 60 if symbolfile.symbol_versioned_in_api(symbol.tags, self.api): 61 version_empty = False 62 pruned_symbols.append(symbol) 63 64 if len(pruned_symbols) > 0: 65 if not version_empty and section_versioned: 66 self.version_script.write(version.name + ' {\n') 67 self.version_script.write(' global:\n') 68 for symbol in pruned_symbols: 69 emit_version = symbolfile.symbol_versioned_in_api( 70 symbol.tags, self.api) 71 if section_versioned and emit_version: 72 self.version_script.write(' ' + symbol.name + ';\n') 73 74 weak = '' 75 if 'weak' in symbol.tags: 76 weak = '__attribute__((weak)) ' 77 78 if 'var' in symbol.tags: 79 self.src_file.write('{}int {} = 0;\n'.format( 80 weak, symbol.name)) 81 else: 82 self.src_file.write('{}void {}() {{}}\n'.format( 83 weak, symbol.name)) 84 85 if not version_empty and section_versioned: 86 base = '' if version.base is None else ' ' + version.base 87 self.version_script.write('}' + base + ';\n') 88 89 90def parse_args() -> argparse.Namespace: 91 """Parses and returns command line arguments.""" 92 parser = argparse.ArgumentParser() 93 94 parser.add_argument('-v', '--verbose', action='count', default=0) 95 96 parser.add_argument( 97 '--api', required=True, help='API level being targeted.') 98 parser.add_argument( 99 '--arch', choices=symbolfile.ALL_ARCHITECTURES, required=True, 100 help='Architecture being targeted.') 101 parser.add_argument( 102 '--llndk', action='store_true', help='Use the LLNDK variant.') 103 parser.add_argument( 104 '--apex', action='store_true', help='Use the APEX variant.') 105 106 # https://github.com/python/mypy/issues/1317 107 # mypy has issues with using os.path.realpath as an argument here. 108 parser.add_argument( 109 '--api-map', 110 type=os.path.realpath, # type: ignore 111 required=True, 112 help='Path to the API level map JSON file.') 113 114 parser.add_argument( 115 'symbol_file', 116 type=os.path.realpath, # type: ignore 117 help='Path to symbol file.') 118 parser.add_argument( 119 'stub_src', 120 type=os.path.realpath, # type: ignore 121 help='Path to output stub source file.') 122 parser.add_argument( 123 'version_script', 124 type=os.path.realpath, # type: ignore 125 help='Path to output version script.') 126 127 return parser.parse_args() 128 129 130def main() -> None: 131 """Program entry point.""" 132 args = parse_args() 133 134 with open(args.api_map) as map_file: 135 api_map = json.load(map_file) 136 api = symbolfile.decode_api_level(args.api, api_map) 137 138 verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) 139 verbosity = args.verbose 140 if verbosity > 2: 141 verbosity = 2 142 logging.basicConfig(level=verbose_map[verbosity]) 143 144 with open(args.symbol_file) as symbol_file: 145 try: 146 versions = symbolfile.SymbolFileParser(symbol_file, api_map, 147 args.arch, api, args.llndk, 148 args.apex).parse() 149 except symbolfile.MultiplyDefinedSymbolError as ex: 150 sys.exit('{}: error: {}'.format(args.symbol_file, ex)) 151 152 with open(args.stub_src, 'w') as src_file: 153 with open(args.version_script, 'w') as version_file: 154 generator = Generator(src_file, version_file, args.arch, api, 155 args.llndk, args.apex) 156 generator.write(versions) 157 158 159if __name__ == '__main__': 160 main() 161