Mercurial > hg > vamp-plugin-load-checker
changeset 4:6f891a9c6434
Make checker with hard-coded knowledge about various plugin types and paths; fix some process management problems
author | Chris Cannam |
---|---|
date | Wed, 13 Apr 2016 12:00:07 +0100 |
parents | 3bae396cf8e0 |
children | 74064d6f5e07 |
files | checker.cpp checker.pro helper.cpp knownplugins.cpp knownplugins.h plugincandidates.cpp plugincandidates.h |
diffstat | 7 files changed, 331 insertions(+), 54 deletions(-) [+] |
line wrap: on
line diff
--- a/checker.cpp Wed Apr 13 09:12:04 2016 +0100 +++ b/checker.cpp Wed Apr 13 12:00:07 2016 +0100 @@ -1,12 +1,23 @@ -#include "plugincandidates.h" +#include "knownplugins.h" -int main(int argc, char **argv) +#include <iostream> + +using namespace std; + +int main(int, char **) { - //!!! just a test - PluginCandidates candidates("./helper"); - candidates.scan("vamp", - { "/usr/lib/vamp", "/usr/local/lib/vamp" }, - "vampGetPluginDescriptor"); + KnownPlugins kp; + + for (auto t: kp.getKnownPluginTypes()) { + cout << "successful libraries for plugin type \"" + << kp.getTagFor(t) << "\":" << endl; + for (auto lib: kp.getCandidateLibrariesFor(t)) { + cout << lib << endl; + } + } + + cout << "Failure message (if any):" << endl; + cout << kp.getFailureReport() << endl; }
--- a/checker.pro Wed Apr 13 09:12:04 2016 +0100 +++ b/checker.pro Wed Apr 13 12:00:07 2016 +0100 @@ -1,23 +1,27 @@ TEMPLATE = app -CONFIG += qt stl c++11 exceptions console +CONFIG += qt stl c++11 exceptions console warn_on QT -= xml network gui widgets # Using the "console" CONFIG flag above should ensure this happens for # normal Windows builds, but this may be necessary when cross-compiling win32-x-g++:QMAKE_LFLAGS += -Wl,-subsystem,console +QMAKE_CXXFLAGS += -Werror + TARGET = checker OBJECTS_DIR = o MOC_DIR = o HEADERS += \ - plugincandidates.h + plugincandidates.h \ + knownplugins.h SOURCES += \ plugincandidates.cpp \ + knownplugins.cpp \ checker.cpp QMAKE_POST_LINK=make -f Makefile.helper
--- a/helper.cpp Wed Apr 13 09:12:04 2016 +0100 +++ b/helper.cpp Wed Apr 13 12:00:07 2016 +0100 @@ -3,10 +3,10 @@ /** * Plugin Load Checker Helper * - * This program accepts the name of a descriptor function as its only + * This program accepts the name of a descriptor symbol as its only * command-line argument. It then reads a list of plugin library paths * from stdin, one per line. For each path read, it attempts to load - * that library and retrieve the named descriptor function, printing a + * that library and retrieve the named descriptor symbol, printing a * line to stdout reporting whether this was successful or not and * then flushing stdout. The output line format is described * below. The program exits with code 0 if all libraries were loaded @@ -57,17 +57,23 @@ string check(string soname, string descriptor) { +// cerr << "helper: trying: " << soname << endl; + void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); if (!handle) { +// cerr << "helper: failed to open" << endl; return "Unable to open plugin library: " + error(); } void *fn = DLSYM(handle, descriptor); if (!fn) { +// cerr << "helper: failed to find descriptor" << endl; return "Failed to find plugin descriptor " + descriptor + " in library: " + error(); } +// cerr << "helper: succeeded" << endl; + return ""; } @@ -78,7 +84,7 @@ if (argc != 2) { cerr << "\nUsage:\n " << argv[0] << " descriptorname\n" - "\nwhere descriptorname is the name of a plugin descriptor function to be sought\n" + "\nwhere descriptorname is the name of a plugin descriptor symbol to be sought\n" "in each library (e.g. vampGetPluginDescriptor for Vamp plugins). The list of\n" "candidate plugin library filenames is read from stdin.\n" << endl; return 2;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/knownplugins.cpp Wed Apr 13 12:00:07 2016 +0100 @@ -0,0 +1,172 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "knownplugins.h" + +#include <sstream> + +using namespace std; + +#if defined(_WIN32) +#define PATH_SEPARATOR ';' +#else +#define PATH_SEPARATOR ':' +#endif + +KnownPlugins::KnownPlugins() : + m_candidates("./helper") //!!!??? wot to do +{ + m_known = { + { + VampPlugin, + { + "vamp", + expandConventionalPath(VampPlugin, "VAMP_PATH"), + "vampGetPluginDescriptor" + }, + }, { + LADSPAPlugin, + { + "ladspa", + expandConventionalPath(LADSPAPlugin, "LADSPA_PATH"), + "ladspa_descriptor" + }, + }, { + DSSIPlugin, + { + "dssi", + expandConventionalPath(DSSIPlugin, "DSSI_PATH"), + "dssi_descriptor" + } + } + }; + + for (const auto &k: m_known) { + m_candidates.scan(k.second.tag, k.second.path, k.second.descriptor); + } +} + +string +KnownPlugins::getDefaultPath(PluginType type) +{ + switch (type) { + +#if defined(_WIN32) + + case VampPlugin: + return "%ProgramFiles%\\Vamp Plugins"; + case LADSPAPlugin: + return "%ProgramFiles%\\LADSPA Plugins;%ProgramFiles%\\Audacity\\Plug-Ins"; + case DSSIPlugin: + return "%ProgramFiles%\\DSSI Plugins"; + +#elif defined(__APPLE__) + + case VampPlugin: + return "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"; + case LADSPAPlugin: + return "$HOME/Library/Audio/Plug-Ins/LADSPA:/Library/Audio/Plug-Ins/LADSPA"; + case DSSIPlugin: + return "$HOME/Library/Audio/Plug-Ins/DSSI:/Library/Audio/Plug-Ins/DSSI"; + +#else /* Linux, BSDs, etc */ + + case VampPlugin: + return "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"; + case LADSPAPlugin: + return "$HOME/ladspa:$HOME/.ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa"; + case DSSIPlugin: + return "$HOME/dssi:$HOME/.dssi:/usr/local/lib/dssi:/usr/lib/dssi"; +#endif + } + + throw logic_error("unknown or unhandled plugin type"); +} + +vector<string> +KnownPlugins::expandConventionalPath(PluginType type, string var) +{ + vector<string> pathList; + string path; + + char *cpath = getenv(var.c_str()); + if (cpath) path = cpath; + + if (path == "") { + + path = getDefaultPath(type); + + if (path != "") { + + char *home = getenv("HOME"); + if (home) { + string::size_type f; + while ((f = path.find("$HOME")) != string::npos && + f < path.length()) { + path.replace(f, 5, home); + } + } + +#ifdef _WIN32 + char *pfiles = getenv("ProgramFiles"); + if (!pfiles) pfiles = "C:\\Program Files"; + { + string::size_type f; + while ((f = path.find("%ProgramFiles%")) != string::npos && + f < path.length()) { + path.replace(f, 14, pfiles); + } + } +#endif + } + } + + string::size_type index = 0, newindex = 0; + + while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) { + pathList.push_back(path.substr(index, newindex - index).c_str()); + index = newindex + 1; + } + + pathList.push_back(path.substr(index)); + + return pathList; +} + +string +KnownPlugins::getFailureReport() const +{ + vector<PluginCandidates::FailureRec> failures; + + for (auto t: getKnownPluginTypes()) { + auto ff = m_candidates.getFailedLibrariesFor(getTagFor(t)); + failures.insert(failures.end(), ff.begin(), ff.end()); + } + + if (failures.empty()) return ""; + + int n = failures.size(); + int i = 0; + + ostringstream os; + + os << "<ul>"; + for (auto f: failures) { + os << "<li>" + f.library; + if (f.message != "") { + os << " (" + f.message + ")"; + } else { + os << " (unknown error)"; + } + os << "</li>"; + + if (n > 10) { + if (++i == 5) { + os << "<li>(... and " << (n - i) << " further failures)</li>"; + break; + } + } + } + os << "</ul>"; + + return os.str(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/knownplugins.h Wed Apr 13 12:00:07 2016 +0100 @@ -0,0 +1,53 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef KNOWN_PLUGINS_H +#define KNOWN_PLUGINS_H + +#include "plugincandidates.h" + +#include <string> +#include <map> +#include <vector> + +class KnownPlugins +{ + typedef std::vector<std::string> stringlist; + +public: + enum PluginType { + VampPlugin, + LADSPAPlugin, + DSSIPlugin + }; + + KnownPlugins(); + + std::vector<PluginType> getKnownPluginTypes() const { + return { VampPlugin, LADSPAPlugin, DSSIPlugin }; + }; + + std::string getTagFor(PluginType type) const { + return m_known.at(type).tag; + } + + stringlist getCandidateLibrariesFor(PluginType type) const { + return m_candidates.getCandidateLibrariesFor(getTagFor(type)); + } + + std::string getFailureReport() const; + +private: + struct TypeRec { + std::string tag; + stringlist path; + std::string descriptor; + }; + std::map<PluginType, TypeRec> m_known; + + stringlist expandConventionalPath(PluginType type, std::string var); + std::string getDefaultPath(PluginType type); + + PluginCandidates m_candidates; +}; + +#endif
--- a/plugincandidates.cpp Wed Apr 13 09:12:04 2016 +0100 +++ b/plugincandidates.cpp Wed Apr 13 12:00:07 2016 +0100 @@ -9,17 +9,13 @@ #include <QProcess> #include <QDir> -#ifdef _WIN32 +#if defined(_WIN32) #define PLUGIN_GLOB "*.dll" -#define PATH_SEPARATOR ';' -#else -#define PATH_SEPARATOR ':' -#ifdef __APPLE__ +#elif defined(__APPLE__) #define PLUGIN_GLOB "*.dylib *.so" #else #define PLUGIN_GLOB "*.so" #endif -#endif using namespace std; @@ -29,15 +25,17 @@ } vector<string> -PluginCandidates::getCandidateLibrariesFor(string tag) +PluginCandidates::getCandidateLibrariesFor(string tag) const { - return m_candidates[tag]; + if (m_candidates.find(tag) == m_candidates.end()) return {}; + else return m_candidates.at(tag); } vector<PluginCandidates::FailureRec> -PluginCandidates::getFailedLibrariesFor(string tag) +PluginCandidates::getFailedLibrariesFor(string tag) const { - return m_failures[tag]; + if (m_failures.find(tag) == m_failures.end()) return {}; + else return m_failures.at(tag); } vector<string> @@ -67,7 +65,7 @@ void PluginCandidates::scan(string tag, vector<string> pluginPath, - string descriptorFunctionName) + string descriptorSymbolName) { vector<string> libraries = getLibrariesInPath(pluginPath); vector<string> remaining = libraries; @@ -78,21 +76,20 @@ vector<string> result; while (result.size() < libraries.size() && runcount < runlimit) { - vector<string> output = runHelper(remaining, descriptorFunctionName); + vector<string> output = runHelper(remaining, descriptorSymbolName); result.insert(result.end(), output.begin(), output.end()); int shortfall = int(remaining.size()) - int(output.size()); if (shortfall > 0) { // Helper bailed out for some reason presumably associated // with the plugin following the last one it reported - // on. Add a null entry for that one and continue with the - // following ones. - result.push_back(""); - if (shortfall == 1) { - remaining = vector<string>(); - } else { - remaining = vector<string> - (remaining.rbegin(), remaining.rbegin() + shortfall - 1); - } + // on. Add a failure entry for that one and continue with + // the following ones. + cerr << "shortfall = " << shortfall << " (of " << remaining.size() << ")" << endl; + result.push_back("FAILURE|" + + *(remaining.rbegin() + shortfall - 1) + + "|Plugin load check failed"); + remaining = vector<string> + (remaining.rbegin(), remaining.rbegin() + shortfall - 1); } ++runcount; } @@ -104,11 +101,12 @@ PluginCandidates::runHelper(vector<string> libraries, string descriptor) { vector<string> output; - cerr << "running helper with following library list:" << endl; - for (auto &lib: libraries) cerr << lib << endl; +// cerr << "running helper with following library list:" << endl; +// for (auto &lib: libraries) cerr << lib << endl; QProcess process; process.setReadChannel(QProcess::StandardOutput); + process.setProcessChannelMode(QProcess::ForwardedErrorChannel); process.start(m_helper.c_str(), { descriptor.c_str() }); if (!process.waitForStarted()) { cerr << "helper failed to start" << endl; @@ -120,21 +118,29 @@ } int buflen = 4096; - while (process.waitForReadyRead()) { + bool done = false; + + while (!done) { char buf[buflen]; qint64 linelen = process.readLine(buf, buflen); -// cerr << "read line: " << buf; - if (linelen < 0) { - cerr << "read failed from plugin load helper" << endl; - return output; - } - output.push_back(buf); - if (output.size() == libraries.size()) { - process.close(); - process.waitForFinished(); - break; + if (linelen > 0) { + output.push_back(buf); + done = (output.size() == libraries.size()); + } else if (linelen < 0) { + // error case + done = true; + } else { + // no error, but no line read (could just be between + // lines, or could be eof) + done = (process.state() == QProcess::NotRunning); + if (!done) process.waitForReadyRead(100); } - } + } + + if (process.state() != QProcess::NotRunning) { + process.close(); + process.waitForFinished(); + } return output; } @@ -142,8 +148,33 @@ void PluginCandidates::recordResult(string tag, vector<string> result) { - cerr << "recordResult: not yet implemented, but result was:" << endl; - for (auto &r: result) cerr << r; - cerr << "(ends)" << endl; + for (auto &r: result) { + + QString s(r.c_str()); + QStringList bits = s.split("|"); + if (bits.size() < 2 || bits.size() > 3) { + cerr << "Invalid helper output line: \"" << r << "\"" << endl; + continue; + } + + string status = bits[0].toStdString(); + + string library = bits[1].toStdString(); + if (bits.size() == 2) library = bits[1].trimmed().toStdString(); + + string message = ""; + if (bits.size() > 2) message = bits[2].trimmed().toStdString(); + + if (status == "SUCCESS") { + m_candidates[tag].push_back(library); + + } else if (status == "FAILURE") { + m_failures[tag].push_back({ library, message }); + + } else { + cerr << "Unexpected status " << status + << " in helper output line: \"" << r << "\"" << endl; + } + } }
--- a/plugincandidates.h Wed Apr 13 09:12:04 2016 +0100 +++ b/plugincandidates.h Wed Apr 13 12:00:07 2016 +0100 @@ -28,7 +28,7 @@ /** Scan the libraries found in the given plugin path (i.e. list * of plugin directories), checking that the given descriptor - * function can be looked up in each. Store the results + * symbol can be looked up in each. Store the results * internally, associated with the given (arbitrary) tag, for * later querying using getCandidateLibrariesFor() and * getFailedLibrariesFor(). @@ -37,12 +37,12 @@ */ void scan(std::string tag, stringlist pluginPath, - std::string descriptorFunctionName); + std::string descriptorSymbolName); /** Return list of plugin library paths that were checked * successfully during the scan for the given tag. */ - stringlist getCandidateLibrariesFor(std::string tag); + stringlist getCandidateLibrariesFor(std::string tag) const; struct FailureRec { std::string library; @@ -52,7 +52,7 @@ /** Return list of failure reports arising from the prior scan for * the given tag. */ - std::vector<FailureRec> getFailedLibrariesFor(std::string tag); + std::vector<FailureRec> getFailedLibrariesFor(std::string tag) const; private: std::string m_helper;