annotate plugin/PluginScan.cpp @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents 40d8b01efbc6
children
rev   line source
Chris@1178 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1178 2
Chris@1178 3 /*
Chris@1178 4 Sonic Visualiser
Chris@1178 5 An audio file viewer and annotation editor.
Chris@1178 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1178 7
Chris@1178 8 This program is free software; you can redistribute it and/or
Chris@1178 9 modify it under the terms of the GNU General Public License as
Chris@1178 10 published by the Free Software Foundation; either version 2 of the
Chris@1178 11 License, or (at your option) any later version. See the file
Chris@1178 12 COPYING included with this distribution for more information.
Chris@1178 13 */
Chris@1178 14
Chris@1178 15 #include "PluginScan.h"
Chris@1178 16
Chris@1178 17 #include "base/Debug.h"
Chris@1246 18 #include "base/Preferences.h"
Chris@1241 19 #include "base/HelperExecPath.h"
Chris@1178 20
Chris@1501 21 #include <sstream>
Chris@1180 22
Chris@1178 23 #include <QMutex>
Chris@1181 24 #include <QCoreApplication>
Chris@1178 25
Chris@1178 26 using std::string;
Chris@1178 27
Chris@1249 28 class PluginScan::Logger
Chris@1249 29 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 30 : public PluginCandidates::LogCallback
Chris@1249 31 #endif
Chris@1180 32 {
Chris@1180 33 protected:
Chris@1600 34 void log(std::string message)
Chris@1600 35 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1600 36 override
Chris@1600 37 #endif
Chris@1600 38 {
Chris@1264 39 SVDEBUG << "PluginScan: " << message << endl;
Chris@1180 40 }
Chris@1180 41 };
Chris@1180 42
Chris@1180 43 PluginScan *PluginScan::getInstance()
Chris@1180 44 {
Chris@1178 45 static QMutex mutex;
Chris@1582 46 static PluginScan *m_instance = nullptr;
Chris@1178 47 mutex.lock();
Chris@1178 48 if (!m_instance) m_instance = new PluginScan();
Chris@1178 49 mutex.unlock();
Chris@1178 50 return m_instance;
Chris@1178 51 }
Chris@1178 52
Chris@1246 53 PluginScan::PluginScan() : m_succeeded(false), m_logger(new Logger) {
Chris@1178 54 }
Chris@1178 55
Chris@1178 56 PluginScan::~PluginScan() {
Chris@1246 57 QMutexLocker locker(&m_mutex);
Chris@1241 58 clear();
Chris@1180 59 delete m_logger;
Chris@1396 60 SVDEBUG << "PluginScan::~PluginScan completed" << endl;
Chris@1178 61 }
Chris@1178 62
Chris@1178 63 void
Chris@1241 64 PluginScan::scan()
Chris@1178 65 {
Chris@1249 66 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 67
Chris@1246 68 QMutexLocker locker(&m_mutex);
Chris@1246 69
Chris@1246 70 bool inProcess = Preferences::getInstance()->getRunPluginsInProcess();
Chris@1246 71
Chris@1246 72 HelperExecPath hep(inProcess ?
Chris@1246 73 HelperExecPath::NativeArchitectureOnly :
Chris@1246 74 HelperExecPath::AllInstalled);
Chris@1246 75
Chris@1352 76 QString helperName("vamp-plugin-load-checker");
Chris@1246 77 auto helpers = hep.getHelperExecutables(helperName);
Chris@1241 78
Chris@1241 79 clear();
Chris@1241 80
Chris@1246 81 for (auto p: helpers) {
Chris@1247 82 SVDEBUG << "NOTE: PluginScan: Found helper: " << p.executable << endl;
Chris@1246 83 }
Chris@1246 84
Chris@1246 85 if (helpers.empty()) {
Chris@1247 86 SVDEBUG << "NOTE: No plugin checker helpers found in installation;"
Chris@1246 87 << " found none of the following:" << endl;
Chris@1246 88 for (auto d: hep.getHelperCandidatePaths(helperName)) {
Chris@1247 89 SVDEBUG << "NOTE: " << d << endl;
Chris@1246 90 }
Chris@1246 91 }
Chris@1246 92
Chris@1246 93 for (auto p: helpers) {
Chris@1241 94 try {
Chris@1474 95 KnownPluginCandidates *kp = new KnownPluginCandidates
Chris@1246 96 (p.executable.toStdString(), m_logger);
Chris@1246 97 if (m_kp.find(p.tag) != m_kp.end()) {
Chris@1247 98 SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag
Chris@1246 99 << " for helpers" << endl;
Chris@1246 100 continue;
Chris@1246 101 }
Chris@1246 102 m_kp[p.tag] = kp;
Chris@1241 103 m_succeeded = true;
Chris@1241 104 } catch (const std::exception &e) {
Chris@1247 105 SVDEBUG << "ERROR: PluginScan::scan: " << e.what()
Chris@1246 106 << " (with helper path = " << p.executable << ")" << endl;
Chris@1241 107 }
Chris@1241 108 }
Chris@1249 109
Chris@1396 110 SVDEBUG << "PluginScan::scan complete" << endl;
Chris@1249 111 #endif
Chris@1241 112 }
Chris@1241 113
Chris@1246 114 bool
Chris@1246 115 PluginScan::scanSucceeded() const
Chris@1246 116 {
Chris@1246 117 QMutexLocker locker(&m_mutex);
Chris@1246 118 return m_succeeded;
Chris@1246 119 }
Chris@1246 120
Chris@1241 121 void
Chris@1241 122 PluginScan::clear()
Chris@1241 123 {
Chris@1246 124 for (auto &p: m_kp) {
Chris@1246 125 delete p.second;
Chris@1246 126 }
Chris@1241 127 m_kp.clear();
Chris@1179 128 m_succeeded = false;
Chris@1179 129 }
Chris@1179 130
Chris@1246 131 QList<PluginScan::Candidate>
Chris@1278 132 PluginScan::getCandidateLibrariesFor(PluginType
Chris@1278 133 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1278 134 type
Chris@1278 135 #endif
Chris@1278 136 ) const
Chris@1179 137 {
Chris@1249 138 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 139
Chris@1246 140 QMutexLocker locker(&m_mutex);
Chris@1246 141
Chris@1180 142 KnownPlugins::PluginType kpt;
Chris@1180 143 switch (type) {
Chris@1180 144 case VampPlugin: kpt = KnownPlugins::VampPlugin; break;
Chris@1180 145 case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break;
Chris@1180 146 case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break;
Chris@1180 147 default: throw std::logic_error("Inconsistency in plugin type enums");
Chris@1180 148 }
Chris@1180 149
Chris@1246 150 QList<Candidate> candidates;
Chris@1245 151
Chris@1246 152 for (auto rec: m_kp) {
Chris@1245 153
Chris@1474 154 KnownPluginCandidates *kp = rec.second;
Chris@1246 155
Chris@1241 156 auto c = kp->getCandidateLibrariesFor(kpt);
Chris@1245 157
Chris@1247 158 SVDEBUG << "PluginScan: helper \"" << kp->getHelperExecutableName()
Chris@1247 159 << "\" likes " << c.size() << " libraries of type "
Chris@1247 160 << kp->getTagFor(kpt) << endl;
Chris@1245 161
Chris@1246 162 for (auto s: c) {
Chris@1246 163 candidates.push_back({ s.c_str(), rec.first });
Chris@1246 164 }
Chris@1245 165
Chris@1245 166 if (type != VampPlugin) {
Chris@1245 167 // We are only interested in querying multiple helpers
Chris@1245 168 // when dealing with Vamp plugins, for which we can use
Chris@1245 169 // external servers and so in some cases can support
Chris@1245 170 // additional architectures. Other plugin formats are
Chris@1245 171 // loaded directly and so must match the host, which is
Chris@1245 172 // what the first helper is supposed to handle -- so
Chris@1245 173 // break after the first one if not querying Vamp
Chris@1245 174 break;
Chris@1245 175 }
Chris@1241 176 }
Chris@1245 177
Chris@1179 178 return candidates;
Chris@1249 179
Chris@1249 180 #else
Chris@1249 181 return {};
Chris@1249 182 #endif
Chris@1178 183 }
Chris@1178 184
Chris@1501 185 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1501 186 QString
Chris@1501 187 PluginScan::formatFailureReport(QString tag,
Chris@1501 188 std::vector<PluginCandidates::FailureRec> failures) const
Chris@1501 189 {
Chris@1501 190 int n = int(failures.size());
Chris@1501 191 int i = 0;
Chris@1501 192
Chris@1501 193 std::ostringstream os;
Chris@1501 194
Chris@1501 195 os << "<ul>";
Chris@1501 196 for (auto f: failures) {
Chris@1562 197 os << "<li><code>" + f.library + "</code>";
Chris@1501 198
Chris@1501 199 SVDEBUG << "PluginScan::formatFailureReport: tag is \"" << tag
Chris@1501 200 << "\", failure code is " << int(f.code) << ", message is \""
Chris@1501 201 << f.message << "\"" << endl;
Chris@1501 202
Chris@1501 203 QString userMessage = QString::fromStdString(f.message);
Chris@1501 204
Chris@1501 205 switch (f.code) {
Chris@1501 206
Chris@1501 207 case PluginCheckCode::FAIL_LIBRARY_NOT_FOUND:
Chris@1501 208 userMessage = QObject::tr("Library file could not be opened");
Chris@1501 209 break;
Chris@1501 210
Chris@1501 211 case PluginCheckCode::FAIL_WRONG_ARCHITECTURE:
Chris@1501 212 if (tag == "64" || (sizeof(void *) == 8 && tag == "")) {
Chris@1501 213 userMessage = QObject::tr
Chris@1505 214 ("Library has wrong architecture - possibly a 32-bit plugin installed in a 64-bit plugin folder");
Chris@1501 215 } else if (tag == "32" || (sizeof(void *) == 4 && tag == "")) {
Chris@1501 216 userMessage = QObject::tr
Chris@1505 217 ("Library has wrong architecture - possibly a 64-bit plugin installed in a 32-bit plugin folder");
Chris@1501 218 }
Chris@1501 219 break;
Chris@1501 220
Chris@1501 221 case PluginCheckCode::FAIL_DEPENDENCY_MISSING:
Chris@1501 222 userMessage = QObject::tr
Chris@1501 223 ("Library depends on another library that cannot be found: %1")
Chris@1501 224 .arg(userMessage);
Chris@1501 225 break;
Chris@1501 226
Chris@1501 227 case PluginCheckCode::FAIL_NOT_LOADABLE:
Chris@1501 228 userMessage = QObject::tr
Chris@1501 229 ("Library cannot be loaded: %1").arg(userMessage);
Chris@1501 230 break;
Chris@1501 231
Chris@1562 232 case PluginCheckCode::FAIL_FORBIDDEN:
Chris@1562 233 userMessage = QObject::tr
Chris@1562 234 ("Permission to load library was refused");
Chris@1562 235 break;
Chris@1562 236
Chris@1501 237 case PluginCheckCode::FAIL_DESCRIPTOR_MISSING:
Chris@1501 238 userMessage = QObject::tr
Chris@1501 239 ("Not a valid plugin library (no descriptor found)");
Chris@1501 240 break;
Chris@1501 241
Chris@1501 242 case PluginCheckCode::FAIL_NO_PLUGINS:
Chris@1501 243 userMessage = QObject::tr
Chris@1501 244 ("Library contains no plugins");
Chris@1501 245 break;
Chris@1501 246
Chris@1501 247 case PluginCheckCode::FAIL_OTHER:
Chris@1501 248 if (userMessage == "") {
Chris@1501 249 userMessage = QObject::tr
Chris@1501 250 ("Unknown error");
Chris@1501 251 }
Chris@1501 252 break;
Chris@1501 253
Chris@1501 254 case PluginCheckCode::SUCCESS:
Chris@1501 255 // success shouldn't happen here!
Chris@1501 256 break;
Chris@1501 257 }
Chris@1501 258
Chris@1501 259 os << "<br><i>" + userMessage.toStdString() + "</i>";
Chris@1501 260 os << "</li>";
Chris@1501 261
Chris@1501 262 if (n > 10) {
Chris@1501 263 if (++i == 5) {
Chris@1501 264 os << "<li>";
Chris@1501 265 os << QObject::tr("... and %n further failure(s)",
Chris@1501 266 "", n - i)
Chris@1501 267 .toStdString();
Chris@1501 268 os << "</li>";
Chris@1501 269 break;
Chris@1501 270 }
Chris@1501 271 }
Chris@1501 272 }
Chris@1501 273 os << "</ul>";
Chris@1501 274
Chris@1501 275 return QString::fromStdString(os.str());
Chris@1501 276 }
Chris@1501 277 #endif
Chris@1501 278
Chris@1178 279 QString
Chris@1178 280 PluginScan::getStartupFailureReport() const
Chris@1178 281 {
Chris@1249 282 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 283
Chris@1246 284 QMutexLocker locker(&m_mutex);
Chris@1246 285
Chris@1179 286 if (!m_succeeded) {
Chris@1429 287 return QObject::tr("<b>Failed to scan for plugins</b>"
Chris@1429 288 "<p>Failed to scan for plugins at startup. Possibly "
Chris@1352 289 "the plugin checker program was not correctly "
Chris@1181 290 "installed alongside %1?</p>")
Chris@1181 291 .arg(QCoreApplication::applicationName());
Chris@1179 292 }
Chris@1241 293 if (m_kp.empty()) {
Chris@1429 294 return QObject::tr("<b>Did not scan for plugins</b>"
Chris@1429 295 "<p>Apparently no scan for plugins was attempted "
Chris@1429 296 "(internal error?)</p>");
Chris@1179 297 }
Chris@1179 298
Chris@1241 299 QString report;
Chris@1241 300 for (auto kp: m_kp) {
Chris@1501 301 auto failures = kp.second->getFailures();
Chris@1502 302 if (!failures.empty()) {
Chris@1502 303 report += formatFailureReport(kp.first, failures);
Chris@1502 304 }
Chris@1241 305 }
Chris@1179 306 if (report == "") {
Chris@1429 307 return report;
Chris@1179 308 }
Chris@1179 309
Chris@1562 310 return QObject::tr("<p>Failed to load one or more plugin libraries:</p>")
Chris@1429 311 + report
Chris@1181 312 + QObject::tr("<p>These plugins may be incompatible with the system, "
Chris@1181 313 "and will be ignored during this run of %1.</p>")
Chris@1181 314 .arg(QCoreApplication::applicationName());
Chris@1249 315
Chris@1249 316 #else
Chris@1249 317 return "";
Chris@1249 318 #endif
Chris@1178 319 }
Chris@1178 320