Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.
4 years ago |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- #include <thread>
- #include <map>
- #include <stdexcept>
- #include <tuple>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <sys/param.h> // for MAXPATHLEN
- #include <fcntl.h>
- #if defined ARCH_WIN
- #include <windows.h>
- #include <direct.h>
- #else
- #include <dlfcn.h> // for dlopen
- #endif
- #include <dirent.h>
-
- #include <osdialog.h>
- #include <jansson.h>
-
- #include <plugin.hpp>
- #include <system.hpp>
- #include <asset.hpp>
- #include <string.hpp>
- #include <context.hpp>
- #include <plugin/callbacks.hpp>
- #include <settings.hpp>
-
-
- namespace rack {
-
- namespace core {
- void init(rack::plugin::Plugin* plugin);
- } // namespace core
-
- namespace plugin {
-
-
- ////////////////////
- // private API
- ////////////////////
-
- static void* loadLibrary(std::string libraryPath) {
- #if defined ARCH_WIN
- SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
- std::wstring libraryFilenameW = string::UTF8toUTF16(libraryPath);
- HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str());
- SetErrorMode(0);
- if (!handle) {
- int error = GetLastError();
- throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error);
- }
- #else
- // Since Rack 2, plugins on Linux/Mac link to the absolute path /tmp/Rack2/libRack.<ext>
- // Create a symlink at /tmp/Rack2 to the system dir containting libRack.
- std::string systemDir = system::getAbsolute(asset::systemDir);
- std::string linkPath = "/tmp/Rack2";
- if (!settings::devMode) {
- // Clean up old symbolic link in case a different edition was run earlier
- system::remove(linkPath);
- system::createSymbolicLink(systemDir, linkPath);
- }
- // Load library with dlopen
- void* handle = NULL;
- #if defined ARCH_LIN
- handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
- #elif defined ARCH_MAC
- handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
- #endif
- if (!handle)
- throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror());
- #endif
- return handle;
- }
-
- typedef void (*InitCallback)(Plugin*);
-
- static InitCallback loadPluginCallback(Plugin* plugin) {
- // Load plugin library
- std::string libraryExt;
- #if defined ARCH_LIN
- libraryExt = "so";
- #elif defined ARCH_WIN
- libraryExt = "dll";
- #elif ARCH_MAC
- libraryExt = "dylib";
- #endif
- std::string libraryPath = system::join(plugin->path, "plugin." + libraryExt);
-
- // Check file existence
- if (!system::isFile(libraryPath))
- throw Exception("Plugin binary not found at %s", libraryPath.c_str());
-
- // Load dynamic/shared library
- plugin->handle = loadLibrary(libraryPath);
-
- // Get plugin's init() function
- InitCallback initCallback;
- #if defined ARCH_WIN
- initCallback = (InitCallback) GetProcAddress((HMODULE) plugin->handle, "init");
- #else
- initCallback = (InitCallback) dlsym(plugin->handle, "init");
- #endif
- if (!initCallback)
- throw Exception("Failed to read init() symbol in %s", libraryPath.c_str());
-
- return initCallback;
- }
-
-
- /** If path is blank, loads Core */
- static Plugin* loadPlugin(std::string path) {
- if (path == "")
- INFO("Loading Core plugin");
- else
- INFO("Loading plugin from %s", path.c_str());
-
- Plugin* plugin = new Plugin;
- try {
- // Set plugin path
- plugin->path = (path == "") ? asset::systemDir : path;
-
- // Get modified timestamp
- if (path != "") {
- struct stat statbuf;
- if (!stat(path.c_str(), &statbuf)) {
- #if defined ARCH_MAC
- plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9;
- #elif defined ARCH_WIN
- plugin->modifiedTimestamp = (double) statbuf.st_mtime;
- #elif defined ARCH_LIN
- plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9;
- #endif
- }
- }
-
- // Load plugin.json
- std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json");
- FILE* file = std::fopen(manifestFilename.c_str(), "r");
- if (!file)
- throw Exception("Manifest file %s does not exist", manifestFilename.c_str());
- DEFER({std::fclose(file);});
-
- json_error_t error;
- json_t* rootJ = json_loadf(file, 0, &error);
- if (!rootJ)
- throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text);
- DEFER({json_decref(rootJ);});
-
- // Call init callback
- InitCallback initCallback;
- if (path == "") {
- initCallback = core::init;
- }
- else {
- initCallback = loadPluginCallback(plugin);
- }
- initCallback(plugin);
-
- // Load manifest
- plugin->fromJson(rootJ);
-
- // Reject plugin if slug already exists
- Plugin* existingPlugin = getExactPlugin(plugin->slug);
- if (existingPlugin)
- throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str());
- }
- catch (Exception& e) {
- WARN("Could not load plugin %s: %s", path.c_str(), e.what());
- delete plugin;
- return NULL;
- }
-
- INFO("Loaded %s v%s", plugin->slug.c_str(), plugin->version.c_str());
- plugins.push_back(plugin);
- return plugin;
- }
-
-
- static void loadPlugins(std::string path) {
- for (std::string pluginPath : system::getEntries(path)) {
- if (!system::isDirectory(pluginPath))
- continue;
- if (!loadPlugin(pluginPath)) {
- // Ignore bad plugins. They are reported in the log.
- }
- }
- }
-
-
- static void extractPackages(std::string path) {
- std::string message;
-
- for (std::string packagePath : system::getEntries(path)) {
- if (!system::isFile(packagePath))
- continue;
- if (system::getExtension(packagePath) != ".vcvplugin")
- continue;
-
- // Extract package
- INFO("Extracting package %s", packagePath.c_str());
- try {
- system::unarchiveToDirectory(packagePath, path);
- }
- catch (Exception& e) {
- WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what());
- message += string::f("Could not extract plugin package %s\n", packagePath.c_str());
- continue;
- }
- // Remove package
- system::remove(packagePath.c_str());
- }
- if (!message.empty()) {
- osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
- }
- }
-
- ////////////////////
- // public API
- ////////////////////
-
- void init() {
- // Don't re-initialize
- assert(plugins.empty());
-
- // Load Core
- loadPlugin("");
-
- pluginsPath = asset::user("plugins");
-
- // Get user plugins directory
- system::createDirectory(pluginsPath);
-
- // Extract packages and load plugins
- extractPackages(pluginsPath);
- loadPlugins(pluginsPath);
-
- // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it
- if (!settings::devMode && !getPlugin("Fundamental")) {
- std::string fundamentalSrc = asset::system("Fundamental.vcvplugin");
- std::string fundamentalDir = system::join(pluginsPath, "Fundamental");
- if (system::isFile(fundamentalSrc)) {
- INFO("Extracting bundled Fundamental package");
- system::unarchiveToDirectory(fundamentalSrc.c_str(), pluginsPath.c_str());
- loadPlugin(fundamentalDir);
- }
- }
- }
-
-
- void destroy() {
- for (Plugin* plugin : plugins) {
- void* handle = plugin->handle;
-
- // Call destroy() if defined in the plugin library
- typedef void (*DestroyCallback)();
- DestroyCallback destroyCallback = NULL;
- if (handle) {
- #if defined ARCH_WIN
- destroyCallback = (DestroyCallback) GetProcAddress((HMODULE) handle, "destroy");
- #else
- destroyCallback = (DestroyCallback) dlsym(handle, "destroy");
- #endif
- }
- if (destroyCallback)
- destroyCallback();
-
- // We must delete the Plugin instance *before* freeing the library, because the vtables of Model subclasses are defined in the library, which are needed in the Plugin destructor.
- delete plugin;
-
- // Free library handle
- if (handle) {
- #if defined ARCH_WIN
- FreeLibrary((HINSTANCE) handle);
- #else
- dlclose(handle);
- #endif
- }
- }
- plugins.clear();
- }
-
-
- // To request fallback slugs to be added to this list, open a GitHub issue.
- static const std::map<std::string, std::string> pluginSlugFallbacks = {
- {"VultModulesFree", "VultModules"},
- {"VultModules", "VultModulesFree"},
- // {"", ""},
- };
-
-
- Plugin* getExactPlugin(const std::string& pluginSlug) {
- if (pluginSlug.empty())
- return NULL;
- auto it = std::find_if(plugins.begin(), plugins.end(), [=](Plugin* p) {
- return p->slug == pluginSlug;
- });
- if (it != plugins.end())
- return *it;
- return NULL;
- }
-
-
- Plugin* getPlugin(const std::string& pluginSlug) {
- Plugin* p = getExactPlugin(pluginSlug);
- if (p)
- return p;
-
- // Use fallback plugin slug
- auto it = pluginSlugFallbacks.find(pluginSlug);
- if (it != pluginSlugFallbacks.end())
- return getExactPlugin(it->second);
- return NULL;
- }
-
-
- // To request fallback slugs to be added to this list, open a GitHub issue.
- using PluginModuleSlug = std::tuple<std::string, std::string>;
- static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = {
- {{"AudibleInstrumentsPreview", "Plaits"}, {"AudibleInstruments", "Plaits"}},
- {{"AudibleInstrumentsPreview", "Marbles"}, {"AudibleInstruments", "Marbles"}},
- {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}},
- {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}},
- // {{"", ""}, {"", ""}},
- };
-
-
- Model* getExactModel(const std::string& pluginSlug, const std::string& modelSlug) {
- if (pluginSlug.empty() || modelSlug.empty())
- return NULL;
- Plugin* p = getPlugin(pluginSlug);
- if (p) {
- Model* model = p->getModel(modelSlug);
- if (model)
- return model;
- }
- return NULL;
- }
-
-
- Model* getModel(const std::string& pluginSlug, const std::string& modelSlug) {
- Model* m = getExactModel(pluginSlug, modelSlug);
- if (m)
- return m;
-
- // Use fallback (module slug, plugin slug)
- auto it = moduleSlugFallbacks.find(std::make_tuple(pluginSlug, modelSlug));
- if (it != moduleSlugFallbacks.end()) {
- return getExactModel(std::get<0>(it->second), std::get<1>(it->second));
- }
- return NULL;
- }
-
-
- Model* modelFromJson(json_t* moduleJ) {
- // Get slugs
- json_t* pluginSlugJ = json_object_get(moduleJ, "plugin");
- if (!pluginSlugJ)
- throw Exception("\"plugin\" property not found in module JSON");
- std::string pluginSlug = json_string_value(pluginSlugJ);
- pluginSlug = normalizeSlug(pluginSlug);
-
- json_t* modelSlugJ = json_object_get(moduleJ, "model");
- if (!modelSlugJ)
- throw Exception("\"model\" property not found in module JSON");
- std::string modelSlug = json_string_value(modelSlugJ);
- modelSlug = normalizeSlug(modelSlug);
-
- // Get Model
- Model* model = getModel(pluginSlug, modelSlug);
- if (!model)
- throw Exception("Could not find module \"%s\" \"%s\"", pluginSlug.c_str(), modelSlug.c_str());
- return model;
- }
-
-
- bool isSlugValid(const std::string& slug) {
- for (char c : slug) {
- if (!(std::isalnum(c) || c == '-' || c == '_'))
- return false;
- }
- return true;
- }
-
-
- std::string normalizeSlug(const std::string& slug) {
- std::string s;
- for (char c : slug) {
- if (!(std::isalnum(c) || c == '-' || c == '_'))
- continue;
- s += c;
- }
- return s;
- }
-
-
- std::string pluginsPath;
- std::vector<Plugin*> plugins;
-
-
- } // namespace plugin
- } // namespace rack
|