annotate vamp-stubber/stubber.cpp @ 237:5ba2749f2024

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