forked from qt-creator/qt-creator
ExtensionSystem: Move Installation logic into PluginSpecs
Change-Id: I5b6d284179bf62be89d6e5157fd7e14df5e65817 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "pluginmanager.h"
|
#include "pluginmanager.h"
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/appinfo.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
@@ -1277,4 +1278,54 @@ void CppPluginSpec::kill()
|
|||||||
d->plugin = nullptr;
|
d->plugin = nullptr;
|
||||||
setState(PluginSpec::Deleted);
|
setState(PluginSpec::Deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::FilePath CppPluginSpec::installLocation(bool inUserFolder) const
|
||||||
|
{
|
||||||
|
return inUserFolder ? appInfo().userPluginsRoot : appInfo().plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList libraryNameFilter()
|
||||||
|
{
|
||||||
|
if (HostOsInfo::isWindowsHost())
|
||||||
|
return {"*.dll"};
|
||||||
|
if (HostOsInfo::isLinuxHost())
|
||||||
|
return {"*.so"};
|
||||||
|
return {"*.dylib"};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<PluginSpec *> createCppPluginsFromArchive(const FilePath &path)
|
||||||
|
{
|
||||||
|
QList<PluginSpec *> results;
|
||||||
|
|
||||||
|
// look for plugin
|
||||||
|
QDirIterator
|
||||||
|
it(path.path(),
|
||||||
|
libraryNameFilter(),
|
||||||
|
QDir::Files | QDir::NoSymLinks,
|
||||||
|
QDirIterator::Subdirectories);
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
expected_str<PluginSpec *> spec = readCppPluginSpec(FilePath::fromUserInput(it.filePath()));
|
||||||
|
if (spec)
|
||||||
|
results.push_back(spec.value());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<PluginFromArchiveFactory> &pluginSpecsFromArchiveFactories()
|
||||||
|
{
|
||||||
|
static QList<PluginFromArchiveFactory> factories = {&createCppPluginsFromArchive};
|
||||||
|
return factories;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<PluginSpec *> pluginSpecsFromArchive(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
QList<PluginSpec *> results;
|
||||||
|
for (const PluginFromArchiveFactory &factory : pluginSpecsFromArchiveFactories()) {
|
||||||
|
results += factory(path);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ExtensionSystem
|
} // namespace ExtensionSystem
|
||||||
|
@@ -147,6 +147,8 @@ public:
|
|||||||
|
|
||||||
virtual void setEnabledBySettings(bool value);
|
virtual void setEnabledBySettings(bool value);
|
||||||
|
|
||||||
|
virtual Utils::FilePath installLocation(bool inUserFolder) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setEnabledByDefault(bool value);
|
virtual void setEnabledByDefault(bool value);
|
||||||
virtual void setEnabledIndirectly(bool value);
|
virtual void setEnabledIndirectly(bool value);
|
||||||
@@ -174,6 +176,10 @@ private:
|
|||||||
std::unique_ptr<Internal::PluginSpecPrivate> d;
|
std::unique_ptr<Internal::PluginSpecPrivate> d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using PluginFromArchiveFactory = std::function<QList<PluginSpec *>(const Utils::FilePath &path)>;
|
||||||
|
EXTENSIONSYSTEM_EXPORT QList<PluginFromArchiveFactory> &pluginSpecsFromArchiveFactories();
|
||||||
|
EXTENSIONSYSTEM_EXPORT QList<PluginSpec *> pluginSpecsFromArchive(const Utils::FilePath &path);
|
||||||
|
|
||||||
EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
|
EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
|
||||||
const Utils::FilePath &filePath);
|
const Utils::FilePath &filePath);
|
||||||
EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
|
EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
|
||||||
@@ -201,6 +207,8 @@ public:
|
|||||||
|
|
||||||
Utils::expected_str<void> readMetaData(const QJsonObject &pluginMetaData) override;
|
Utils::expected_str<void> readMetaData(const QJsonObject &pluginMetaData) override;
|
||||||
|
|
||||||
|
Utils::FilePath installLocation(bool inUserFolder) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CppPluginSpec();
|
CppPluginSpec();
|
||||||
|
|
||||||
|
@@ -46,17 +46,9 @@ struct Data
|
|||||||
FilePath sourcePath;
|
FilePath sourcePath;
|
||||||
FilePath extractedPath;
|
FilePath extractedPath;
|
||||||
bool installIntoApplication = false;
|
bool installIntoApplication = false;
|
||||||
|
std::unique_ptr<PluginSpec> pluginSpec = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static QStringList libraryNameFilter()
|
|
||||||
{
|
|
||||||
if (HostOsInfo::isWindowsHost())
|
|
||||||
return {"*.dll"};
|
|
||||||
if (HostOsInfo::isLinuxHost())
|
|
||||||
return {"*.so"};
|
|
||||||
return {"*.dylib"};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool hasLibSuffix(const FilePath &path)
|
static bool hasLibSuffix(const FilePath &path)
|
||||||
{
|
{
|
||||||
return (HostOsInfo::isWindowsHost() && path.endsWith(".dll"))
|
return (HostOsInfo::isWindowsHost() && path.endsWith(".dll"))
|
||||||
@@ -64,12 +56,6 @@ static bool hasLibSuffix(const FilePath &path)
|
|||||||
|| (HostOsInfo::isMacHost() && path.endsWith(".dylib"));
|
|| (HostOsInfo::isMacHost() && path.endsWith(".dylib"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static FilePath pluginInstallPath(bool installIntoApplication)
|
|
||||||
{
|
|
||||||
return FilePath::fromString(installIntoApplication ? Core::ICore::pluginPath()
|
|
||||||
: Core::ICore::userPluginPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class SourcePage : public WizardPage
|
class SourcePage : public WizardPage
|
||||||
@@ -135,45 +121,31 @@ public:
|
|||||||
Data *m_data = nullptr;
|
Data *m_data = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArchiveIssue
|
using CheckResult = expected_str<PluginSpec *>;
|
||||||
{
|
|
||||||
QString message;
|
|
||||||
InfoLabel::InfoType type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Async. Result is set if any issue was found.
|
// Async. Result is set if any issue was found.
|
||||||
void checkContents(QPromise<ArchiveIssue> &promise, const FilePath &tempDir)
|
void checkContents(QPromise<CheckResult> &promise, const FilePath &tempDir)
|
||||||
{
|
{
|
||||||
PluginSpec *coreplugin = PluginManager::specForPlugin(Internal::CorePlugin::instance());
|
QList<PluginSpec *> plugins = pluginSpecsFromArchive(tempDir);
|
||||||
|
if (plugins.isEmpty()) {
|
||||||
|
promise.addResult(Utils::make_unexpected(Tr::tr("No plugins found.")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (plugins.size() > 1) {
|
||||||
|
promise.addResult(Utils::make_unexpected(Tr::tr("More than one plugin found.")));
|
||||||
|
qDeleteAll(plugins);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// look for plugin
|
if (!plugins.front()->resolveDependencies(PluginManager::plugins())) {
|
||||||
QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks,
|
promise.addResult(Utils::make_unexpected(
|
||||||
QDirIterator::Subdirectories);
|
Tr::tr("Plugin failed to resolve dependencies:") + " "
|
||||||
while (it.hasNext()) {
|
+ plugins.front()->errorString()));
|
||||||
if (promise.isCanceled())
|
qDeleteAll(plugins);
|
||||||
return;
|
return;
|
||||||
it.next();
|
|
||||||
expected_str<PluginSpec *> spec = readCppPluginSpec(FilePath::fromUserInput(it.filePath()));
|
|
||||||
if (spec) {
|
|
||||||
// Is a Qt Creator plugin. Let's see if we find a Core dependency and check the
|
|
||||||
// version
|
|
||||||
const QVector<PluginDependency> dependencies = (*spec)->dependencies();
|
|
||||||
const auto found = std::find_if(dependencies.constBegin(), dependencies.constEnd(),
|
|
||||||
[coreplugin](const PluginDependency &d) { return d.name == coreplugin->name(); });
|
|
||||||
if (found == dependencies.constEnd())
|
|
||||||
return;
|
|
||||||
if ((*spec)->provides(coreplugin, *found))
|
|
||||||
return;
|
|
||||||
promise.addResult(
|
|
||||||
ArchiveIssue{Tr::tr("Plugin requires an incompatible version of %1 (%2).")
|
|
||||||
.arg(QGuiApplication::applicationDisplayName(), found->version),
|
|
||||||
InfoLabel::Error});
|
|
||||||
return; // successful / no error
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
promise.addResult(
|
promise.addResult(plugins.front());
|
||||||
ArchiveIssue{Tr::tr("Did not find %1 plugin.").arg(QGuiApplication::applicationDisplayName()),
|
|
||||||
InfoLabel::Error});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CheckArchivePage : public WizardPage
|
class CheckArchivePage : public WizardPage
|
||||||
@@ -236,30 +208,33 @@ public:
|
|||||||
m_label->setText(Tr::tr("There was an error while unarchiving."));
|
m_label->setText(Tr::tr("There was an error while unarchiving."));
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto onCheckerSetup = [this](Async<ArchiveIssue> &async) {
|
const auto onCheckerSetup = [this](Async<CheckResult> &async) {
|
||||||
if (!m_tempDir)
|
if (!m_tempDir)
|
||||||
return SetupResult::StopWithError;
|
return SetupResult::StopWithError;
|
||||||
|
|
||||||
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) {
|
const auto onCheckerDone = [this](const Async<CheckResult> &async) {
|
||||||
m_isComplete = !async.isResultAvailable();
|
expected_str<PluginSpec *> result = async.result();
|
||||||
if (m_isComplete) {
|
if (!result) {
|
||||||
|
m_label->setType(InfoLabel::Error);
|
||||||
|
m_label->setText(result.error());
|
||||||
|
} else {
|
||||||
m_label->setType(InfoLabel::Ok);
|
m_label->setType(InfoLabel::Ok);
|
||||||
m_label->setText(Tr::tr("Archive is OK."));
|
m_label->setText(Tr::tr("Archive is OK."));
|
||||||
} else {
|
m_data->pluginSpec.reset(*result);
|
||||||
const ArchiveIssue issue = async.result();
|
m_isComplete = true;
|
||||||
m_label->setType(issue.type);
|
|
||||||
m_label->setText(issue.message);
|
|
||||||
}
|
}
|
||||||
emit completeChanged();
|
emit completeChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const Group root{
|
const Group root{
|
||||||
UnarchiverTask(onUnarchiverSetup, onUnarchiverError, CallDoneIf::Error),
|
UnarchiverTask(onUnarchiverSetup, onUnarchiverError, CallDoneIf::Error),
|
||||||
AsyncTask<ArchiveIssue>(onCheckerSetup, onCheckerDone, CallDoneIf::Success)
|
AsyncTask<CheckResult>(onCheckerSetup, onCheckerDone, CallDoneIf::Success)
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
m_cancelButton->setVisible(true);
|
m_cancelButton->setVisible(true);
|
||||||
m_taskTreeRunner.start(root, {}, [this](DoneWith) { m_cancelButton->setVisible(false); });
|
m_taskTreeRunner.start(root, {}, [this](DoneWith) { m_cancelButton->setVisible(false); });
|
||||||
}
|
}
|
||||||
@@ -346,8 +321,9 @@ public:
|
|||||||
{
|
{
|
||||||
m_summaryLabel->setText(
|
m_summaryLabel->setText(
|
||||||
Tr::tr("\"%1\" will be installed into \"%2\".")
|
Tr::tr("\"%1\" will be installed into \"%2\".")
|
||||||
.arg(m_data->sourcePath.toUserOutput(),
|
.arg(m_data->sourcePath.toUserOutput())
|
||||||
pluginInstallPath(m_data->installIntoApplication).toUserOutput()));
|
.arg(m_data->pluginSpec->installLocation(!m_data->installIntoApplication)
|
||||||
|
.toUserOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -420,7 +396,7 @@ bool executePluginInstallWizard(const FilePath &archive)
|
|||||||
wizard.addPage(summaryPage);
|
wizard.addPage(summaryPage);
|
||||||
|
|
||||||
if (wizard.exec()) {
|
if (wizard.exec()) {
|
||||||
const FilePath installPath = pluginInstallPath(data.installIntoApplication);
|
const FilePath installPath = data.pluginSpec->installLocation(!data.installIntoApplication);
|
||||||
if (hasLibSuffix(data.sourcePath)) {
|
if (hasLibSuffix(data.sourcePath)) {
|
||||||
return copyPluginFile(data.sourcePath, installPath);
|
return copyPluginFile(data.sourcePath, installPath);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -266,6 +266,21 @@ public:
|
|||||||
|
|
||||||
Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); });
|
Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); });
|
||||||
|
|
||||||
|
pluginSpecsFromArchiveFactories().push_back([](const FilePath &path) {
|
||||||
|
QList<PluginSpec *> plugins;
|
||||||
|
auto dirs = path.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const auto &dir : dirs) {
|
||||||
|
const auto specFilePath = dir / (dir.fileName() + ".lua");
|
||||||
|
if (specFilePath.exists()) {
|
||||||
|
Utils::expected_str<PluginSpec *> spec = loadPlugin(specFilePath);
|
||||||
|
QTC_CHECK_EXPECTED(spec);
|
||||||
|
if (spec)
|
||||||
|
plugins.push_back(*spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
});
|
||||||
|
|
||||||
m_pane = new LuaPane(this);
|
m_pane = new LuaPane(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -170,4 +170,12 @@ bool LuaPluginSpec::printToOutputPane() const
|
|||||||
return d->printToOutputPane;
|
return d->printToOutputPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::FilePath LuaPluginSpec::installLocation(bool inUserFolder) const
|
||||||
|
{
|
||||||
|
if (inUserFolder)
|
||||||
|
return appInfo().userLuaPlugins;
|
||||||
|
|
||||||
|
return appInfo().luaPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Lua
|
} // namespace Lua
|
||||||
|
@@ -55,6 +55,8 @@ public:
|
|||||||
ExtensionSystem::IPlugin::ShutdownFlag stop() override;
|
ExtensionSystem::IPlugin::ShutdownFlag stop() override;
|
||||||
void kill() override;
|
void kill() override;
|
||||||
|
|
||||||
|
Utils::FilePath installLocation(bool inUserFolder) const override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool printToOutputPane() const;
|
bool printToOutputPane() const;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user