Skip to content

Commit

Permalink
Add JSON parsing support to qmake.
Browse files Browse the repository at this point in the history
Add qjson* implementation files from corelib/json
to the qmake build. Add a read-only compile mode,
enabled by defining QT_JSON_READONLY.

Add qmake built-in function parseJson(file, into)
which parses a json file into the given variable.

qmake uses a flat key -> value-list implementation
for storing variables, which means that some hackery
is need to represent arbitrarily nested JSON. Use a
special "_KEYS_" variable for arrays and objects:

Arrays:
["item1", "item2"]
$${array._KEYS_} -> 0 1 2
$${array.0} -> "item1"
$${array.1} -> "item2"

Objects:
{ "key1" : "value1", "key2" : "value2" }
$${object._KEYS_} -> key1 key2
$${object.key1} -> value1
$${object.key2} -> value2

Change-Id: I0aa2e4e4ae14fa25be8242bc16d3cffce32504d2
Reviewed-by: Lars Knoll <[email protected]>
  • Loading branch information
Morten Johan Sørvig authored and The Qt Project committed Oct 17, 2013
1 parent f47958f commit 89ef515
Show file tree
Hide file tree
Showing 16 changed files with 220 additions and 15 deletions.
27 changes: 26 additions & 1 deletion qmake/Makefile.unix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ QOBJS=qtextcodec.o qutfcodec.o qstring.o qstringbuilder.o qtextstream.o qiodevic
qabstractfileengine.o qtemporaryfile.o qmap.o qmetatype.o qsettings.o qsystemerror.o qlibraryinfo.o \
qvariant.o qvsnprintf.o qlocale.o qlocale_tools.o qlinkedlist.o qnumeric.o \
qcryptographichash.o qxmlstream.o qxmlutils.o qlogging.o \
qjson.o qjsondocument.o qjsonparser.o qjsonarray.o qjsonobject.o qjsonvalue.o \
$(QTOBJS)


Expand Down Expand Up @@ -80,6 +81,12 @@ DEPEND_SRC = \
$(SOURCE_PATH)/src/corelib/global/qlogging.cpp \
$(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp \
$(SOURCE_PATH)/tools/shared/windows/registry.cpp \
$(SOURCE_PATH)/src/corelib/json/qjson.cpp \
$(SOURCE_PATH)/src/corelib/json/qjsondocument.cpp \
$(SOURCE_PATH)/src/corelib/json/qjsonparser.cpp \
$(SOURCE_PATH)/src/corelib/json/qjsonarray.cpp \
$(SOURCE_PATH)/src/corelib/json/qjsonobject.cpp \
$(SOURCE_PATH)/src/corelib/json/qjsonvalue.cpp \
$(QTSRCS)

CPPFLAGS = -g $(EXTRA_CPPFLAGS) \
Expand All @@ -93,7 +100,7 @@ CPPFLAGS = -g $(EXTRA_CPPFLAGS) \
-DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DPROEVALUATOR_FULL \
-DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_COMPRESS \
-DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \
-DQT_CRYPTOGRAPHICHASH_ONLY_SHA1
-DQT_CRYPTOGRAPHICHASH_ONLY_SHA1 -DQT_JSON_READONLY

CXXFLAGS = $(EXTRA_CXXFLAGS) $(CPPFLAGS)

Expand Down Expand Up @@ -385,4 +392,22 @@ qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp
registry.o: $(SOURCE_PATH)/tools/shared/windows/registry.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/tools/shared/windows/registry.cpp

qjson.o: $(SOURCE_PATH)/src/corelib/json/qjson.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjson.cpp

qjsondocument.o: $(SOURCE_PATH)/src/corelib/json/qjsondocument.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjsondocument.cpp

qjsonparser.o: $(SOURCE_PATH)/src/corelib/json/qjsonparser.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjsonparser.cpp

qjsonarray.o: $(SOURCE_PATH)/src/corelib/json/qjsonarray.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjsonarray.cpp

qjsonobject.o: $(SOURCE_PATH)/src/corelib/json/qjsonobject.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjsonobject.cpp

qjsonvalue.o: $(SOURCE_PATH)/src/corelib/json/qjsonvalue.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/json/qjsonvalue.cpp

# DO NOT DELETE THIS LINE -- make depend depends on it
13 changes: 11 additions & 2 deletions qmake/Makefile.win32
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ CFLAGS_BARE = -c -Fo./ \
-DQT_BUILD_QMAKE -DQT_BOOTSTRAPPED -DPROEVALUATOR_FULL \
-DQT_NO_TEXTCODEC -DQT_NO_UNICODETABLES -DQT_NO_COMPONENT -DQT_NO_COMPRESS \
-DQT_NO_THREAD -DQT_NO_QOBJECT -DQT_NO_GEOM_VARIANT -DQT_NO_DATASTREAM \
-DUNICODE -DQT_CRYPTOGRAPHICHASH_ONLY_SHA1
-DUNICODE -DQT_CRYPTOGRAPHICHASH_ONLY_SHA1 -DQT_JSON_READONLY
CFLAGS = -Yuqmake_pch.h -FIqmake_pch.h -Fpqmake_pch.pch $(CFLAGS_BARE) $(CFLAGS) $(EXTRA_CPPFLAGS)

CXXFLAGS_BARE = $(CFLAGS_BARE)
Expand Down Expand Up @@ -120,7 +120,13 @@ QTOBJS= \
qxmlstream.obj \
qxmlutils.obj \
qnumeric.obj \
qlogging.obj
qlogging.obj \
qjson.obj \
qjsondocument.obj \
qjsonparser.obj \
qjsonarray.obj \
qjsonobject.obj \
qjsonvalue.obj

first all: qmake.exe

Expand Down Expand Up @@ -207,5 +213,8 @@ qmake_pch.obj:
{$(SOURCE_PATH)\src\corelib\xml}.cpp{}.obj::
$(CXX) $(CXXFLAGS) $<

{$(SOURCE_PATH)\src\corelib\json}.cpp{}.obj::
$(CXX) $(CXXFLAGS) $<

{$(SOURCE_PATH)\tools\shared\windows}.cpp{}.obj::
$(CXX) $(CXXFLAGS) $<
91 changes: 90 additions & 1 deletion qmake/library/qmakebuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
#include <qset.h>
#include <qstringlist.h>
#include <qtextstream.h>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
# include <qjsondocument.h>
# include <qjsonobject.h>
# include <qjsonarray.h>
#endif
#ifdef PROEVALUATOR_THREAD_SAFE
# include <qthreadpool.h>
#endif
Expand Down Expand Up @@ -101,7 +106,7 @@ enum TestFunc {
T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS,
T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM,
T_DEFINED, T_CONTAINS, T_INFILE,
T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
T_COUNT, T_ISEMPTY, T_PARSE_JSON, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF,
T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE
};

Expand Down Expand Up @@ -178,6 +183,9 @@ void QMakeEvaluator::initFunctionStatics()
{ "infile", T_INFILE },
{ "count", T_COUNT },
{ "isEmpty", T_ISEMPTY },
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
{ "parseJson", T_PARSE_JSON },
#endif
{ "load", T_LOAD },
{ "include", T_INCLUDE },
{ "debug", T_DEBUG },
Expand Down Expand Up @@ -286,6 +294,75 @@ quoteValue(const ProString &val)
return ret;
}

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map);

static void insertJsonKeyValue(const QString &key, const QStringList &values, ProValueMap *map)
{
map->insert(ProKey(key), ProStringList(values));
}

static void addJsonArray(const QJsonArray &array, const QString &keyPrefix, ProValueMap *map)
{
QStringList keys;
for (int i = 0; i < array.count(); ++i) {
keys.append(QString::number(i));
addJsonValue(array.at(i), keyPrefix + QString::number(i), map);
}
insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), keys, map);
}

static void addJsonObject(const QJsonObject &object, const QString &keyPrefix, ProValueMap *map)
{
foreach (const QString &key, object.keys())
addJsonValue(object.value(key), keyPrefix + key, map);

insertJsonKeyValue(keyPrefix + QLatin1String("_KEYS_"), object.keys(), map);
}

static void addJsonValue(const QJsonValue &value, const QString &keyPrefix, ProValueMap *map)
{
switch (value.type()) {
case QJsonValue::Bool:
insertJsonKeyValue(keyPrefix, QStringList() << (value.toBool() ? QLatin1String("true") : QLatin1String("false")), map);
break;
case QJsonValue::Double:
insertJsonKeyValue(keyPrefix, QStringList() << QString::number(value.toDouble()), map);
break;
case QJsonValue::String:
insertJsonKeyValue(keyPrefix, QStringList() << value.toString(), map);
break;
case QJsonValue::Array:
addJsonArray(value.toArray(), keyPrefix + QLatin1Char('.'), map);
break;
case QJsonValue::Object:
addJsonObject(value.toObject(), keyPrefix + QLatin1Char('.'), map);
break;
default:
break;
}
}

static QMakeEvaluator::VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value)
{
QJsonDocument document = QJsonDocument::fromJson(json);
if (document.isNull())
return QMakeEvaluator::ReturnFalse;

QString currentKey = into + QLatin1Char('.');

// top-level item is either an array or object
if (document.isArray())
addJsonArray(document.array(), currentKey, value);
else if (document.isObject())
addJsonObject(document.object(), currentKey, value);
else
return QMakeEvaluator::ReturnFalse;

return QMakeEvaluator::ReturnTrue;
}
#endif

QMakeEvaluator::VisitReturn
QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
const QString &contents)
Expand Down Expand Up @@ -1278,6 +1355,18 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
m_valuemapStack.top()[var] = statics.fakeValue;
return ReturnTrue;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
case T_PARSE_JSON: {
if (args.count() != 2) {
evalError(fL1S("parseJson(variable, into) requires two arguments."));
return ReturnFalse;
}

QByteArray json = values(args.at(0).toKey()).join(QLatin1Char(' ')).toUtf8();
QString parseInto = args.at(1).toQString(m_tmp2);
return parseJsonInto(json, parseInto, &m_valuemapStack.top());
}
#endif
case T_INCLUDE: {
if (args.count() < 1 || args.count() > 3) {
evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments."));
Expand Down
17 changes: 15 additions & 2 deletions qmake/qmake.pri
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ bootstrap { #Qt code
qvsnprintf.cpp \
qxmlstream.cpp \
qxmlutils.cpp \
qlogging.cpp
qlogging.cpp \
qjson.cpp \
qjsondocument.cpp \
qjsonparser.cpp \
qjsonarray.cpp \
qjsonobject.cpp \
qjsonvalue.cpp

HEADERS+= \
qbitarray.h \
Expand Down Expand Up @@ -126,7 +132,14 @@ bootstrap { #Qt code
quuid.h \
qvector.h \
qxmlstream.h \
qxmlutils.h
qxmlutils.h \
qjson.h \
qjsondocument.h \
qjsonparser.h \
qjsonwriter.h \
qjsonarray.h \
qjsonobject.h \
qjsonvalue.h

unix {
SOURCES += qfilesystemengine_unix.cpp qfilesystemiterator_unix.cpp qfsfileengine_unix.cpp
Expand Down
1 change: 1 addition & 0 deletions qmake/qmake.pro
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ VPATH += $$QT_SOURCE_TREE/src/corelib/global \
$$QT_SOURCE_TREE/src/corelib/plugin \
$$QT_SOURCE_TREE/src/corelib/xml \
$$QT_SOURCE_TREE/src/corelib/io \
$$QT_SOURCE_TREE/src/corelib/json \
$$QT_SOURCE_TREE/tools/shared/windows

INCLUDEPATH += . \
Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonarray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@ void QJsonArray::compact()
}


#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
QDebug operator<<(QDebug dbg, const QJsonArray &a)
{
if (!a.a) {
Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonarray.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class Q_CORE_EXPORT QJsonArray
QJsonPrivate::Array *a;
};

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &);
#endif

Expand Down
6 changes: 5 additions & 1 deletion src/corelib/json/qjsondocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,12 @@ QVariant QJsonDocument::toVariant() const
\sa fromJson()
*/
#ifndef QT_JSON_READONLY
QByteArray QJsonDocument::toJson() const
{
return toJson(Indented);
}
#endif

/*!
\enum QJsonDocument::JsonFormat
Expand Down Expand Up @@ -338,6 +340,7 @@ QByteArray QJsonDocument::toJson() const
\sa fromJson(), JsonFormat
*/
#ifndef QT_JSON_READONLY
QByteArray QJsonDocument::toJson(JsonFormat format) const
{
if (!d)
Expand All @@ -352,6 +355,7 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const

return json;
}
#endif

/*!
Parses a UTF-8 encoded JSON document and creates a QJsonDocument
Expand Down Expand Up @@ -562,7 +566,7 @@ bool QJsonDocument::isNull() const
return (d == 0);
}

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
QDebug operator<<(QDebug dbg, const QJsonDocument &o)
{
if (!o.d) {
Expand Down
4 changes: 2 additions & 2 deletions src/corelib/json/qjsondocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class Q_CORE_EXPORT QJsonDocument

#ifdef Q_QDOC
QByteArray toJson(JsonFormat format = Indented) const;
#else
#elif !defined(QT_JSON_READONLY)
QByteArray toJson() const; //### Merge in Qt6
QByteArray toJson(JsonFormat format) const;
#endif
Expand Down Expand Up @@ -148,7 +148,7 @@ class Q_CORE_EXPORT QJsonDocument
QJsonPrivate::Data *d;
};

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &);
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ void QJsonObject::setValueAt(int i, const QJsonValue &val)
insert(e->key(), val);
}

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
QDebug operator<<(QDebug dbg, const QJsonObject &o)
{
if (!o.o) {
Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class Q_CORE_EXPORT QJsonObject
QJsonPrivate::Object *o;
};

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &);
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonvalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ QJsonValue QJsonValueRef::toValue() const
return o->valueAt(index);
}

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
QDebug operator<<(QDebug dbg, const QJsonValue &o)
{
switch (o.t) {
Expand Down
2 changes: 1 addition & 1 deletion src/corelib/json/qjsonvalue.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class Q_CORE_EXPORT QJsonValueRef
uint index : 31;
};

#ifndef QT_NO_DEBUG_STREAM
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &);
#endif

Expand Down
26 changes: 26 additions & 0 deletions tests/auto/tools/qmake/testdata/json/json.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
jsontext = $$cat($$PWD/test.json)
parseJson(jsontext, json)

# print all keys
message(json._KEYS_ $${json._KEYS_})

# print array
message(json.array._KEYS_ $${json.array._KEYS_})
for(key, json.array._KEYS_): \
message(json.array.$${key} $$eval(json.array.$${key}))

# print object
message(json.object._KEYS_ $${json.object._KEYS_})
for(key, json.object._KEYS_): \
message(json.object.$${key} $$eval(json.object.$${key}))

# print value tyes
message(json.string: $${json.string})
message(json.number: $${json.number})
message(json.true: $${json.true})
message(json.false: $${json.false})
message(json.null: $${json.null})

# check that booleans work
$${json.true}: message(json.true is true)
!$${json.false}: message(json.false is false)
9 changes: 9 additions & 0 deletions tests/auto/tools/qmake/testdata/json/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"array" : ["arrayItem1", "arrayItem2", "arrayItem3"],
"object" : { "key1" : "objectValue1", "key2" : "objectValue2" },
"string" : "test string",
"number" : 999,
"true" : true,
"false" :false,
"null" : null
}
Loading

0 comments on commit 89ef515

Please sign in to comment.