comparison plugincandidates.cpp @ 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
comparison
equal deleted inserted replaced
3:3bae396cf8e0 4:6f891a9c6434
7 #include <iostream> 7 #include <iostream>
8 8
9 #include <QProcess> 9 #include <QProcess>
10 #include <QDir> 10 #include <QDir>
11 11
12 #ifdef _WIN32 12 #if defined(_WIN32)
13 #define PLUGIN_GLOB "*.dll" 13 #define PLUGIN_GLOB "*.dll"
14 #define PATH_SEPARATOR ';' 14 #elif defined(__APPLE__)
15 #else
16 #define PATH_SEPARATOR ':'
17 #ifdef __APPLE__
18 #define PLUGIN_GLOB "*.dylib *.so" 15 #define PLUGIN_GLOB "*.dylib *.so"
19 #else 16 #else
20 #define PLUGIN_GLOB "*.so" 17 #define PLUGIN_GLOB "*.so"
21 #endif
22 #endif 18 #endif
23 19
24 using namespace std; 20 using namespace std;
25 21
26 PluginCandidates::PluginCandidates(string helperExecutableName) : 22 PluginCandidates::PluginCandidates(string helperExecutableName) :
27 m_helper(helperExecutableName) 23 m_helper(helperExecutableName)
28 { 24 {
29 } 25 }
30 26
31 vector<string> 27 vector<string>
32 PluginCandidates::getCandidateLibrariesFor(string tag) 28 PluginCandidates::getCandidateLibrariesFor(string tag) const
33 { 29 {
34 return m_candidates[tag]; 30 if (m_candidates.find(tag) == m_candidates.end()) return {};
31 else return m_candidates.at(tag);
35 } 32 }
36 33
37 vector<PluginCandidates::FailureRec> 34 vector<PluginCandidates::FailureRec>
38 PluginCandidates::getFailedLibrariesFor(string tag) 35 PluginCandidates::getFailedLibrariesFor(string tag) const
39 { 36 {
40 return m_failures[tag]; 37 if (m_failures.find(tag) == m_failures.end()) return {};
38 else return m_failures.at(tag);
41 } 39 }
42 40
43 vector<string> 41 vector<string>
44 PluginCandidates::getLibrariesInPath(vector<string> path) 42 PluginCandidates::getLibrariesInPath(vector<string> path)
45 { 43 {
65 } 63 }
66 64
67 void 65 void
68 PluginCandidates::scan(string tag, 66 PluginCandidates::scan(string tag,
69 vector<string> pluginPath, 67 vector<string> pluginPath,
70 string descriptorFunctionName) 68 string descriptorSymbolName)
71 { 69 {
72 vector<string> libraries = getLibrariesInPath(pluginPath); 70 vector<string> libraries = getLibrariesInPath(pluginPath);
73 vector<string> remaining = libraries; 71 vector<string> remaining = libraries;
74 72
75 int runlimit = 20; 73 int runlimit = 20;
76 int runcount = 0; 74 int runcount = 0;
77 75
78 vector<string> result; 76 vector<string> result;
79 77
80 while (result.size() < libraries.size() && runcount < runlimit) { 78 while (result.size() < libraries.size() && runcount < runlimit) {
81 vector<string> output = runHelper(remaining, descriptorFunctionName); 79 vector<string> output = runHelper(remaining, descriptorSymbolName);
82 result.insert(result.end(), output.begin(), output.end()); 80 result.insert(result.end(), output.begin(), output.end());
83 int shortfall = int(remaining.size()) - int(output.size()); 81 int shortfall = int(remaining.size()) - int(output.size());
84 if (shortfall > 0) { 82 if (shortfall > 0) {
85 // Helper bailed out for some reason presumably associated 83 // Helper bailed out for some reason presumably associated
86 // with the plugin following the last one it reported 84 // with the plugin following the last one it reported
87 // on. Add a null entry for that one and continue with the 85 // on. Add a failure entry for that one and continue with
88 // following ones. 86 // the following ones.
89 result.push_back(""); 87 cerr << "shortfall = " << shortfall << " (of " << remaining.size() << ")" << endl;
90 if (shortfall == 1) { 88 result.push_back("FAILURE|" +
91 remaining = vector<string>(); 89 *(remaining.rbegin() + shortfall - 1) +
92 } else { 90 "|Plugin load check failed");
93 remaining = vector<string> 91 remaining = vector<string>
94 (remaining.rbegin(), remaining.rbegin() + shortfall - 1); 92 (remaining.rbegin(), remaining.rbegin() + shortfall - 1);
95 }
96 } 93 }
97 ++runcount; 94 ++runcount;
98 } 95 }
99 96
100 recordResult(tag, result); 97 recordResult(tag, result);
102 99
103 vector<string> 100 vector<string>
104 PluginCandidates::runHelper(vector<string> libraries, string descriptor) 101 PluginCandidates::runHelper(vector<string> libraries, string descriptor)
105 { 102 {
106 vector<string> output; 103 vector<string> output;
107 cerr << "running helper with following library list:" << endl; 104 // cerr << "running helper with following library list:" << endl;
108 for (auto &lib: libraries) cerr << lib << endl; 105 // for (auto &lib: libraries) cerr << lib << endl;
109 106
110 QProcess process; 107 QProcess process;
111 process.setReadChannel(QProcess::StandardOutput); 108 process.setReadChannel(QProcess::StandardOutput);
109 process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
112 process.start(m_helper.c_str(), { descriptor.c_str() }); 110 process.start(m_helper.c_str(), { descriptor.c_str() });
113 if (!process.waitForStarted()) { 111 if (!process.waitForStarted()) {
114 cerr << "helper failed to start" << endl; 112 cerr << "helper failed to start" << endl;
115 throw runtime_error("plugin load helper failed to start"); 113 throw runtime_error("plugin load helper failed to start");
116 } 114 }
118 process.write(lib.c_str(), lib.size()); 116 process.write(lib.c_str(), lib.size());
119 process.write("\n", 1); 117 process.write("\n", 1);
120 } 118 }
121 119
122 int buflen = 4096; 120 int buflen = 4096;
123 while (process.waitForReadyRead()) { 121 bool done = false;
122
123 while (!done) {
124 char buf[buflen]; 124 char buf[buflen];
125 qint64 linelen = process.readLine(buf, buflen); 125 qint64 linelen = process.readLine(buf, buflen);
126 // cerr << "read line: " << buf; 126 if (linelen > 0) {
127 if (linelen < 0) { 127 output.push_back(buf);
128 cerr << "read failed from plugin load helper" << endl; 128 done = (output.size() == libraries.size());
129 return output; 129 } else if (linelen < 0) {
130 } 130 // error case
131 output.push_back(buf); 131 done = true;
132 if (output.size() == libraries.size()) { 132 } else {
133 process.close(); 133 // no error, but no line read (could just be between
134 process.waitForFinished(); 134 // lines, or could be eof)
135 break; 135 done = (process.state() == QProcess::NotRunning);
136 if (!done) process.waitForReadyRead(100);
136 } 137 }
137 } 138 }
139
140 if (process.state() != QProcess::NotRunning) {
141 process.close();
142 process.waitForFinished();
143 }
138 144
139 return output; 145 return output;
140 } 146 }
141 147
142 void 148 void
143 PluginCandidates::recordResult(string tag, vector<string> result) 149 PluginCandidates::recordResult(string tag, vector<string> result)
144 { 150 {
145 cerr << "recordResult: not yet implemented, but result was:" << endl; 151 for (auto &r: result) {
146 for (auto &r: result) cerr << r; 152
147 cerr << "(ends)" << endl; 153 QString s(r.c_str());
154 QStringList bits = s.split("|");
155 if (bits.size() < 2 || bits.size() > 3) {
156 cerr << "Invalid helper output line: \"" << r << "\"" << endl;
157 continue;
158 }
159
160 string status = bits[0].toStdString();
161
162 string library = bits[1].toStdString();
163 if (bits.size() == 2) library = bits[1].trimmed().toStdString();
164
165 string message = "";
166 if (bits.size() > 2) message = bits[2].trimmed().toStdString();
167
168 if (status == "SUCCESS") {
169 m_candidates[tag].push_back(library);
170
171 } else if (status == "FAILURE") {
172 m_failures[tag].push_back({ library, message });
173
174 } else {
175 cerr << "Unexpected status " << status
176 << " in helper output line: \"" << r << "\"" << endl;
177 }
178 }
148 } 179 }
149 180