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 21from pathlib import Path 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, 32 symbol_list: TextIO, arch: Arch, api: int, llndk: bool, 33 apex: bool) -> None: 34 self.src_file = src_file 35 self.version_script = version_script 36 self.symbol_list = symbol_list 37 self.arch = arch 38 self.api = api 39 self.llndk = llndk 40 self.apex = apex 41 42 def write(self, versions: Iterable[Version]) -> None: 43 """Writes all symbol data to the output files.""" 44 self.symbol_list.write('[abi_symbol_list]\n') 45 for version in versions: 46 self.write_version(version) 47 48 def write_version(self, version: Version) -> None: 49 """Writes a single version block's data to the output files.""" 50 if symbolfile.should_omit_version(version, self.arch, self.api, 51 self.llndk, self.apex): 52 return 53 54 section_versioned = symbolfile.symbol_versioned_in_api( 55 version.tags, self.api) 56 version_empty = True 57 pruned_symbols = [] 58 for symbol in version.symbols: 59 if symbolfile.should_omit_symbol(symbol, self.arch, self.api, 60 self.llndk, self.apex): 61 continue 62 63 if symbolfile.symbol_versioned_in_api(symbol.tags, self.api): 64 version_empty = False 65 pruned_symbols.append(symbol) 66 67 if len(pruned_symbols) > 0: 68 if not version_empty and section_versioned: 69 self.version_script.write(version.name + ' {\n') 70 self.version_script.write(' global:\n') 71 for symbol in pruned_symbols: 72 emit_version = symbolfile.symbol_versioned_in_api( 73 symbol.tags, self.api) 74 if section_versioned and emit_version: 75 self.version_script.write(' ' + symbol.name + ';\n') 76 77 weak = '' 78 if 'weak' in symbol.tags: 79 weak = '__attribute__((weak)) ' 80 81 if 'var' in symbol.tags: 82 self.src_file.write(f'{weak}int {symbol.name} = 0;\n') 83 else: 84 self.src_file.write(f'{weak}void {symbol.name}() {{}}\n') 85 86 self.symbol_list.write(f'{symbol.name}\n') 87 88 if not version_empty and section_versioned: 89 base = '' if version.base is None else ' ' + version.base 90 self.version_script.write('}' + base + ';\n') 91 92 93def parse_args() -> argparse.Namespace: 94 """Parses and returns command line arguments.""" 95 parser = argparse.ArgumentParser() 96 97 def resolved_path(raw: str) -> Path: 98 """Returns a resolved Path for the given string.""" 99 return Path(raw).resolve() 100 101 parser.add_argument('-v', '--verbose', action='count', default=0) 102 103 parser.add_argument( 104 '--api', required=True, help='API level being targeted.') 105 parser.add_argument( 106 '--arch', choices=symbolfile.ALL_ARCHITECTURES, required=True, 107 help='Architecture being targeted.') 108 parser.add_argument( 109 '--llndk', action='store_true', help='Use the LLNDK variant.') 110 parser.add_argument( 111 '--apex', 112 action='store_true', 113 help='Use the APEX variant. Note: equivalent to --system-api.') 114 parser.add_argument( 115 '--system-api', 116 action='store_true', 117 dest='apex', 118 help='Use the SystemAPI variant. Note: equivalent to --apex.') 119 120 parser.add_argument('--api-map', 121 type=resolved_path, 122 required=True, 123 help='Path to the API level map JSON file.') 124 125 parser.add_argument('symbol_file', 126 type=resolved_path, 127 help='Path to symbol file.') 128 parser.add_argument('stub_src', 129 type=resolved_path, 130 help='Path to output stub source file.') 131 parser.add_argument('version_script', 132 type=resolved_path, 133 help='Path to output version script.') 134 parser.add_argument('symbol_list', 135 type=resolved_path, 136 help='Path to output abigail symbol list.') 137 138 return parser.parse_args() 139 140 141def main() -> None: 142 """Program entry point.""" 143 args = parse_args() 144 145 with args.api_map.open() as map_file: 146 api_map = json.load(map_file) 147 api = symbolfile.decode_api_level(args.api, api_map) 148 149 verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) 150 verbosity = args.verbose 151 if verbosity > 2: 152 verbosity = 2 153 logging.basicConfig(level=verbose_map[verbosity]) 154 155 with args.symbol_file.open() as symbol_file: 156 try: 157 versions = symbolfile.SymbolFileParser(symbol_file, api_map, 158 args.arch, api, args.llndk, 159 args.apex).parse() 160 except symbolfile.MultiplyDefinedSymbolError as ex: 161 sys.exit(f'{args.symbol_file}: error: {ex}') 162 163 with args.stub_src.open('w') as src_file: 164 with args.version_script.open('w') as version_script: 165 with args.symbol_list.open('w') as symbol_list: 166 generator = Generator(src_file, version_script, symbol_list, 167 args.arch, api, args.llndk, args.apex) 168 generator.write(versions) 169 170 171if __name__ == '__main__': 172 main() 173