Convert qtwaylandscanner into a class

Encapsulate what the scanner needs of functions and state into a class,
Scanner, instead of keeping everything in the global namespace.

Change-Id: Idd4b412bb7f709f24c86abe82b135c09b7985878
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2018-08-06 17:30:26 +02:00 committed by Johan Helsing
parent 0f9595ae09
commit 83abc8bf60

View File

@ -41,92 +41,152 @@
#include <QFile>
#include <QXmlStreamReader>
enum Option {
ClientHeader,
ServerHeader,
ClientCode,
ServerCode
} option;
bool isServerSide()
class Scanner
{
return option == ServerHeader || option == ServerCode;
public:
explicit Scanner() {}
~Scanner() { delete m_xml; }
bool parseArguments(int argc, char **argv);
void printUsage();
bool process();
void printErrors();
private:
struct WaylandEnumEntry {
QByteArray name;
QByteArray value;
QByteArray summary;
};
struct WaylandEnum {
QByteArray name;
QList<WaylandEnumEntry> entries;
};
struct WaylandArgument {
QByteArray name;
QByteArray type;
QByteArray interface;
QByteArray summary;
bool allowNull;
};
struct WaylandEvent {
bool request;
QByteArray name;
QByteArray type;
QList<WaylandArgument> arguments;
};
struct WaylandInterface {
QByteArray name;
int version;
QList<WaylandEnum> enums;
QList<WaylandEvent> events;
QList<WaylandEvent> requests;
};
bool isServerSide();
bool parseOption(const char *str);
QByteArray byteArrayValue(const QXmlStreamReader &xml, const char *name);
int intValue(const QXmlStreamReader &xml, const char *name, int defaultValue = 0);
bool boolValue(const QXmlStreamReader &xml, const char *name);
WaylandEvent readEvent(QXmlStreamReader &xml, bool request);
Scanner::WaylandEnum readEnum(QXmlStreamReader &xml);
Scanner::WaylandInterface readInterface(QXmlStreamReader &xml);
QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &interface);
QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray);
const Scanner::WaylandArgument *newIdArgument(const QList<WaylandArgument> &arguments);
void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource = false);
void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent = true);
void printEnums(const QList<WaylandEnum> &enums);
QByteArray stripInterfaceName(const QByteArray &name);
bool ignoreInterface(const QByteArray &name);
enum Option {
ClientHeader,
ServerHeader,
ClientCode,
ServerCode
} m_option;
QByteArray m_protocolName;
QByteArray m_protocolFilePath;
QByteArray m_scannerName;
QByteArray m_headerPath;
QByteArray m_prefix;
QXmlStreamReader *m_xml = nullptr;
};
bool Scanner::parseArguments(int argc, char **argv)
{
QByteArray m_scannerName = argv[0];
if (argc <= 2 || !parseOption(argv[1]))
return false;
m_protocolFilePath = QByteArray(argv[2]);
if (argc >= 4)
m_headerPath = QByteArray(argv[3]);
if (argc == 5)
m_prefix = QByteArray(argv[4]);
return true;
}
QByteArray protocolName;
void Scanner::printUsage()
{
fprintf(stderr, "Usage: %s [client-header|server-header|client-code|server-code] specfile [header-path] [prefix]\n", m_scannerName.constData());
}
bool parseOption(const char *str, Option *option)
bool Scanner::isServerSide()
{
return m_option == ServerHeader || m_option == ServerCode;
}
bool Scanner::parseOption(const char *str)
{
if (str == QLatin1String("client-header"))
*option = ClientHeader;
m_option = ClientHeader;
else if (str == QLatin1String("server-header"))
*option = ServerHeader;
m_option = ServerHeader;
else if (str == QLatin1String("client-code"))
*option = ClientCode;
m_option = ClientCode;
else if (str == QLatin1String("server-code"))
*option = ServerCode;
m_option = ServerCode;
else
return false;
return true;
}
struct WaylandEnumEntry {
QByteArray name;
QByteArray value;
QByteArray summary;
};
struct WaylandEnum {
QByteArray name;
QList<WaylandEnumEntry> entries;
};
struct WaylandArgument {
QByteArray name;
QByteArray type;
QByteArray interface;
QByteArray summary;
bool allowNull;
};
struct WaylandEvent {
bool request;
QByteArray name;
QByteArray type;
QList<WaylandArgument> arguments;
};
struct WaylandInterface {
QByteArray name;
int version;
QList<WaylandEnum> enums;
QList<WaylandEvent> events;
QList<WaylandEvent> requests;
};
QByteArray byteArrayValue(const QXmlStreamReader &xml, const char *name)
QByteArray Scanner::byteArrayValue(const QXmlStreamReader &xml, const char *name)
{
if (xml.attributes().hasAttribute(name))
return xml.attributes().value(name).toUtf8();
return QByteArray();
}
int intValue(const QXmlStreamReader &xml, const char *name, int defaultValue = 0)
int Scanner::intValue(const QXmlStreamReader &xml, const char *name, int defaultValue)
{
bool ok;
int result = byteArrayValue(xml, name).toInt(&ok);
return ok ? result : defaultValue;
}
bool boolValue(const QXmlStreamReader &xml, const char *name)
bool Scanner::boolValue(const QXmlStreamReader &xml, const char *name)
{
return byteArrayValue(xml, name) == "true";
}
WaylandEvent readEvent(QXmlStreamReader &xml, bool request)
Scanner::WaylandEvent Scanner::readEvent(QXmlStreamReader &xml, bool request)
{
WaylandEvent event;
event.request = request;
@ -148,7 +208,7 @@ WaylandEvent readEvent(QXmlStreamReader &xml, bool request)
return event;
}
WaylandEnum readEnum(QXmlStreamReader &xml)
Scanner::WaylandEnum Scanner::readEnum(QXmlStreamReader &xml)
{
WaylandEnum result;
result.name = byteArrayValue(xml, "name");
@ -168,7 +228,7 @@ WaylandEnum readEnum(QXmlStreamReader &xml)
return result;
}
WaylandInterface readInterface(QXmlStreamReader &xml)
Scanner::WaylandInterface Scanner::readInterface(QXmlStreamReader &xml)
{
WaylandInterface interface;
interface.name = byteArrayValue(xml, "name");
@ -188,7 +248,7 @@ WaylandInterface readInterface(QXmlStreamReader &xml)
return interface;
}
QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &interface)
QByteArray Scanner::waylandToCType(const QByteArray &waylandType, const QByteArray &interface)
{
if (waylandType == "string")
return "const char *";
@ -212,7 +272,7 @@ QByteArray waylandToCType(const QByteArray &waylandType, const QByteArray &inter
return waylandType;
}
QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray)
QByteArray Scanner::waylandToQtType(const QByteArray &waylandType, const QByteArray &interface, bool cStyleArray)
{
if (waylandType == "string")
return "const QString &";
@ -222,7 +282,7 @@ QByteArray waylandToQtType(const QByteArray &waylandType, const QByteArray &inte
return waylandToCType(waylandType, interface);
}
const WaylandArgument *newIdArgument(const QList<WaylandArgument> &arguments)
const Scanner::WaylandArgument *Scanner::newIdArgument(const QList<WaylandArgument> &arguments)
{
for (int i = 0; i < arguments.size(); ++i) {
if (arguments.at(i).type == "new_id")
@ -231,7 +291,7 @@ const WaylandArgument *newIdArgument(const QList<WaylandArgument> &arguments)
return nullptr;
}
void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource = false)
void Scanner::printEvent(const WaylandEvent &e, bool omitNames, bool withResource)
{
printf("%s(", e.name.constData());
bool needsComma = false;
@ -274,7 +334,7 @@ void printEvent(const WaylandEvent &e, bool omitNames = false, bool withResource
printf(")");
}
void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent = true)
void Scanner::printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName, bool deepIndent)
{
const char *indent = deepIndent ? " " : "";
printf("handle_%s(\n", e.name.constData());
@ -299,7 +359,7 @@ void printEventHandlerSignature(const WaylandEvent &e, const char *interfaceName
printf(")");
}
void printEnums(const QList<WaylandEnum> &enums)
void Scanner::printEnums(const QList<WaylandEnum> &enums)
{
for (int i = 0; i < enums.size(); ++i) {
printf("\n");
@ -318,66 +378,73 @@ void printEnums(const QList<WaylandEnum> &enums)
}
}
QByteArray stripInterfaceName(const QByteArray &name, const QByteArray &prefix)
QByteArray Scanner::stripInterfaceName(const QByteArray &name)
{
if (!prefix.isEmpty() && name.startsWith(prefix))
return name.mid(prefix.size());
if (!m_prefix.isEmpty() && name.startsWith(m_prefix))
return name.mid(m_prefix.size());
if (name.startsWith("qt_") || name.startsWith("wl_"))
return name.mid(3);
return name;
}
bool ignoreInterface(const QByteArray &name)
bool Scanner::ignoreInterface(const QByteArray &name)
{
return name == "wl_display"
|| (isServerSide() && name == "wl_registry");
}
void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArray &prefix)
bool Scanner::process()
{
if (!xml.readNextStartElement())
return;
if (xml.name() != "protocol") {
xml.raiseError(QStringLiteral("The file is not a wayland protocol file."));
return;
QFile file(m_protocolFilePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
fprintf(stderr, "Unable to open file %s\n", m_protocolFilePath.constData());
return false;
}
protocolName = byteArrayValue(xml, "name");
m_xml = new QXmlStreamReader(&file);
if (!m_xml->readNextStartElement())
return false;
if (protocolName.isEmpty()) {
xml.raiseError(QStringLiteral("Missing protocol name."));
return;
if (m_xml->name() != "protocol") {
m_xml->raiseError(QStringLiteral("The file is not a wayland protocol file."));
return false;
}
m_protocolName = byteArrayValue(*m_xml, "name");
if (m_protocolName.isEmpty()) {
m_xml->raiseError(QStringLiteral("Missing protocol name."));
return false;
}
//We should convert - to _ so that the preprocessor wont generate code which will lead to unexpected behavior
//However, the wayland-scanner doesn't do so we will do the same for now
//QByteArray preProcessorProtocolName = QByteArray(protocolName).replace('-', '_').toUpper();
QByteArray preProcessorProtocolName = QByteArray(protocolName).toUpper();
//QByteArray preProcessorProtocolName = QByteArray(m_protocolName).replace('-', '_').toUpper();
QByteArray preProcessorProtocolName = QByteArray(m_protocolName).toUpper();
QList<WaylandInterface> interfaces;
while (xml.readNextStartElement()) {
if (xml.name() == "interface")
interfaces << readInterface(xml);
while (m_xml->readNextStartElement()) {
if (m_xml->name() == "interface")
interfaces << readInterface(*m_xml);
else
xml.skipCurrentElement();
m_xml->skipCurrentElement();
}
if (xml.hasError())
return;
if (m_xml->hasError())
return false;
if (option == ServerHeader) {
if (m_option == ServerHeader) {
QByteArray inclusionGuard = QByteArray("QT_WAYLAND_SERVER_") + preProcessorProtocolName.constData();
printf("#ifndef %s\n", inclusionGuard.constData());
printf("#define %s\n", inclusionGuard.constData());
printf("\n");
printf("#include \"wayland-server.h\"\n");
if (headerPath.isEmpty())
printf("#include \"wayland-%s-server-protocol.h\"\n", QByteArray(protocolName).replace('_', '-').constData());
if (m_headerPath.isEmpty())
printf("#include \"wayland-%s-server-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData());
else
printf("#include <%s/wayland-%s-server-protocol.h>\n", headerPath.constData(), QByteArray(protocolName).replace('_', '-').constData());
printf("#include <%s/wayland-%s-server-protocol.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData());
printf("#include <QByteArray>\n");
printf("#include <QMultiMap>\n");
printf("#include <QString>\n");
@ -396,7 +463,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n");
printf("QT_WARNING_DISABLE_CLANG(\"-Wmissing-field-initializers\")\n");
QByteArray serverExport;
if (headerPath.size()) {
if (m_headerPath.size()) {
serverExport = QByteArray("Q_WAYLAND_SERVER_") + preProcessorProtocolName + "_EXPORT";
printf("\n");
printf("#if !defined(%s)\n", serverExport.constData());
@ -418,7 +485,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
const char *interfaceName = interface.name.constData();
QByteArray stripped = stripInterfaceName(interface.name, prefix);
QByteArray stripped = stripInterfaceName(interface.name);
const char *interfaceNameStripped = stripped.constData();
printf(" class %s %s\n {\n", serverExport.constData(), interfaceName);
@ -547,11 +614,11 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
printf("#endif\n");
}
if (option == ServerCode) {
if (headerPath.isEmpty())
printf("#include \"qwayland-server-%s.h\"\n", QByteArray(protocolName).replace('_', '-').constData());
if (m_option == ServerCode) {
if (m_headerPath.isEmpty())
printf("#include \"qwayland-server-%s.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData());
else
printf("#include <%s/qwayland-server-%s.h>\n", headerPath.constData(), QByteArray(protocolName).replace('_', '-').constData());
printf("#include <%s/qwayland-server-%s.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData());
printf("\n");
printf("QT_BEGIN_NAMESPACE\n");
printf("QT_WARNING_PUSH\n");
@ -573,7 +640,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
const char *interfaceName = interface.name.constData();
QByteArray stripped = stripInterfaceName(interface.name, prefix);
QByteArray stripped = stripInterfaceName(interface.name);
const char *interfaceNameStripped = stripped.constData();
printf(" %s::%s(struct ::wl_client *client, int id, int version)\n", interfaceName, interfaceName);
@ -860,15 +927,15 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
printf("QT_END_NAMESPACE\n");
}
if (option == ClientHeader) {
if (m_option == ClientHeader) {
QByteArray inclusionGuard = QByteArray("QT_WAYLAND_") + preProcessorProtocolName.constData();
printf("#ifndef %s\n", inclusionGuard.constData());
printf("#define %s\n", inclusionGuard.constData());
printf("\n");
if (headerPath.isEmpty())
printf("#include \"wayland-%s-client-protocol.h\"\n", QByteArray(protocolName).replace('_', '-').constData());
if (m_headerPath.isEmpty())
printf("#include \"wayland-%s-client-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData());
else
printf("#include <%s/wayland-%s-client-protocol.h>\n", headerPath.constData(), QByteArray(protocolName).replace('_', '-').constData());
printf("#include <%s/wayland-%s-client-protocol.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData());
printf("#include <QByteArray>\n");
printf("#include <QString>\n");
printf("\n");
@ -878,7 +945,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
QByteArray clientExport;
if (headerPath.size()) {
if (m_headerPath.size()) {
clientExport = QByteArray("Q_WAYLAND_CLIENT_") + preProcessorProtocolName + "_EXPORT";
printf("\n");
printf("#if !defined(%s)\n", clientExport.constData());
@ -899,7 +966,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
const char *interfaceName = interface.name.constData();
QByteArray stripped = stripInterfaceName(interface.name, prefix);
QByteArray stripped = stripInterfaceName(interface.name);
const char *interfaceNameStripped = stripped.constData();
printf(" class %s %s\n {\n", clientExport.constData(), interfaceName);
@ -978,11 +1045,11 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
printf("#endif\n");
}
if (option == ClientCode) {
if (headerPath.isEmpty())
printf("#include \"qwayland-%s.h\"\n", QByteArray(protocolName).replace('_', '-').constData());
if (m_option == ClientCode) {
if (m_headerPath.isEmpty())
printf("#include \"qwayland-%s.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData());
else
printf("#include <%s/qwayland-%s.h>\n", headerPath.constData(), QByteArray(protocolName).replace('_', '-').constData());
printf("#include <%s/qwayland-%s.h>\n", m_headerPath.constData(), QByteArray(m_protocolName).replace('_', '-').constData());
printf("\n");
printf("QT_BEGIN_NAMESPACE\n");
printf("QT_WARNING_PUSH\n");
@ -997,7 +1064,7 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
const char *interfaceName = interface.name.constData();
QByteArray stripped = stripInterfaceName(interface.name, prefix);
QByteArray stripped = stripInterfaceName(interface.name);
const char *interfaceNameStripped = stripped.constData();
bool hasEvents = !interface.events.isEmpty();
@ -1172,34 +1239,30 @@ void process(QXmlStreamReader &xml, const QByteArray &headerPath, const QByteArr
printf("QT_WARNING_POP\n");
printf("QT_END_NAMESPACE\n");
}
return true;
}
void Scanner::printErrors()
{
if (m_xml->hasError())
fprintf(stderr, "XML error: %s\nLine %lld, column %lld\n", m_xml->errorString().toLocal8Bit().constData(), m_xml->lineNumber(), m_xml->columnNumber());
}
int main(int argc, char **argv)
{
if (argc <= 2 || !parseOption(argv[1], &option)) {
fprintf(stderr, "Usage: %s [client-header|server-header|client-code|server-code] specfile [header-path] [prefix]\n", argv[0]);
return 1;
}
QCoreApplication app(argc, argv);
Scanner scanner;
QByteArray headerPath;
if (argc >= 4)
headerPath = QByteArray(argv[3]);
QByteArray prefix;
if (argc == 5)
prefix = QByteArray(argv[4]);
QFile file(argv[2]);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
fprintf(stderr, "Unable to open file %s\n", argv[2]);
return 1;
if (!scanner.parseArguments(argc, argv)) {
scanner.printUsage();
return EXIT_FAILURE;
}
QXmlStreamReader xml(&file);
process(xml, headerPath, prefix);
if (xml.hasError()) {
fprintf(stderr, "XML error: %s\nLine %lld, column %lld\n", xml.errorString().toLocal8Bit().constData(), xml.lineNumber(), xml.columnNumber());
return 1;
if (!scanner.process()) {
scanner.printErrors();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}