Mercurial > hg > vamp-plugin-load-checker
comparison src/plugincandidates.cpp @ 8:25e00373f597
Much renaming to ease inclusion into another project
author | Chris Cannam |
---|---|
date | Thu, 14 Apr 2016 16:52:19 +0100 |
parents | plugincandidates.cpp@61dbb18f2369 |
children | 9f62684e1911 |
comparison
equal
deleted
inserted
replaced
7:846464771d06 | 8:25e00373f597 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 /* | |
3 Copyright (c) 2016 Queen Mary, University of London | |
4 | |
5 Permission is hereby granted, free of charge, to any person | |
6 obtaining a copy of this software and associated documentation | |
7 files (the "Software"), to deal in the Software without | |
8 restriction, including without limitation the rights to use, copy, | |
9 modify, merge, publish, distribute, sublicense, and/or sell copies | |
10 of the Software, and to permit persons to whom the Software is | |
11 furnished to do so, subject to the following conditions: | |
12 | |
13 The above copyright notice and this permission notice shall be | |
14 included in all copies or substantial portions of the Software. | |
15 | |
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
20 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
21 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
23 | |
24 Except as contained in this notice, the names of the Centre for | |
25 Digital Music and Queen Mary, University of London shall not be | |
26 used in advertising or otherwise to promote the sale, use or other | |
27 dealings in this Software without prior written authorization. | |
28 */ | |
29 | |
30 #include "plugincandidates.h" | |
31 | |
32 #include <set> | |
33 #include <stdexcept> | |
34 #include <iostream> | |
35 | |
36 #include <QProcess> | |
37 #include <QDir> | |
38 #include <QTime> | |
39 | |
40 #if defined(_WIN32) | |
41 #define PLUGIN_GLOB "*.dll" | |
42 #elif defined(__APPLE__) | |
43 #define PLUGIN_GLOB "*.dylib *.so" | |
44 #else | |
45 #define PLUGIN_GLOB "*.so" | |
46 #endif | |
47 | |
48 using namespace std; | |
49 | |
50 PluginCandidates::PluginCandidates(string helperExecutableName) : | |
51 m_helper(helperExecutableName), | |
52 m_logCallback(0) | |
53 { | |
54 } | |
55 | |
56 void | |
57 PluginCandidates::setLogCallback(LogCallback *cb) | |
58 { | |
59 m_logCallback = cb; | |
60 } | |
61 | |
62 vector<string> | |
63 PluginCandidates::getCandidateLibrariesFor(string tag) const | |
64 { | |
65 if (m_candidates.find(tag) == m_candidates.end()) return {}; | |
66 else return m_candidates.at(tag); | |
67 } | |
68 | |
69 vector<PluginCandidates::FailureRec> | |
70 PluginCandidates::getFailedLibrariesFor(string tag) const | |
71 { | |
72 if (m_failures.find(tag) == m_failures.end()) return {}; | |
73 else return m_failures.at(tag); | |
74 } | |
75 | |
76 void | |
77 PluginCandidates::log(string message) | |
78 { | |
79 if (m_logCallback) m_logCallback->log("PluginCandidates: " + message); | |
80 } | |
81 | |
82 vector<string> | |
83 PluginCandidates::getLibrariesInPath(vector<string> path) | |
84 { | |
85 vector<string> candidates; | |
86 | |
87 for (string dirname: path) { | |
88 | |
89 log("scanning directory " + dirname + "\n"); | |
90 | |
91 QDir dir(dirname.c_str(), PLUGIN_GLOB, | |
92 QDir::Name | QDir::IgnoreCase, | |
93 QDir::Files | QDir::Readable); | |
94 | |
95 for (unsigned int i = 0; i < dir.count(); ++i) { | |
96 QString soname = dir.filePath(dir[i]); | |
97 candidates.push_back(soname.toStdString()); | |
98 } | |
99 } | |
100 | |
101 return candidates; | |
102 } | |
103 | |
104 void | |
105 PluginCandidates::scan(string tag, | |
106 vector<string> pluginPath, | |
107 string descriptorSymbolName) | |
108 { | |
109 vector<string> libraries = getLibrariesInPath(pluginPath); | |
110 vector<string> remaining = libraries; | |
111 | |
112 int runlimit = 20; | |
113 int runcount = 0; | |
114 | |
115 vector<string> result; | |
116 | |
117 while (result.size() < libraries.size() && runcount < runlimit) { | |
118 vector<string> output = runHelper(remaining, descriptorSymbolName); | |
119 result.insert(result.end(), output.begin(), output.end()); | |
120 int shortfall = int(remaining.size()) - int(output.size()); | |
121 if (shortfall > 0) { | |
122 // Helper bailed out for some reason presumably associated | |
123 // with the plugin following the last one it reported | |
124 // on. Add a failure entry for that one and continue with | |
125 // the following ones. | |
126 string failed = *(remaining.rbegin() + shortfall - 1); | |
127 log("helper output ended before result for plugin " + failed + "\n"); | |
128 result.push_back("FAILURE|" + failed + "|Plugin load check failed or timed out"); | |
129 remaining = vector<string> | |
130 (remaining.rbegin(), remaining.rbegin() + shortfall - 1); | |
131 } | |
132 ++runcount; | |
133 } | |
134 | |
135 recordResult(tag, result); | |
136 } | |
137 | |
138 vector<string> | |
139 PluginCandidates::runHelper(vector<string> libraries, string descriptor) | |
140 { | |
141 vector<string> output; | |
142 | |
143 log("running helper with following library list:\n"); | |
144 for (auto &lib: libraries) log(lib + "\n"); | |
145 | |
146 QProcess process; | |
147 process.setReadChannel(QProcess::StandardOutput); | |
148 process.setProcessChannelMode(QProcess::ForwardedErrorChannel); | |
149 process.start(m_helper.c_str(), { descriptor.c_str() }); | |
150 if (!process.waitForStarted()) { | |
151 cerr << "helper failed to start" << endl; | |
152 throw runtime_error("plugin load helper failed to start"); | |
153 } | |
154 for (auto &lib: libraries) { | |
155 process.write(lib.c_str(), lib.size()); | |
156 process.write("\n", 1); | |
157 } | |
158 | |
159 QTime t; | |
160 t.start(); | |
161 int timeout = 3000; // ms | |
162 | |
163 int buflen = 4096; | |
164 bool done = false; | |
165 | |
166 while (!done) { | |
167 char buf[buflen]; | |
168 qint64 linelen = process.readLine(buf, buflen); | |
169 if (linelen > 0) { | |
170 output.push_back(buf); | |
171 done = (output.size() == libraries.size()); | |
172 } else if (linelen < 0) { | |
173 // error case | |
174 log("received error code while reading from helper\n"); | |
175 done = true; | |
176 } else { | |
177 // no error, but no line read (could just be between | |
178 // lines, or could be eof) | |
179 done = (process.state() == QProcess::NotRunning); | |
180 if (!done) { | |
181 if (t.elapsed() > timeout) { | |
182 // this is purely an emergency measure | |
183 log("timeout: helper took too long, killing it\n"); | |
184 process.kill(); | |
185 done = true; | |
186 } else { | |
187 process.waitForReadyRead(200); | |
188 } | |
189 } | |
190 } | |
191 } | |
192 | |
193 if (process.state() != QProcess::NotRunning) { | |
194 process.close(); | |
195 process.waitForFinished(); | |
196 } | |
197 | |
198 return output; | |
199 } | |
200 | |
201 void | |
202 PluginCandidates::recordResult(string tag, vector<string> result) | |
203 { | |
204 for (auto &r: result) { | |
205 | |
206 QString s(r.c_str()); | |
207 QStringList bits = s.split("|"); | |
208 | |
209 log("read output line from helper: " + r); | |
210 | |
211 if (bits.size() < 2 || bits.size() > 3) { | |
212 log("invalid output line (wrong number of |-separated fields)\n"); | |
213 continue; | |
214 } | |
215 | |
216 string status = bits[0].toStdString(); | |
217 | |
218 string library = bits[1].toStdString(); | |
219 if (bits.size() == 2) library = bits[1].trimmed().toStdString(); | |
220 | |
221 string message = ""; | |
222 if (bits.size() > 2) message = bits[2].trimmed().toStdString(); | |
223 | |
224 if (status == "SUCCESS") { | |
225 m_candidates[tag].push_back(library); | |
226 | |
227 } else if (status == "FAILURE") { | |
228 m_failures[tag].push_back({ library, message }); | |
229 | |
230 } else { | |
231 log("unexpected status \"" + status + "\" in output line\n"); | |
232 } | |
233 } | |
234 } | |
235 |