comparison generator/generator.cpp @ 153:38675dcea44f

Bring the stub generator into this repo (from piper-vamp-cpp)
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 14 Jun 2017 10:06:30 +0100
parents
children 94050aba32c3
comparison
equal deleted inserted replaced
152:c43d2e93153f 153:38675dcea44f
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 /*
3 Piper C++
4
5 An API for audio analysis and feature extraction plugins.
6
7 Centre for Digital Music, Queen Mary, University of London.
8 Copyright 2006-2017 Chris Cannam and QMUL.
9
10 Permission is hereby granted, free of charge, to any person
11 obtaining a copy of this software and associated documentation
12 files (the "Software"), to deal in the Software without
13 restriction, including without limitation the rights to use, copy,
14 modify, merge, publish, distribute, sublicense, and/or sell copies
15 of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be
19 included in all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29 Except as contained in this notice, the names of the Centre for
30 Digital Music; Queen Mary, University of London; and Chris Cannam
31 shall not be used in advertising or otherwise to promote the sale,
32 use or other dealings in this Software without prior written
33 authorization.
34 */
35
36 #include "vamp-json/VampJson.h"
37 #include "vamp-support/RequestOrResponse.h"
38 #include "vamp-support/LoaderRequests.h"
39 #include "vamp-support/RdfTypes.h"
40
41 #include <iostream>
42 #include <sstream>
43 #include <stdexcept>
44
45 #include <map>
46 #include <set>
47
48 // for _setmode stuff and _dup
49 #ifdef _WIN32
50 #include <io.h>
51 #include <fcntl.h>
52 #endif
53
54 // for dup, open etc
55 #ifndef _WIN32
56 #include <fcntl.h>
57 #include <unistd.h>
58 #endif
59
60 using namespace std;
61 using namespace json11;
62 using namespace piper_vamp;
63 using namespace Vamp;
64
65 static string myname = "piper-vamp-stub-generator";
66
67 static void version()
68 {
69 cout << "1.0" << endl;
70 exit(0);
71 }
72
73 static void usage(bool successful = false)
74 {
75 cerr << "\n" << myname <<
76 ": Emit a preliminary version of the main code\nfor a Piper Adapter implementation of a Vamp plugin library\n\n"
77 " Usage: " << myname << " [-d] <libname>\n"
78 " " << myname << " -v\n"
79 " " << myname << " -h\n\n"
80 " where\n"
81 " <libname>: the Vamp plugin library name to emit code for\n"
82 " -v: print version number to stdout and exit\n"
83 " -h: print this text to stderr and exit\n\n";
84 if (successful) exit(0);
85 else exit(2);
86 }
87
88 // We write our output to stdout, but want to ensure that the plugin
89 // doesn't write anything itself. To do this we open a null file
90 // descriptor and dup2() it into place of stdout in the gaps between
91 // our own output activity.
92
93 static int normalFd = -1;
94 static int suspendedFd = -1;
95
96 static void initFds(bool binary)
97 {
98 #ifdef _WIN32
99 if (binary) {
100 int result = _setmode(0, _O_BINARY);
101 if (result == -1) {
102 throw runtime_error("Failed to set binary mode on stdin");
103 }
104 result = _setmode(1, _O_BINARY);
105 if (result == -1) {
106 throw runtime_error("Failed to set binary mode on stdout");
107 }
108 }
109 normalFd = _dup(1);
110 suspendedFd = _open("NUL", _O_WRONLY);
111 #else
112 (void)binary;
113 normalFd = dup(1);
114 suspendedFd = open("/dev/null", O_WRONLY);
115 #endif
116
117 if (normalFd < 0 || suspendedFd < 0) {
118 throw runtime_error("Failed to initialise fds for stdio suspend/resume");
119 }
120 }
121
122 static void suspendOutput()
123 {
124 #ifdef _WIN32
125 _dup2(suspendedFd, 1);
126 #else
127 dup2(suspendedFd, 1);
128 #endif
129 }
130
131 static void resumeOutput()
132 {
133 #ifdef _WIN32
134 _dup2(normalFd, 1);
135 #else
136 dup2(normalFd, 1);
137 #endif
138 }
139
140 ListResponse
141 makeRequest(string libname)
142 {
143 ListRequest req;
144 req.from.push_back(libname);
145 return LoaderRequests().listPluginData(req);
146 }
147
148 struct PlausibleMetadata
149 {
150 string className;
151 string adapterName;
152 };
153
154 PlausibleMetadata
155 inventPlausibleMetadata(string key)
156 {
157 string className, adapterName;
158 bool initial = true, interCap = false;
159
160 int idIdx = 0;
161 for (int i = 0; i < int(key.size()); ++i) {
162 char c = key[i];
163 if (c == ':') {
164 idIdx = i + 1;
165 break;
166 }
167 }
168
169 for (int i = idIdx; i < int(key.size()); ++i) {
170 char c = key[i];
171 if (isalpha(c)) {
172 if (initial || interCap) {
173 className += toupper(c);
174 } else {
175 className += c;
176 }
177 if (interCap) {
178 adapterName += toupper(c);
179 } else {
180 adapterName += c;
181 }
182 interCap = false;
183 } else {
184 interCap = true;
185 }
186 initial = false;
187 }
188 adapterName += "Adapter";
189
190 PlausibleMetadata pm;
191 pm.className = className;
192 pm.adapterName = adapterName;
193 return pm;
194 }
195
196 void
197 emitFor(string libname, const ListResponse &resp)
198 {
199 // The same plugin key may appear more than once in the available
200 // list, if the same plugin is installed in more than one location
201 // on the Vamp path. To avoid emitting its wrapper definition
202 // multiple times, we need to keep a record of which ones we've
203 // emitted for each stage
204 set<string> emitted;
205
206 cout <<
207 "\n#include \"PiperExport.h\"\n"
208 "\n"
209 "// !!! The following #include(s) are guesses. Replace them with the\n"
210 "// real header files in which your plugin classes are defined.\n"
211 "//\n";
212
213 for (const auto &plugin: resp.available) {
214
215 string key = plugin.pluginKey;
216 if (emitted.find(key) != emitted.end()) {
217 continue;
218 }
219
220 PlausibleMetadata pm = inventPlausibleMetadata(key);
221
222 cout << "#include \"" << pm.className << ".h\"\n";
223
224 emitted.insert(key);
225 }
226
227 cout <<
228 "\n"
229 "using piper_vamp_js::PiperAdapter;\n"
230 "using piper_vamp_js::PiperPluginLibrary;\n"
231 "\n"
232 "static std::string libname(\"" << libname << "\");\n"
233 "\n";
234
235 emitted.clear();
236 for (const auto &plugin: resp.available) {
237
238 string key = plugin.pluginKey;
239 if (emitted.find(key) != emitted.end()) {
240 continue;
241 }
242
243 PlausibleMetadata pm = inventPlausibleMetadata(key);
244 cout << "// !!! Replace " << pm.className << " in the following line with the real\n"
245 "// name of the class implementing the \"" << plugin.basic.identifier << "\" plugin.\n"
246 "//\n";
247 cout << "static PiperAdapter<"
248 << pm.className
249 << ">\n" << pm.adapterName
250 << "(\n libname,\n ";
251
252 string catString = "{ ";
253 bool first = true;
254 for (auto c: plugin.category) {
255 if (!first) catString += ", ";
256 catString += "\"" + c + "\"";
257 first = false;
258 }
259 catString += " }";
260 cout << catString << ",\n ";
261
262 cout << "{\n ";
263 first = true;
264 for (auto o: plugin.basicOutputInfo) {
265 if (!first) {
266 cout << ",\n ";
267 }
268 cout << " ";
269 string outputId = o.identifier;
270 cout << "{ \"" << outputId << "\",\n { \"";
271 if (plugin.staticOutputInfo.find(outputId) !=
272 plugin.staticOutputInfo.end()) {
273 cout << plugin.staticOutputInfo.at(outputId).typeURI;
274 }
275 cout << "\" }\n }";
276 first = false;
277 }
278 cout << "\n }\n";
279 cout << " );\n\n";
280 emitted.insert(key);
281 }
282
283 cout << "static PiperPluginLibrary library({\n ";
284 emitted.clear();
285 for (const auto &plugin: resp.available) {
286
287 string key = plugin.pluginKey;
288 if (emitted.find(key) != emitted.end()) {
289 continue;
290 }
291
292 PlausibleMetadata pm = inventPlausibleMetadata(key);
293 if (!emitted.empty()) {
294 cout << ",\n ";
295 }
296 cout << "&" << pm.adapterName;
297 emitted.insert(key);
298 }
299 cout << "\n});\n\n";
300 cout << "PIPER_EXPORT_LIBRARY(library);\n" << endl;
301 }
302
303 int main(int argc, char **argv)
304 {
305 if (argc != 2 && argc != 3) {
306 usage();
307 }
308
309 string arg = argv[1];
310 if (arg == "-h") {
311 if (argc == 2) {
312 usage(true);
313 } else {
314 usage();
315 }
316 } else if (arg == "-v") {
317 if (argc == 2) {
318 version();
319 } else {
320 usage();
321 }
322 }
323
324 string libname = arg;
325
326 auto li = libname.rfind('.');
327 if (li != std::string::npos) {
328 cerr << "NOTE: library name is to be specified without extension, dropping \"" << libname.substr(li) << "\"" << endl;
329 libname = libname.substr(0, li);
330 }
331
332 try {
333 initFds(false);
334 } catch (exception &e) {
335 cerr << "ERROR: " << e.what() << endl;
336 exit(1);
337 }
338
339 suspendOutput();
340
341 ListResponse resp = makeRequest(libname);
342
343 resumeOutput();
344
345 if (resp.available.empty()) {
346 cerr << "ERROR: No plugin(s) found in library named \""
347 << libname << "\"\nCheck that the library name is correct and the right files are in $VAMP_PATH.\nThat would mean either "
348 << libname << ".so, " << libname << ".dll, or " << libname
349 << ".dylib (depending on your platform), as well as "
350 << libname << ".n3 and " << libname << ".cat."
351 << endl;
352 exit(1);
353 }
354
355 emitFor(libname, resp);
356
357 exit(0);
358 }