167 lines
5.0 KiB
Python
Executable File
167 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
##===--- iwyu-mapgen-qt.py ------------------------------------------------===##
|
|
#
|
|
# The LLVM Compiler Infrastructure
|
|
#
|
|
# This file is distributed under the University of Illinois Open Source
|
|
# License. See LICENSE.TXT for details.
|
|
#
|
|
##===----------------------------------------------------------------------===##
|
|
|
|
""" Generates mappings for Qt API headers.
|
|
|
|
Qt has quite a strong module convention, where there's one public header for
|
|
every module class, e.g.:
|
|
|
|
- For a module X there's typically...
|
|
- ... a set of classes called QtXy, QtXyz...
|
|
- ... and a corresponding public header for each called QtXy, QtXyz...
|
|
- ... and possibly a set of private headers called qtxy.h, qtxyz.h...
|
|
|
|
Use these conventions to generate symbol and include mappings for the entire Qt
|
|
tree.
|
|
"""
|
|
|
|
import argparse
|
|
import glob
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
|
|
OUTFILEHDR = ("# Do not edit! This file was generated by the script %s." %
|
|
os.path.basename(__file__))
|
|
|
|
QOBJECT_SYMBOLS = [
|
|
"QObjectList",
|
|
"qFindChildren",
|
|
"qobject_cast",
|
|
"QT_NO_NARROWING_CONVERSIONS_IN_CONNECT",
|
|
"Q_CLASSINFO",
|
|
"Q_DISABLE_COPY",
|
|
"Q_DISABLE_COPY_MOVE",
|
|
"Q_DISABLE_MOVE",
|
|
"Q_EMIT",
|
|
"Q_ENUM",
|
|
"Q_ENUM_NS",
|
|
"Q_FLAG",
|
|
"Q_FLAG_NS",
|
|
"Q_GADGET",
|
|
"Q_INTERFACES",
|
|
"Q_INVOKABLE",
|
|
"Q_NAMESPACE",
|
|
"Q_NAMESPACE_EXPORT",
|
|
"Q_OBJECT",
|
|
"Q_PROPERTY",
|
|
"Q_REVISION",
|
|
"Q_SET_OBJECT_NAME",
|
|
"Q_SIGNAL",
|
|
"Q_SIGNALS",
|
|
"Q_SLOT",
|
|
"Q_SLOTS",
|
|
"emit",
|
|
"slots",
|
|
"signals",
|
|
"SIGNAL",
|
|
"SLOT",
|
|
]
|
|
|
|
|
|
class QtHeader(object):
|
|
""" Carry data associated with a Qt header """
|
|
def __init__(self, headername):
|
|
self.headername = headername
|
|
self.classname = os.path.basename(headername)
|
|
self.modulename = os.path.basename(os.path.dirname(headername))
|
|
self._private_headers = None
|
|
|
|
def get_private_headers(self):
|
|
""" Return a list of headernames included by this header """
|
|
if self._private_headers is None:
|
|
with open(self.headername, 'r') as headerfile:
|
|
included = re.findall(r'#include "(.*)\.h"', headerfile.read())
|
|
self._private_headers = list(included)
|
|
return self._private_headers
|
|
|
|
|
|
def generate_imp_lines(symbols_map, includes_map):
|
|
""" Generate json-formatted strings in .imp format.
|
|
|
|
This should ideally return a jsonable structure instead, and use json.dump
|
|
to write it to the output file directly. But there doesn't seem to be a
|
|
simple way to convince Python's json library to generate a "packed"
|
|
formatting, it always prefers to wrap dicts onto multiple lines.
|
|
|
|
Cheat, and use json.dumps for escaping and build a string instead.
|
|
"""
|
|
def jsonline(mapping, indent):
|
|
return (indent * " ") + json.dumps(mapping)
|
|
|
|
for symbol, header in symbols_map:
|
|
map_to = "<" + header + ">"
|
|
yield jsonline({"symbol": [symbol, "private", map_to, "public"]},
|
|
indent=2)
|
|
|
|
for module, include, header in includes_map:
|
|
# Use regex map-from to match both quoted and angled includes and
|
|
# optional directory prefix (e.g. <QtCore/qnamespace.h> is equivalent to
|
|
# "qnamespace.h").
|
|
map_from = r'@["<](%s/)?%s\.h[">]' % (module, include)
|
|
map_to = "<" + header + ">"
|
|
yield jsonline({"include": [map_from, "private", map_to, "public"]},
|
|
indent=2)
|
|
|
|
|
|
def add_mapping_rules(header, symbols_map, includes_map):
|
|
""" Add symbol and include mappings for a Qt module. """
|
|
symbols_map += [(header.classname, header.classname)]
|
|
for include in header.get_private_headers():
|
|
includes_map += [(header.modulename, include, header.classname)]
|
|
|
|
|
|
def main(qtroot):
|
|
""" Entry point. """
|
|
symbols_map = []
|
|
includes_map = []
|
|
deferred_headers = []
|
|
|
|
# Add manual overrides.
|
|
symbols_map += [("qDebug", "QtGlobal")]
|
|
symbols_map += [(symbol, "QObject") for symbol in QOBJECT_SYMBOLS]
|
|
includes_map += [("QtCore", "qnamespace", "Qt")]
|
|
|
|
# Collect mapping information from Qt directory tree.
|
|
headers = glob.glob(os.path.join(qtroot, '**/*[!.h]'))
|
|
for header in headers:
|
|
if os.path.isdir(header):
|
|
continue
|
|
|
|
header = QtHeader(header)
|
|
if header.classname == "QInternal":
|
|
continue
|
|
|
|
if header.classname == header.modulename:
|
|
deferred_headers.append(header)
|
|
else:
|
|
add_mapping_rules(header, symbols_map, includes_map)
|
|
|
|
for header in deferred_headers:
|
|
add_mapping_rules(header, symbols_map, includes_map)
|
|
|
|
# Print mappings
|
|
print(OUTFILEHDR)
|
|
print("[")
|
|
print(",\n".join(generate_imp_lines(symbols_map, includes_map)))
|
|
print("]")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("qtroot",
|
|
help="Qt include root (e.g. /usr/include/.../qt5)")
|
|
args = parser.parse_args()
|
|
sys.exit(main(args.qtroot))
|