changeset 1246:75aefcc9f07d piper

Use plugin scan results to inform the list requests issued to Piper servers
author Chris Cannam
date Thu, 03 Nov 2016 14:14:09 +0000
parents 0492e54ccd56
children 8f076d02569a
files base/HelperExecPath.cpp base/HelperExecPath.h plugin/LADSPAPluginFactory.cpp plugin/NativeVampPluginFactory.cpp plugin/PiperVampPluginFactory.cpp plugin/PiperVampPluginFactory.h plugin/PluginScan.cpp plugin/PluginScan.h
diffstat 8 files changed, 163 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/base/HelperExecPath.cpp	Tue Nov 01 16:02:15 2016 +0000
+++ b/base/HelperExecPath.cpp	Thu Nov 03 14:14:09 2016 +0000
@@ -20,13 +20,17 @@
 #include <QDir>
 #include <QFileInfo>
 
-static QStringList
-getSuffixes()
+QStringList
+HelperExecPath::getTags()
 {
     if (sizeof(void *) == 8) {
-        return { "-64", "", "-32" };
+        if (m_type == NativeArchitectureOnly) {
+            return { "64", "" };
+        } else {
+            return { "64", "", "32" };
+        }
     } else {
-        return { "", "-32" };
+        return { "", "32" };
     }
 }
 
@@ -36,7 +40,7 @@
     return QFile(path).exists() && QFileInfo(path).isExecutable();
 }
 
-QStringList
+QList<HelperExecPath::HelperExec>
 HelperExecPath::getHelperExecutables(QString basename)
 {
     QStringList dummy;
@@ -46,9 +50,9 @@
 QString
 HelperExecPath::getHelperExecutable(QString basename)
 {
-    QStringList execs = getHelperExecutables(basename);
+    auto execs = getHelperExecutables(basename);
     if (execs.empty()) return "";
-    else return execs[0];
+    else return execs[0].executable;
 }
 
 QStringList
@@ -69,7 +73,7 @@
     return candidates;
 }
 
-QStringList
+QList<HelperExecPath::HelperExec>
 HelperExecPath::search(QString basename, QStringList &candidates)
 {
     // Helpers are expected to exist either in the same directory as
@@ -80,15 +84,17 @@
     extension = ".exe";
 #endif
 
-    QStringList executables;
+    QList<HelperExec> executables;
     QStringList dirs = getHelperDirPaths();
     
-    for (QString s: getSuffixes()) {
+    for (QString t: getTags()) {
         for (QString d: dirs) {
-            QString path = d + QDir::separator() + basename + s + extension;
+            QString path = d + QDir::separator() + basename;
+            if (t != QString()) path += "-" + t;
+            path += extension;
             candidates.push_back(path);
             if (isGood(path)) {
-                executables.push_back(path);
+                executables.push_back({ path, t });
                 break;
             }
         }
--- a/base/HelperExecPath.h	Tue Nov 01 16:02:15 2016 +0000
+++ b/base/HelperExecPath.h	Thu Nov 03 14:14:09 2016 +0000
@@ -33,35 +33,52 @@
 class HelperExecPath
 {
 public:
+    enum SearchType {
+        NativeArchitectureOnly,
+        AllInstalled
+    };
+    
+    HelperExecPath(SearchType type) : m_type(type) { }
+    
     /**
      * Find a helper executable with the given base name in the bundle
      * directory or installation location, if one exists, and return
      * its full path. Equivalent to calling getHelperExecutables() and
      * taking the first result from the returned list (or "" if empty).
      */
-    static QString getHelperExecutable(QString basename);
+    QString getHelperExecutable(QString basename);
+
+    struct HelperExec {
+        QString executable;
+        QString tag;
+    };
     
     /**
      * Find all helper executables with the given base name in the
      * bundle directory or installation location, and return their
-     * full paths in order of priority.
+     * full paths in order of priority. The "tag" string contains an
+     * identifier for the location or architecture of the helper, for
+     * example "32", "64", "js" etc. An empty tag signifies a default
+     * helper that matches the application's architecture.
      */
-    static QStringList getHelperExecutables(QString basename);
+    QList<HelperExec> getHelperExecutables(QString basename);
 
     /**
      * Return the list of directories searched for helper
      * executables.
      */
-    static QStringList getHelperDirPaths();
+    QStringList getHelperDirPaths();
     
     /**
      * Return the list of executable paths examined in the search for
      * the helper executable with the given basename.
      */
-    static QStringList getHelperCandidatePaths(QString basename);
+    QStringList getHelperCandidatePaths(QString basename);
 
 private:
-    static QStringList search(QString, QStringList &);
+    SearchType m_type;
+    QList<HelperExec> search(QString, QStringList &);
+    QStringList getTags();
 };
 
 #endif
--- a/plugin/LADSPAPluginFactory.cpp	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/LADSPAPluginFactory.cpp	Thu Nov 03 14:14:09 2016 +0000
@@ -668,11 +668,11 @@
 
     generateFallbackCategories();
 
-    QStringList candidates =
+    auto candidates =
         PluginScan::getInstance()->getCandidateLibrariesFor(getPluginType());
 
-    for (QString c: candidates) {
-        discoverPluginsFrom(c);
+    for (auto c: candidates) {
+        discoverPluginsFrom(c.libraryPath);
     }
 }
 
--- a/plugin/NativeVampPluginFactory.cpp	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/NativeVampPluginFactory.cpp	Thu Nov 03 14:14:09 2016 +0000
@@ -81,10 +81,12 @@
         return m_identifiers;
     }
     
-    QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor
+    auto candidates = PluginScan::getInstance()->getCandidateLibrariesFor
         (PluginScan::VampPlugin);
     
-    for (QString soname : candidates) {
+    for (auto candidate : candidates) {
+
+        QString soname = candidate.libraryPath;
 
         void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
             
--- a/plugin/PiperVampPluginFactory.cpp	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/PiperVampPluginFactory.cpp	Thu Nov 03 14:14:09 2016 +0000
@@ -50,27 +50,23 @@
 {
     QString serverName = "piper-vamp-simple-server";
 
-    m_servers = HelperExecPath::getHelperExecutables(serverName);
+    HelperExecPath hep(HelperExecPath::AllInstalled);
+    m_servers = hep.getHelperExecutables(serverName);
 
+    for (auto n: m_servers) {
+        cerr << "NOTE: PiperVampPluginFactory: Found server: "
+             << n.executable << endl;
+    }
+    
     if (m_servers.empty()) {
         cerr << "NOTE: No Piper Vamp servers found in installation;"
              << " found none of the following:" << endl;
-        for (auto d: HelperExecPath::getHelperCandidatePaths(serverName)) {
+        for (auto d: hep.getHelperCandidatePaths(serverName)) {
             cerr << "NOTE: " << d << endl;
         }
     }
 }
 
-QStringList
-PiperVampPluginFactory::getServerSuffixes()
-{
-    if (sizeof(void *) == 8) {
-        return { "-64", "", "-32" };
-    } else {
-        return { "", "-32" };
-    }
-}
-
 vector<QString>
 PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage)
 {
@@ -149,7 +145,7 @@
 {
     QString someError;
 
-    for (QString s: m_servers) {
+    for (auto s: m_servers) {
 
         populateFrom(s, someError);
 
@@ -160,10 +156,40 @@
 }
 
 void
-PiperVampPluginFactory::populateFrom(QString server, QString &errorMessage)
+PiperVampPluginFactory::populateFrom(const HelperExecPath::HelperExec &server,
+                                     QString &errorMessage)
 {
-    piper_vamp::client::ProcessQtTransport transport(server.toStdString(),
-                                                     "capnp");
+    QString tag = server.tag;
+    string executable = server.executable.toStdString();
+
+    PluginScan *scan = PluginScan::getInstance();
+    auto candidateLibraries =
+        scan->getCandidateLibrariesFor(PluginScan::VampPlugin);
+
+    vector<string> from;
+    for (const auto &c: candidateLibraries) {
+        if (c.helperTag == tag) {
+            string soname = QFileInfo(c.libraryPath).baseName().toStdString();
+            cerr << "INFO: For tag \"" << tag << "\" giving library " << soname << endl;
+            from.push_back(soname);
+        }
+    }
+
+    if (from.empty()) {
+        cerr << "PiperVampPluginFactory: No candidate libraries for tag \""
+             << tag << "\"";
+        if (scan->scanSucceeded()) {
+            // we have to assume that they all failed to load (i.e. we
+            // exclude them all) rather than sending an empty list
+            // (which would mean no exclusions)
+            cerr << ", skipping" << endl;
+            return;
+        } else {
+            cerr << ", but it seems the scan failed, so bumbling on anyway" << endl;
+        }
+    }
+    
+    piper_vamp::client::ProcessQtTransport transport(executable, "capnp");
     if (!transport.isOK()) {
         errorMessage = QObject::tr("Could not start external plugin host");
         return;
@@ -184,7 +210,7 @@
         return;
     }
 
-    cerr << "PiperVampPluginFactory: server \"" << server << "\" lists "
+    cerr << "PiperVampPluginFactory: server \"" << executable << "\" lists "
          << lr.available.size() << " plugin(s)" << endl;
 
     for (const auto &pd: lr.available) {
@@ -198,7 +224,7 @@
             continue;
         }
 
-        m_origins[identifier] = server;
+        m_origins[identifier] = server.executable;
         
         m_pluginData[identifier] = pd;
 
--- a/plugin/PiperVampPluginFactory.h	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/PiperVampPluginFactory.h	Thu Nov 03 14:14:09 2016 +0000
@@ -23,6 +23,7 @@
 #include <map>
 
 #include "base/Debug.h"
+#include "base/HelperExecPath.h"
 
 /**
  * FeatureExtractionPluginFactory type for Vamp plugins hosted in a
@@ -49,15 +50,13 @@
 
 protected:
     QMutex m_mutex;
-    QStringList m_servers; // executable file paths
+    QList<HelperExecPath::HelperExec> m_servers; // executable file paths
     std::map<QString, QString> m_origins; // plugin identifier -> server path
     std::map<QString, piper_vamp::PluginStaticData> m_pluginData; // identifier -> data
     std::map<QString, QString> m_taxonomy; // identifier -> category string
 
     void populate(QString &errorMessage);
-    void populateFrom(QString server, QString &errorMessage);
-
-    static QStringList getServerSuffixes();
+    void populateFrom(const HelperExecPath::HelperExec &, QString &errorMessage);
 };
 
 #endif
--- a/plugin/PluginScan.cpp	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/PluginScan.cpp	Thu Nov 03 14:14:09 2016 +0000
@@ -15,6 +15,7 @@
 #include "PluginScan.h"
 
 #include "base/Debug.h"
+#include "base/Preferences.h"
 #include "base/HelperExecPath.h"
 
 #include "checker/knownplugins.h"
@@ -47,10 +48,11 @@
     return m_instance;
 }
 
-PluginScan::PluginScan() : m_kp(0), m_succeeded(false), m_logger(new Logger) {
+PluginScan::PluginScan() : m_succeeded(false), m_logger(new Logger) {
 }
 
 PluginScan::~PluginScan() {
+    QMutexLocker locker(&m_mutex);
     clear();
     delete m_logger;
 }
@@ -58,34 +60,71 @@
 void
 PluginScan::scan()
 {
-    QStringList helperPaths =
-        HelperExecPath::getHelperExecutables("plugin-checker-helper");
+    QMutexLocker locker(&m_mutex);
+
+    bool inProcess = Preferences::getInstance()->getRunPluginsInProcess();
+
+    HelperExecPath hep(inProcess ?
+                       HelperExecPath::NativeArchitectureOnly :
+                       HelperExecPath::AllInstalled);
+
+    QString helperName("plugin-checker-helper");
+    auto helpers = hep.getHelperExecutables(helperName);
 
     clear();
 
-    for (auto p: helperPaths) {
+    for (auto p: helpers) {
+        cerr << "NOTE: PluginScan: Found helper: " << p.executable << endl;
+    }
+    
+    if (helpers.empty()) {
+        cerr << "NOTE: No plugin checker helpers found in installation;"
+             << " found none of the following:" << endl;
+        for (auto d: hep.getHelperCandidatePaths(helperName)) {
+            cerr << "NOTE: " << d << endl;
+        }
+    }
+
+    for (auto p: helpers) {
         try {
-            KnownPlugins *kp = new KnownPlugins(p.toStdString(), m_logger);
-            m_kp.push_back(kp);
+            KnownPlugins *kp = new KnownPlugins
+                (p.executable.toStdString(), m_logger);
+            if (m_kp.find(p.tag) != m_kp.end()) {
+                cerr << "WARNING: PluginScan::scan: Duplicate tag " << p.tag
+                     << " for helpers" << endl;
+                continue;
+            }
+            m_kp[p.tag] = kp;
             m_succeeded = true;
         } catch (const std::exception &e) {
             cerr << "ERROR: PluginScan::scan: " << e.what()
-                 << " (with helper path = " << p << ")" << endl;
+                 << " (with helper path = " << p.executable << ")" << endl;
         }
     }
 }
 
+bool
+PluginScan::scanSucceeded() const
+{
+    QMutexLocker locker(&m_mutex);
+    return m_succeeded;
+}
+
 void
 PluginScan::clear()
 {
-    for (auto &p: m_kp) delete p;
+    for (auto &p: m_kp) {
+        delete p.second;
+    }
     m_kp.clear();
     m_succeeded = false;
 }
 
-QStringList
+QList<PluginScan::Candidate>
 PluginScan::getCandidateLibrariesFor(PluginType type) const
 {
+    QMutexLocker locker(&m_mutex);
+
     KnownPlugins::PluginType kpt;
     switch (type) {
     case VampPlugin: kpt = KnownPlugins::VampPlugin; break;
@@ -94,17 +133,21 @@
     default: throw std::logic_error("Inconsistency in plugin type enums");
     }
     
-    QStringList candidates;
+    QList<Candidate> candidates;
 
-    for (auto kp: m_kp) {
+    for (auto rec: m_kp) {
 
+        KnownPlugins *kp = rec.second;
+        
         auto c = kp->getCandidateLibrariesFor(kpt);
 
         std::cerr << "PluginScan: helper \"" << kp->getHelperExecutableName()
                   << "\" likes " << c.size() << " libraries of type "
                   << kp->getTagFor(kpt) << std::endl;
 
-        for (auto s: c) candidates.push_back(s.c_str());
+        for (auto s: c) {
+            candidates.push_back({ s.c_str(), rec.first });
+        }
 
         if (type != VampPlugin) {
             // We are only interested in querying multiple helpers
@@ -124,6 +167,8 @@
 QString
 PluginScan::getStartupFailureReport() const
 {
+    QMutexLocker locker(&m_mutex);
+
     if (!m_succeeded) {
 	return QObject::tr("<b>Failed to scan for plugins</b>"
 			   "<p>Failed to scan for plugins at startup. Possibly "
@@ -139,7 +184,7 @@
 
     QString report;
     for (auto kp: m_kp) {
-        report += QString::fromStdString(kp->getFailureReport());
+        report += QString::fromStdString(kp.second->getFailureReport());
     }
     if (report == "") {
 	return report;
--- a/plugin/PluginScan.h	Tue Nov 01 16:02:15 2016 +0000
+++ b/plugin/PluginScan.h	Thu Nov 03 14:14:09 2016 +0000
@@ -16,7 +16,9 @@
 #define PLUGIN_SCAN_H
 
 #include <QStringList>
+#include <QMutex>
 #include <vector>
+#include <map>
 
 class KnownPlugins;
 
@@ -34,7 +36,11 @@
 	LADSPAPlugin,
 	DSSIPlugin
     };
-    QStringList getCandidateLibrariesFor(PluginType) const;
+    struct Candidate {
+        QString libraryPath;
+        QString helperTag;
+    };
+    QList<Candidate> getCandidateLibrariesFor(PluginType) const;
 
     QString getStartupFailureReport() const;
 
@@ -43,8 +49,10 @@
     ~PluginScan();
 
     void clear();
+
+    mutable QMutex m_mutex; // while scanning; definitely can't multi-thread this
     
-    std::vector<KnownPlugins *> m_kp;
+    std::map<QString, KnownPlugins *> m_kp; // tag -> KnownPlugins client
     bool m_succeeded;
 
     class Logger;