back port qml scaner from cqtdeployer 1.5

This commit is contained in:
Andrei Yankovich 2022-10-26 10:26:40 +03:00
parent 70c6e5f305
commit d61172c64a
10 changed files with 370 additions and 100 deletions

View File

@ -10,6 +10,7 @@
#include "pluginsparser.h"
#include "configparser.h"
#include "metafilemanager.h"
#include "qmlqt5.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
@ -464,13 +465,20 @@ bool Extracter::extractQml() {
continue;
}
QML ownQmlScaner(cnf->qtDir.getQmls(), cnf->isNeededQt(i.key()));
auto QtVersion = cnf->isNeededQt(i.key());
QSharedPointer<iQML> qmlScaner;
if (QtVersion & QtMajorVersion::Qt6) {
qmlScaner = QSharedPointer<QMLQt6>::create(cnf->qtDir.getQmls());
} else if (QtVersion & QtMajorVersion::Qt5) {
qmlScaner = QSharedPointer<QMLQt5>::create(cnf->qtDir.getQmls());
}
if (!ownQmlScaner.scan(plugins, info.absoluteFilePath())) {
if (qmlScaner && !qmlScaner->scan(plugins, info.absoluteFilePath())) {
QuasarAppUtils::Params::log("Failed to run qml scanner",
QuasarAppUtils::Error);
continue;
}
}

View File

@ -14,7 +14,7 @@
#include "dependencymap.h"
#include "deploy_global.h"
#include "filemanager.h"
#include "qml.h"
#include "qmlqt6.h"
class ConfigParser;
class MetaFileManager;

13
src/Deploy/src/iqml.cpp Normal file
View File

@ -0,0 +1,13 @@
//#
//# Copyright (C) 2022-2022 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 "iqml.h"
iQML::iQML(const QString &qmlRoot) {
_qmlRoot = qmlRoot;
}

40
src/Deploy/src/iqml.h Normal file
View File

@ -0,0 +1,40 @@
//#
//# Copyright (C) 2022-2022 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.
//#
#ifndef IQML_H
#define IQML_H
#include <QString>
#include "deploy_global.h"
/**
* @brief The iQML class Bae interface for scannin qml depends.
*/
class DEPLOYSHARED_EXPORT iQML
{
public:
/**
* @brief iQML base constructor
* @param qmlRoot This is qml root dir of Qt destribution. Example: Qt/6.4.0/gcc_64/qml
*/
iQML(const QString& qmlRoot);
virtual ~iQML() = default;
/**
* @brief scan This method scane qml files in the qmlRoot and return needed to deploy files
* @param res This is result of scan. List of needed to deploy files
* @param _qmlProjectDir This is
* @return true if function finished succesful
*/
virtual bool scan(QStringList &res, const QString &_qmlProjectDir) = 0;
protected:
QString _qmlRoot;
};
#endif // IQML_H

View File

@ -5,7 +5,7 @@
* of this license document, but changing it is not allowed.
*/
#include "qml.h"
#include "qmlqt5.h"
#include <QDir>
#include <QFile>
@ -13,18 +13,18 @@
#include "deploycore.h"
#include "qregularexpression.h"
QStringList QML::extractImportLine(const QString& line) const {
QStringList QMLQt5::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)) {
if (list[2] == "auto") {
// qt6
result << (list[1].replace(".", "/"));
return result;
}
// qt5
result << (list[2][0] + QString("#") + list[1].replace(".", "/"));
result << (QString(list[2][0]) + "#" + list[1].replace(".", "/"));
} else if (list.count() == 2 || (list.count() == 4 && list[2] == "as")) {
// qt6
result << (list[1].replace(".", "/"));
@ -33,7 +33,7 @@ QStringList QML::extractImportLine(const QString& line) const {
return result;
}
QStringList QML::extractImportsFromFile(const QString &filepath) const {
QStringList QMLQt5::extractImportsFromFile(const QString &filepath) const {
QStringList imports;
QFile F(filepath);
if (!F.open(QIODevice::ReadOnly)) return QStringList();
@ -65,7 +65,7 @@ QStringList QML::extractImportsFromFile(const QString &filepath) const {
return imports;
}
bool QML::extractImportsFromDir(const QString &path, bool recursive) {
bool QMLQt5::extractImportsFromDir(const QString &path, bool recursive) {
QDir dir(path);
if (!dir.isReadable()) {
@ -82,33 +82,22 @@ bool QML::extractImportsFromDir(const QString &path, bool recursive) {
for (const auto &import : qAsConst(imports)) {
if (!_imports.contains(import)) {
_imports.insert(import);
extractImportsFromDir(getPathFromImport(import), false);
extractImportsFromDir(getPathFromImport(import), recursive);
}
}
}
for (const auto& module: qAsConst(qmlmodule)) {
QStringList imports = extractImportsFromQmlModule(module.absoluteFilePath());
imports += extractImportsFromFile(module.absoluteFilePath());
for (const auto &import : qAsConst(imports)) {
if (!_imports.contains(import)) {
_imports.insert(import);
extractImportsFromDir(getPathFromImport(import), false);
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), false);
}
}
if (recursive) {
for (const auto &info: dirs) {
extractImportsFromDir(info.absoluteFilePath(), recursive);
@ -118,7 +107,7 @@ bool QML::extractImportsFromDir(const QString &path, bool recursive) {
return true;
}
QString QML::getPathFromImport(const QString &import, bool checkVersions) {
QString QMLQt5::getPathFromImport(const QString &import, bool checkVersions) {
if (!import.contains("#")) {
// qt 6
auto info = QFileInfo(_qmlRoot + "/" + import);
@ -136,6 +125,7 @@ QString QML::getPathFromImport(const QString &import, bool checkVersions) {
} else {
return "";
}
QRegularExpression matcher;
matcher.setPattern("[/\\\\]");
auto words = importData.value(index).split(matcher);
@ -161,7 +151,7 @@ QString QML::getPathFromImport(const QString &import, bool checkVersions) {
return info.absoluteFilePath();
}
bool QML::deployPath(const QString &path, QStringList &res) {
bool QMLQt5::deployPath(const QString &path, QStringList &res) {
QDir dir(path);
auto infoList = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
@ -178,7 +168,7 @@ bool QML::deployPath(const QString &path, QStringList &res) {
return true;
}
bool QML::scanQmlTree(const QString &qmlTree) {
bool QMLQt5::scanQmlTree(const QString &qmlTree) {
QDir dir(qmlTree);
if (!dir.isReadable()) {
@ -198,7 +188,7 @@ bool QML::scanQmlTree(const QString &qmlTree) {
return true;
}
QStringList QML::extractImportsFromQmlModule(const QString &module) const {
QStringList QMLQt5::extractImportsFromQmlModule(const QString &module) const {
QStringList imports;
QFile F(module);
if (!F.open(QIODevice::ReadOnly)) return QStringList();
@ -217,17 +207,12 @@ QStringList QML::extractImportsFromQmlModule(const QString &module) const {
return imports;
}
void QML::setQtVersion(const QtMajorVersion &qtVersion) {
_qtVersion = qtVersion;
}
QML::QML(const QString &qmlRoot, QtMajorVersion qtVersion) {
_qmlRoot = qmlRoot;
setQtVersion(qtVersion);
QMLQt5::QMLQt5(const QString &qmlRoot): iQML(qmlRoot) {}
}
QMLQt5::~QMLQt5() {}
bool QML::scan(QStringList &res, const QString& _qmlProjectDir) {
bool QMLQt5::scan(QStringList &res, const QString& _qmlProjectDir) {
if (!scanQmlTree(_qmlRoot)) {
return false;

View File

@ -5,15 +5,14 @@
//# of this license document, but changing it is not allowed.
//#
#ifndef QML_DEPLY_H
#define QML_DEPLY_H
#ifndef QML_QT5_DEPLY_H
#define QML_QT5_DEPLY_H
#include <QSet>
#include <QStringList>
#include "deploy_global.h"
#include "deploycore.h"
#include "iqml.h"
class DEPLOYSHARED_EXPORT QML {
class DEPLOYSHARED_EXPORT QMLQt5: public iQML {
private:
QStringList extractImportsFromFile(const QString &filepath) const;
@ -25,22 +24,20 @@ private:
bool deployPath( const QString& path, QStringList& res);
bool scanQmlTree(const QString& qmlTree);
QStringList extractImportsFromQmlModule(const QString& module) const;
QString _qmlRoot = "";
QSet<QString> _imports;
QSet<QString> secondVersions;
QtMajorVersion _qtVersion = QtMajorVersion::Qt5;
QStringList extractImportLine(const QString &line) const;
public:
QML(const QString& qmlRoot, QtMajorVersion isQt6);
QMLQt5(const QString& qmlRoot);
~QMLQt5() override;
bool scan(QStringList &res, const QString &_qmlProjectDir);
void setQtVersion(const QtMajorVersion &qtVersion);
bool scan(QStringList &res, const QString &_qmlProjectDir) override;
friend class deploytest;
friend class QmlScanerTest;
friend class QMLExtractTest;
};
#endif // QML_DEPLY_H
#endif // QML_QT5_DEPLY_H

226
src/Deploy/src/qmlqt6.cpp Normal file
View File

@ -0,0 +1,226 @@
/*
* Copyright (C) 2018-2022 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 "qmlqt6.h"
#include <QDir>
#include <QFile>
#include "defines.h"
#include "deploycore.h"
#include "qregularexpression.h"
QStringList QMLQt6::extractImportLine(const QString& line) const {
QStringList result;
QStringList list = line.split(" ", splitbehavior);
result << (list[1].replace(".", "/"));
return result;
return result;
}
QStringList QMLQt6::extractImportsFromFile(const QString &filepath) const {
QStringList imports;
QFile F(filepath);
if (!F.open(QIODevice::ReadOnly)) return QStringList();
QString content = F.readAll();
QRegularExpression matcher;
matcher.setPattern("\\{(.*)\\}");
content.remove(matcher);
matcher.setPattern("/\\*(.*)\\*/");
content.remove(matcher);
// Replace optional imports to restrict option, because before Qt 6.3 The Qml app needee of optional qml packas.
content.replace("optional import", "import");
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 QMLQt6::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), false);
}
}
}
for (const auto& module: qAsConst(qmlmodule)) {
QStringList imports = extractImportsFromQmlModule(module.absoluteFilePath());
imports += extractImportsFromFile(module.absoluteFilePath());
for (const auto &import : qAsConst(imports)) {
if (!_imports.contains(import)) {
_imports.insert(import);
extractImportsFromDir(getPathFromImport(import), false);
}
}
}
// 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.
auto importQtQml = "QtQml";
if (!_imports.contains(importQtQml)) {
_imports.insert(importQtQml);
extractImportsFromDir(getPathFromImport(importQtQml), false);
}
if (recursive) {
for (const auto &info: dirs) {
extractImportsFromDir(info.absoluteFilePath(), recursive);
}
}
return true;
}
QString QMLQt6::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 "";
}
QRegularExpression matcher;
matcher.setPattern("[/\\\\]");
auto words = importData.value(index).split(matcher);
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 QMLQt6::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 QMLQt6::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 QMLQt6::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;
imports += extractImportLine(line);
}
return imports;
}
QMLQt6::QMLQt6(const QString &qmlRoot): iQML(qmlRoot) {}
QMLQt6::~QMLQt6(){}
bool QMLQt6::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;
}

45
src/Deploy/src/qmlqt6.h Normal file
View File

@ -0,0 +1,45 @@
//#
//# Copyright (C) 2018-2022 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.
//#
#ifndef QML_QT6_DEPLY_H
#define QML_QT6_DEPLY_H
#include <QSet>
#include <QStringList>
#include "iqml.h"
class DEPLOYSHARED_EXPORT QMLQt6: public iQML {
public:
QMLQt6(const QString& qmlRoot);
~QMLQt6() override;
bool scan(QStringList &res, const QString &_qmlProjectDir) override;
private:
QStringList extractImportsFromFile(const QString &filepath) const;
bool extractImportsFromDir(const QString &path, bool recursive = false);
QString getPathFromImport(const QString& import, bool checkVersions = true);
bool deployPath( const QString& path, QStringList& res);
bool scanQmlTree(const QString& qmlTree);
QStringList extractImportsFromQmlModule(const QString& module) const;
QStringList extractImportLine(const QString &line) const;
QSet<QString> _imports;
QSet<QString> secondVersions;
friend class deploytest;
friend class QmlScanerTest;
friend class QMLExtractTest;
};
#endif // QML_QT6_DEPLY_H

View File

@ -7,7 +7,7 @@
#include "qmlextracttest.h"
#include "qml.h"
#include "qmlqt5.h"
#include "qmlcreator.h"
#include <configparser.h>
#include <dependenciesscanner.h>
@ -23,7 +23,7 @@ void QMLExtractTest::test() {
auto qmlFiles = creator.getCopyedQml();
QML scaner("./", QtMajorVersion::Qt5);
QMLQt5 scaner("./");
for (const auto &file : qAsConst(qmlFiles)) {

View File

@ -7,7 +7,7 @@
#include "qmlscanertest.h"
#include "qml.h"
#include "qmlqt6.h"
#include <configparser.h>
#include <dependenciesscanner.h>
#include <filemanager.h>
@ -18,80 +18,36 @@
void QmlScanerTest::test() {
// qt5
auto qmlRoot = QFileInfo(TestQtDir + "/qml").absoluteFilePath();
QML *scaner = new QML(qmlRoot, QtMajorVersion::Qt5);
auto imports = scaner->extractImportsFromFile(":/qmlFile.qml");
scaner->scanQmlTree(qmlRoot);
QSet<QString> results = {
{qmlRoot + "/QtQuick.2/"},
{qmlRoot + "/QtQuick/Controls.2/"},
{qmlRoot + "/QtQuick/Controls.2/Material/"},
{qmlRoot + "/QtQuick/Layouts/"},
};
QVERIFY(results.size() == imports.size());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
for (const auto &import: qAsConst(imports)) {
auto path = scaner->getPathFromImport(import);
QVERIFY(results.contains(path));
}
#endif
QMLQt6 scaner6(qmlRoot);
// qt6
scaner->setQtVersion(QtMajorVersion::Qt6);
results = {
QSet<QString> results = {
{qmlRoot + "/QtQuick"},
{qmlRoot + "/QtQuick/Controls"},
{qmlRoot + "/QtQuick/Controls/Material"},
{qmlRoot + "/QtQuick/Layouts"},
};
imports = scaner->extractImportsFromFile(":/qmlFileQt6.qml");
auto imports = scaner6.extractImportsFromFile(":/qmlFileQt6.qml");
QVERIFY(results.size() == imports.size());
for (const auto &import: qAsConst(imports)) {
auto path = scaner->getPathFromImport(import);
auto path = scaner6.getPathFromImport(import);
QVERIFY(results.contains(path));
}
imports = scaner->extractImportsFromFile(":/qmlFile.qml");
imports = scaner6.extractImportsFromFile(":/qmlFile.qml");
QVERIFY(results.size() == imports.size());
for (const auto & import: qAsConst(imports)) {
auto path = scaner->getPathFromImport(import);
auto path = scaner6.getPathFromImport(import);
QVERIFY(results.contains(path));
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// qt5
scaner->setQtVersion(QtMajorVersion::Qt5);
results = {
{qmlRoot + "/QtQuick.2/"},
{qmlRoot + "/QtQuick/Window.2/"},
{qmlRoot + "/QtQuick/Layouts/"},
{qmlRoot + "/Qt/labs/folderlistmodel/"},
{qmlRoot + "/QtQuick/VirtualKeyboard/Settings/"},
{qmlRoot + "/QtQuick/VirtualKeyboard/Styles/"},
};
imports = scaner->extractImportsFromQmlModule(":/qmlDir");
QVERIFY(results.size() == imports.size());
for (const auto &import: qAsConst(imports)) {
auto path = scaner->getPathFromImport(import);
QVERIFY(results.contains(path));
}
#endif
// qt6
scaner->setQtVersion(QtMajorVersion::Qt6);
results = {
{qmlRoot + "/QtQuick"},
@ -102,12 +58,12 @@ void QmlScanerTest::test() {
{qmlRoot + "/QtQuick/VirtualKeyboard/Styles"},
};
imports = scaner->extractImportsFromQmlModule(":/qmlDir");
imports = scaner6.extractImportsFromQmlModule(":/qmlDir");
QVERIFY(results.size() == imports.size());
for (const auto &import: qAsConst(imports)) {
auto path = scaner->getPathFromImport(import);
auto path = scaner6.getPathFromImport(import);
QVERIFY(results.contains(path));
}