Revision control

Copy as Markdown

Other Tools

#!/usr/bin/python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Parse rnp/rnp.h header file and build a symbols file suitable
for use with mozbuild.
This script is meant to be run when the public C API of librnp adds or removes functions so that
they can be exported by the shared library.
Limitations: The regex that captures the function name is very basic and may need adjusting if
the third_party/rnp/include/rnp/rnp.h format changes too much.
Also note that APIs that are marked deprecated are not checked for.
Dependencies: Only Python 3
Running:
python3 rnp_symbols.py [-h] [rnp.h path] [rnp.symbols path]
Both file path arguments are optional. By default, the header file will be
read from "comm/third_party/rnp/include/rnp/rnp.h" and the symbols file will
be written to "comm/third_party/rnp/rnp.symbols".
Path arguments are relative to the current working directory, the defaults
will be determined based on the location of this script.
Either path argument can be '-' to use stdin or stdout respectively.
"""
import argparse
import os
import re
import sys
HERE = os.path.dirname(__file__)
TOPSRCDIR = os.path.abspath(os.path.join(HERE, "../../../../"))
THIRD_SRCDIR = os.path.join(TOPSRCDIR, "comm/third_party")
HEADER_FILE_REL = "rnp/include/rnp/rnp.h"
HEADER_FILE = os.path.join(THIRD_SRCDIR, HEADER_FILE_REL)
SYMBOLS_FILE_REL = "rnp/rnp.symbols"
SYMBOLS_FILE = os.path.join(THIRD_SRCDIR, SYMBOLS_FILE_REL)
FUNC_DECL_RE = re.compile(r"^RNP_API\s+.*?([a-zA-Z0-9_]+)\(.*$")
class FileArg:
"""Based on argparse.FileType from the Python standard library.
Modified to not open the filehandles until the open() method is
called.
"""
def __init__(self, mode="r"):
self._mode = mode
self._fp = None
self._file = None
def __call__(self, string):
# the special argument "-" means sys.std{in,out}
if string == "-":
if "r" in self._mode:
self._fp = sys.stdin.buffer if "b" in self._mode else sys.stdin
elif "w" in self._mode:
self._fp = sys.stdout.buffer if "b" in self._mode else sys.stdout
else:
raise ValueError(f"Invalid mode {self._mode} for stdin/stdout")
else:
if "r" in self._mode:
if not os.path.isfile(string):
raise ValueError(f"Cannot read file {string}, does not exist.")
elif "w" in self._mode:
if not os.access(string, os.W_OK):
raise ValueError(f"Cannot write file {string}, permission denied.")
self._file = string
return self
def open(self):
if self._fp:
return self._fp
return open(self._file, self._mode)
def get_func_name(line):
"""
Extract the function name from a RNP_API function declaration.
Examples:
RNP_API rnp_result_t rnp_enable_debug(const char *file);
RNP_API rnp_result_t rnp_ffi_create(rnp_ffi_t * ffi,
"""
m = FUNC_DECL_RE.match(line)
return m.group(1)
def extract_func_defs(filearg):
"""
Look for RNP_API in the header file to find the names of the symbols that should be exported
"""
with filearg.open() as fp:
for line in fp:
if line.startswith("RNP_API") and "RNP_DEPRECATED" not in line:
func_name = get_func_name(line)
yield func_name
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Update rnp.symbols file from rnp.h",
epilog="To use stdin or stdout pass '-' for the argument.",
)
parser.add_argument(
"header_file",
default=HEADER_FILE,
type=FileArg("r"),
nargs="?",
help=f"input path to rnp.h header file (default: {HEADER_FILE_REL})",
)
parser.add_argument(
"symbols_file",
default=SYMBOLS_FILE,
type=FileArg("w"),
nargs="?",
help=f"output path to symbols file (default: {SYMBOLS_FILE_REL})",
)
args = parser.parse_args()
with args.symbols_file.open() as out_fp:
for symbol in sorted(list(extract_func_defs(args.header_file))):
out_fp.write(f"{symbol}\n")