diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index f4369cbc217..610f6d9c78d 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -8,6 +8,7 @@ #include "pluginmanager.h" #include +#include #include #include #include @@ -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 createCppPluginsFromArchive(const FilePath &path) +{ + QList results; + + // look for plugin + QDirIterator + it(path.path(), + libraryNameFilter(), + QDir::Files | QDir::NoSymLinks, + QDirIterator::Subdirectories); + + while (it.hasNext()) { + it.next(); + expected_str spec = readCppPluginSpec(FilePath::fromUserInput(it.filePath())); + if (spec) + results.push_back(spec.value()); + } + return results; +} + +QList &pluginSpecsFromArchiveFactories() +{ + static QList factories = {&createCppPluginsFromArchive}; + return factories; +} + +QList pluginSpecsFromArchive(const Utils::FilePath &path) +{ + QList results; + for (const PluginFromArchiveFactory &factory : pluginSpecsFromArchiveFactories()) { + results += factory(path); + } + return results; +} + } // namespace ExtensionSystem diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index db6070bd65a..480fbad4d36 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -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 d; }; +using PluginFromArchiveFactory = std::function(const Utils::FilePath &path)>; +EXTENSIONSYSTEM_EXPORT QList &pluginSpecsFromArchiveFactories(); +EXTENSIONSYSTEM_EXPORT QList pluginSpecsFromArchive(const Utils::FilePath &path); + EXTENSIONSYSTEM_EXPORT Utils::expected_str readCppPluginSpec( const Utils::FilePath &filePath); EXTENSIONSYSTEM_EXPORT Utils::expected_str readCppPluginSpec( @@ -201,6 +207,8 @@ public: Utils::expected_str readMetaData(const QJsonObject &pluginMetaData) override; + Utils::FilePath installLocation(bool inUserFolder) const override; + protected: CppPluginSpec(); diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index 84c90c43043..bc7f56ed792 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -46,17 +46,9 @@ struct Data FilePath sourcePath; FilePath extractedPath; bool installIntoApplication = false; + std::unique_ptr 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; // Async. Result is set if any issue was found. -void checkContents(QPromise &promise, const FilePath &tempDir) +void checkContents(QPromise &promise, const FilePath &tempDir) { - PluginSpec *coreplugin = PluginManager::specForPlugin(Internal::CorePlugin::instance()); - - // look for plugin - QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks, - QDirIterator::Subdirectories); - while (it.hasNext()) { - if (promise.isCanceled()) - return; - it.next(); - expected_str 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 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 - } + QList plugins = pluginSpecsFromArchive(tempDir); + if (plugins.isEmpty()) { + promise.addResult(Utils::make_unexpected(Tr::tr("No plugins found."))); + return; } - promise.addResult( - ArchiveIssue{Tr::tr("Did not find %1 plugin.").arg(QGuiApplication::applicationDisplayName()), - InfoLabel::Error}); + if (plugins.size() > 1) { + promise.addResult(Utils::make_unexpected(Tr::tr("More than one plugin found."))); + qDeleteAll(plugins); + return; + } + + if (!plugins.front()->resolveDependencies(PluginManager::plugins())) { + promise.addResult(Utils::make_unexpected( + Tr::tr("Plugin failed to resolve dependencies:") + " " + + plugins.front()->errorString())); + qDeleteAll(plugins); + return; + } + + 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 &async) { + const auto onCheckerSetup = [this](Async &async) { if (!m_tempDir) return SetupResult::StopWithError; async.setConcurrentCallData(checkContents, m_tempDir->path()); return SetupResult::Continue; }; - const auto onCheckerDone = [this](const Async &async) { - m_isComplete = !async.isResultAvailable(); - if (m_isComplete) { + const auto onCheckerDone = [this](const Async &async) { + expected_str 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(onCheckerSetup, onCheckerDone, CallDoneIf::Success) + AsyncTask(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 { diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index 375e276a4e2..99dded6124f 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -266,6 +266,21 @@ public: Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); }); + pluginSpecsFromArchiveFactories().push_back([](const FilePath &path) { + QList 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 spec = loadPlugin(specFilePath); + QTC_CHECK_EXPECTED(spec); + if (spec) + plugins.push_back(*spec); + } + } + return plugins; + }); + m_pane = new LuaPane(this); } diff --git a/src/plugins/lua/luapluginspec.cpp b/src/plugins/lua/luapluginspec.cpp index 125f9295ad4..17a303bdf1f 100644 --- a/src/plugins/lua/luapluginspec.cpp +++ b/src/plugins/lua/luapluginspec.cpp @@ -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 diff --git a/src/plugins/lua/luapluginspec.h b/src/plugins/lua/luapluginspec.h index ff042416669..3e29b1cde18 100644 --- a/src/plugins/lua/luapluginspec.h +++ b/src/plugins/lua/luapluginspec.h @@ -55,6 +55,8 @@ public: ExtensionSystem::IPlugin::ShutdownFlag stop() override; void kill() override; + Utils::FilePath installLocation(bool inUserFolder) const override; + public: bool printToOutputPane() const; };