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 <utils/algorithm.h>
|
||||
#include <utils/appinfo.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stringutils.h>
|
||||
@@ -1277,4 +1278,54 @@ void CppPluginSpec::kill()
|
||||
d->plugin = nullptr;
|
||||
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
|
||||
|
@@ -147,6 +147,8 @@ public:
|
||||
|
||||
virtual void setEnabledBySettings(bool value);
|
||||
|
||||
virtual Utils::FilePath installLocation(bool inUserFolder) const = 0;
|
||||
|
||||
protected:
|
||||
virtual void setEnabledByDefault(bool value);
|
||||
virtual void setEnabledIndirectly(bool value);
|
||||
@@ -174,6 +176,10 @@ private:
|
||||
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(
|
||||
const Utils::FilePath &filePath);
|
||||
EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
|
||||
@@ -201,6 +207,8 @@ public:
|
||||
|
||||
Utils::expected_str<void> readMetaData(const QJsonObject &pluginMetaData) override;
|
||||
|
||||
Utils::FilePath installLocation(bool inUserFolder) const override;
|
||||
|
||||
protected:
|
||||
CppPluginSpec();
|
||||
|
||||
|
@@ -46,17 +46,9 @@ struct Data
|
||||
FilePath sourcePath;
|
||||
FilePath extractedPath;
|
||||
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)
|
||||
{
|
||||
return (HostOsInfo::isWindowsHost() && path.endsWith(".dll"))
|
||||
@@ -64,12 +56,6 @@ static bool hasLibSuffix(const FilePath &path)
|
||||
|| (HostOsInfo::isMacHost() && path.endsWith(".dylib"));
|
||||
}
|
||||
|
||||
static FilePath pluginInstallPath(bool installIntoApplication)
|
||||
{
|
||||
return FilePath::fromString(installIntoApplication ? Core::ICore::pluginPath()
|
||||
: Core::ICore::userPluginPath());
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class SourcePage : public WizardPage
|
||||
@@ -135,45 +121,31 @@ public:
|
||||
Data *m_data = nullptr;
|
||||
};
|
||||
|
||||
struct ArchiveIssue
|
||||
{
|
||||
QString message;
|
||||
InfoLabel::InfoType type;
|
||||
};
|
||||
using CheckResult = expected_str<PluginSpec *>;
|
||||
|
||||
// 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
|
||||
QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks,
|
||||
QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
if (promise.isCanceled())
|
||||
if (!plugins.front()->resolveDependencies(PluginManager::plugins())) {
|
||||
promise.addResult(Utils::make_unexpected(
|
||||
Tr::tr("Plugin failed to resolve dependencies:") + " "
|
||||
+ plugins.front()->errorString()));
|
||||
qDeleteAll(plugins);
|
||||
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(
|
||||
ArchiveIssue{Tr::tr("Did not find %1 plugin.").arg(QGuiApplication::applicationDisplayName()),
|
||||
InfoLabel::Error});
|
||||
|
||||
promise.addResult(plugins.front());
|
||||
}
|
||||
|
||||
class CheckArchivePage : public WizardPage
|
||||
@@ -236,30 +208,33 @@ public:
|
||||
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)
|
||||
return SetupResult::StopWithError;
|
||||
|
||||
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) {
|
||||
m_isComplete = !async.isResultAvailable();
|
||||
if (m_isComplete) {
|
||||
const auto onCheckerDone = [this](const Async<CheckResult> &async) {
|
||||
expected_str<PluginSpec *> result = async.result();
|
||||
if (!result) {
|
||||
m_label->setType(InfoLabel::Error);
|
||||
m_label->setText(result.error());
|
||||
} else {
|
||||
m_label->setType(InfoLabel::Ok);
|
||||
m_label->setText(Tr::tr("Archive is OK."));
|
||||
} else {
|
||||
const ArchiveIssue issue = async.result();
|
||||
m_label->setType(issue.type);
|
||||
m_label->setText(issue.message);
|
||||
m_data->pluginSpec.reset(*result);
|
||||
m_isComplete = true;
|
||||
}
|
||||
emit completeChanged();
|
||||
};
|
||||
|
||||
const Group root {
|
||||
// clang-format off
|
||||
const Group root{
|
||||
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_taskTreeRunner.start(root, {}, [this](DoneWith) { m_cancelButton->setVisible(false); });
|
||||
}
|
||||
@@ -346,8 +321,9 @@ public:
|
||||
{
|
||||
m_summaryLabel->setText(
|
||||
Tr::tr("\"%1\" will be installed into \"%2\".")
|
||||
.arg(m_data->sourcePath.toUserOutput(),
|
||||
pluginInstallPath(m_data->installIntoApplication).toUserOutput()));
|
||||
.arg(m_data->sourcePath.toUserOutput())
|
||||
.arg(m_data->pluginSpec->installLocation(!m_data->installIntoApplication)
|
||||
.toUserOutput()));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -420,7 +396,7 @@ bool executePluginInstallWizard(const FilePath &archive)
|
||||
wizard.addPage(summaryPage);
|
||||
|
||||
if (wizard.exec()) {
|
||||
const FilePath installPath = pluginInstallPath(data.installIntoApplication);
|
||||
const FilePath installPath = data.pluginSpec->installLocation(!data.installIntoApplication);
|
||||
if (hasLibSuffix(data.sourcePath)) {
|
||||
return copyPluginFile(data.sourcePath, installPath);
|
||||
} else {
|
||||
|
@@ -266,6 +266,21 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -170,4 +170,12 @@ bool LuaPluginSpec::printToOutputPane() const
|
||||
return d->printToOutputPane;
|
||||
}
|
||||
|
||||
Utils::FilePath LuaPluginSpec::installLocation(bool inUserFolder) const
|
||||
{
|
||||
if (inUserFolder)
|
||||
return appInfo().userLuaPlugins;
|
||||
|
||||
return appInfo().luaPlugins;
|
||||
}
|
||||
|
||||
} // namespace Lua
|
||||
|
@@ -55,6 +55,8 @@ public:
|
||||
ExtensionSystem::IPlugin::ShutdownFlag stop() override;
|
||||
void kill() override;
|
||||
|
||||
Utils::FilePath installLocation(bool inUserFolder) const override;
|
||||
|
||||
public:
|
||||
bool printToOutputPane() const;
|
||||
};
|
||||
|
Reference in New Issue
Block a user