diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 89abb79fcdf..b35679ed348 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -3,10 +3,21 @@ #include "converters.h" -#include +#include namespace QmlProjectManager::Converters { +const static QStringList qmlFilesFilter{QStringLiteral("*.qml")}; +const static QStringList javaScriptFilesFilter{QStringLiteral("*.js"), QStringLiteral("*.ts")}; +const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"), + QStringLiteral("*.jpg"), + QStringLiteral("*.png"), + QStringLiteral("*.svg"), + QStringLiteral("*.hdr"), + QStringLiteral("*.ktx")}; + +QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented); + QString jsonToQmlProject(const QJsonObject &rootObject) { QString qmlProjectString; @@ -19,6 +30,11 @@ QString jsonToQmlProject(const QJsonObject &rootObject) QJsonObject environmentConfig = rootObject["environment"].toObject(); QJsonObject deploymentConfig = rootObject["deployment"].toObject(); QJsonArray filesConfig = rootObject["fileGroups"].toArray(); + QJsonObject otherProperties = rootObject["otherProperties"].toObject(); + + QJsonObject mcuObject = rootObject["mcu"].toObject(); + QJsonObject mcuConfig = mcuObject["config"].toObject(); + QJsonObject mcuModule = mcuObject["module"].toObject(); int indentationLevel = 0; @@ -35,6 +51,8 @@ QString jsonToQmlProject(const QJsonObject &rootObject) }; auto appendString = [&appendItem](const QString &key, const QString &val) { + if (val.isEmpty()) + return; appendItem(key, val, true); }; @@ -42,7 +60,9 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendItem(key, QString::fromStdString(val ? "true" : "false"), false); }; - auto appendArray = [&appendItem](const QString &key, const QStringList &vals) { + auto appendStringArray = [&appendItem](const QString &key, const QStringList &vals) { + if (vals.isEmpty()) + return; QString finalString; for (const QString &value : vals) finalString.append("\"").append(value).append("\"").append(","); @@ -52,6 +72,27 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendItem(key, finalString, false); }; + auto appendJsonArray = [&appendItem, &indentationLevel](const QString &key, + const QJsonArray &vals) { + if (vals.isEmpty()) + return; + appendItem(key, jsonValueToString(vals, indentationLevel, /*indented*/ true), false); + }; + + auto appendProperties = [&appendItem, &indentationLevel](const QJsonObject &props, + const QString &prefix) { + for (const auto &key : props.keys()) { + QJsonValue val = props[key]; + QString keyWithPrefix = key; + if (!prefix.isEmpty()) { + keyWithPrefix.prepend(prefix + "."); + } + appendItem(keyWithPrefix, + jsonValueToString(val, indentationLevel, /*indented*/ false), + false); + } + }; + auto startObject = [&ts, &indentationLevel](const QString &objectName) { ts << Qt::endl << QString(" ").repeated(indentationLevel * 4) << objectName << " {" << Qt::endl; @@ -63,22 +104,32 @@ QString jsonToQmlProject(const QJsonObject &rootObject) ts << QString(" ").repeated(indentationLevel * 4) << "}" << Qt::endl; }; - auto appendFileGroup = - [&startObject, &endObject, &appendString, &appendArray](const QJsonObject &fileGroup, - const QString &qmlKey) { - startObject(qmlKey); - appendString("directory", fileGroup["directory"].toString()); - appendString("filter", fileGroup["filters"].toVariant().toStringList().join(";")); - appendArray("files", fileGroup["files"].toVariant().toStringList()); - endObject(); - }; - - auto appendQmlFileGroup = - [&startObject, &endObject, &appendString](const QJsonObject &fileGroup) { - startObject("QmlFiles"); - appendString("directory", fileGroup["directory"].toString()); - endObject(); - }; + auto appendFileGroup = [&startObject, + &endObject, + &appendString, + &appendProperties, + &appendJsonArray](const QJsonObject &fileGroup, + const QString &nodeName) { + startObject(nodeName); + appendString("directory", fileGroup["directory"].toString()); + QStringList filters = fileGroup["filters"].toVariant().toStringList(); + QStringList filter = {}; + if (nodeName.toLower() == "qmlfiles") { + filter = qmlFilesFilter; + } else if (nodeName.toLower() == "imagefiles") { + filter = imageFilesFilter; + } else if (nodeName.toLower() == "javascriptfiles") { + filter = javaScriptFilesFilter; + } + for (const QString &entry : filter) { + filters.removeOne(entry); + } + appendString("filter", filters.join(";")); + appendJsonArray("files", fileGroup["files"].toArray()); + appendProperties(fileGroup["mcuProperties"].toObject(), "MCU"); + appendProperties(fileGroup["otherProperties"].toObject(), ""); + endObject(); + }; // start creating the file content appendComment("prop: json-converted"); @@ -93,24 +144,37 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("mainUiFile", runConfig["mainUiFile"].toString()); appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); appendBool("widgetApp", runConfig["widgetApp"].toBool()); - appendArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); + appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); appendBreak(); appendString("qdsVersion", versionConfig["designStudio"].toString()); appendString("quickVersion", versionConfig["qtQuick"].toString()); appendBool("qt6Project", versionConfig["qt"].toString() == "6"); - appendBool("qtForMCUs", !(rootObject["mcuConfig"].toObject().isEmpty())); - appendBreak(); - appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); - appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); - appendArray("supportedLanguages", - languageConfig["supportedLanguages"].toVariant().toStringList()); + appendBool("qtForMCUs", + mcuObject["enabled"].toBool() || !mcuConfig.isEmpty() || !mcuModule.isEmpty()); + if (!languageConfig.isEmpty()) { + appendBreak(); + appendBool("multilanguageSupport", languageConfig["multiLanguageSupport"].toBool()); + appendString("primaryLanguage", languageConfig["primaryLanguage"].toString()); + appendStringArray("supportedLanguages", + languageConfig["supportedLanguages"].toVariant().toStringList()); + } + + // Since different versions of Qt for MCUs may introduce new properties, we collect all + // unknown properties in a separate object. + // We need to avoid losing content regardless of which QDS/QUL version combo is used. + if (!otherProperties.isEmpty()) { + appendBreak(); + appendProperties(otherProperties, ""); + } // append Environment object - startObject("Environment"); - for (const QString &key : environmentConfig.keys()) - appendItem(key, environmentConfig[key].toString(), true); - - endObject(); + if (!environmentConfig.isEmpty()) { + startObject("Environment"); + for (const QString &key : environmentConfig.keys()) { + appendItem(key, environmentConfig[key].toString(), true); + } + endObject(); + } // append ShaderTool object if (!shaderConfig["args"].toVariant().toStringList().isEmpty()) { @@ -118,16 +182,35 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("args", shaderConfig["args"].toVariant().toStringList().join(" ").replace("\"", "\\\"")); - appendArray("files", shaderConfig["files"].toVariant().toStringList()); + appendStringArray("files", shaderConfig["files"].toVariant().toStringList()); + endObject(); + } + + // append the MCU.Config object + if (!mcuConfig.isEmpty()) { + // Append MCU.Config + startObject("MCU.Config"); + appendProperties(mcuConfig, ""); + endObject(); + } + + // Append the MCU.Module object + if (!mcuModule.isEmpty()) { + // Append MCU.Module + startObject("MCU.Module"); + appendProperties(mcuModule, ""); endObject(); } // append files objects for (const QJsonValue &fileGroup : filesConfig) { - if (fileGroup["filters"].toArray().contains("*.qml")) - appendQmlFileGroup(fileGroup.toObject()); - else - appendFileGroup(fileGroup.toObject(), "Files"); + QString nodeType = QString("%1Files").arg(fileGroup["type"].toString()); + if (fileGroup["type"].toString().isEmpty() + && fileGroup["filters"].toArray().contains("*.qml")) { + // TODO: IS this important? It turns Files node with *.qml in the filters into QmlFiles nodes + nodeType = "QmlFiles"; + } + appendFileGroup(fileGroup.toObject(), nodeType); } endObject(); // Closing 'Project' @@ -169,7 +252,12 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) QJsonObject runConfigObject; QJsonObject deploymentObject; QJsonObject mcuObject; + QJsonObject mcuConfigObject; + QJsonObject mcuModuleObject; QJsonObject shaderToolObject; + QJsonObject otherProperties; + + bool qtForMCUs = false; // convert the the non-object props for (const QString &propName : rootNode->propertyNames()) { @@ -177,10 +265,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) QString objKey = QString(propName).remove("QDS.", Qt::CaseInsensitive); QJsonValue value = rootNode->property(propName).value.toJsonValue(); - if (propName.startsWith("mcu.", Qt::CaseInsensitive)) { - currentObj = &mcuObject; - objKey = QString(propName).remove("MCU."); - } else if (propName.contains("language", Qt::CaseInsensitive)) { + if (propName.contains("language", Qt::CaseInsensitive)) { currentObj = &languageObject; if (propName.contains("multilanguagesupport", Qt::CaseInsensitive)) // fixing the camelcase @@ -200,12 +285,17 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) { currentObj = &deploymentObject; } else if (propName.contains("qtformcus", Qt::CaseInsensitive)) { - currentObj = &mcuObject; - objKey = "mcuEnabled"; + qtForMCUs = value.toBool(); + continue; } else if (propName.contains("qt6project", Qt::CaseInsensitive)) { currentObj = &versionObject; objKey = "qt"; value = rootNode->property(propName).value.toBool() ? "6" : "5"; + } else if (propName.contains("importpaths", Qt::CaseInsensitive)) { + objKey = "importPaths"; + } else { + currentObj = &otherProperties; + objKey = propName; // With prefix } currentObj->insert(objKey, value); @@ -220,14 +310,20 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) versionObject.insert("qt", "5"); } + rootObject.insert("otherProperties", otherProperties); + // convert the the object props for (const QmlJS::SimpleReaderNode::Ptr &childNode : rootNode->children()) { if (childNode->name().contains("files", Qt::CaseInsensitive)) { - const QString childNodeName = childNode->name().toLower().remove("qds."); + QString childNodeName = childNode->name().remove("qds.", Qt::CaseInsensitive); + qsizetype filesPos = childNodeName.indexOf("files", 0, Qt::CaseInsensitive); + const QString childNodeType = childNodeName.first(filesPos); + childNodeName = childNodeName.toLower(); + QJsonArray childNodeFiles = childNode->property("files").value.toJsonArray(); QString childNodeDirectory = childNode->property("directory").value.toString(); - QStringList filters = childNode->property("filter").value.toString().split(";"); - filters.removeAll(""); + QStringList filters + = childNode->property("filter").value.toString().split(";", Qt::SkipEmptyParts); QJsonArray childNodeFilters = QJsonArray::fromStringList(filters); // files have priority over filters @@ -239,25 +335,22 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) std::for_each(filterSource.begin(), filterSource.end(), [&childNodeFilters](const auto &value) { - childNodeFilters << value; + if (!childNodeFilters.contains(value)) { + childNodeFilters << value; + } }); }; // Those 3 file groups are the special ones - // that have a default set of filters. After the first - // conversion (QmlProject -> JSON) they are converted to - // the generic file group format ('Files' or 'QDS.Files') + // that have a default set of filters. + // The default filters are written to the + // qmlproject file after conversion if (childNodeName == "qmlfiles") { - inserter({QStringLiteral("*.qml")}); + inserter(qmlFilesFilter); } else if (childNodeName == "javascriptfiles") { - inserter({QStringLiteral("*.js"), QStringLiteral("*.ts")}); + inserter(javaScriptFilesFilter); } else if (childNodeName == "imagefiles") { - inserter({QStringLiteral("*.jpeg"), - QStringLiteral("*.jpg"), - QStringLiteral("*.png"), - QStringLiteral("*.svg"), - QStringLiteral("*.hdr"), - QStringLiteral(".ktx")}); + inserter(imageFilesFilter); } } @@ -266,6 +359,26 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) targetObject.insert("directory", childNodeDirectory); targetObject.insert("filters", childNodeFilters); targetObject.insert("files", childNodeFiles); + targetObject.insert("type", childNodeType); + + QJsonObject mcuPropertiesObject; + QJsonObject otherPropertiesObject; + for (const auto &propName : childNode->propertyNames()) { + if (propName == "directory" || propName == "filter" || propName == "files") { + continue; + } + + auto val = QJsonValue::fromVariant(childNode->property(propName).value); + + if (propName.startsWith("MCU.", Qt::CaseInsensitive)) { + mcuPropertiesObject.insert(propName.mid(4), val); + } else { + otherPropertiesObject.insert(propName, val); + } + } + + targetObject.insert("mcuProperties", mcuPropertiesObject); + targetObject.insert("otherProperties", otherPropertiesObject); fileGroupsObject.append(targetObject); } else if (childNode->name().contains("shadertool", Qt::CaseInsensitive)) { @@ -283,21 +396,51 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) shaderToolObject.insert("args", QJsonArray::fromStringList(args)); shaderToolObject.insert("files", childNode->property("files").value.toJsonValue()); + } else if (childNode->name().contains("config", Qt::CaseInsensitive)) { + mcuConfigObject = nodeToJsonObject(childNode); + } else if (childNode->name().contains("module", Qt::CaseInsensitive)) { + mcuModuleObject = nodeToJsonObject(childNode); } else { rootObject.insert(toCamelCase(childNode->name().remove("qds.", Qt::CaseInsensitive)), nodeToJsonObject(childNode)); } } + mcuObject.insert("config", mcuConfigObject); + mcuObject.insert("module", mcuModuleObject); + qtForMCUs |= !(mcuModuleObject.isEmpty() && mcuConfigObject.isEmpty()); + mcuObject.insert("enabled", qtForMCUs); + rootObject.insert("fileGroups", fileGroupsObject); rootObject.insert("language", languageObject); rootObject.insert("versions", versionObject); rootObject.insert("runConfig", runConfigObject); rootObject.insert("deployment", deploymentObject); - rootObject.insert("mcuConfig", mcuObject); + rootObject.insert("mcu", mcuObject); if (!shaderToolObject.isEmpty()) rootObject.insert("shaderTool", shaderToolObject); rootObject.insert("fileVersion", 1); return rootObject; } + +QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented) +{ + if (val.isArray()) { + auto jsonFormat = indented ? QJsonDocument::JsonFormat::Indented + : QJsonDocument::JsonFormat::Compact; + QString str = QString::fromUtf8((QJsonDocument(val.toArray()).toJson(jsonFormat))); + if (indented) { + // Strip trailing newline + str.chop(1); + } + return str.replace("\n", QString(" ").repeated(indentationLevel * 4).prepend("\n")); + } else if (val.isBool()) { + return val.toBool() ? QString("true") : QString("false"); + } else if (val.isDouble()) { + return QString("%1").arg(val.toDouble()); + } else { + return val.toString().prepend("\"").append("\""); + } +} + } // namespace QmlProjectManager::Converters diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index c80f13e9b2b..77d0eea82d5 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -130,7 +130,7 @@ QString QmlProjectItem::targetDirectory() const bool QmlProjectItem::isQt4McuProject() const { - return m_project["mcuConfig"].toObject()["mcuEnabled"].toBool(); + return m_project["mcu"].toObject()["enabled"].toBool(); } Utils::EnvironmentItems QmlProjectItem::environment() const diff --git a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp index a1ad263cf7c..adef15462a7 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/converters-test.cpp @@ -67,7 +67,9 @@ INSTANTIATE_TEST_SUITE_P(QmlProjectItem, QmlProjectConverter, ::testing::Values(QString("test-set-1"), QString("test-set-2"), - QString("test-set-3"))); + QString("test-set-3"), + QString("test-set-mcu-1"), + QString("test-set-mcu-2"))); TEST_P(QmlProjectConverter, qml_project_to_json) { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md index bffd46b79ee..c73ef285904 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/README.md +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/README.md @@ -30,6 +30,37 @@ Test functions iterate over the "test-set-*" folders and run the tests by using * **purpose**: testing `QDS.` prefixes * **origin**: copy of test-set-1 +### Qt for MCUs test sets + +These tests define the requirements for Qt for MCUs (QUL) projects. They are maintained by the Qt for MCUs +team. Please do not make changes to these test sets before consulting them. These tests help make sure +that QDS and QUL are aligned on the qmlproject format and that new features in QDS does not break +qmlproject files for MCU projects. The test set will be tested in the Qt for MCUs repositories +to make sure both the original and the converted qmlprojects build correctly. + +The qmlproject files in the test set aim to cover all the possible contents of a Qt for MCUs qmlproject, +but since new features are added with every release, it is not guaranteed to be exhaustive. + +Some main points for qmlproject files for MCU projects: + +* Unknown content in the qmlproject file will cause errors in the QUL tooling +* Any node or property with the `QDS`-prefix is ignored by QUL. When adding new properties, + these must either be prefixed or communicated with the MCU team to whitelist them in the tooling. +* Plain `Files` nodes are ignored by QUL, it needs to know the node type. The node contents will be processed + by different tools depending on which type of files it includes. The node types used by QUL are: + + `QmlFiles` + + `ImageFiles` + + `FontFiles` + + `ModuleFiles` + + `InterfaceFiles` + + `TranslationFiles` +* In addition to adding files to the project, any of the `*Files` nodes listed can also contain properties to + apply to the file group added by the node. +* MCU projects may have MCU-specific configuration nodes: `MCU.Config` and `MCU.Module`. +* A new version of Qt for MCUs may add new properties to any node, including `Project`. For this reason + it is not possible to define an exact set of properties to support which is valid across all versions. +* Qt for MCUs developers must still edit and maintain qmlproject files manually. The converted format + of the qmlproject file should be easy to read and edit by hand ## File Filters test data diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml index 4db64285c7e..2a67b12d505 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml @@ -40,76 +40,56 @@ Project { directory: "imports" } - Files { + JavaScriptFiles { directory: "content" - filter: "*.js;*.ts" - files: [ ] } - Files { + JavaScriptFiles { directory: "imports" - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "content" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } - Files { + ImageFiles { directory: "asset_imports" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } Files { - directory: "" filter: "*.ttf;*.otf;*.ctf" - files: [ ] } Files { - directory: "" filter: "*.wav;*.mp3" - files: [ ] } Files { - directory: "" filter: "*.mp4" - files: [ ] } Files { - directory: "" filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - files: [ ] } Files { directory: "asset_imports" filter: "*.mesh" - files: [ ] } QmlFiles { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson index 4b039c2e1ed..c4475af39c4 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson @@ -16,7 +16,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "imports", @@ -24,7 +29,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "content", @@ -33,7 +43,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "imports", @@ -42,7 +57,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "content", @@ -54,8 +74,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "asset_imports", @@ -67,8 +92,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -76,7 +106,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -84,7 +119,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -92,7 +132,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -102,7 +147,12 @@ "*.ttf", "*.otf", "*.ctf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -111,7 +161,12 @@ "filters": [ "*.wav", "*.mp3" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -119,7 +174,12 @@ ], "filters": [ "*.mp4" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -134,7 +194,12 @@ "*.vert", "*.frag", "*.trag" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -142,7 +207,12 @@ ], "filters": [ "*.mesh" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -150,7 +220,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -165,8 +240,14 @@ "en" ] }, - "mcuConfig": { - "mcuEnabled": true + "mcu": { + "config": { + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml index 484a425b91c..5878bdafc73 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml @@ -5,20 +5,13 @@ import QmlProject Project { mainFile: "fileSelectors.qml" - mainUiFile: "" targetDirectory: "/opt/fileSelectors" widgetApp: false importPaths: [ "imports" ] - qdsVersion: "" - quickVersion: "" qt6Project: false qtForMCUs: false - multilanguageSupport: false - primaryLanguage: "" - supportedLanguages: [ ] - Environment { QT_AUTO_SCREEN_SCALE_FACTOR: "1" QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" @@ -28,33 +21,26 @@ Project { directory: "." } - Files { + JavaScriptFiles { directory: "." - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "." - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson index abe1b455e4b..a26e0fc1607 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson @@ -13,7 +13,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": ".", @@ -22,7 +27,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": ".", @@ -34,8 +44,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -43,7 +58,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -51,7 +71,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -59,7 +84,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -68,7 +98,14 @@ ], "language": { }, - "mcuConfig": { + "mcu": { + "config": { + }, + "enabled": false, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml index 4db64285c7e..2a67b12d505 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml @@ -40,76 +40,56 @@ Project { directory: "imports" } - Files { + JavaScriptFiles { directory: "content" - filter: "*.js;*.ts" - files: [ ] } - Files { + JavaScriptFiles { directory: "imports" - filter: "*.js;*.ts" - files: [ ] } - Files { + ImageFiles { directory: "content" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } - Files { + ImageFiles { directory: "asset_imports" - filter: "*.jpeg;*.jpg;*.png;*.svg;*.hdr;.ktx" - files: [ ] } Files { - directory: "" - filter: "" - files: [ "qtquickcontrols2.conf" ] + files: [ + "qtquickcontrols2.conf" + ] } Files { - directory: "" filter: "*.conf" - files: [ ] } Files { directory: "." filter: "qmldir" - files: [ ] } Files { - directory: "" filter: "*.ttf;*.otf;*.ctf" - files: [ ] } Files { - directory: "" filter: "*.wav;*.mp3" - files: [ ] } Files { - directory: "" filter: "*.mp4" - files: [ ] } Files { - directory: "" filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag;*.trag" - files: [ ] } Files { directory: "asset_imports" filter: "*.mesh" - files: [ ] } QmlFiles { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson index 4b039c2e1ed..c4475af39c4 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -16,7 +16,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "imports", @@ -24,7 +29,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" }, { "directory": "content", @@ -33,7 +43,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "imports", @@ -42,7 +57,12 @@ "filters": [ "*.js", "*.ts" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" }, { "directory": "content", @@ -54,8 +74,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "asset_imports", @@ -67,8 +92,13 @@ "*.png", "*.svg", "*.hdr", - ".ktx" - ] + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" }, { "directory": "", @@ -76,7 +106,12 @@ "qtquickcontrols2.conf" ], "filters": [ - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -84,7 +119,12 @@ ], "filters": [ "*.conf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": ".", @@ -92,7 +132,12 @@ ], "filters": [ "qmldir" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -102,7 +147,12 @@ "*.ttf", "*.otf", "*.ctf" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -111,7 +161,12 @@ "filters": [ "*.wav", "*.mp3" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -119,7 +174,12 @@ ], "filters": [ "*.mp4" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "", @@ -134,7 +194,12 @@ "*.vert", "*.frag", "*.trag" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -142,7 +207,12 @@ ], "filters": [ "*.mesh" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" }, { "directory": "asset_imports", @@ -150,7 +220,12 @@ ], "filters": [ "*.qml" - ] + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" } ], "fileVersion": 1, @@ -165,8 +240,14 @@ "en" ] }, - "mcuConfig": { - "mcuEnabled": true + "mcu": { + "config": { + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { }, "runConfig": { "fileSelectors": [ diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml new file mode 100644 index 00000000000..99e4f60bb34 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml @@ -0,0 +1,111 @@ +// prop: json-converted +// prop: auto-generated + +import QmlProject + +Project { + mainFile: "Main.qml" + targetDirectory: "/opt/UntitledProject13" + widgetApp: true + importPaths: [ "imports","asset_imports" ] + + qdsVersion: "4.0" + quickVersion: "6.2" + qt6Project: false + qtForMCUs: true + + multilanguageSupport: true + primaryLanguage: "en" + supportedLanguages: [ "no" ] + + idBasedTranslations: true + projectRootPath: ".." + + Environment { + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_ENABLE_HIGHDPI_SCALING: "0" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + } + + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } + + MCU.Config { + defaultFontFamily: "Roboto" + fontEngine: "Spark" + maxResourceCacheSize: [[128,128],[16384,1]] + resourceCompression: true + } + + QmlFiles { + directory: "content" + MCU.copyQmlFiles: true + } + + QmlFiles { + files: [ + "qml/Header.qml", + "qml/Footer.qml" + ] + } + + ImageFiles { + directory: "assets" + } + + ImageFiles { + files: [ + "images/clock.png" + ] + MCU.resourceCompression: false + } + + ImageFiles { + files: [ + "images/weather.png", + "images/navigation.png" + ] + MCU.base: "images" + MCU.prefix: "assets" + } + + ModuleFiles { + files: [ + "qmlproject/module/module.qmlproject" + ] + MCU.qulModules: ["Controls","Timeline"] + } + + FontFiles { + files: [ + "fonts/RobotoFonts.fmp" + ] + } + + InterfaceFiles { + files: [ + "src/BoardInterface.h" + ] + } + + TranslationFiles { + files: [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ] + MCU.omitSourceLanguage: true + } + + JavaScriptFiles { + directory: "scripts" + } + + Files { + directory: "more_files" + filter: "*.mp3" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject new file mode 100644 index 00000000000..1b5b10ec18b --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmlproject @@ -0,0 +1,156 @@ +import QmlProject 1.3 + +// This file works with Qt for MCUs 2.6, with the testfile of set-set-mcu-2 used as the module. +// Do not make any changes to this or the expected output before communicating with the MCU team + +// The set of properties may not be exhaustive and may not match every supported Qt for MCUs version + +Project { +// These properties are used by Qt for MCUs +// They should never have the QDS-prefix + mainFile: "Main.qml" + supportedLanguages: ["no"] + primaryLanguage: "en" + importPaths: [ "imports", "asset_imports" ] + idBasedTranslations: true + projectRootPath: ".." +// END of common properties + +// The following properties are ignored by Qt for MCUs +// They can have the QDS-prefix, but do not need it + targetDirectory: "/opt/UntitledProject13" + qdsVersion: "4.0" + quickVersion: "6.2" + qtForMCUs: true + widgetApp: true + multilanguageSupport: true +// END of ignored properties + +// The following properties must have the QDS prefix as of Qt for MCUs 2.6 +// empty +// END of QDS prefixed properties + +// These nodes are used by Qt for MCUs as well as Design Studio +// Their object type and content must never change based on conversion +// Any child properties prefixed with MCU must remain unchanged + +// The nodes can have properties set on the file group. +// This test set does not contain an exhaustive set of the available properties + + QmlFiles { + directory: "content" + MCU.copyQmlFiles: true + } + + QmlFiles { + files: [ + "qml/Header.qml", + "qml/Footer.qml" + ] + } + + ImageFiles { + directory: "assets" + } + + ImageFiles { + files: [ + "images/clock.png" + ] + MCU.resourceCompression: false + } + + ImageFiles { + files: [ + "images/weather.png", + "images/navigation.png" + ] + MCU.prefix: "assets" + MCU.base: "images" + } + +// END common nodes + +// These nodes are used by Qt for MCUs but not Design Studio +// Their object type and content must never change based on conversion +// Any child properties prefixed with MCU must remain unchanged + ModuleFiles { + files: [ + "qmlproject/module/module.qmlproject" + ] + MCU.qulModules: [ + "Controls", + "Timeline" + ] + } + + FontFiles { + files: [ + "fonts/RobotoFonts.fmp" + ] + } + + InterfaceFiles { + files: [ + "src/BoardInterface.h" + ] + } + + TranslationFiles { + files: [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ] + MCU.omitSourceLanguage: true + } +// END MCU nodes + +// This node is prefixed with MCU. Its props are not prefixed +// It must remain unchanged after conversion. Modules may have MCU.Module + MCU.Config { + resourceCompression: true + fontEngine: "Spark" + defaultFontFamily: "Roboto" + + maxResourceCacheSize: [ + [128, 128], + [16384, 1] + ] + + } +//END MCU-prefixed node + +// The following nodes are ignored by Qt for MCUs +// They do not need the QDS-prefix, but may have it + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + JavaScriptFiles { + directory: "scripts" + } + + Files { + directory: "more_files" + filter: "*.mp3" + } +// END of ignored nodes + +// The following nodes are unknown to Qt for MCUs (2.6) +// Unknown nodes cause a warning in MCU tooling, but no error as of Qt for MCUs 2.6 + ShaderTool { + args: "-s --glsl \"100 es,120,150\" --hlsl 50 --msl 12" + files: [ "content/shaders/*" ] + } +// END of unknown nodes +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson new file mode 100644 index 00000000000..303bfc3899d --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson @@ -0,0 +1,236 @@ +{ + "deployment": { + "targetDirectory": "/opt/UntitledProject13" + }, + "environment": { + "QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT": "1", + "QT_AUTO_SCREEN_SCALE_FACTOR": "1", + "QT_ENABLE_HIGHDPI_SCALING": "0", + "QT_LOGGING_RULES": "qt.qml.connections=false", + "QT_QUICK_CONTROLS_CONF": "qtquickcontrols2.conf" + }, + "fileGroups": [ + { + "directory": "content", + "files": [ + ], + "filters": [ + "*.qml" + ], + "mcuProperties": { + "copyQmlFiles": true + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "", + "files": [ + "qml/Header.qml", + "qml/Footer.qml" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "assets", + "files": [ + ], + "filters": [ + "*.jpeg", + "*.jpg", + "*.png", + "*.svg", + "*.hdr", + "*.ktx" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "images/clock.png" + ], + "filters": [ + ], + "mcuProperties": { + "resourceCompression": false + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "images/weather.png", + "images/navigation.png" + ], + "filters": [ + ], + "mcuProperties": { + "base": "images", + "prefix": "assets" + }, + "otherProperties": { + }, + "type": "Image" + }, + { + "directory": "", + "files": [ + "qmlproject/module/module.qmlproject" + ], + "filters": [ + ], + "mcuProperties": { + "qulModules": [ + "Controls", + "Timeline" + ] + }, + "otherProperties": { + }, + "type": "Module" + }, + { + "directory": "", + "files": [ + "fonts/RobotoFonts.fmp" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Font" + }, + { + "directory": "", + "files": [ + "src/BoardInterface.h" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Interface" + }, + { + "directory": "", + "files": [ + "additional_translations/qml_en.ts", + "i18n/qml_no.ts" + ], + "filters": [ + ], + "mcuProperties": { + "omitSourceLanguage": true + }, + "otherProperties": { + }, + "type": "Translation" + }, + { + "directory": "scripts", + "files": [ + ], + "filters": [ + "*.js", + "*.ts" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "JavaScript" + }, + { + "directory": "more_files", + "files": [ + ], + "filters": [ + "*.mp3" + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "" + } + ], + "fileVersion": 1, + "importPaths": [ + "imports", + "asset_imports" + ], + "language": { + "multiLanguageSupport": true, + "primaryLanguage": "en", + "supportedLanguages": [ + "no" + ] + }, + "mcu": { + "config": { + "defaultFontFamily": "Roboto", + "fontEngine": "Spark", + "maxResourceCacheSize": [ + [ + 128, + 128 + ], + [ + 16384, + 1 + ] + ], + "resourceCompression": true + }, + "enabled": true, + "module": { + } + }, + "otherProperties": { + "idBasedTranslations": true, + "projectRootPath": ".." + }, + "runConfig": { + "fileSelectors": [ + ], + "mainFile": "Main.qml", + "widgetApp": true + }, + "shaderTool": { + "args": [ + "-s", + "--glsl", + "\"100 es,120,150\"", + "--hlsl", + "50", + "--msl", + "12" + ], + "files": [ + "content/shaders/*" + ] + }, + "versions": { + "designStudio": "4.0", + "qt": "5", + "qtQuick": "6.2" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml new file mode 100644 index 00000000000..2e73146cdaf --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml @@ -0,0 +1,32 @@ +// prop: json-converted +// prop: auto-generated + +import QmlProject + +Project { + widgetApp: false + + qt6Project: false + qtForMCUs: true + + projectRootPath: "../.." + + MCU.Module { + generateQmltypes: true + uri: "MyModule" + } + + QmlFiles { + files: [ + "qml/TextIcon.qml" + ] + } + + ImageFiles { + files: [ + "images/qt_logo.png" + ] + MCU.base: "images" + MCU.prefix: "logo" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject new file mode 100644 index 00000000000..bb4e83f0338 --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmlproject @@ -0,0 +1,29 @@ +import QmlProject 1.3 + +// This file represents the module contained in the testfile of test-set-mcu-1 + +// Do not modify without discussing with the MCU team + +Project { + + projectRootPath: "../.." + + MCU.Module { + uri: "MyModule" + generateQmltypes: true + } + + QmlFiles { + files: [ + "qml/TextIcon.qml" + ] + } + + ImageFiles { + files: [ + "images/qt_logo.png" + ] + MCU.base: "images" + MCU.prefix: "logo" + } +} diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson new file mode 100644 index 00000000000..0ef011b615c --- /dev/null +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.qmltojson @@ -0,0 +1,56 @@ +{ + "deployment": { + }, + "fileGroups": [ + { + "directory": "", + "files": [ + "qml/TextIcon.qml" + ], + "filters": [ + ], + "mcuProperties": { + }, + "otherProperties": { + }, + "type": "Qml" + }, + { + "directory": "", + "files": [ + "images/qt_logo.png" + ], + "filters": [ + ], + "mcuProperties": { + "base": "images", + "prefix": "logo" + }, + "otherProperties": { + }, + "type": "Image" + } + ], + "fileVersion": 1, + "language": { + }, + "mcu": { + "config": { + }, + "enabled": true, + "module": { + "generateQmltypes": true, + "uri": "MyModule" + } + }, + "otherProperties": { + "projectRootPath": "../.." + }, + "runConfig": { + "fileSelectors": [ + ] + }, + "versions": { + "qt": "5" + } +}