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 &notFoundLibs)
+{
+	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 &notFoundLibs);
+
+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> &copySuccess)
+{
+	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> &copySuccess);
+
+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 &notFoundImports);
+
+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