Chris@1178: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1178: Chris@1178: /* Chris@1178: Sonic Visualiser Chris@1178: An audio file viewer and annotation editor. Chris@1178: Centre for Digital Music, Queen Mary, University of London. Chris@1178: Chris@1178: This program is free software; you can redistribute it and/or Chris@1178: modify it under the terms of the GNU General Public License as Chris@1178: published by the Free Software Foundation; either version 2 of the Chris@1178: License, or (at your option) any later version. See the file Chris@1178: COPYING included with this distribution for more information. Chris@1178: */ Chris@1178: Chris@1178: #include "PluginScan.h" Chris@1178: Chris@1178: #include "base/Debug.h" Chris@1246: #include "base/Preferences.h" Chris@1241: #include "base/HelperExecPath.h" Chris@1178: Chris@1501: #include Chris@1180: Chris@1178: #include Chris@1181: #include Chris@1178: Chris@1178: using std::string; Chris@1178: Chris@1249: class PluginScan::Logger Chris@1249: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1249: : public PluginCandidates::LogCallback Chris@1249: #endif Chris@1180: { Chris@1180: protected: Chris@1600: void log(std::string message) Chris@1600: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1600: override Chris@1600: #endif Chris@1600: { Chris@1264: SVDEBUG << "PluginScan: " << message << endl; Chris@1180: } Chris@1180: }; Chris@1180: Chris@1180: PluginScan *PluginScan::getInstance() Chris@1180: { Chris@1178: static QMutex mutex; Chris@1582: static PluginScan *m_instance = nullptr; Chris@1178: mutex.lock(); Chris@1178: if (!m_instance) m_instance = new PluginScan(); Chris@1178: mutex.unlock(); Chris@1178: return m_instance; Chris@1178: } Chris@1178: Chris@1246: PluginScan::PluginScan() : m_succeeded(false), m_logger(new Logger) { Chris@1178: } Chris@1178: Chris@1178: PluginScan::~PluginScan() { Chris@1246: QMutexLocker locker(&m_mutex); Chris@1241: clear(); Chris@1180: delete m_logger; Chris@1396: SVDEBUG << "PluginScan::~PluginScan completed" << endl; Chris@1178: } Chris@1178: Chris@1178: void Chris@1241: PluginScan::scan() Chris@1178: { Chris@1249: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1249: Chris@1246: QMutexLocker locker(&m_mutex); Chris@1246: Chris@1246: bool inProcess = Preferences::getInstance()->getRunPluginsInProcess(); Chris@1246: Chris@1246: HelperExecPath hep(inProcess ? Chris@1246: HelperExecPath::NativeArchitectureOnly : Chris@1246: HelperExecPath::AllInstalled); Chris@1246: Chris@1352: QString helperName("vamp-plugin-load-checker"); Chris@1246: auto helpers = hep.getHelperExecutables(helperName); Chris@1241: Chris@1241: clear(); Chris@1241: Chris@1246: for (auto p: helpers) { Chris@1247: SVDEBUG << "NOTE: PluginScan: Found helper: " << p.executable << endl; Chris@1246: } Chris@1246: Chris@1246: if (helpers.empty()) { Chris@1247: SVDEBUG << "NOTE: No plugin checker helpers found in installation;" Chris@1246: << " found none of the following:" << endl; Chris@1246: for (auto d: hep.getHelperCandidatePaths(helperName)) { Chris@1247: SVDEBUG << "NOTE: " << d << endl; Chris@1246: } Chris@1246: } Chris@1246: Chris@1246: for (auto p: helpers) { Chris@1241: try { Chris@1474: KnownPluginCandidates *kp = new KnownPluginCandidates Chris@1246: (p.executable.toStdString(), m_logger); Chris@1246: if (m_kp.find(p.tag) != m_kp.end()) { Chris@1247: SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag Chris@1246: << " for helpers" << endl; Chris@1246: continue; Chris@1246: } Chris@1246: m_kp[p.tag] = kp; Chris@1241: m_succeeded = true; Chris@1241: } catch (const std::exception &e) { Chris@1247: SVDEBUG << "ERROR: PluginScan::scan: " << e.what() Chris@1246: << " (with helper path = " << p.executable << ")" << endl; Chris@1241: } Chris@1241: } Chris@1249: Chris@1396: SVDEBUG << "PluginScan::scan complete" << endl; Chris@1249: #endif Chris@1241: } Chris@1241: Chris@1246: bool Chris@1246: PluginScan::scanSucceeded() const Chris@1246: { Chris@1246: QMutexLocker locker(&m_mutex); Chris@1246: return m_succeeded; Chris@1246: } Chris@1246: Chris@1241: void Chris@1241: PluginScan::clear() Chris@1241: { Chris@1246: for (auto &p: m_kp) { Chris@1246: delete p.second; Chris@1246: } Chris@1241: m_kp.clear(); Chris@1179: m_succeeded = false; Chris@1179: } Chris@1179: Chris@1246: QList Chris@1278: PluginScan::getCandidateLibrariesFor(PluginType Chris@1278: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1278: type Chris@1278: #endif Chris@1278: ) const Chris@1179: { Chris@1249: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1249: Chris@1246: QMutexLocker locker(&m_mutex); Chris@1246: Chris@1180: KnownPlugins::PluginType kpt; Chris@1180: switch (type) { Chris@1180: case VampPlugin: kpt = KnownPlugins::VampPlugin; break; Chris@1180: case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break; Chris@1180: case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break; Chris@1180: default: throw std::logic_error("Inconsistency in plugin type enums"); Chris@1180: } Chris@1180: Chris@1246: QList candidates; Chris@1245: Chris@1246: for (auto rec: m_kp) { Chris@1245: Chris@1474: KnownPluginCandidates *kp = rec.second; Chris@1246: Chris@1241: auto c = kp->getCandidateLibrariesFor(kpt); Chris@1245: Chris@1247: SVDEBUG << "PluginScan: helper \"" << kp->getHelperExecutableName() Chris@1247: << "\" likes " << c.size() << " libraries of type " Chris@1247: << kp->getTagFor(kpt) << endl; Chris@1245: Chris@1246: for (auto s: c) { Chris@1246: candidates.push_back({ s.c_str(), rec.first }); Chris@1246: } Chris@1245: Chris@1245: if (type != VampPlugin) { Chris@1245: // We are only interested in querying multiple helpers Chris@1245: // when dealing with Vamp plugins, for which we can use Chris@1245: // external servers and so in some cases can support Chris@1245: // additional architectures. Other plugin formats are Chris@1245: // loaded directly and so must match the host, which is Chris@1245: // what the first helper is supposed to handle -- so Chris@1245: // break after the first one if not querying Vamp Chris@1245: break; Chris@1245: } Chris@1241: } Chris@1245: Chris@1179: return candidates; Chris@1249: Chris@1249: #else Chris@1249: return {}; Chris@1249: #endif Chris@1178: } Chris@1178: Chris@1501: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1501: QString Chris@1501: PluginScan::formatFailureReport(QString tag, Chris@1501: std::vector failures) const Chris@1501: { Chris@1501: int n = int(failures.size()); Chris@1501: int i = 0; Chris@1501: Chris@1501: std::ostringstream os; Chris@1501: Chris@1501: os << "
    "; Chris@1501: for (auto f: failures) { Chris@1562: os << "
  • " + f.library + ""; Chris@1501: Chris@1501: SVDEBUG << "PluginScan::formatFailureReport: tag is \"" << tag Chris@1501: << "\", failure code is " << int(f.code) << ", message is \"" Chris@1501: << f.message << "\"" << endl; Chris@1501: Chris@1501: QString userMessage = QString::fromStdString(f.message); Chris@1501: Chris@1501: switch (f.code) { Chris@1501: Chris@1501: case PluginCheckCode::FAIL_LIBRARY_NOT_FOUND: Chris@1501: userMessage = QObject::tr("Library file could not be opened"); Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::FAIL_WRONG_ARCHITECTURE: Chris@1501: if (tag == "64" || (sizeof(void *) == 8 && tag == "")) { Chris@1501: userMessage = QObject::tr Chris@1505: ("Library has wrong architecture - possibly a 32-bit plugin installed in a 64-bit plugin folder"); Chris@1501: } else if (tag == "32" || (sizeof(void *) == 4 && tag == "")) { Chris@1501: userMessage = QObject::tr Chris@1505: ("Library has wrong architecture - possibly a 64-bit plugin installed in a 32-bit plugin folder"); Chris@1501: } Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::FAIL_DEPENDENCY_MISSING: Chris@1501: userMessage = QObject::tr Chris@1501: ("Library depends on another library that cannot be found: %1") Chris@1501: .arg(userMessage); Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::FAIL_NOT_LOADABLE: Chris@1501: userMessage = QObject::tr Chris@1501: ("Library cannot be loaded: %1").arg(userMessage); Chris@1501: break; Chris@1501: Chris@1562: case PluginCheckCode::FAIL_FORBIDDEN: Chris@1562: userMessage = QObject::tr Chris@1562: ("Permission to load library was refused"); Chris@1562: break; Chris@1562: Chris@1501: case PluginCheckCode::FAIL_DESCRIPTOR_MISSING: Chris@1501: userMessage = QObject::tr Chris@1501: ("Not a valid plugin library (no descriptor found)"); Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::FAIL_NO_PLUGINS: Chris@1501: userMessage = QObject::tr Chris@1501: ("Library contains no plugins"); Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::FAIL_OTHER: Chris@1501: if (userMessage == "") { Chris@1501: userMessage = QObject::tr Chris@1501: ("Unknown error"); Chris@1501: } Chris@1501: break; Chris@1501: Chris@1501: case PluginCheckCode::SUCCESS: Chris@1501: // success shouldn't happen here! Chris@1501: break; Chris@1501: } Chris@1501: Chris@1501: os << "
    " + userMessage.toStdString() + ""; Chris@1501: os << "
  • "; Chris@1501: Chris@1501: if (n > 10) { Chris@1501: if (++i == 5) { Chris@1501: os << "
  • "; Chris@1501: os << QObject::tr("... and %n further failure(s)", Chris@1501: "", n - i) Chris@1501: .toStdString(); Chris@1501: os << "
  • "; Chris@1501: break; Chris@1501: } Chris@1501: } Chris@1501: } Chris@1501: os << "
"; Chris@1501: Chris@1501: return QString::fromStdString(os.str()); Chris@1501: } Chris@1501: #endif Chris@1501: Chris@1178: QString Chris@1178: PluginScan::getStartupFailureReport() const Chris@1178: { Chris@1249: #ifdef HAVE_PLUGIN_CHECKER_HELPER Chris@1249: Chris@1246: QMutexLocker locker(&m_mutex); Chris@1246: Chris@1179: if (!m_succeeded) { Chris@1429: return QObject::tr("Failed to scan for plugins" Chris@1429: "

Failed to scan for plugins at startup. Possibly " Chris@1352: "the plugin checker program was not correctly " Chris@1181: "installed alongside %1?

") Chris@1181: .arg(QCoreApplication::applicationName()); Chris@1179: } Chris@1241: if (m_kp.empty()) { Chris@1429: return QObject::tr("Did not scan for plugins" Chris@1429: "

Apparently no scan for plugins was attempted " Chris@1429: "(internal error?)

"); Chris@1179: } Chris@1179: Chris@1241: QString report; Chris@1241: for (auto kp: m_kp) { Chris@1501: auto failures = kp.second->getFailures(); Chris@1502: if (!failures.empty()) { Chris@1502: report += formatFailureReport(kp.first, failures); Chris@1502: } Chris@1241: } Chris@1179: if (report == "") { Chris@1429: return report; Chris@1179: } Chris@1179: Chris@1562: return QObject::tr("

Failed to load one or more plugin libraries:

") Chris@1429: + report Chris@1181: + QObject::tr("

These plugins may be incompatible with the system, " Chris@1181: "and will be ignored during this run of %1.

") Chris@1181: .arg(QCoreApplication::applicationName()); Chris@1249: Chris@1249: #else Chris@1249: return ""; Chris@1249: #endif Chris@1178: } Chris@1178: