From e284be38110e12d27ec6a90106a6e9b3ba777931 Mon Sep 17 00:00:00 2001 From: Unknown <shahriar25.ss@gmail.com> Date: Mon, 12 Mar 2018 11:43:03 +0330 Subject: [PATCH] Added the already written project --- CPP/baseclass.cpp | 21 ++++++ CPP/baseclass.h | 29 ++++++++ CPP/cppmanager.cpp | 118 +++++++++++++++++++++++++++++++ CPP/cppmanager.h | 50 +++++++++++++ CPP/mainmanager.cpp | 78 +++++++++++++++++++++ CPP/mainmanager.h | 47 +++++++++++++ CPP/outputmanager.cpp | 154 +++++++++++++++++++++++++++++++++++++++++ CPP/outputmanager.h | 49 +++++++++++++ CPP/pluginmanager.cpp | 102 +++++++++++++++++++++++++++ CPP/pluginmanager.h | 34 +++++++++ CPP/qmlmanager.cpp | 147 +++++++++++++++++++++++++++++++++++++++ CPP/qmlmanager.h | 47 +++++++++++++ QML/ExtendableView.qml | 87 +++++++++++++++++++++++ QML/PathChooser.qml | 70 +++++++++++++++++++ QML/PreparePage.qml | 114 ++++++++++++++++++++++++++++++ QML/ProcessPage.qml | 40 +++++++++++ QML/ResultPage.qml | 63 +++++++++++++++++ QML/StartPage.qml | 69 ++++++++++++++++++ QML/TopBar.qml | 14 ++++ QML/main.qml | 43 ++++++++++++ QtLinuxDeployer.pro | 44 ++++++++++++ main.cpp | 35 ++++++++++ qml.qrc | 13 ++++ qtquickcontrols2.conf | 15 ++++ 24 files changed, 1483 insertions(+) create mode 100755 CPP/baseclass.cpp create mode 100755 CPP/baseclass.h create mode 100755 CPP/cppmanager.cpp create mode 100755 CPP/cppmanager.h create mode 100755 CPP/mainmanager.cpp create mode 100755 CPP/mainmanager.h create mode 100755 CPP/outputmanager.cpp create mode 100755 CPP/outputmanager.h create mode 100755 CPP/pluginmanager.cpp create mode 100755 CPP/pluginmanager.h create mode 100755 CPP/qmlmanager.cpp create mode 100755 CPP/qmlmanager.h create mode 100755 QML/ExtendableView.qml create mode 100755 QML/PathChooser.qml create mode 100755 QML/PreparePage.qml create mode 100755 QML/ProcessPage.qml create mode 100755 QML/ResultPage.qml create mode 100755 QML/StartPage.qml create mode 100755 QML/TopBar.qml create mode 100755 QML/main.qml create mode 100755 QtLinuxDeployer.pro create mode 100755 main.cpp create mode 100755 qml.qrc create mode 100755 qtquickcontrols2.conf diff --git a/CPP/baseclass.cpp b/CPP/baseclass.cpp new file mode 100755 index 0000000..d37a17e --- /dev/null +++ b/CPP/baseclass.cpp @@ -0,0 +1,21 @@ +#include "baseclass.h" + +QString BaseClass::m_qtdir = QString(); +QString BaseClass::m_outputdir = QString(); +QString BaseClass::m_projectdir = QString(); +QString BaseClass::m_executablepath = QString(); + +QStringList BaseClass::findFilesInsideDir(const QString &name, const QString &dirpath) +{ + QStringList files; + + QDir dir(dirpath); + dir.setNameFilters(QStringList(name)); + + QDirIterator it(dir, QDirIterator::Subdirectories); + while (it.hasNext()) files << it.next(); + + return files; +} + +BaseClass::BaseClass(QObject *parent) : QObject(parent) {} diff --git a/CPP/baseclass.h b/CPP/baseclass.h new file mode 100755 index 0000000..674c436 --- /dev/null +++ b/CPP/baseclass.h @@ -0,0 +1,29 @@ +#ifndef BASECLASS_H +#define BASECLASS_H + +#include <QDebug> +#include <QDir> +#include <QDirIterator> +#include <QFile> +#include <QFileInfo> +#include <QObject> +#include <QProcess> +#include <QTextStream> + +class BaseClass : public QObject +{ + Q_OBJECT + +protected: + static QString m_qtdir; + static QString m_outputdir; + static QString m_projectdir; + static QString m_executablepath; + + QStringList findFilesInsideDir(const QString &name, const QString &dirpath); + +public: + explicit BaseClass(QObject *parent = nullptr); +}; + +#endif // BASECLASS_H diff --git a/CPP/cppmanager.cpp b/CPP/cppmanager.cpp new file mode 100755 index 0000000..dcdfd16 --- /dev/null +++ b/CPP/cppmanager.cpp @@ -0,0 +1,118 @@ +#include "cppmanager.h" + +void CppManager::setQtLibraries(const QStringList &qtLibraries) +{ + if (m_qtLibraries == qtLibraries) return; + + m_qtLibraries = qtLibraries; + emit qtLibrariesChanged(m_qtLibraries); +} + +void CppManager::setCppLibraries(const QStringList &cppLibraries) +{ + if (m_cppLibraries == cppLibraries) return; + + m_cppLibraries = cppLibraries; + emit cppLibrariesChanged(m_cppLibraries); +} + +void CppManager::setNotFoundLibs(const QStringList ¬FoundLibs) +{ + if (m_notFoundLibs == notFoundLibs) return; + + m_notFoundLibs = notFoundLibs; + emit notFoundLibsChanged(m_notFoundLibs); +} + +void CppManager::extractAllLibs(const QStringList &execfiles) +{ + for (const QString &execfile : execfiles) + for (const QString &lib : extractLibsFromExecutable(execfile)) + if (!m_cppLibraries.contains(lib)) + { + m_cppLibraries << lib; + extractAllLibs(QStringList(lib)); + } +} + +QStringList CppManager::extractLibsFromExecutable(const QString &execpath) +{ + QProcess P; + P.start("ldd " + execpath, QProcess::ReadOnly); + + if (!P.waitForStarted()) return QStringList(); + if (!P.waitForFinished()) return QStringList(); + + auto data = QString(P.readAll()); + QStringList libs; + + for (QString &line : data.split("\n", QString::SkipEmptyParts)) + { + line = line.simplified(); + auto innerlist = line.split(" "); + + if (innerlist.count() < 3) continue; + line = innerlist[2]; + + if (!line.startsWith("/")) + m_notFoundLibs << innerlist[0]; + else + libs << line; + } + + return libs; +} + +void CppManager::divideLibraries() +{ + for (const QString &lib : m_cppLibraries) + { + QString name; + + try + { + name = QFileInfo(lib).fileName(); + } + catch (std::exception E) + { + qDebug() << E.what(); + } + + if (!name.isEmpty() && name.startsWith("libQt")) + { + m_qtLibraries << name; + m_cppLibraries.removeOne(lib); + } + } +} + +void CppManager::start(const QStringList &executables) +{ + m_qtLibraries.clear(); + m_cppLibraries.clear(); + m_notFoundLibs.clear(); + + extractAllLibs(executables); + divideLibraries(); + + m_notFoundLibs.removeDuplicates(); + + emit qtLibrariesChanged(m_qtLibraries); + emit cppLibrariesChanged(m_cppLibraries); + emit notFoundLibsChanged(m_notFoundLibs); +} + +QStringList CppManager::getQtLibrariesFullPaths() +{ + QStringList paths; + + auto dir = m_qtdir + "/lib"; + for (const QString &qtlib : m_qtLibraries) paths << dir + "/" + qtlib; + + return paths; +} + +CppManager::CppManager(QObject *parent) : BaseClass(parent) {} +QStringList CppManager::qtLibraries() const { return m_qtLibraries; } +QStringList CppManager::cppLibraries() const { return m_cppLibraries; } +QStringList CppManager::notFoundLibs() const { return m_notFoundLibs; } diff --git a/CPP/cppmanager.h b/CPP/cppmanager.h new file mode 100755 index 0000000..3272940 --- /dev/null +++ b/CPP/cppmanager.h @@ -0,0 +1,50 @@ +#ifndef CPPMANAGER_H +#define CPPMANAGER_H + +#include "baseclass.h" + +class CppManager : public BaseClass +{ + Q_OBJECT + Q_PROPERTY(QStringList qtLibraries READ qtLibraries WRITE setQtLibraries + NOTIFY qtLibrariesChanged) + + Q_PROPERTY(QStringList cppLibraries READ cppLibraries WRITE setCppLibraries + NOTIFY cppLibrariesChanged) + + Q_PROPERTY(QStringList notFoundLibs READ notFoundLibs WRITE setNotFoundLibs + NOTIFY notFoundLibsChanged) + + QStringList m_qtLibraries; + QStringList m_cppLibraries; + QStringList m_notFoundLibs; + +public: // TODO remove this line + void extractAllLibs(const QStringList &execfiles); + QStringList extractLibsFromExecutable(const QString &execpath); + + void divideLibraries(); + +public: + explicit CppManager(QObject *parent = nullptr); + + QStringList qtLibraries() const; + QStringList cppLibraries() const; + QStringList notFoundLibs() const; + + QStringList getQtLibrariesFullPaths(); + + void start(const QStringList &executables); + +public slots: + void setQtLibraries(const QStringList &qtLibraries); + void setCppLibraries(const QStringList &cppLibraries); + void setNotFoundLibs(const QStringList ¬FoundLibs); + +signals: + void qtLibrariesChanged(QStringList qtLibraries); + void cppLibrariesChanged(QStringList cppLibraries); + void notFoundLibsChanged(QStringList notFoundLibs); +}; + +#endif // CPPMANAGER_H diff --git a/CPP/mainmanager.cpp b/CPP/mainmanager.cpp new file mode 100755 index 0000000..3bd11f1 --- /dev/null +++ b/CPP/mainmanager.cpp @@ -0,0 +1,78 @@ +#include "mainmanager.h" + +QStringList MainManager::getAllExecutables() +{ + QStringList list = m_qml->getAllSoFiles(); + list << m_plg->getAllSoFiles(); + list << m_executablepath; + + return list; +} + +MainManager::MainManager(CppManager *cpp, QmlManager *qml, OutputManager *out, + PluginManager *plg, QObject *parent) + : BaseClass(parent) +{ + setState(0); + + m_cpp = cpp; + m_qml = qml; + m_out = out; + m_plg = plg; +} + +void MainManager::prepare(const QString &qtdir, const QString &execpath, + const QString &projectdir, const QString &outdir) +{ + QStringList list; + list << qtdir << execpath << projectdir << outdir; + + for (QString &S : list) + if (S[S.count() - 1] == "/") S.remove(S.count() - 1, 1); + + m_qtdir = list[0]; + m_executablepath = list[1]; + m_projectdir = list[2]; + m_outputdir = list[3]; + + m_qml->start(); + m_plg->start(); + m_cpp->start(getAllExecutables()); +} + +void MainManager::start(bool erase) +{ + setState(1); + + m_out->copyAll(m_cpp->getQtLibrariesFullPaths(), m_cpp->cppLibraries(), + m_qml->foundImports(), m_plg->neededPlugins(), erase); + + setState(2); +} + +bool MainManager::hasPrems(const QString &path) +{ + QFileInfo info(path); + return (info.isReadable() && info.isWritable()); +} + +QString MainManager::stringFromUrl(QString url) +{ + return url.remove("file://"); +} + +bool MainManager::pathExists(bool isdir, const QString &path) +{ + if (isdir) return QDir(path).exists(); + return QFile(path).exists(); +} + +void MainManager::setState(int state) +{ + if (m_state == state) return; + + m_state = state; + emit stateChanged(m_state); +} + +int MainManager::state() const { return m_state; } diff --git a/CPP/mainmanager.h b/CPP/mainmanager.h new file mode 100755 index 0000000..4099c12 --- /dev/null +++ b/CPP/mainmanager.h @@ -0,0 +1,47 @@ +#ifndef MAINMANAGER_H +#define MAINMANAGER_H + +#include "baseclass.h" +#include "cppmanager.h" +#include "outputmanager.h" +#include "pluginmanager.h" +#include "qmlmanager.h" + +class MainManager : public BaseClass +{ + Q_OBJECT + + Q_PROPERTY(int state READ state WRITE setState NOTIFY stateChanged) + + CppManager *m_cpp; + QmlManager *m_qml; + PluginManager *m_plg; + OutputManager *m_out; + + int m_state; + + QStringList getAllExecutables(); + +public: + explicit MainManager(CppManager *cpp, QmlManager *qml, OutputManager *out, + PluginManager *plg, QObject *parent = nullptr); + + int state() const; + +public slots: + void prepare(const QString &qtdir, const QString &execpath, + const QString &projectdir, const QString &outdir); + + void start(bool erase); + + bool hasPrems(const QString &path); + QString stringFromUrl(QString url); + bool pathExists(bool isdir, const QString &path); + + void setState(int state); + +signals: + void stateChanged(int state); +}; + +#endif // MAINMANAGER_H diff --git a/CPP/outputmanager.cpp b/CPP/outputmanager.cpp new file mode 100755 index 0000000..e697b75 --- /dev/null +++ b/CPP/outputmanager.cpp @@ -0,0 +1,154 @@ +#include "outputmanager.h" + +void OutputManager::setPathsToCopy(const QStringList &pathsToCopy) +{ + if (m_pathsToCopy == pathsToCopy) return; + + m_pathsToCopy = pathsToCopy; + emit pathsToCopyChanged(m_pathsToCopy); +} + +void OutputManager::setCopySuccess(const QList<bool> ©Success) +{ + if (m_copySuccess == copySuccess) return; + + m_copySuccess = copySuccess; + emit copySuccessChanged(m_copySuccess); +} + +bool OutputManager::copyDir(const QString &source, const QString &destin) +{ + QDir().mkpath(destin); + + QProcess P; + P.start(QString("cp -a %1/. %2/").arg(source, destin)); + + P.waitForStarted(1000); + P.waitForFinished(); + + return P.exitStatus() == QProcess::NormalExit; +} + +void OutputManager::copyCpp(const QStringList &libs) +{ + auto libdir = m_outputdir + "/lib"; + + for (const QString &S : libs) + { + auto path = libdir + "/" + QFileInfo(S).fileName(); + m_pathsToCopy << path; + m_copySuccess << copyFile(S, path); + } +} + +void OutputManager::copyAll(const QStringList &qtlibs, const QStringList &libs, + const QStringList &dirs, const QStringList &plugins, + bool erase) +{ + m_pathsToCopy.clear(); + m_copySuccess.clear(); + + if (erase) + { + QDir(m_outputdir).removeRecursively(); + QDir dir(m_outputdir); + dir.cdUp(); + dir.mkdir(QDir(m_outputdir).dirName()); + } + + createDirectories(); + + if (libs.count() != 0) copyCpp(libs); + copyCpp(qtlibs); + copyQml(dirs); + copyPlugins(plugins); + copyExec(); + createRunFile(); + + emit pathsToCopyChanged(m_pathsToCopy); + emit copySuccessChanged(m_copySuccess); +} + +void OutputManager::copyQml(const QStringList &dirs) +{ + auto qmldir = m_outputdir + "/qml"; + + for (const QString &S : dirs) + { + QString path = qmldir + "/" + S; + m_pathsToCopy << path + "/"; + m_copySuccess << copyDir(m_qtdir + "/qml/" + S, path); + } +} + +void OutputManager::copyPlugins(const QStringList &plugins) +{ + QString qtpath = m_qtdir + "/plugins/"; + QString path = m_outputdir + "/plugins/"; + + for (const QString &plugin : plugins) + { + m_pathsToCopy << path + plugin + "/"; + m_copySuccess << copyDir(qtpath + plugin, path + plugin); + } +} + +void OutputManager::copyExec() +{ + auto path = m_outputdir + "/bin/" + QFileInfo(m_executablepath).fileName(); + + m_pathsToCopy << path; + m_copySuccess << copyFile(m_executablepath, path); +} + +void OutputManager::createRunFile() +{ + QString content = + "#!/bin/sh\n" + "export LD_LIBRARY_PATH=`pwd`/lib\n" + "export QML_IMPORT_PATH=`pwd`/qml\n" + "export QML2_IMPORT_PATH=`pwd`/qml\n" + "export QT_PLUGIN_PATH=`pwd`/plugins\n" + "export QT_QPA_PLATFORM_PLUGIN_PATH=`pwd`/plugins/platforms\n" + "./bin/%1"; + + content = content.arg(QFileInfo(m_executablepath).completeBaseName()); + + QString fname = m_outputdir + QDir::separator() + + QFileInfo(m_executablepath).completeBaseName() + ".sh"; + + QFile F(fname); + m_pathsToCopy << fname; + m_copySuccess << F.open(QIODevice::WriteOnly); + + F.write(content.toUtf8()); + F.flush(); + F.close(); + + F.setPermissions(QFileDevice::ExeUser | QFileDevice::WriteUser | + QFileDevice::ReadUser); +} + +void OutputManager::createDirectories() +{ + m_pathsToCopy << m_outputdir + "/bin/"; + m_copySuccess << QDir(m_outputdir).mkdir("bin"); + + m_pathsToCopy << m_outputdir + "/qml/"; + m_copySuccess << QDir(m_outputdir).mkdir("qml"); + + m_pathsToCopy << m_outputdir + "/lib/"; + m_copySuccess << QDir(m_outputdir).mkdir("lib"); + + m_pathsToCopy << m_outputdir + "/plugins/"; + m_copySuccess << QDir(m_outputdir).mkdir("plugins"); +} + +bool OutputManager::copyFile(const QString &source, const QString &destin) +{ + return QFile::copy(source, destin); +} + +OutputManager::OutputManager(QObject *parent) : BaseClass(parent) {} +QStringList OutputManager::pathsToCopy() const { return m_pathsToCopy; } +QList<bool> OutputManager::copySuccess() const { return m_copySuccess; } diff --git a/CPP/outputmanager.h b/CPP/outputmanager.h new file mode 100755 index 0000000..9d9bdec --- /dev/null +++ b/CPP/outputmanager.h @@ -0,0 +1,49 @@ +#ifndef OUTPUTMANAGER_H +#define OUTPUTMANAGER_H + +#include "baseclass.h" + +class OutputManager : public BaseClass +{ + Q_OBJECT + + Q_PROPERTY(QStringList pathsToCopy READ pathsToCopy WRITE setPathsToCopy + NOTIFY pathsToCopyChanged) + + Q_PROPERTY(QList<bool> copySuccess READ copySuccess WRITE setCopySuccess + NOTIFY copySuccessChanged) + + QStringList m_pathsToCopy; + QList<bool> m_copySuccess; + + bool copyDir(const QString &source, const QString &destin); + bool copyFile(const QString &source, const QString &destin); + + void copyCpp(const QStringList &libs); + void copyQml(const QStringList &dirs); + void copyPlugins(const QStringList &plugins); + + void createDirectories(); + void createRunFile(); + void copyExec(); + +public: + explicit OutputManager(QObject *parent = nullptr); + + QStringList pathsToCopy() const; + QList<bool> copySuccess() const; + + void copyAll(const QStringList &qtlibs, const QStringList &libs, + const QStringList &dirs, const QStringList &plugins, + bool erase); + +public slots: + void setPathsToCopy(const QStringList &pathsToCopy); + void setCopySuccess(const QList<bool> ©Success); + +signals: + void pathsToCopyChanged(QStringList pathsToCopy); + void copySuccessChanged(QList<bool> copySuccess); +}; + +#endif // OUTPUTMANAGER_H diff --git a/CPP/pluginmanager.cpp b/CPP/pluginmanager.cpp new file mode 100755 index 0000000..d7c9976 --- /dev/null +++ b/CPP/pluginmanager.cpp @@ -0,0 +1,102 @@ +#include "pluginmanager.h" + +void PluginManager::setNeededPlugins(const QStringList &neededPlugins) +{ + if (m_neededPlugins == neededPlugins) return; + + m_neededPlugins = neededPlugins; + emit neededPluginsChanged(m_neededPlugins); +} + +void PluginManager::start() +{ + m_neededPlugins.clear(); + + m_neededPlugins << "xcbglintegrations" + << "platforms" + << "imageformats" + << "iconengines"; + + m_neededPlugins << findPluginsFromModules(); + + emit neededPluginsChanged(m_neededPlugins); +} + +QStringList PluginManager::getAllSoFiles() +{ + QStringList sofiles; + + for (const QString &plugindir : m_neededPlugins) + sofiles << findFilesInsideDir("*.so", + m_qtdir + "/plugins/" + plugindir); + + return sofiles; +} + +QStringList PluginManager::findPluginsFromModules() +{ + QStringList plugins; + + QStringList modules = extractModules(); + + if (modules.contains("sql")) plugins << "sqldrivers"; + if (modules.contains("gamepad")) plugins << "gamepads"; + if (modules.contains("printsupport")) plugins << "printsupport"; + + if (modules.contains("sensors")) + plugins << "sensors" + << "sensorgestures"; + + if (modules.contains("positioning")) + plugins << "geoservices" + << "position" + << "geometryloaders"; + + if (modules.contains("multimedia")) + plugins << "audio" + << "mediaservice" + << "playlistformats"; + + return plugins; +} + +QStringList PluginManager::extractModules() +{ + QStringList proandprifiles = findFilesInsideDir("*.pro", m_projectdir); + proandprifiles << findFilesInsideDir("*.pri", m_projectdir); + + QStringList modules; + for (const QString &file : proandprifiles) + modules << extractMudoulesFromFile(file); + + modules.removeDuplicates(); + return modules; +} + +QStringList PluginManager::extractMudoulesFromFile(const QString &filepath) +{ + QStringList modules; + QFile F(filepath); + if (!F.open(QIODevice::ReadOnly)) return QStringList(); + + QString temp; + QTextStream S(&F); + + while (S.readLineInto(&temp)) + { + temp = temp.simplified(); + if (temp.startsWith("#")) continue; + if (!temp.startsWith("QT")) continue; + + QStringList list = temp.split("+=", QString::SkipEmptyParts); + if (list.count() != 2) list = temp.split("=", QString::SkipEmptyParts); + if (list.count() != 2) continue; + + modules << list[1].split(" ", QString::SkipEmptyParts); + } + + return modules; +} + +PluginManager::PluginManager(QObject *parent) : BaseClass(parent) {} +QStringList PluginManager::neededPlugins() const { return m_neededPlugins; } diff --git a/CPP/pluginmanager.h b/CPP/pluginmanager.h new file mode 100755 index 0000000..a81f3e9 --- /dev/null +++ b/CPP/pluginmanager.h @@ -0,0 +1,34 @@ +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "baseclass.h" + +class PluginManager : public BaseClass +{ + Q_OBJECT + + Q_PROPERTY(QStringList neededPlugins READ neededPlugins WRITE + setNeededPlugins NOTIFY neededPluginsChanged) + + QStringList m_neededPlugins; + + QStringList extractModules(); + QStringList findPluginsFromModules(); + QStringList extractMudoulesFromFile(const QString &filepath); + +public: + explicit PluginManager(QObject *parent = nullptr); + + QStringList neededPlugins() const; + + void start(); + QStringList getAllSoFiles(); + +public slots: + void setNeededPlugins(const QStringList &neededPlugins); + +signals: + void neededPluginsChanged(QStringList neededPlugins); +}; + +#endif // PLUGINMANAGER_H diff --git a/CPP/qmlmanager.cpp b/CPP/qmlmanager.cpp new file mode 100755 index 0000000..5a2c8d5 --- /dev/null +++ b/CPP/qmlmanager.cpp @@ -0,0 +1,147 @@ +#include "qmlmanager.h" + +void QmlManager::extractImportsFromDir(const QString &dirpath) +{ + for (const QString &filepath : findFilesInsideDir("*.qml", dirpath)) + for (QString &import : extractImportsFromFile(filepath)) + { + QStringList list = import.split("#"); + import = list[1]; + + if (list[0] == "2" && + m_secondVersionImports.contains(import.split("/").last())) + import += ".2"; + + if (!m_foundImports.contains(import)) + { + m_foundImports << import; + extractImportsFromDir(m_qtdir + "/qml/" + import); + } + } +} + +QStringList QmlManager::extractImportsFromFile(const QString &filepath) +{ + QStringList imports; + QFile F(filepath); + if (!F.open(QIODevice::ReadOnly)) return QStringList(); + + QString content = F.readAll(); + content.remove(QRegExp("\\{(.*)\\}")); + content.remove(QRegExp("/\\*(.*)\\*/")); + + for (const QString &line : content.split("\n")) + for (QString &word : line.split(";", QString::SkipEmptyParts)) + { + word = word.simplified(); + if (word.startsWith("//")) continue; + if (!word.startsWith("import")) continue; + + QStringList list = word.split(" ", QString::SkipEmptyParts); + if (list.count() != 3) + { + if (list.count() == 5) + { + if (list[3] != "as") continue; + } + else + continue; + } + + imports << (list[2][0] + "#" + list[1].replace(".", "/")); + } + + return imports; +} + +QStringList QmlManager::getAllQtQmlImports() +{ + QStringList dirs; + + QString dir = m_qtdir + "/qml"; + + QDir qdir(dir); + qdir.setFilter(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); + + QDirIterator it(qdir, QDirIterator::Subdirectories); + while (it.hasNext()) dirs << it.next().remove(0, dir.count() + 1); + + return dirs; +} + +void QmlManager::removeInnerImports() +{ + for (const QString &import : m_foundImports) + { + bool contains = false; + + for (const QString &S : m_foundImports) + if (import.startsWith(S + "/")) contains = true; + + if (contains) m_foundImports.removeOne(import); + } +} + +void QmlManager::divideFoundAndNotFound() +{ + QStringList dirs = getAllQtQmlImports(); + + for (const QString &S : m_foundImports) + if (!dirs.contains(S)) + { + m_notFoundImports << S; + m_foundImports.removeOne(S); + } +} + +QmlManager::QmlManager(QObject *parent) : BaseClass(parent) +{ + m_secondVersionImports << "QtQuick" + << "Models" + << "Window" + << "Controls" + << "Particles" + << "Templates"; +} + +QStringList QmlManager::getAllSoFiles() +{ + QStringList sofiles; + + for (const QString &dirpath : m_foundImports) + sofiles << findFilesInsideDir("*.so", m_qtdir + "/qml/" + dirpath); + + return sofiles; +} + +void QmlManager::start() +{ + m_foundImports.clear(); + m_notFoundImports.clear(); + + extractImportsFromDir(m_projectdir); + removeInnerImports(); + divideFoundAndNotFound(); + + emit foundImportsChanged(m_foundImports); + emit notFoundImportsChanged(m_notFoundImports); +} + +void QmlManager::setFoundImports(const QStringList &foundImports) +{ + if (m_foundImports == foundImports) return; + + m_foundImports = foundImports; + emit foundImportsChanged(m_foundImports); +} + +void QmlManager::setNotFoundImports(const QStringList &nonFoundImports) +{ + if (m_notFoundImports == nonFoundImports) return; + + m_notFoundImports = nonFoundImports; + emit notFoundImportsChanged(m_notFoundImports); +} + +QStringList QmlManager::foundImports() const { return m_foundImports; } +QStringList QmlManager::notFoundImports() const { return m_notFoundImports; } diff --git a/CPP/qmlmanager.h b/CPP/qmlmanager.h new file mode 100755 index 0000000..08a0f74 --- /dev/null +++ b/CPP/qmlmanager.h @@ -0,0 +1,47 @@ +#ifndef QMLMANAGER_H +#define QMLMANAGER_H + +#include "baseclass.h" + +class QmlManager : public BaseClass +{ + Q_OBJECT + + Q_PROPERTY(QStringList foundImports READ foundImports WRITE setFoundImports + NOTIFY foundImportsChanged) + + Q_PROPERTY(QStringList notFoundImports READ notFoundImports WRITE + setNotFoundImports NOTIFY notFoundImportsChanged) + + QStringList m_foundImports; + QStringList m_notFoundImports; + + QStringList m_secondVersionImports; + +public: // TODO remove + void extractImportsFromDir(const QString &dirpath); + QStringList extractImportsFromFile(const QString &filepath); + + QStringList getAllQtQmlImports(); + void removeInnerImports(); + void divideFoundAndNotFound(); + +public: + explicit QmlManager(QObject *parent = nullptr); + + QStringList foundImports() const; + QStringList notFoundImports() const; + + void start(); + QStringList getAllSoFiles(); + +public slots: + void setFoundImports(const QStringList &foundImports); + void setNotFoundImports(const QStringList ¬FoundImports); + +signals: + void foundImportsChanged(QStringList foundImports); + void notFoundImportsChanged(QStringList notFoundImports); +}; + +#endif // QMLMANAGER_H diff --git a/QML/ExtendableView.qml b/QML/ExtendableView.qml new file mode 100755 index 0000000..65cbeb2 --- /dev/null +++ b/QML/ExtendableView.qml @@ -0,0 +1,87 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 + +Page { + id: page + clip: true + width: listview.width + + property var model + property bool checkable + property bool expanded + + height: expanded ? implicitHeight:header.height + + background: Rectangle { + color: "#eee" + } + + Behavior on height { + SmoothedAnimation { + velocity: page.implicitHeight*2 + } + } + + header: AbstractButton { + id: head + padding: 6 + onClicked: expanded = !expanded + + background: Rectangle { + color: Material.background + + Rectangle { + height: 1 + width: parent.width + color: Material.accent + anchors.bottom: parent.bottom + } + } + + contentItem: RowLayout { + Label { + text: "▶" + rotation: expanded ? 90:0 + font.pointSize: mediumFont + + Behavior on rotation { + NumberAnimation {} + } + } + + Label { + leftPadding: 6 + text: page.title + Layout.fillWidth: true + elide: Text.ElideRight + font.pointSize: mediumFont + } + } + } + + contentItem: Column { + Repeater { + model: page.model + + delegate: Label { + id: label + padding: 10 + width: parent.width + elide: Text.ElideRight + + text: (page.checkable ? (OutputManager.copySuccess[index] + ? "✔ ":"🞪 "):"") + modelData + } + } + } + + Rectangle { + height: 1 + parent: page + width: parent.width + color: Material.primary + anchors.bottom: parent.bottom + } +} diff --git a/QML/PathChooser.qml b/QML/PathChooser.qml new file mode 100755 index 0000000..6f6023a --- /dev/null +++ b/QML/PathChooser.qml @@ -0,0 +1,70 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +ColumnLayout { + id: root + Layout.fillHeight: false + + property string title + property bool isdir: true + property bool confirmed: false + property alias content: field.text + + Label { + text: title + elide: Text.ElideRight + Layout.fillWidth: true + font.pointSize: mediumFont + } + + RowLayout { + Layout.fillHeight: false + + TextField { + id: field + Layout.fillWidth: true + placeholderText: "Enter path or browse" + + onTextChanged: { + if (!MainManager.pathExists(isdir, text)) + { + wlabel.text = "Path doesn't exist" + confirmed = false + } + else if (!MainManager.hasPrems(text)) + { + wlabel.text = "I don't have permission to access this path" + confirmed = false + } + else + { + wlabel.text = "This path is OK" + confirmed = true + } + } + } + + Button { + text: "Browse" + onClicked: fd.open() + Material.background: buttonColor + } + } + + Label { + id: wlabel + elide: Text.ElideRight + Layout.fillWidth: true + } + + FileDialog { + id: fd + selectFolder: isdir + selectMultiple: false + + onAccepted: field.text = MainManager.stringFromUrl(isdir ? folder:fileUrl) + } +} diff --git a/QML/PreparePage.qml b/QML/PreparePage.qml new file mode 100755 index 0000000..2d6752c --- /dev/null +++ b/QML/PreparePage.qml @@ -0,0 +1,114 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import Qt.labs.settings 1.0 + +Page { + id: page + clip: true + + header: TopBar { + text: "Prepare" + + ToolButton { + text: "➔" + rotation: 180 + font.pointSize: largeFont + anchors.right: parent.right + onClicked: swipeview.currentIndex = 0 + anchors.verticalCenter: parent.verticalCenter + } + } + + property string outdir + property var cpplibs: [] + + ColumnLayout { + spacing: 15 + anchors.margins: 15 + anchors.fill: parent + + Label { + elide: Text.ElideRight + Layout.fillWidth: true + font.pointSize: mediumFont + text: "Choose Non-Qt Libraries To Copy" + } + + Flickable { + id: flick + Layout.fillWidth: true + Layout.fillHeight: true + contentWidth: width + contentHeight: column.height + clip: true + + ScrollBar.vertical: ScrollBar { } + + Rectangle { + parent: flick + anchors.fill: parent + color: "transparent" + border.color: Material.accent + } + + Column { + id: column + width: parent.width + + Repeater { + model: CppManager.cppLibraries + + delegate: CheckDelegate { + id: del + text: modelData + width: parent.width + + Connections { + target: checkAll + onCheckedChanged: del.checked = checkAll.checked + } + + onCheckedChanged: { + var place = cpplibs.indexOf(del.text) + + if (checked && place < 0) + cpplibs.push(del.text) + else if (place > -1) + cpplibs.splice(place, 1) + } + } + } + } + } + + CheckBox { + id: checkAll + Layout.fillWidth: true + text: "Check All The Above" + } + + CheckBox { + id: erase + Layout.fillWidth: true + text: "Erase Everything In: " + outdir + } + + Button { + text: "Next" + Material.background: buttonColor + Layout.alignment: Qt.AlignRight + + onClicked: { + pp.erase = erase.checked + swipeview.currentIndex = 2 + CppManager.cppLibraries = cpplibs + } + } + } + + Settings { + property alias erase: erase.checked + } +} diff --git a/QML/ProcessPage.qml b/QML/ProcessPage.qml new file mode 100755 index 0000000..fd2eb28 --- /dev/null +++ b/QML/ProcessPage.qml @@ -0,0 +1,40 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 + +Page { + id: page + clip: true + + property bool erase + property int state: MainManager.state + + header: TopBar { + text: "Qt Linux Deployer" + + ToolButton { + text: "➔" + rotation: 180 + font.pointSize: largeFont + anchors.right: parent.right + onClicked: swipeview.currentIndex = 0 + anchors.verticalCenter: parent.verticalCenter + } + } + + Button { + width: 200 + padding: 18 + anchors.centerIn: parent + Material.background: buttonColor + text: page.state == 0 ? "Go!":(page.state == 1 ? "Wait!":"Done!") + + onClicked: { + if (page.state == 0) + MainManager.start(erase) + else if (page.state == 2) + swipeview.currentIndex = 3 + } + } +} diff --git a/QML/ResultPage.qml b/QML/ResultPage.qml new file mode 100755 index 0000000..fd8dbc9 --- /dev/null +++ b/QML/ResultPage.qml @@ -0,0 +1,63 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 + +Page { + clip: true + + header: TopBar { + text: "Result" + + ToolButton { + text: "➔" + rotation: 180 + font.pointSize: largeFont + anchors.right: parent.right + onClicked: swipeview.currentIndex = 0 + anchors.verticalCenter: parent.verticalCenter + } + } + + ListView { + id: listview + anchors.fill: parent + ScrollBar.vertical: ScrollBar {} + + model: VisualItemModel { + ExtendableView { + checkable: false + title: "Qt Libraries" + model: CppManager.qtLibraries + } + + ExtendableView { + checkable: false + title: "Non-Qt Libraries" + model: CppManager.cppLibraries + } + + ExtendableView { + checkable: false + title: "Unknown Libraries" + model: CppManager.notFoundLibs + } + + ExtendableView { + checkable: false + title: "Qml Imports" + model: QmlManager.foundImports + } + + ExtendableView { + checkable: false + title: "Failed Qml Imports" + model: QmlManager.notFoundImports + } + + ExtendableView { + checkable: true + title: "Copied Paths" + model: OutputManager.pathsToCopy + } + } + } +} diff --git a/QML/StartPage.qml b/QML/StartPage.qml new file mode 100755 index 0000000..fa5cca6 --- /dev/null +++ b/QML/StartPage.qml @@ -0,0 +1,69 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import Qt.labs.settings 1.0 + +Page { + id: page + clip: true + + header: TopBar { + text: "Qt Linux Deployer" + } + + Settings { + property alias qtdir: qtdir.content + property alias outdir: outdir.content + property alias execpath: execpath.content + property alias projectdir: projectdir.content + } + + ColumnLayout { + spacing: 15 + anchors.margins: 15 + anchors.fill: parent + + PathChooser { + id: qtdir + title: "Qt Build Directory" + } + + PathChooser { + id: execpath + isdir: false + title: "Executable File Path" + } + + PathChooser { + id: projectdir + title: "Project Directory" + } + + PathChooser { + id: outdir + title: "Final Output Directory" + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + Button { + text: "Next" + Material.background: buttonColor + Layout.alignment: Qt.AlignRight + enabled: qtdir.confirmed && execpath.confirmed + && projectdir.confirmed && outdir.confirmed + + onClicked: { + MainManager.prepare(qtdir.content, execpath.content, + projectdir.content, outdir.content) + + prp.outdir = outdir.content + swipeview.currentIndex = 1 + } + } + } +} diff --git a/QML/TopBar.qml b/QML/TopBar.qml new file mode 100755 index 0000000..df40ca2 --- /dev/null +++ b/QML/TopBar.qml @@ -0,0 +1,14 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 + +ToolBar { + id: control + + property string text + + Label { + text: control.text + anchors.centerIn: parent + font.pointSize: control.font.pointSize + 3 + } +} diff --git a/QML/main.qml b/QML/main.qml new file mode 100755 index 0000000..c562ef8 --- /dev/null +++ b/QML/main.qml @@ -0,0 +1,43 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Material 2.0 + +ApplicationWindow { + id: window + visible: true + title: qsTr("Qt Linux Deployer") + + width: 600 + height: sp.implicitHeight + 30 + + property real smallFont: window.font.pointSize + property real mediumFont: window.font.pointSize + 2 + property real largeFont: window.font.pointSize + 4 + + Material.accent: Material.Red + Material.primary: Material.Red + + property var buttonColor: Material.accent + + SwipeView { + id: swipeview + interactive: false + anchors.fill: parent + + StartPage { + id: sp + } + + PreparePage { + id: prp + } + + ProcessPage { + id: pp + } + + ResultPage { + id: rp + } + } +} diff --git a/QtLinuxDeployer.pro b/QtLinuxDeployer.pro new file mode 100755 index 0000000..049a7e8 --- /dev/null +++ b/QtLinuxDeployer.pro @@ -0,0 +1,44 @@ +QT += qml quick + +CONFIG += c++11 + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +SOURCES += \ + main.cpp \ + CPP/baseclass.cpp \ + CPP/cppmanager.cpp \ + CPP/mainmanager.cpp \ + CPP/outputmanager.cpp \ + CPP/pluginmanager.cpp \ + CPP/qmlmanager.cpp + +HEADERS += \ + CPP/baseclass.h \ + CPP/cppmanager.h \ + CPP/mainmanager.h \ + CPP/outputmanager.h \ + CPP/pluginmanager.h \ + CPP/qmlmanager.h diff --git a/main.cpp b/main.cpp new file mode 100755 index 0000000..e2982ee --- /dev/null +++ b/main.cpp @@ -0,0 +1,35 @@ +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> + +#include <CPP/cppmanager.h> +#include <CPP/mainmanager.h> +#include <CPP/outputmanager.h> +#include <CPP/pluginmanager.h> +#include <CPP/qmlmanager.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + CppManager C; + QmlManager Q; + PluginManager P; + OutputManager O; + MainManager M(&C, &Q, &O, &P); + + QQmlApplicationEngine engine; + + auto *R = engine.rootContext(); + R->setContextProperty("CppManager", &C); + R->setContextProperty("QmlManager", &Q); + R->setContextProperty("PluginManager", &P); + R->setContextProperty("MainManager", &M); + R->setContextProperty("OutputManager", &O); + + engine.load(QUrl(QLatin1String("qrc:/QML/main.qml"))); + if (engine.rootObjects().isEmpty()) return -1; + + return app.exec(); +} diff --git a/qml.qrc b/qml.qrc new file mode 100755 index 0000000..b9d731e --- /dev/null +++ b/qml.qrc @@ -0,0 +1,13 @@ +<RCC> + <qresource prefix="/"> + <file>qtquickcontrols2.conf</file> + <file>QML/ExtendableView.qml</file> + <file>QML/main.qml</file> + <file>QML/PathChooser.qml</file> + <file>QML/PreparePage.qml</file> + <file>QML/ProcessPage.qml</file> + <file>QML/ResultPage.qml</file> + <file>QML/StartPage.qml</file> + <file>QML/TopBar.qml</file> + </qresource> +</RCC> diff --git a/qtquickcontrols2.conf b/qtquickcontrols2.conf new file mode 100755 index 0000000..0c8980f --- /dev/null +++ b/qtquickcontrols2.conf @@ -0,0 +1,15 @@ +; This file can be edited to change the style of the application +; See Styling Qt Quick Controls 2 in the documentation for details: +; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html + +[Controls] +Style=Material + +[Universal] +Theme=Light +;Accent=Steel + +[Material] +Theme=Light +;Accent=BlueGrey +;Primary=BlueGray