blob: 5d7bada00ba5ce13ebfe11efa95ff21841a77cca [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2021 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.
import argparse
import enum
import os
import sys
_TRACE_POINT = '__tracepoint_'
_TRACE_ITER = '__traceiter_'
class Status(enum.Enum):
UNKNOWN = 0
ALLOWED = 1
FORBIDDEN = 2
def _validate_symbols(symbol_list, symbols):
"""Validates Tracepoints consistenty in a given symbol list."""
missing = []
for symbol in symbols:
if not symbol.startswith((_TRACE_POINT, _TRACE_ITER)):
continue
if symbol.startswith(_TRACE_POINT):
other = symbol.replace(_TRACE_POINT, _TRACE_ITER)
if other not in symbols:
missing.append(other)
if symbol.startswith(_TRACE_ITER):
other = symbol.replace(_TRACE_ITER, _TRACE_POINT)
if other not in symbols:
missing.append(other)
if missing:
print(
'ERROR: Missing symbols: ',
missing,
'in ',
os.path.basename(symbol_list),
file=sys.stderr,
)
sys.exit(1)
def _read_config(allow_file, deny_file):
"""Reads symbol configuration file."""
config = {}
def read_file(status, config_file):
with open(config_file) as file:
for line in file:
fields = line.rstrip('\n').split(None, 1)
if not fields:
continue
symbol = fields[0]
if symbol.startswith('#'):
continue
reason = ''
if len(fields) > 1:
reason = fields[1]
if symbol in config:
print(f"symbol '{symbol}' duplicate configuration", file=sys.stderr)
continue
config[symbol] = (status, reason)
read_file(Status.FORBIDDEN, deny_file)
read_file(Status.ALLOWED, allow_file)
return config
def _read_symbol_lists(symbol_lists):
"""Reads libabigail symbol list files as a list of lines."""
all_lines = []
for symbol_list in symbol_lists:
with open(symbol_list) as sl:
lines = sl.read().splitlines(keepends=True)
all_lines.extend(lines)
# Separate files or at least protect against missing final newlines.
all_lines.append('\n')
# validate symbols by file
_validate_symbols(symbol_list, _get_symbols(lines))
return all_lines
def _get_symbols(lines):
"""Gets symbols from symbol list lines."""
symbols = set()
for line in lines:
stripped = line.strip()
if stripped and not stripped.startswith(('#', '[')):
symbols.add(stripped)
return symbols
def _check_symbols(config, symbols):
"""Checks symbols against configuration."""
report = []
for symbol in sorted(symbols):
if symbol in config:
status, reason = config[symbol]
report.append([symbol, status, reason])
else:
report.append([symbol, Status.UNKNOWN, ''])
return report
def main():
dir = os.path.dirname(sys.argv[0])
allow_file = os.path.join(dir, 'symbols.allow')
deny_file = os.path.join(dir, 'symbols.deny')
parser = argparse.ArgumentParser()
parser.add_argument(
'symbol_lists',
metavar='FILE',
type=str,
nargs='+',
help='a symbol list file',
)
parser.add_argument(
'--in-dir', required=True, help='where to find the symbol list files'
)
parser.add_argument(
'--out-dir',
required=True,
help='where to put the combined symbol list and report',
)
parser.add_argument(
'--out-file', required=True, help='combined symbol list file name'
)
parser.add_argument(
'--report-file', required=True, help='symbol list report file name'
)
parser.add_argument(
'--verbose', action='store_true', help='increase verbosity of the output'
)
args = parser.parse_args()
in_directory = args.in_dir
out_directory = args.out_dir
symbol_lists = [os.path.join(in_directory, s) for s in args.symbol_lists]
out_file = os.path.join(out_directory, args.out_file)
report_file = os.path.join(out_directory, args.report_file)
config = _read_config(allow_file, deny_file)
lines = _read_symbol_lists(symbol_lists)
symbols = _get_symbols(lines)
report = _check_symbols(config, symbols)
if args.verbose:
print('========================================================')
print(f'Generating ABI symbol list definition in {out_file}')
with open(out_file, 'w') as sl:
sl.writelines(lines)
exit_status = 0
if args.verbose:
print(f'Generating ABI symbol report {report_file}')
with open(report_file, 'w') as rf:
for symbol, status, reason in report:
rf.write(f'{symbol}\t{status.name}\t{reason}\n')
if status == Status.FORBIDDEN:
print(f"symbol '{symbol}' is not allowed: {reason}", file=sys.stderr)
exit_status = 1
return exit_status
if __name__ == '__main__':
sys.exit(main())