From 2ee6fbbd74767f86261d54dba6c91f2861f705cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= <morten.sorvig@digia.com> Date: Mon, 28 Oct 2013 19:09:58 +0100 Subject: [PATCH] Add JSON parsing support to qmake. 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 <lars.knoll@digia.com> (cherry picked from qtbase/89ef515177fd5a0b5d95dcffd5fd0b0669e3625a) Reviewed-by: Daniel Teske <daniel.teske@digia.com> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com> --- src/shared/proparser/qmakebuiltins.cpp | 91 +++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 3e30c3b85e1..36515f11320 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -44,6 +44,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 @@ -89,7 +94,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 }; @@ -166,6 +171,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 }, @@ -274,6 +282,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) @@ -1266,6 +1343,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.")); -- GitLab