File indexing completed on 2024-05-19 04:41:20

0001 /*
0002     SPDX-FileCopyrightText: 2019 Daniel Mensinger <daniel@mensinger-ka.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mesonoptions.h"
0008 
0009 #include <debug.h>
0010 
0011 #include <QHash>
0012 #include <QJsonArray>
0013 #include <QJsonObject>
0014 #include <QList>
0015 #include <QWidget>
0016 
0017 #include <algorithm>
0018 #include <vector>
0019 
0020 using namespace std;
0021 
0022 static const QHash<QString, MesonOptionBase::Section> STRING2SECTION = {
0023     { QStringLiteral("core"), MesonOptionBase::CORE },
0024     { QStringLiteral("backend"), MesonOptionBase::BACKEND },
0025     { QStringLiteral("base"), MesonOptionBase::BASE },
0026     { QStringLiteral("compiler"), MesonOptionBase::COMPILER },
0027     { QStringLiteral("directory"), MesonOptionBase::DIRECTORY },
0028     { QStringLiteral("user"), MesonOptionBase::USER },
0029     { QStringLiteral("test"), MesonOptionBase::TEST },
0030 };
0031 
0032 static const QHash<QString, MesonOptionBase::Type> STRING2TYPE = {
0033     { QStringLiteral("array"), MesonOptionBase::ARRAY },   { QStringLiteral("boolean"), MesonOptionBase::BOOLEAN },
0034     { QStringLiteral("combo"), MesonOptionBase::COMBO },   { QStringLiteral("integer"), MesonOptionBase::INTEGER },
0035     { QStringLiteral("string"), MesonOptionBase::STRING },
0036 };
0037 
0038 MesonOptions::MesonOptions(const QJsonArray& arr)
0039 {
0040     fromJSON(arr);
0041 }
0042 
0043 // Option constructors
0044 
0045 MesonOptionBase::MesonOptionBase(const QString& name, const QString& description, MesonOptionBase::Section section)
0046     : m_name(name)
0047     , m_description(description)
0048     , m_section(section)
0049 {
0050 }
0051 
0052 MesonOptionArray::MesonOptionArray(const QString& name, const QString& description, MesonOptionBase::Section section,
0053                                    QStringList value)
0054     : MesonOptionBase(name, description, section)
0055     , m_value(value)
0056     , m_initialValue(value)
0057 {
0058 }
0059 
0060 MesonOptionBool::MesonOptionBool(const QString& name, const QString& description, MesonOptionBase::Section section,
0061                                  bool value)
0062     : MesonOptionBase(name, description, section)
0063     , m_value(value)
0064     , m_initialValue(value)
0065 {
0066 }
0067 
0068 MesonOptionCombo::MesonOptionCombo(const QString& name, const QString& description, MesonOptionBase::Section section,
0069                                    QString value, QStringList choices)
0070     : MesonOptionBase(name, description, section)
0071     , m_value(value)
0072     , m_initialValue(value)
0073     , m_choices(choices)
0074 {
0075 }
0076 
0077 MesonOptionInteger::MesonOptionInteger(const QString& name, const QString& description,
0078                                        MesonOptionBase::Section section, int value)
0079     : MesonOptionBase(name, description, section)
0080     , m_value(value)
0081     , m_initialValue(value)
0082 {
0083 }
0084 
0085 MesonOptionString::MesonOptionString(const QString& name, const QString& description, MesonOptionBase::Section section,
0086                                      QString value)
0087     : MesonOptionBase(name, description, section)
0088     , m_value(value)
0089     , m_initialValue(value)
0090 {
0091 }
0092 
0093 QStringList MesonOptionCombo::choices() const
0094 {
0095     return m_choices;
0096 }
0097 
0098 // Type functions
0099 
0100 MesonOptionBase::Type MesonOptionArray::type() const
0101 {
0102     return ARRAY;
0103 }
0104 
0105 MesonOptionBase::Type MesonOptionBool::type() const
0106 {
0107     return BOOLEAN;
0108 }
0109 
0110 MesonOptionBase::Type MesonOptionCombo::type() const
0111 {
0112     return COMBO;
0113 }
0114 
0115 MesonOptionBase::Type MesonOptionInteger::type() const
0116 {
0117     return INTEGER;
0118 }
0119 
0120 MesonOptionBase::Type MesonOptionString::type() const
0121 {
0122     return STRING;
0123 }
0124 
0125 // Value functions
0126 
0127 QString MesonOptionArray::value() const
0128 {
0129     QStringList tmp;
0130     tmp.reserve(m_value.size());
0131     transform(begin(m_value), end(m_value), back_inserter(tmp),
0132               [](const QString& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); });
0133     return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]");
0134 }
0135 
0136 QString MesonOptionBool::value() const
0137 {
0138     return m_value ? QStringLiteral("true") : QStringLiteral("false");
0139 }
0140 
0141 QString MesonOptionCombo::value() const
0142 {
0143     return m_value;
0144 }
0145 
0146 QString MesonOptionInteger::value() const
0147 {
0148     return QString::number(m_value);
0149 }
0150 
0151 QString MesonOptionString::value() const
0152 {
0153     return m_value;
0154 }
0155 
0156 // Initial value functions
0157 
0158 QString MesonOptionArray::initialValue() const
0159 {
0160     QStringList tmp;
0161     tmp.reserve(m_initialValue.size());
0162     transform(begin(m_initialValue), end(m_initialValue), back_inserter(tmp),
0163               [](const QString& str) -> QString { return QStringLiteral("'") + str + QStringLiteral("'"); });
0164     return QStringLiteral("[") + tmp.join(QStringLiteral(", ")) + QStringLiteral("]");
0165 }
0166 
0167 QString MesonOptionBool::initialValue() const
0168 {
0169     return m_initialValue ? QStringLiteral("true") : QStringLiteral("false");
0170 }
0171 
0172 QString MesonOptionCombo::initialValue() const
0173 {
0174     return m_initialValue;
0175 }
0176 
0177 QString MesonOptionInteger::initialValue() const
0178 {
0179     return QString::number(m_initialValue);
0180 }
0181 
0182 QString MesonOptionString::initialValue() const
0183 {
0184     return m_initialValue;
0185 }
0186 
0187 // Reset functions
0188 
0189 void MesonOptionArray::reset()
0190 {
0191     m_value = m_initialValue;
0192 }
0193 
0194 void MesonOptionBool::reset()
0195 {
0196     m_value = m_initialValue;
0197 }
0198 
0199 void MesonOptionCombo::reset()
0200 {
0201     m_value = m_initialValue;
0202 }
0203 
0204 void MesonOptionInteger::reset()
0205 {
0206     m_value = m_initialValue;
0207 }
0208 
0209 void MesonOptionString::reset()
0210 {
0211     m_value = m_initialValue;
0212 }
0213 
0214 // Raw value functions
0215 
0216 QStringList MesonOptionArray::rawValue() const
0217 {
0218     return m_value;
0219 }
0220 
0221 bool MesonOptionBool::rawValue() const
0222 {
0223     return m_value;
0224 }
0225 
0226 QString MesonOptionCombo::rawValue() const
0227 {
0228     return m_value;
0229 }
0230 
0231 int MesonOptionInteger::rawValue() const
0232 {
0233     return m_value;
0234 }
0235 
0236 QString MesonOptionString::rawValue() const
0237 {
0238     return m_value;
0239 }
0240 
0241 // Set value functions
0242 
0243 void MesonOptionArray::setValue(const QStringList& val)
0244 {
0245     m_value = val;
0246 }
0247 
0248 void MesonOptionBool::setValue(bool val)
0249 {
0250     m_value = val;
0251 }
0252 
0253 void MesonOptionCombo::setValue(const QString& val)
0254 {
0255     m_value = val;
0256 }
0257 
0258 void MesonOptionInteger::setValue(int val)
0259 {
0260     m_value = val;
0261 }
0262 
0263 void MesonOptionString::setValue(const QString& val)
0264 {
0265     m_value = val;
0266 }
0267 
0268 // Set value from string
0269 
0270 void MesonOptionArray::setFromString(const QString& value)
0271 {
0272     setValue({ value });
0273 }
0274 
0275 void MesonOptionBool::setFromString(const QString& value)
0276 {
0277     setValue(value.toLower() == QLatin1String("true"));
0278 }
0279 
0280 void MesonOptionCombo::setFromString(const QString& value)
0281 {
0282     setValue(value);
0283 }
0284 
0285 void MesonOptionInteger::setFromString(const QString& value)
0286 {
0287     setValue(value.toInt());
0288 }
0289 
0290 void MesonOptionString::setFromString(const QString& value)
0291 {
0292     setValue(value);
0293 }
0294 
0295 // Base option functions
0296 
0297 MesonOptionBase::~MesonOptionBase() {}
0298 
0299 QString MesonOptionBase::name() const
0300 {
0301     return m_name;
0302 }
0303 
0304 QString MesonOptionBase::description() const
0305 {
0306     return m_description;
0307 }
0308 
0309 MesonOptionBase::Section MesonOptionBase::section() const
0310 {
0311     return m_section;
0312 }
0313 
0314 QString MesonOptionBase::mesonArg() const
0315 {
0316     return QStringLiteral("-D") + m_name + QStringLiteral("=") + value();
0317 }
0318 
0319 bool MesonOptionBase::isUpdated() const
0320 {
0321     return value() != initialValue();
0322 }
0323 
0324 MesonOptionPtr MesonOptionBase::fromJSON(const QJsonObject& obj)
0325 {
0326     auto nameJV = obj[QStringLiteral("name")];
0327     auto descriptionJV = obj[QStringLiteral("description")];
0328     auto sectionJV = obj[QStringLiteral("section")];
0329     auto typeJV = obj[QStringLiteral("type")];
0330     auto valueJV = obj[QStringLiteral("value")];
0331 
0332     // Check all values
0333     for (const auto& i : { nameJV, descriptionJV, sectionJV, typeJV }) {
0334         if (!i.isString()) {
0335             qCWarning(KDEV_Meson) << "OPT: Base type validation failed" << typeJV.toString();
0336             return nullptr;
0337         }
0338     }
0339 
0340     // Work around meson bug https://github.com/mesonbuild/meson/pull/5646 by
0341     // removing the first space and everything after that.
0342     QString sectionStr = sectionJV.toString();
0343     int spacePos = sectionStr.indexOf(QLatin1Char(' '));
0344     if (spacePos > 0) {
0345         sectionStr = sectionStr.left(spacePos);
0346     }
0347 
0348     auto sectionIter = STRING2SECTION.find(sectionStr);
0349     auto typeIter = STRING2TYPE.find(typeJV.toString());
0350 
0351     if (sectionIter == end(STRING2SECTION) || typeIter == end(STRING2TYPE)) {
0352         qCWarning(KDEV_Meson) << "OPT: Unknown type or section " << typeJV.toString() << " / " << sectionJV.toString();
0353         return nullptr;
0354     }
0355 
0356     Section section = *sectionIter;
0357     Type type = *typeIter;
0358     QString name = nameJV.toString();
0359     QString description = descriptionJV.toString();
0360 
0361     switch (type) {
0362     case ARRAY: {
0363         if (!valueJV.isArray()) {
0364             return nullptr;
0365         }
0366 
0367         QJsonArray raw = valueJV.toArray();
0368         QStringList values;
0369         values.reserve(raw.size());
0370         transform(begin(raw), end(raw), back_inserter(values), [](const QJsonValue& v) { return v.toString(); });
0371         return make_shared<MesonOptionArray>(name, description, section, values);
0372     }
0373 
0374     case BOOLEAN:
0375         if (!valueJV.isBool()) {
0376             return nullptr;
0377         }
0378         return make_shared<MesonOptionBool>(name, description, section, valueJV.toBool());
0379 
0380     case COMBO: {
0381         auto choicesJV = obj[QStringLiteral("choices")];
0382         if (!valueJV.isString() || !choicesJV.isArray()) {
0383             return nullptr;
0384         }
0385 
0386         QJsonArray raw = choicesJV.toArray();
0387         QStringList choices;
0388         choices.reserve(raw.size());
0389         transform(begin(raw), end(raw), back_inserter(choices), [](const QJsonValue& v) { return v.toString(); });
0390         return make_shared<MesonOptionCombo>(name, description, section, valueJV.toString(), choices);
0391     }
0392 
0393     case INTEGER:
0394         if (!valueJV.isDouble()) {
0395             return nullptr;
0396         }
0397         return make_shared<MesonOptionInteger>(name, description, section, valueJV.toInt());
0398 
0399     case STRING:
0400         if (!valueJV.isString()) {
0401             return nullptr;
0402         }
0403         return make_shared<MesonOptionString>(name, description, section, valueJV.toString());
0404     }
0405 
0406     qCWarning(KDEV_Meson) << "OPT: Unknown type " << typeJV.toString();
0407     return nullptr;
0408 }
0409 
0410 int MesonOptions::numChanged() const
0411 {
0412     int sum = 0;
0413     for (auto i : m_options) {
0414         if (i && i->isUpdated()) {
0415             ++sum;
0416         }
0417     }
0418     return sum;
0419 }
0420 
0421 QStringList MesonOptions::getMesonArgs() const
0422 {
0423     QStringList result;
0424     result.reserve(m_options.size());
0425 
0426     for (auto i : m_options) {
0427         if (i && i->isUpdated()) {
0428             result << i->mesonArg();
0429         }
0430     }
0431     return result;
0432 }
0433 
0434 void MesonOptions::fromJSON(const QJsonArray& arr)
0435 {
0436     m_options.clear();
0437     m_options.reserve(arr.size());
0438 
0439     for (const QJsonValue& i : arr) {
0440         if (!i.isObject()) {
0441             continue;
0442         }
0443 
0444         auto ptr = MesonOptionBase::fromJSON(i.toObject());
0445         if (ptr) {
0446             m_options += ptr;
0447         } else {
0448             qCWarning(KDEV_Meson) << "OPT: Failed to parse " << i.toObject();
0449         }
0450     }
0451 }
0452 
0453 void MesonOptions::print() const
0454 {
0455     for (const auto& i : m_options) {
0456         qCDebug(KDEV_Meson) << i->name() << " = " << i->value() << "  [" << i->type() << "] -- " << i->section();
0457     }
0458 }
0459 
0460 QVector<MesonOptionPtr> MesonOptions::options()
0461 {
0462     return m_options;
0463 }