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