Last active
February 9, 2022 13:25
-
-
Save jcfr/816febb58a9afdfe6e12675b95770fc9 to your computer and use it in GitHub Desktop.
Execuable to find all Qt modules referenced by a set of C++ source files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This executable allows to find all Qt modules referenced by a set of C++ source files. | |
A Qt module (e.g QtNetwork) aggregates together a collection of Qt classes and is | |
associated with a set of public and private headers. | |
For example, to select multiple files and identify associated Qt modules: | |
QT_INCLUDE_DIR=/path/to/Qt5.15.2/5.15.2/gcc_64/include | |
python FindQtModules.py $QT_INCLUDE_DIR $(find /path/to/Awesome/Lib/ -type f) | |
Limitations: | |
* Preprocessor directives are not evaluated. | |
* Tested with Qt 5.x | |
This code is licensed under the terms of the Apache License, Version 2.0. | |
""" | |
import argparse | |
import os | |
import re | |
def configure_parser(parser): | |
parser.add_argument( | |
"qt_include_dir", type=str, metavar="QT_INCLUDE_DIR", | |
help="Path to the Qt include directory (e.g /path/to/Qt5.15.2/5.15.2/gcc_64/include)" | |
) | |
parser.add_argument( | |
"source_files", type=str, nargs="+", metavar="SOURCE_FILE", | |
help="Path of the source file to inspect" | |
) | |
def collect_qt_headers(qt_include_dir): | |
"""Return a map of Qt header to Qt module name. | |
The Qt modules are identified as the direct sub-directory of ``qt_include_dir``. | |
""" | |
qt_modules = os.listdir(qt_include_dir) | |
if "QtCore" not in qt_modules: | |
raise RuntimeException("Invalid QT_INCLUDE_DIR: Missing QtCore sub-directory") | |
qt_header_without_extension_pattern = re.compile(r'^[qQ]\w+(?!\.h)$') | |
qt_headers_to_modules = dict() | |
for qt_module in qt_modules: | |
for dirname, dirnames, filenames in os.walk(os.path.join(qt_include_dir, qt_module)): | |
for filename in filenames: | |
if filename.endswith(".h") or qt_header_without_extension_pattern.match(filename): | |
qt_headers_to_modules[filename] = qt_module | |
return qt_headers_to_modules | |
def is_binary_file(filepath): | |
"""Return True if ``filepath`` exists and is a binary file. | |
Adapted from https://stackoverflow.com/questions/898669/how-can-i-detect-if-a-file-is-binary-non-text-in-python/7392391#7392391 | |
""" | |
textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f}) | |
is_binary_string = lambda bytes: bool(bytes.translate(None, textchars)) | |
with open(filepath, 'rb') as fp: | |
return is_binary_string(fp.read(1024)) | |
def inspect_source_file(source_file, qt_headers_to_modules): | |
"""Return a map of Qt module to header referenced by ``source_file``. | |
""" | |
referenced_qt_modules = dict() | |
include_pattern = re.compile(r'\#\s*include\s*[\<\"]([\w\.]+)[\>\"]') | |
try: | |
with open(source_file) as contents: | |
for line in contents: | |
match_object = include_pattern.search(line) | |
if match_object is not None: | |
header_filename = match_object.group(1) | |
if header_filename in qt_headers_to_modules: | |
qt_module = qt_headers_to_modules[header_filename] | |
referenced_qt_modules[qt_module] = header_filename | |
except UnicodeDecodeError as exc: | |
print(f"Ignoring {source_file}: {exc}") | |
return referenced_qt_modules | |
def main(argv=None): | |
parser = argparse.ArgumentParser(description=__doc__) | |
configure_parser(parser) | |
args = parser.parse_args(argv[1:] if argv else None) | |
referenced_qt_modules = dict() | |
qt_headers_to_modules = collect_qt_headers(args.qt_include_dir) | |
for source_file in args.source_files: | |
if is_binary_file(source_file): | |
continue | |
referenced_qt_modules = {**referenced_qt_modules, **inspect_source_file(source_file, qt_headers_to_modules)} | |
for qt_module in referenced_qt_modules: | |
print(qt_module) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment