diff options
Diffstat (limited to 'src/tools/moc')
-rw-r--r-- | src/tools/moc/collectjson.cpp | 103 | ||||
-rw-r--r-- | src/tools/moc/collectjson.h | 42 | ||||
-rw-r--r-- | src/tools/moc/generator.cpp | 10 | ||||
-rw-r--r-- | src/tools/moc/main.cpp | 172 | ||||
-rw-r--r-- | src/tools/moc/moc.cpp | 209 | ||||
-rw-r--r-- | src/tools/moc/moc.h | 14 | ||||
-rw-r--r-- | src/tools/moc/moc.pri | 7 | ||||
-rw-r--r-- | src/tools/moc/parser.cpp | 2 | ||||
-rw-r--r-- | src/tools/moc/preprocessor.cpp | 2 |
9 files changed, 548 insertions, 13 deletions
diff --git a/src/tools/moc/collectjson.cpp b/src/tools/moc/collectjson.cpp new file mode 100644 index 0000000000..4029bca5e9 --- /dev/null +++ b/src/tools/moc/collectjson.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qfile.h> +#include <qjsonarray.h> +#include <qjsondocument.h> +#include <qjsonobject.h> +#include <qhashfunctions.h> +#include <qstringlist.h> +#include <cstdlib> + +static bool readFromDevice(QIODevice *device, QJsonArray *allMetaObjects) +{ + const QByteArray contents = device->readAll(); + if (contents.isEmpty()) + return true; + + QJsonParseError error {}; + QJsonDocument metaObjects = QJsonDocument::fromJson(contents, &error); + if (error.error != QJsonParseError::NoError) { + fprintf(stderr, "%s at %d\n", error.errorString().toUtf8().constData(), error.offset); + return false; + } + + allMetaObjects->append(metaObjects.object()); + return true; +} + +int collectJson(const QStringList &jsonFiles, const QString &outputFile) +{ + qSetGlobalQHashSeed(0); + + QFile output; + if (outputFile.isEmpty()) { + if (!output.open(stdout, QIODevice::WriteOnly)) { + fprintf(stderr, "Error opening stdout for writing\n"); + return EXIT_FAILURE; + } + } else { + output.setFileName(outputFile); + if (!output.open(QIODevice::WriteOnly)) { + fprintf(stderr, "Error opening %s for writing\n", qPrintable(outputFile)); + return EXIT_FAILURE; + } + } + + QJsonArray allMetaObjects; + if (jsonFiles.isEmpty()) { + QFile f; + if (!f.open(stdin, QIODevice::ReadOnly)) { + fprintf(stderr, "Error opening stdin for reading\n"); + return EXIT_FAILURE; + } + + if (!readFromDevice(&f, &allMetaObjects)) { + fprintf(stderr, "Error parsing data from stdin\n"); + return EXIT_FAILURE; + } + } + + for (const QString &jsonFile: jsonFiles) { + QFile f(jsonFile); + if (!f.open(QIODevice::ReadOnly)) { + fprintf(stderr, "Error opening %s for reading\n", qPrintable(jsonFile)); + return EXIT_FAILURE; + } + + if (!readFromDevice(&f, &allMetaObjects)) { + fprintf(stderr, "Error parsing %s\n", qPrintable(jsonFile)); + return EXIT_FAILURE; + } + } + + QJsonDocument doc(allMetaObjects); + output.write(doc.toJson()); + + return EXIT_SUCCESS; +} diff --git a/src/tools/moc/collectjson.h b/src/tools/moc/collectjson.h new file mode 100644 index 0000000000..9d329c96ca --- /dev/null +++ b/src/tools/moc/collectjson.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COLLECTJSON_H +#define COLLECTJSON_H + +#include <qglobal.h> +#include <qstring.h> +#include <qstringlist.h> + +QT_BEGIN_NAMESPACE + +int collectJson(const QStringList &jsonFiles, const QString &outputFile); + +QT_END_NAMESPACE + +#endif // COLLECTOJSON_H diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 6a74e739e6..8d4fb2efc6 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -77,7 +77,7 @@ static const char *metaTypeEnumValueString(int type) QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) } #undef RETURN_METATYPENAME_STRING - return 0; + return nullptr; } Generator::Generator(ClassDef *classDef, const QVector<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile) @@ -462,7 +462,7 @@ void Generator::generateCode() // Build extra array // QVector<QByteArray> extraList; - QHash<QByteArray, QByteArray> knownExtraMetaObject = knownGadgets; + QMultiHash<QByteArray, QByteArray> knownExtraMetaObject = knownGadgets; knownExtraMetaObject.unite(knownQObjectClasses); for (int i = 0; i < cdef->propertyList.count(); ++i) { @@ -541,8 +541,10 @@ void Generator::generateCode() if (isQObject) fprintf(out, " nullptr,\n"); - else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass))) + else if (cdef->superclassList.size() && !cdef->hasQGadget) // for qobject, we know the super class must have a static metaobject fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData()); + else if (cdef->superclassList.size()) // for gadgets we need to query at compile time for it + fprintf(out, " QtPrivate::MetaObjectForType<%s>::value(),\n", purestSuperClass.constData()); else fprintf(out, " nullptr,\n"); fprintf(out, " qt_meta_stringdata_%s.data,\n" @@ -862,6 +864,8 @@ void Generator::generateProperties() flags |= Constant; if (p.final) flags |= Final; + if (p.required) + flags |= Required; fprintf(out, " %4d, ", stridx(p.name)); generateTypeInfo(p.type); diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp index ba559b572f..b8c2d7f594 100644 --- a/src/tools/moc/main.cpp +++ b/src/tools/moc/main.cpp @@ -30,6 +30,7 @@ #include "preprocessor.h" #include "moc.h" #include "outputrevision.h" +#include "collectjson.h" #include <qfile.h> #include <qfileinfo.h> @@ -37,10 +38,12 @@ #include <stdio.h> #include <stdlib.h> #include <ctype.h> +#include <errno.h> #include <qcoreapplication.h> #include <qcommandlineoption.h> #include <qcommandlineparser.h> +#include <qscopedpointer.h> QT_BEGIN_NAMESPACE @@ -77,6 +80,10 @@ void error(const char *msg = "Invalid argument") fprintf(stderr, "moc: %s\n", msg); } +struct ScopedPointerFileCloser +{ + static inline void cleanup(FILE *handle) { if (handle) fclose(handle); } +}; static inline bool hasNext(const Symbols &symbols, int i) { return (i < symbols.size()); } @@ -168,6 +175,49 @@ static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments) return allArguments; } +// Escape characters in given path. Dependency paths are Make-style, not NMake/Jom style. +// The paths can also be consumed by Ninja. +// "$" replaced by "$$" +// "#" replaced by "\#" +// " " replaced by "\ " +// "\#" replaced by "\\#" +// "\ " replaced by "\\\ " +// +// The escape rules are according to what clang / llvm escapes when generating a Make-style +// dependency file. +// Is a template function, because input param can be either a QString or a QByteArray. +template <typename T> struct CharType; +template <> struct CharType<QString> { using type = QLatin1Char; }; +template <> struct CharType<QByteArray> { using type = char; }; +template <typename StringType> +StringType escapeDependencyPath(const StringType &path) +{ + using CT = typename CharType<StringType>::type; + StringType escapedPath; + int size = path.size(); + escapedPath.reserve(size); + for (int i = 0; i < size; ++i) { + if (path[i] == CT('$')) { + escapedPath.append(CT('$')); + } else if (path[i] == CT('#')) { + escapedPath.append(CT('\\')); + } else if (path[i] == CT(' ')) { + escapedPath.append(CT('\\')); + int backwards_it = i - 1; + while (backwards_it > 0 && path[backwards_it] == CT('\\')) { + escapedPath.append(CT('\\')); + --backwards_it; + } + } + escapedPath.append(path[i]); + } + return escapedPath; +} + +QByteArray escapeAndEncodeDependencyPath(const QString &path) +{ + return QFile::encodeName(escapeDependencyPath(path)); +} int runMoc(int argc, char **argv) { @@ -293,10 +343,36 @@ int runMoc(int argc, char **argv) ignoreConflictsOption.setDescription(QStringLiteral("Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option.")); parser.addOption(ignoreConflictsOption); + QCommandLineOption jsonOption(QStringLiteral("output-json")); + jsonOption.setDescription(QStringLiteral("In addition to generating C++ code, create a machine-readable JSON file in a file that matches the output file and an extra .json extension.")); + parser.addOption(jsonOption); + + QCommandLineOption collectOption(QStringLiteral("collect-json")); + collectOption.setDescription(QStringLiteral("Instead of processing C++ code, collect previously generated JSON output into a single file.")); + parser.addOption(collectOption); + + QCommandLineOption depFileOption(QStringLiteral("output-dep-file")); + depFileOption.setDescription( + QStringLiteral("Output a Make-style dep file for build system consumption.")); + parser.addOption(depFileOption); + + QCommandLineOption depFilePathOption(QStringLiteral("dep-file-path")); + depFilePathOption.setDescription(QStringLiteral("Path where to write the dep file.")); + depFilePathOption.setValueName(QStringLiteral("file")); + parser.addOption(depFilePathOption); + + QCommandLineOption depFileRuleNameOption(QStringLiteral("dep-file-rule-name")); + depFileRuleNameOption.setDescription( + QStringLiteral("The rule name (first line) of the dep file.")); + depFileRuleNameOption.setValueName(QStringLiteral("rule name")); + parser.addOption(depFileRuleNameOption); + parser.addPositionalArgument(QStringLiteral("[header-file]"), QStringLiteral("Header file to read from, otherwise stdin.")); parser.addPositionalArgument(QStringLiteral("[@option-file]"), QStringLiteral("Read additional options from option-file.")); + parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"), + QStringLiteral("MOC generated json output")); const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments()); if (arguments.isEmpty()) @@ -305,6 +381,10 @@ int runMoc(int argc, char **argv) parser.process(arguments); const QStringList files = parser.positionalArguments(); + output = parser.value(outputOption); + if (parser.isSet(collectOption)) + return collectJson(files, output); + if (files.count() > 1) { error(qPrintable(QLatin1String("Too many input files specified: '") + files.join(QLatin1String("' '")) + QLatin1Char('\''))); parser.showHelp(1); @@ -313,7 +393,6 @@ int runMoc(int argc, char **argv) } const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption); - output = parser.value(outputOption); pp.preprocessOnly = parser.isSet(preprocessOption); if (parser.isSet(noIncludeOption)) { moc.noInclude = true; @@ -456,6 +535,7 @@ int runMoc(int argc, char **argv) // 1. preprocess const auto includeFiles = parser.values(includeOption); + QStringList validIncludesFiles; for (const QString &includeName : includeFiles) { QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename); if (rawName.isEmpty()) { @@ -468,6 +548,7 @@ int runMoc(int argc, char **argv) moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName); moc.symbols += pp.preprocessed(rawName, &f); moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName); + validIncludesFiles.append(includeName); } else { fprintf(stderr, "Warning: Cannot open %s included by moc file %s: %s\n", rawName.constData(), @@ -485,6 +566,9 @@ int runMoc(int argc, char **argv) // 3. and output meta object code + QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput; + + bool outputToFile = true; if (output.size()) { // output file specified #if defined(_MSC_VER) if (_wfopen_s(&out, reinterpret_cast<const wchar_t *>(output.utf16()), L"w") != 0) @@ -496,8 +580,24 @@ int runMoc(int argc, char **argv) fprintf(stderr, "moc: Cannot create %s\n", QFile::encodeName(output).constData()); return 1; } + + if (parser.isSet(jsonOption)) { + const QString jsonOutputFileName = output + QLatin1String(".json"); + FILE *f; +#if defined(_MSC_VER) + if (_wfopen_s(&f, reinterpret_cast<const wchar_t *>(jsonOutputFileName.utf16()), L"w") != 0) +#else + f = fopen(QFile::encodeName(jsonOutputFileName).constData(), "w"); + if (!f) +#endif + fprintf(stderr, "moc: Cannot create JSON output file %s. %s\n", + QFile::encodeName(jsonOutputFileName).constData(), + strerror(errno)); + jsonOutput.reset(f); + } } else { // use stdout out = stdout; + outputToFile = false; } if (pp.preprocessOnly) { @@ -506,12 +606,80 @@ int runMoc(int argc, char **argv) if (moc.classList.isEmpty()) moc.note("No relevant classes found. No output generated."); else - moc.generate(out); + moc.generate(out, jsonOutput.data()); } if (output.size()) fclose(out); + if (parser.isSet(depFileOption)) { + // 4. write a Make-style dependency file (can also be consumed by Ninja). + QString depOutputFileName; + QString depRuleName = output; + + if (parser.isSet(depFileRuleNameOption)) + depRuleName = parser.value(depFileRuleNameOption); + + if (parser.isSet(depFilePathOption)) { + depOutputFileName = parser.value(depFilePathOption); + } else if (outputToFile) { + depOutputFileName = output + QLatin1String(".d"); + } else { + fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n"); + } + + QScopedPointer<FILE, ScopedPointerFileCloser> depFileHandle; + FILE *depFileHandleRaw; +#if defined(_MSC_VER) + if (_wfopen_s(&depFileHandleRaw, + reinterpret_cast<const wchar_t *>(depOutputFileName.utf16()), L"w") != 0) +#else + depFileHandleRaw = fopen(QFile::encodeName(depOutputFileName).constData(), "w"); + if (!depFileHandleRaw) +#endif + fprintf(stderr, "moc: Cannot create dep output file '%s'. %s\n", + QFile::encodeName(depOutputFileName).constData(), + strerror(errno)); + depFileHandle.reset(depFileHandleRaw); + + if (!depFileHandle.isNull()) { + // First line is the path to the generated file. + fprintf(depFileHandle.data(), "%s: ", + escapeAndEncodeDependencyPath(depRuleName).constData()); + + QByteArrayList dependencies; + + // If there's an input file, it's the first dependency. + if (!filename.isEmpty()) { + dependencies.append(escapeAndEncodeDependencyPath(filename).constData()); + } + + // Additional passed-in includes are dependencies (like moc_predefs.h). + for (const QString &includeName : validIncludesFiles) { + dependencies.append(escapeAndEncodeDependencyPath(includeName).constData()); + } + + // Plugin metadata json files discovered via Q_PLUGIN_METADATA macros are also + // dependencies. + for (const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) { + dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData()); + } + + // All pre-processed includes are dependnecies. + // Sort the entries for easier human consumption. + auto includeList = pp.preprocessedIncludes.values(); + std::sort(includeList.begin(), includeList.end()); + + for (QByteArray &includeName : includeList) { + dependencies.append(escapeDependencyPath(includeName)); + } + + // Join dependencies, output them, and output a final new line. + const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n ")); + fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData()); + } + } + return 0; } diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 50946443be..b562416c31 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -35,6 +35,7 @@ #include <QtCore/qfile.h> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> +#include <QtCore/qjsondocument.h> // for normalizeTypeInternal #include <private/qmetaobject_moc_p.h> @@ -929,9 +930,9 @@ void Moc::parse() if (it != classList.end()) { it->classInfoList += def.classInfoList; - it->enumDeclarations.unite(def.enumDeclarations); + it->enumDeclarations.insert(def.enumDeclarations); it->enumList += def.enumList; - it->flagAliases.unite(def.flagAliases); + it->flagAliases.insert(def.flagAliases); } else { knownGadgets.insert(def.classname, def.qualified); knownGadgets.insert(def.qualified, def.qualified); @@ -999,7 +1000,7 @@ static QByteArrayList requiredQtContainers(const QVector<ClassDef> &classes) return required; } -void Moc::generate(FILE *out) +void Moc::generate(FILE *out, FILE *jsonOutput) { QByteArray fn = filename; int i = filename.length()-1; @@ -1062,6 +1063,23 @@ void Moc::generate(FILE *out) fprintf(out, "QT_WARNING_POP\n"); fprintf(out, "QT_END_MOC_NAMESPACE\n"); + + if (jsonOutput) { + QJsonObject mocData; + mocData[QLatin1String("outputRevision")] = mocOutputRevision; + mocData[QLatin1String("inputFile")] = QLatin1String(fn.constData()); + + QJsonArray classesJsonFormatted; + + for (const ClassDef &cdef: qAsConst(classList)) + classesJsonFormatted.append(cdef.toJson()); + + if (!classesJsonFormatted.isEmpty()) + mocData[QLatin1String("classes")] = classesJsonFormatted; + + QJsonDocument jsonDoc(mocData); + fputs(jsonDoc.toJson().constData(), jsonOutput); + } } void Moc::parseSlots(ClassDef *def, FunctionDef::Access access) @@ -1220,6 +1238,9 @@ void Moc::createPropertyDef(PropertyDef &propDef) } else if(l[0] == 'F' && l == "FINAL") { propDef.final = true; continue; + } else if (l[0] == 'R' && l == "REQUIRED") { + propDef.required = true; + continue; } QByteArray v, v2; @@ -1364,6 +1385,7 @@ void Moc::parsePluginData(ClassDef *def) error(msg.constData()); return; } + parsedPluginMetadataFiles.append(fi.canonicalFilePath()); metaData = file.readAll(); } } @@ -1784,6 +1806,187 @@ void Moc::checkProperties(ClassDef *cdef) } } +QJsonObject ClassDef::toJson() const +{ + QJsonObject cls; + cls[QLatin1String("className")] = QString::fromUtf8(classname.constData()); + cls[QLatin1String("qualifiedClassName")] = QString::fromUtf8(qualified.constData()); + + QJsonArray classInfos; + for (const auto &info: qAsConst(classInfoList)) { + QJsonObject infoJson; + infoJson[QLatin1String("name")] = QString::fromUtf8(info.name); + infoJson[QLatin1String("value")] = QString::fromUtf8(info.value); + classInfos.append(infoJson); + } + + if (classInfos.size()) + cls[QLatin1String("classInfos")] = classInfos; + + const auto appendFunctions = [&cls](const QString &type, const QVector<FunctionDef> &funcs) { + QJsonArray jsonFuncs; + + for (const FunctionDef &fdef: funcs) + jsonFuncs.append(fdef.toJson()); + + if (!jsonFuncs.isEmpty()) + cls[type] = jsonFuncs; + }; + + appendFunctions(QLatin1String("signals"), signalList); + appendFunctions(QLatin1String("slots"), slotList); + appendFunctions(QLatin1String("constructors"), constructorList); + appendFunctions(QLatin1String("methods"), methodList); + + QJsonArray props; + + for (const PropertyDef &propDef: qAsConst(propertyList)) + props.append(propDef.toJson()); + + if (!props.isEmpty()) + cls[QLatin1String("properties")] = props; + + if (hasQGadget) + cls[QLatin1String("gadget")] = true; + + QJsonArray superClasses; + + for (const auto &super: qAsConst(superclassList)) { + const auto name = super.first; + const auto access = super.second; + QJsonObject superCls; + superCls[QLatin1String("name")] = QString::fromUtf8(name); + FunctionDef::accessToJson(&superCls, access); + superClasses.append(superCls); + } + + if (!superClasses.isEmpty()) + cls[QLatin1String("superClasses")] = superClasses; + + QJsonArray enums; + for (const EnumDef &enumDef: qAsConst(enumList)) + enums.append(enumDef.toJson(*this)); + if (!enums.isEmpty()) + cls[QLatin1String("enums")] = enums; + + QJsonArray ifaces; + for (const QVector<Interface> &ifaceList: interfaceList) { + QJsonArray jsonList; + for (const Interface &iface: ifaceList) { + QJsonObject ifaceJson; + ifaceJson[QLatin1String("id")] = QString::fromUtf8(iface.interfaceId); + ifaceJson[QLatin1String("className")] = QString::fromUtf8(iface.className); + jsonList.append(ifaceJson); + } + ifaces.append(jsonList); + } + if (!ifaces.isEmpty()) + cls[QLatin1String("interfaces")] = ifaces; + + return cls; +} + +QJsonObject FunctionDef::toJson() const +{ + QJsonObject fdef; + fdef[QLatin1String("name")] = QString::fromUtf8(name); + if (!tag.isEmpty()) + fdef[QLatin1String("tag")] = QString::fromUtf8(tag); + fdef[QLatin1String("returnType")] = QString::fromUtf8(normalizedType); + + QJsonArray args; + for (const ArgumentDef &arg: arguments) + args.append(arg.toJson()); + + if (!args.isEmpty()) + fdef[QLatin1String("arguments")] = args; + + accessToJson(&fdef, access); + + if (revision > 0) + fdef[QLatin1String("revision")] = revision; + + return fdef; +} + +void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs) +{ + switch (acs) { + case Private: (*obj)[QLatin1String("access")] = QLatin1String("private"); break; + case Public: (*obj)[QLatin1String("access")] = QLatin1String("public"); break; + case Protected: (*obj)[QLatin1String("access")] = QLatin1String("protected"); break; + } +} + +QJsonObject ArgumentDef::toJson() const +{ + QJsonObject arg; + arg[QLatin1String("type")] = QString::fromUtf8(normalizedType); + if (!name.isEmpty()) + arg[QLatin1String("name")] = QString::fromUtf8(name); + return arg; +} + +QJsonObject PropertyDef::toJson() const +{ + QJsonObject prop; + prop[QLatin1String("name")] = QString::fromUtf8(name); + prop[QLatin1String("type")] = QString::fromUtf8(type); + + const auto jsonify = [&prop](const char *str, const QByteArray &member) { + if (!member.isEmpty()) + prop[QLatin1String(str)] = QString::fromUtf8(member); + }; + + jsonify("member", member); + jsonify("read", read); + jsonify("write", write); + jsonify("reset", reset); + jsonify("notify", notify); + jsonify("privateClass", inPrivateClass); + + const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) { + QJsonValue value; + if (boolOrString == "true") + value = true; + else if (boolOrString == "false") + value = false; + else + value = QString::fromUtf8(boolOrString); // function name to query at run-time + prop[QLatin1String(str)] = value; + }; + + jsonifyBoolOrString("designable", designable); + jsonifyBoolOrString("scriptable", scriptable); + jsonifyBoolOrString("stored", stored); + jsonifyBoolOrString("user", user); + + prop[QLatin1String("constant")] = constant; + prop[QLatin1String("final")] = final; + prop[QLatin1String("required")] = required; + if (revision > 0) + prop[QLatin1String("revision")] = revision; + + return prop; +} + +QJsonObject EnumDef::toJson(const ClassDef &cdef) const +{ + QJsonObject def; + def[QLatin1String("name")] = QString::fromUtf8(name); + if (!enumName.isEmpty()) + def[QLatin1String("alias")] = QString::fromUtf8(enumName); + def[QLatin1String("isFlag")] = cdef.enumDeclarations.value(name); + def[QLatin1String("isClass")] = isEnumClass; + + QJsonArray valueArr; + for (const QByteArray &value: values) + valueArr.append(QString::fromUtf8(value)); + if (!valueArr.isEmpty()) + def[QLatin1String("values")] = valueArr; + + return def; +} QT_END_NAMESPACE diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index bb1c9501fe..04814b85a1 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -61,6 +61,7 @@ struct Type }; Q_DECLARE_TYPEINFO(Type, Q_MOVABLE_TYPE); +struct ClassDef; struct EnumDef { QByteArray name; @@ -68,6 +69,7 @@ struct EnumDef QVector<QByteArray> values; bool isEnumClass; // c++11 enum class EnumDef() : isEnumClass(false) {} + QJsonObject toJson(const ClassDef &cdef) const; }; Q_DECLARE_TYPEINFO(EnumDef, Q_MOVABLE_TYPE); @@ -78,6 +80,8 @@ struct ArgumentDef QByteArray rightType, normalizedType, name; QByteArray typeNameForCast; // type name to be used in cast from void * in metacall bool isDefault; + + QJsonObject toJson() const; }; Q_DECLARE_TYPEINFO(ArgumentDef, Q_MOVABLE_TYPE); @@ -111,6 +115,9 @@ struct FunctionDef bool isConstructor = false; bool isDestructor = false; bool isAbstract = false; + + QJsonObject toJson() const; + static void accessToJson(QJsonObject *obj, Access acs); }; Q_DECLARE_TYPEINFO(FunctionDef, Q_MOVABLE_TYPE); @@ -130,6 +137,9 @@ struct PropertyDef int revision = 0; bool constant = false; bool final = false; + bool required = false; + + QJsonObject toJson() const; }; Q_DECLARE_TYPEINFO(PropertyDef, Q_MOVABLE_TYPE); @@ -183,6 +193,7 @@ struct ClassDef : BaseDef { bool hasQObject = false; bool hasQGadget = false; + QJsonObject toJson() const; }; Q_DECLARE_TYPEINFO(ClassDef, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ClassDef::Interface, Q_MOVABLE_TYPE); @@ -213,9 +224,10 @@ public: QHash<QByteArray, QByteArray> knownQObjectClasses; QHash<QByteArray, QByteArray> knownGadgets; QMap<QString, QJsonArray> metaArgs; + QVector<QString> parsedPluginMetadataFiles; void parse(); - void generate(FILE *out); + void generate(FILE *out, FILE *jsonOutput); bool parseClassHead(ClassDef *def); inline bool inClass(const ClassDef *def) const { diff --git a/src/tools/moc/moc.pri b/src/tools/moc/moc.pri index 90839a445b..278d5607cd 100644 --- a/src/tools/moc/moc.pri +++ b/src/tools/moc/moc.pri @@ -10,9 +10,12 @@ HEADERS = $$PWD/moc.h \ $$PWD/utils.h \ $$PWD/generator.h \ $$PWD/outputrevision.h \ - $$PWD/cbordevice.h + $$PWD/cbordevice.h \ + $$PWD/collectjson.h + SOURCES = $$PWD/moc.cpp \ $$PWD/preprocessor.cpp \ $$PWD/generator.cpp \ $$PWD/parser.cpp \ - $$PWD/token.cpp + $$PWD/token.cpp \ + $$PWD/collectjson.cpp diff --git a/src/tools/moc/parser.cpp b/src/tools/moc/parser.cpp index b7aefae1ec..068f75d4bd 100644 --- a/src/tools/moc/parser.cpp +++ b/src/tools/moc/parser.cpp @@ -37,7 +37,7 @@ QT_BEGIN_NAMESPACE Symbol::LexemStore Symbol::lexemStore; #endif -static const char *error_msg = 0; +static const char *error_msg = nullptr; #ifdef Q_CC_MSVC #define ErrorFormatString "%s(%d): " diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index d135bddb4c..a99b8cc80c 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -943,7 +943,7 @@ int PP_Expression::primary_expression() test(PP_RPAREN); } else { next(); - value = lexem().toInt(0, 0); + value = lexem().toInt(nullptr, 0); } return value; } |