Add support for machine-readable JSON output to the MOC
The --output-json parameter will make moc produce a .json file next to the regular output file. With --collect-json the .json files for a module can be merged into a single one. Task-number: QTBUG-68796 Change-Id: I0e8fb802d47bd22da219701a8df947973d4bd7b5 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
64473c9320
commit
da284ef10e
34
mkspecs/features/metatypes.prf
Normal file
34
mkspecs/features/metatypes.prf
Normal file
@ -0,0 +1,34 @@
|
||||
qtPrepareTool(MOC_COLLECT_JSON, moc)
|
||||
|
||||
QMAKE_MOC_OPTIONS += --output-json
|
||||
|
||||
moc_json_header.input = HEADERS
|
||||
moc_json_header.output = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}.json
|
||||
moc_json_header.CONFIG = no_link moc_verify
|
||||
moc_json_header.depends = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}
|
||||
moc_json_header.commands = $$escape_expand(\\n) # force creation of rule
|
||||
moc_json_header.variable_out = MOC_JSON_FILES
|
||||
|
||||
moc_json_source.input = SOURCES
|
||||
moc_json_source.output = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}.json
|
||||
moc_json_source.CONFIG = no_link moc_verify
|
||||
moc_json_source.depends = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC}
|
||||
moc_json_source.commands = $$escape_expand(\\n) # force creation of rule
|
||||
moc_json_source.variable_out = MOC_JSON_FILES
|
||||
|
||||
MOC_COLLECT_JSON_OUTPUT = $$lower($$basename(TARGET))_metatypes.json
|
||||
|
||||
moc_collect_json.CONFIG += no_link combine
|
||||
moc_collect_json.commands = $$MOC_COLLECT_JSON --collect-json -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
|
||||
moc_collect_json.input = MOC_JSON_FILES
|
||||
moc_collect_json.output = $$MOC_COLLECT_JSON_OUTPUT
|
||||
moc_collect_json.name = Collect moc JSON output into central file
|
||||
|
||||
install_metatypes {
|
||||
do_install.path = $$[QT_INSTALL_LIBS]/metatypes
|
||||
do_install.files = $$OUT_PWD/$$MOC_COLLECT_JSON_OUTPUT
|
||||
prefix_build: INSTALLS += do_install
|
||||
else: COPIES += do_install
|
||||
}
|
||||
|
||||
QMAKE_EXTRA_COMPILERS += moc_collect_json moc_json_header moc_json_source
|
103
src/tools/moc/collectjson.cpp
Normal file
103
src/tools/moc/collectjson.cpp
Normal file
@ -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;
|
||||
}
|
42
src/tools/moc/collectjson.h
Normal file
42
src/tools/moc/collectjson.h
Normal file
@ -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
|
@ -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()); }
|
||||
@ -293,10 +300,20 @@ 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);
|
||||
|
||||
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 +322,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 +334,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;
|
||||
@ -485,6 +505,8 @@ int runMoc(int argc, char **argv)
|
||||
|
||||
// 3. and output meta object code
|
||||
|
||||
QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput;
|
||||
|
||||
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,6 +518,21 @@ 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;
|
||||
}
|
||||
@ -506,7 +543,7 @@ 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())
|
||||
|
@ -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>
|
||||
@ -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)
|
||||
@ -1784,6 +1802,186 @@ 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;
|
||||
|
||||
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
|
||||
|
@ -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,8 @@ struct PropertyDef
|
||||
int revision = 0;
|
||||
bool constant = false;
|
||||
bool final = false;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(PropertyDef, Q_MOVABLE_TYPE);
|
||||
|
||||
@ -183,6 +192,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);
|
||||
@ -215,7 +225,7 @@ public:
|
||||
QMap<QString, QJsonArray> metaArgs;
|
||||
|
||||
void parse();
|
||||
void generate(FILE *out);
|
||||
void generate(FILE *out, FILE *jsonOutput);
|
||||
|
||||
bool parseClassHead(ClassDef *def);
|
||||
inline bool inClass(const ClassDef *def) const {
|
||||
|
@ -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
|
||||
|
1
tests/auto/tools/moc/.gitignore
vendored
1
tests/auto/tools/moc/.gitignore
vendored
@ -1 +1,2 @@
|
||||
tst_moc
|
||||
allmocs.json
|
||||
|
2608
tests/auto/tools/moc/allmocs_baseline_in.json
Normal file
2608
tests/auto/tools/moc/allmocs_baseline_in.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,9 @@ HEADERS += using-namespaces.h no-keywords.h task87883.h c-comments.h backslash-n
|
||||
namespace.h cxx17-namespaces.h \
|
||||
cxx-attributes.h
|
||||
|
||||
# No platform specifics in the JSON files, so that we can compare them
|
||||
JSON_HEADERS = $$HEADERS
|
||||
JSON_HEADERS -= cxx-attributes.h
|
||||
|
||||
if(*-g++*|*-icc*|*-clang*|*-llvm):!win32-*: HEADERS += os9-newlines.h win-newlines.h
|
||||
if(*-g++*|*-clang*): HEADERS += dollars.h
|
||||
@ -50,3 +53,49 @@ QMAKE_MOC_OPTIONS += -Muri=com.company.app -Muri=com.company.app.private
|
||||
# Define macro on the command lines used in parse-defines.h
|
||||
QMAKE_MOC_OPTIONS += "-DDEFINE_CMDLINE_EMPTY=" "\"-DDEFINE_CMDLINE_SIGNAL=void cmdlineSignal(const QMap<int, int> &i)\""
|
||||
|
||||
QMAKE_MOC_OPTIONS += --output-json
|
||||
|
||||
debug_and_release {
|
||||
CONFIG(debug, debug|release) {
|
||||
MOC_CPP_DIR = $$MOC_DIR/debug
|
||||
} else {
|
||||
MOC_CPP_DIR = $$MOC_DIR/release
|
||||
}
|
||||
} else {
|
||||
MOC_CPP_DIR = $$MOC_DIR
|
||||
}
|
||||
|
||||
moc_json_header.input = JSON_HEADERS
|
||||
moc_json_header.output = $$MOC_CPP_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}.json
|
||||
moc_json_header.CONFIG = no_link moc_verify
|
||||
moc_json_header.depends = $$MOC_CPP_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)}
|
||||
moc_json_header.commands = $$escape_expand(\\n) # force creation of rule
|
||||
moc_json_header.variable_out = MOC_JSON_HEADERS
|
||||
|
||||
BASELINE_IN = allmocs_baseline_in.json
|
||||
copy_baseline.commands = $${QMAKE_COPY} $$shell_path(${QMAKE_FILE_NAME}) ${QMAKE_FILE_OUT}
|
||||
copy_baseline.input = BASELINE_IN
|
||||
copy_baseline.output = $$OUT_PWD/allmocs_baseline.json
|
||||
copy_baseline.CONFIG = no_link
|
||||
|
||||
qtPrepareTool(MOC_COLLECT_JSON, moc)
|
||||
jsoncollector.CONFIG += combine
|
||||
jsoncollector.commands = $$MOC_COLLECT_JSON --collect-json -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
|
||||
jsoncollector.input = MOC_JSON_HEADERS
|
||||
jsoncollector.output = $$OUT_PWD/allmocs.json
|
||||
jsoncollector.variable_out = GENERATED_FILES
|
||||
|
||||
allmocs_contents = \
|
||||
"<!DOCTYPE RCC><RCC version=\"1.0\">"\
|
||||
"<qresource prefix=\"/\">"\
|
||||
"<file>allmocs.json</file>"\
|
||||
"<file>allmocs_baseline.json</file>"\
|
||||
"</qresource>"\
|
||||
"</RCC>"
|
||||
|
||||
allmocs_file = $$OUT_PWD/allmocs.qrc
|
||||
|
||||
!write_file($$allmocs_file, allmocs_contents): error()
|
||||
RESOURCES += $$allmocs_file
|
||||
|
||||
QMAKE_EXTRA_COMPILERS += moc_json_header copy_baseline jsoncollector
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <stdio.h>
|
||||
#include <qobject.h>
|
||||
#include <qmetaobject.h>
|
||||
#include <qjsondocument.h>
|
||||
|
||||
#include "using-namespaces.h"
|
||||
#include "assign-namespace.h"
|
||||
@ -717,6 +718,7 @@ private slots:
|
||||
void testQNamespace();
|
||||
void cxx17Namespaces();
|
||||
void cxxAttributes();
|
||||
void mocJsonOutput();
|
||||
|
||||
signals:
|
||||
void sigWithUnsignedArg(unsigned foo);
|
||||
@ -3971,6 +3973,57 @@ void tst_Moc::cxxAttributes()
|
||||
QCOMPARE(meta.keyCount(), 7);
|
||||
}
|
||||
|
||||
void tst_Moc::mocJsonOutput()
|
||||
{
|
||||
const auto readFile = [](const QString &fileName) {
|
||||
QFile f(fileName);
|
||||
f.open(QIODevice::ReadOnly);
|
||||
return QJsonDocument::fromJson(f.readAll());
|
||||
};
|
||||
|
||||
const QString actualFile = QStringLiteral(":/allmocs.json");
|
||||
const QString expectedFile = QStringLiteral(":/allmocs_baseline.json");
|
||||
|
||||
QVERIFY2(QFile::exists(actualFile), qPrintable(actualFile));
|
||||
QVERIFY2(QFile::exists(expectedFile), qPrintable(expectedFile));
|
||||
|
||||
QJsonDocument actualOutput = readFile(QLatin1String(":/allmocs.json"));
|
||||
QJsonDocument expectedOutput = readFile(QLatin1String(":/allmocs_baseline.json"));
|
||||
|
||||
const auto showPotentialDiff = [](const QJsonDocument &actual, const QJsonDocument &expected) -> QByteArray {
|
||||
#if defined(Q_OS_UNIX)
|
||||
QByteArray actualStr = actual.toJson();
|
||||
QByteArray expectedStr = expected.toJson();
|
||||
|
||||
QTemporaryFile actualFile;
|
||||
if (!actualFile.open())
|
||||
return "Error opening actual temp file";
|
||||
actualFile.write(actualStr);
|
||||
actualFile.flush();
|
||||
|
||||
QTemporaryFile expectedFile;
|
||||
if (!expectedFile.open())
|
||||
return "Error opening expected temp file";
|
||||
expectedFile.write(expectedStr);
|
||||
expectedFile.flush();
|
||||
|
||||
QProcess diffProc;
|
||||
diffProc.setProgram("diff");
|
||||
diffProc.setArguments(QStringList() << "-ub" << expectedFile.fileName() << actualFile.fileName());
|
||||
diffProc.start();
|
||||
if (!diffProc.waitForStarted())
|
||||
return "Error waiting for diff process to start.";
|
||||
if (!diffProc.waitForFinished())
|
||||
return "Error waiting for diff process to finish.";
|
||||
return diffProc.readAllStandardOutput();
|
||||
#else
|
||||
return "Cannot launch diff. Please check allmocs.json and allmocs_baseline.json on disk.";
|
||||
#endif
|
||||
};
|
||||
|
||||
QVERIFY2(actualOutput == expectedOutput, showPotentialDiff(actualOutput, expectedOutput).constData());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_Moc)
|
||||
|
||||
// the generated code must compile with QT_NO_KEYWORDS
|
||||
|
Loading…
Reference in New Issue
Block a user