changeset 1182:a1f410f895d3 3.0-integration

Merge from branch pluginscan
author Chris Cannam
date Fri, 15 Apr 2016 16:26:15 +0100
parents 4018fc0189bc (current diff) 2f628dc9a0b0 (diff)
children aa61e73cf580 e5a1d815f331
files
diffstat 14 files changed, 190 insertions(+), 199 deletions(-) [+]
line wrap: on
line diff
--- a/config.pri.in	Fri Mar 18 14:25:05 2016 +0000
+++ b/config.pri.in	Fri Apr 15 16:26:15 2016 +0100
@@ -10,7 +10,7 @@
 QMAKE_CXXFLAGS += @CXXFLAGS@ 
 QMAKE_LFLAGS += @LDFLAGS@
 
-linux*:LIBS += -lasound
+linux*:LIBS += -lasound -ldl
 
 macx*:DEFINES += HAVE_COREAUDIO MACOSX_DEPLOYMENT_TARGET=1060
 
--- a/data/fileio/test/test.pro	Fri Mar 18 14:25:05 2016 +0000
+++ b/data/fileio/test/test.pro	Fri Apr 15 16:26:15 2016 +0100
@@ -36,6 +36,9 @@
         DEFINES += HAVE_COREAUDIO
         LIBS += -framework CoreAudio -framework CoreMidi -framework AudioUnit -framework AudioToolbox -framework CoreFoundation -framework CoreServices -framework Accelerate
     }
+    linux* {
+        LIBS += -ldl
+    }
 }
 
 CONFIG += qt thread warn_on stl rtti exceptions console c++11
--- a/plugin/DSSIPluginFactory.h	Fri Mar 18 14:25:05 2016 +0000
+++ b/plugin/DSSIPluginFactory.h	Fri Apr 15 16:26:15 2016 +0100
@@ -48,6 +48,10 @@
     DSSIPluginFactory();
     friend class RealTimePluginFactory;
 
+    virtual PluginScan::PluginType getPluginType() const {
+        return PluginScan::DSSIPlugin;
+    }
+
     virtual std::vector<QString> getPluginPath();
 
     virtual std::vector<QString> getLRDFPath(QString &baseUri);
--- a/plugin/FeatureExtractionPluginFactory.cpp	Fri Mar 18 14:25:05 2016 +0000
+++ b/plugin/FeatureExtractionPluginFactory.cpp	Fri Apr 15 16:26:15 2016 +0100
@@ -21,6 +21,8 @@
 
 #include "system/System.h"
 
+#include "PluginScan.h"
+
 #include <QDir>
 #include <QFile>
 #include <QFileInfo>
@@ -111,87 +113,14 @@
 }
 
 vector<QString>
-FeatureExtractionPluginFactory::getPluginCandidateFiles()
-{
-    vector<QString> path = getPluginPath();
-    vector<QString> candidates;
-
-    for (QString dirname : path) {
-
-#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
-        cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << dirname << endl;
-#endif
-
-	QDir pluginDir(dirname, PLUGIN_GLOB,
-                       QDir::Name | QDir::IgnoreCase,
-                       QDir::Files | QDir::Readable);
-
-	for (unsigned int j = 0; j < pluginDir.count(); ++j) {
-            QString soname = pluginDir.filePath(pluginDir[j]);
-            candidates.push_back(soname);
-        }
-    }
-
-    return candidates;
-}
-
-vector<QString>
-FeatureExtractionPluginFactory::winnowPluginCandidates(vector<QString> candidates,
-                                                       QString &warningMessage)
-{
-    vector<QString> good, bad;
-    vector<PluginLoadStatus> badStatuses;
-    
-    for (QString c: candidates) {
-
-        PluginLoadStatus status =
-            TestPluginLoadability(c, "vampGetPluginDescriptor");
-
-        if (status == PluginLoadOK) {
-            good.push_back(c);
-        } else if (status == UnknownPluginLoadStatus) {
-            cerr << "WARNING: Unknown load status for plugin candidate \""
-                 << c << "\", continuing" << endl;
-            good.push_back(c);
-        } else {
-            bad.push_back(c);
-            badStatuses.push_back(status);
-        }
-    }
-    
-    if (!bad.empty()) {
-        warningMessage =
-            QObject::tr("<b>Failed to load plugins</b>"
-                        "<p>Failed to load one or more plugin libraries:</p>\n");
-        warningMessage += "<ul>";
-        for (int i = 0; in_range_for(bad, i); ++i) {
-            QString m;
-            if (badStatuses[i] == PluginLoadFailedToLoadLibrary) {
-                m = QObject::tr("Failed to load library");
-            } else if (badStatuses[i] == PluginLoadFailedToFindDescriptor) {
-                m = QObject::tr("Failed to query plugins from library after loading");
-            } else if (badStatuses[i] == PluginLoadFailedElsewhere) {
-                m = QObject::tr("Unknown failure");
-            } else {
-                m = QObject::tr("Success: internal error?");
-            }
-            warningMessage += QString("<li>%1 (%2)</li>\n")
-                .arg(bad[i])
-                .arg(m);
-        }
-        warningMessage += "</ul>";
-    }
-    return good;
-}
-
-vector<QString>
 FeatureExtractionPluginFactory::getPluginIdentifiers()
 {
     Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
 
     vector<QString> rv;
-    vector<QString> candidates = winnowPluginCandidates(getPluginCandidateFiles(),
-                                                        m_pluginScanError);
+
+    QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor
+        (PluginScan::VampPlugin);
     
     for (QString soname : candidates) {
 
--- a/plugin/FeatureExtractionPluginFactory.h	Fri Mar 18 14:25:05 2016 +0000
+++ b/plugin/FeatureExtractionPluginFactory.h	Fri Apr 15 16:26:15 2016 +0100
@@ -37,14 +37,6 @@
     virtual std::vector<QString> getPluginPath();
 
     virtual std::vector<QString> getPluginIdentifiers();
-
-    /**
-     * Return any error message arising from the initial plugin
-     * scan. The return value will either be an empty string (nothing
-     * to report) or an HTML string suitable for dropping into a
-     * dialog and showing the user.
-     */
-    virtual QString getPluginPopulationWarning() { return m_pluginScanError; }
     
     virtual QString findPluginFile(QString soname, QString inDir = "");
 
@@ -66,13 +58,7 @@
     void pluginDeleted(Vamp::Plugin *);
     std::map<Vamp::Plugin *, void *> m_handleMap;
     
-    std::vector<QString> getPluginCandidateFiles();
-    std::vector<QString> winnowPluginCandidates(std::vector<QString> candidates,
-                                                QString &warningMessage);
-    
     void generateTaxonomy();
-
-    QString m_pluginScanError;
 };
 
 #endif
--- a/plugin/LADSPAPluginFactory.cpp	Fri Mar 18 14:25:05 2016 +0000
+++ b/plugin/LADSPAPluginFactory.cpp	Fri Apr 15 16:26:15 2016 +0100
@@ -668,14 +668,11 @@
 
     generateFallbackCategories();
 
-    for (std::vector<QString>::iterator i = pathList.begin();
-	 i != pathList.end(); ++i) {
+    QStringList candidates =
+        PluginScan::getInstance()->getCandidateLibrariesFor(getPluginType());
 
-	QDir pluginDir(*i, PLUGIN_GLOB);
-
-	for (unsigned int j = 0; j < pluginDir.count(); ++j) {
-	    discoverPluginsFrom(QString("%1/%2").arg(*i).arg(pluginDir[j]));
-	}
+    for (QString c: candidates) {
+        discoverPluginsFrom(c);
     }
 }
 
--- a/plugin/LADSPAPluginFactory.h	Fri Mar 18 14:25:05 2016 +0000
+++ b/plugin/LADSPAPluginFactory.h	Fri Apr 15 16:26:15 2016 +0100
@@ -24,6 +24,8 @@
 #include "RealTimePluginFactory.h"
 #include "api/ladspa.h"
 
+#include "PluginScan.h"
+
 #include <vector>
 #include <map>
 #include <set>
@@ -63,6 +65,10 @@
     LADSPAPluginFactory();
     friend class RealTimePluginFactory;
 
+    virtual PluginScan::PluginType getPluginType() const {
+        return PluginScan::LADSPAPlugin;
+    }
+
     virtual std::vector<QString> getPluginPath();
 
     virtual std::vector<QString> getLRDFPath(QString &baseUri);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PluginScan.cpp	Fri Apr 15 16:26:15 2016 +0100
@@ -0,0 +1,112 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "PluginScan.h"
+
+#include "base/Debug.h"
+
+#include "checker/knownplugins.h"
+
+#include <QMutex>
+#include <QCoreApplication>
+
+using std::string;
+
+class PluginScan::Logger : public PluginCandidates::LogCallback
+{
+protected:
+    void log(std::string message) {
+        SVDEBUG << "PluginScan: " << message;
+    }
+};
+
+PluginScan *PluginScan::getInstance()
+{
+    static QMutex mutex;
+    static PluginScan *m_instance = 0;
+    mutex.lock();
+    if (!m_instance) m_instance = new PluginScan();
+    mutex.unlock();
+    return m_instance;
+}
+
+PluginScan::PluginScan() : m_kp(0), m_succeeded(false), m_logger(new Logger) {
+}
+
+PluginScan::~PluginScan() {
+    delete m_kp;
+    delete m_logger;
+}
+
+void
+PluginScan::scan(QString helperExecutablePath)
+{
+    delete m_kp;
+    m_succeeded = false;
+    try {
+	m_kp = new KnownPlugins(helperExecutablePath.toStdString(), m_logger);
+	m_succeeded = true;
+    } catch (const std::exception &e) {
+	cerr << "ERROR: PluginScan::scan: " << e.what() << endl;
+	m_kp = 0;
+    }
+}
+
+QStringList
+PluginScan::getCandidateLibrariesFor(PluginType type) const
+{
+    KnownPlugins::PluginType kpt;
+    switch (type) {
+    case VampPlugin: kpt = KnownPlugins::VampPlugin; break;
+    case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break;
+    case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break;
+    default: throw std::logic_error("Inconsistency in plugin type enums");
+    }
+    
+    QStringList candidates;
+    if (!m_kp) return candidates;
+    auto c = m_kp->getCandidateLibrariesFor(kpt);
+    for (auto s: c) candidates.push_back(s.c_str());
+    return candidates;
+}
+
+QString
+PluginScan::getStartupFailureReport() const
+{
+    if (!m_succeeded) {
+	return QObject::tr("<b>Failed to scan for plugins</b>"
+			   "<p>Failed to scan for plugins at startup. Possibly "
+                           "the plugin checker helper program was not correctly "
+                           "installed alongside %1?</p>")
+            .arg(QCoreApplication::applicationName());
+    }
+    if (!m_kp) {
+	return QObject::tr("<b>Did not scan for plugins</b>"
+			   "<p>Apparently no scan for plugins was attempted "
+			   "(internal error?)</p>");
+    }
+
+    string report = m_kp->getFailureReport();
+    if (report == "") {
+	return QString(report.c_str());
+    }
+
+    return QObject::tr("<b>Failed to load plugins</b>"
+		       "<p>Failed to load one or more plugin libraries:</p>")
+	+ QString(report.c_str())
+        + QObject::tr("<p>These plugins may be incompatible with the system, "
+                      "and will be ignored during this run of %1.</p>")
+        .arg(QCoreApplication::applicationName());
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PluginScan.h	Fri Apr 15 16:26:15 2016 +0100
@@ -0,0 +1,50 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef PLUGIN_SCAN_H
+#define PLUGIN_SCAN_H
+
+#include <QStringList>
+
+class KnownPlugins;
+
+class PluginScan
+{
+public:
+    static PluginScan *getInstance();
+
+    void scan(QString helperExecutablePath);
+
+    bool scanSucceeded() const;
+    
+    enum PluginType {
+	VampPlugin,
+	LADSPAPlugin,
+	DSSIPlugin
+    };
+    QStringList getCandidateLibrariesFor(PluginType) const;
+
+    QString getStartupFailureReport() const;
+
+private:
+    PluginScan();
+    ~PluginScan();
+    KnownPlugins *m_kp;
+    bool m_succeeded;
+
+    class Logger;
+    Logger *m_logger;
+};
+
+#endif
--- a/svcore.pro	Fri Mar 18 14:25:05 2016 +0000
+++ b/svcore.pro	Fri Apr 15 16:26:15 2016 +0100
@@ -39,7 +39,7 @@
 TARGET = svcore
 
 DEPENDPATH += . data plugin plugin/api/alsa
-INCLUDEPATH += . data plugin plugin/api/alsa ../dataquay
+INCLUDEPATH += . data plugin plugin/api/alsa ../dataquay ../checker
 OBJECTS_DIR = o
 MOC_DIR = o
 
@@ -223,7 +223,8 @@
            data/osc/OSCMessage.cpp \
            data/osc/OSCQueue.cpp 
 
-HEADERS += plugin/DSSIPluginFactory.h \
+HEADERS += plugin/PluginScan.h \
+           plugin/DSSIPluginFactory.h \
            plugin/DSSIPluginInstance.h \
            plugin/FeatureExtractionPluginFactory.h \
            plugin/LADSPAPluginFactory.h \
@@ -243,7 +244,8 @@
            plugin/api/alsa/sound/asequencer.h
 
 
-SOURCES += plugin/DSSIPluginFactory.cpp \
+SOURCES += plugin/PluginScan.cpp \
+           plugin/DSSIPluginFactory.cpp \
            plugin/DSSIPluginInstance.cpp \
            plugin/FeatureExtractionPluginFactory.cpp \
            plugin/LADSPAPluginFactory.cpp \
--- a/system/System.cpp	Fri Mar 18 14:25:05 2016 +0000
+++ b/system/System.cpp	Fri Apr 15 16:26:15 2016 +0100
@@ -325,66 +325,3 @@
 double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; }
 float princargf(float a) { return float(princarg(a)); }
 
-#ifndef _WIN32
-
-#include <unistd.h>
-#include <sys/wait.h>
-
-PluginLoadStatus
-TestPluginLoadability(QString soname, QString descriptorFn)
-{
-    //!!! This is POSIX only, no equivalent on Windows, where we'll
-    //!!! have to do something completely different
-    
-    pid_t pid = fork();
-
-    if (pid < 0) {
-        return UnknownPluginLoadStatus; // fork failed
-    }
-
-    if (pid == 0) { // the child process
-
-        void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL);
-        if (!handle) {
-            cerr << "isPluginLibraryLoadable: Failed to open plugin library \""
-                 << soname << "\": " << dlerror() << "\n";
-            cerr << "exiting with status 1" << endl;
-            exit(1);
-        }
-
-        void *fn = DLSYM(handle, descriptorFn.toLocal8Bit().data());
-        if (!fn) {
-            cerr << "isPluginLibraryLoadable: Failed to find plugin descriptor function \"" << descriptorFn << "\" in library \"" << soname << "\": " << dlerror() << "\n";
-            exit(2);
-        }
-
-//        cerr << "isPluginLibraryLoadable: Successfully loaded library \"" << soname << "\" and retrieved descriptor function" << endl;
-        
-        exit(0);
-
-    } else { // the parent process
-
-        int status = 0;
-
-        do {
-            waitpid(pid, &status, 0);
-        } while (WIFSTOPPED(status));
-
-        if (WIFEXITED(status)) {
-            switch (WEXITSTATUS(status)) {
-            case 0: return PluginLoadOK; // success
-            case 1: return PluginLoadFailedToLoadLibrary;
-            case 2: return PluginLoadFailedToFindDescriptor;
-            default: return PluginLoadFailedElsewhere;
-            }
-        }
-
-        if (WIFSIGNALED(status)) { 
-            return PluginLoadFailedElsewhere;
-        }
-
-        return UnknownPluginLoadStatus;
-    }
-}
-
-#endif
--- a/system/System.h	Fri Mar 18 14:25:05 2016 +0000
+++ b/system/System.h	Fri Apr 15 16:26:15 2016 +0100
@@ -154,21 +154,6 @@
 extern void StoreStartupLocale();
 extern void RestoreStartupLocale();
 
-enum PluginLoadStatus {
-    UnknownPluginLoadStatus,
-    PluginLoadOK,
-    PluginLoadFailedToLoadLibrary,
-    PluginLoadFailedToFindDescriptor,
-    PluginLoadFailedElsewhere
-};
-
-// Check whether a plugin library is loadable without crashing (may
-// need to spawn an external process to do it). Descriptor fn is the
-// name of a LADSPA/DSSI/Vamp-style descriptor function to try
-// calling; may be an empty string if the plugin doesn't follow that
-// convention.
-PluginLoadStatus TestPluginLoadability(QString soname, QString descriptorFn);
-
 #include <cmath>
 
 #ifndef M_PI
--- a/transform/TransformFactory.cpp	Fri Mar 18 14:25:05 2016 +0000
+++ b/transform/TransformFactory.cpp	Fri Apr 15 16:26:15 2016 +0100
@@ -399,18 +399,6 @@
     m_transformsPopulated = true;
 }
 
-QString
-TransformFactory::getPluginPopulationWarning()
-{
-    FeatureExtractionPluginFactory *vfactory =
-        FeatureExtractionPluginFactory::instance("vamp");
-    QString warningMessage;
-    if (vfactory) {
-        warningMessage = vfactory->getPluginPopulationWarning();
-    }
-    return warningMessage;
-}
-
 void
 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
 {
--- a/transform/TransformFactory.h	Fri Mar 18 14:25:05 2016 +0000
+++ b/transform/TransformFactory.h	Fri Apr 15 16:26:15 2016 +0100
@@ -195,14 +195,6 @@
      */
     void setParametersFromPluginConfigurationXml(Transform &transform,
                                                  QString xml);
-
-    /**
-     * Return any error message arising from the initial plugin
-     * scan. The return value will either be an empty string (nothing
-     * to report) or an HTML string suitable for dropping into a
-     * dialog and showing the user.
-     */
-    QString getPluginPopulationWarning();
     
 protected:
     typedef std::map<TransformId, TransformDescription> TransformDescriptionMap;