/* * Copyright (C) 2018-2021 QuasarApp. * Distributed under the lgplv3 software license, see the accompanying * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ #include "qml.h" #include #include #include "quasarapp.h" #include "deploycore.h" #include "deployconfig.h" QStringList QML::extractImportLine(const QString& line) const { QStringList result; QStringList list = line.split(" ", splitbehavior); if (list.count() == 3 || (list.count() == 5 && list[3] == "as")) { if (list[2] == "auto" || (_qtVersion & QtMajorVersion::Qt6)) { // qt6 result << (list[1].replace(".", "/")); return result; } // qt5 result << (list[2][0] + "#" + list[1].replace(".", "/")); } else if (list.count() == 2 || (list.count() == 4 && list[2] == "as")) { // qt6 result << (list[1].replace(".", "/")); } return result; } QStringList QML::extractImportsFromFile(const QString &filepath) const { QStringList imports; QFile F(filepath); if (!F.open(QIODevice::ReadOnly)) return QStringList(); QString content = F.readAll(); content.remove(QRegExp("\\{(.*)\\}")); content.remove(QRegExp("/\\*(.*)\\*/")); const auto list = content.split("\n"); for (const QString &line : list) for (QString &word : line.split(";", splitbehavior)) { word = word.simplified(); if (word.startsWith("//")) continue; if (!word.startsWith("import")) continue; imports += extractImportLine(word); } return imports; } bool QML::extractImportsFromDir(const QString &path, bool recursive) { QDir dir(path); if (!dir.isReadable()) { return false; } auto files = dir.entryInfoList(QStringList() << "*.qml" << "*.QML", QDir::Files); auto qmlmodule = dir.entryInfoList(QStringList() << "qmldir", QDir::Files); auto dirs = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs); for (const auto &info: files) { auto imports = extractImportsFromFile(info.absoluteFilePath()); for (const auto &import : qAsConst(imports)) { if (!_imports.contains(import)) { _imports.insert(import); extractImportsFromDir(getPathFromImport(import), recursive); } } } for (const auto& module: qAsConst(qmlmodule)) { QStringList imports = extractImportsFromQmlModule(module.absoluteFilePath()); for (const auto &import : qAsConst(imports)) { if (!_imports.contains(import)) { _imports.insert(import); extractImportsFromDir(getPathFromImport(import), recursive); } } } // task https://github.com/QuasarApp/CQtDeployer/issues/600 // There are no import lines for the qt models module in Qt 6.1, but this module is required for all qml applications. if (_qtVersion & QtMajorVersion::Qt6) { auto importQtQml = "QtQml"; if (!_imports.contains(importQtQml)) { _imports.insert(importQtQml); extractImportsFromDir(getPathFromImport(importQtQml), recursive); } } if (recursive) { for (const auto &info: dirs) { extractImportsFromDir(info.absoluteFilePath(), recursive); } } return true; } QString QML::getPathFromImport(const QString &import, bool checkVersions) { if (!import.contains("#")) { // qt 6 auto info = QFileInfo(_qmlRoot + "/" + import); return info.absoluteFilePath(); } auto importData = import.split("#"); int index; if (importData.size() == 2) index = 1; else if (!importData.isEmpty()) { index = 0; } else { return ""; } auto words = importData.value(index).split(QRegExp("[/\\\\]")); const bool isSecond = importData.first() == "2" && checkVersions; bool secondVersion = isSecond; QString path; for (auto i = words.rbegin(); i != words.rend(); ++i) { QString word = *i; if (secondVersion && secondVersions.contains(word)) { secondVersion = false; word.push_back(".2"); } path.push_front(word + "/"); } auto info = QFileInfo(_qmlRoot + "/" + path); if (isSecond && !info.exists()) { return getPathFromImport(import, false); } return info.absoluteFilePath(); } bool QML::deployPath(const QString &path, QStringList &res) { QDir dir(path); auto infoList = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const auto &info : qAsConst(infoList)) { if (DeployCore::isDebugFile(info.fileName())) { QuasarAppUtils::Params::log("Skip debug library " + info.absoluteFilePath()); continue; } res.push_back(info.absoluteFilePath()); } return true; } bool QML::scanQmlTree(const QString &qmlTree) { QDir dir(qmlTree); if (!dir.isReadable()) { return false; } auto list = dir.entryInfoList( QDir::Dirs | QDir::NoDotAndDotDot); for (const auto &info : list) { if (info.fileName().contains(".2")) { secondVersions.insert(info.fileName().left(info.fileName().size() - 2)); } scanQmlTree(info.absoluteFilePath()); } return true; } QStringList QML::extractImportsFromQmlModule(const QString &module) const { QStringList imports; QFile F(module); if (!F.open(QIODevice::ReadOnly)) return QStringList(); QString content = F.readAll(); const auto list = content.split("\n"); for (QString line : list) { line = line.simplified(); if (line.startsWith("//") || line.startsWith("#")) continue; if (!line.startsWith("depends")) continue; QStringList list = line.split(" ", splitbehavior); imports += extractImportLine(line); } return imports; } void QML::setQtVersion(const QtMajorVersion &qtVersion) { _qtVersion = qtVersion; } QML::QML(const QString &qmlRoot, QtMajorVersion qtVersion) { _qmlRoot = qmlRoot; setQtVersion(qtVersion); } bool QML::scan(QStringList &res, const QString& _qmlProjectDir) { if (!scanQmlTree(_qmlRoot)) { return false; } if (!extractImportsFromDir(_qmlProjectDir, true)) { return false; } for (const auto &import : qAsConst(_imports)) { res.push_back(getPathFromImport(import)); } return true; }