#ifndef OFX_PLUGIN_CACHE_H #define OFX_PLUGIN_CACHE_H // Copyright OpenFX and contributors to the OpenFX project. // SPDX-License-Identifier: BSD-3-Clause #include #include #include #include #include #include #include "expat.h" #include "ofxCore.h" #include "ofxhPropertySuite.h" #include "ofxhPluginAPICache.h" #include "ofxhBinary.h" namespace OFX { namespace Host { class Host; // forward delcarations class PluginDesc; class Plugin; class PluginBinary; class PluginCache; /// C++ version of the information kept inside an OfxPlugin struct class PluginDesc { protected : std::string _pluginApi; ///< the API I implement int _apiVersion; ///< the version of the API std::string _identifier; ///< the identifier of the plugin std::string _rawIdentifier; ///< the original identifier of the plugin int _versionMajor; ///< the plugin major version int _versionMinor; ///< the plugin minor version public: const std::string &getPluginApi() const { return _pluginApi; } int getApiVersion() const { return _apiVersion; } const std::string &getIdentifier() const { return _identifier; } const std::string &getRawIdentifier() const { return _rawIdentifier; } int getVersionMajor() const { return _versionMajor; } int getVersionMinor() const { return _versionMinor; } PluginDesc() : _apiVersion(-1) { } virtual ~PluginDesc() {} PluginDesc(const std::string &api, int apiVersion, const std::string &identifier, const std::string &rawIdentifier, int versionMajor, int versionMinor) : _pluginApi(api) , _apiVersion(apiVersion) , _identifier(identifier) , _rawIdentifier(rawIdentifier) , _versionMajor(versionMajor) , _versionMinor(versionMinor) { } /// constructor for the case where we have already loaded the plugin binary and /// are populating this object from it PluginDesc(OfxPlugin *ofxPlugin) { _pluginApi = ofxPlugin->pluginApi; _apiVersion = ofxPlugin->apiVersion; _rawIdentifier = ofxPlugin->pluginIdentifier; _identifier = ofxPlugin->pluginIdentifier; // Who says the pluginIdentifier is case-insensitive? OFX 1.3 spec doesn't mention this. // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id472588 //for (size_t i=0;i<_identifier.size();i++) { // _identifier[i] = tolower(_identifier[i]); //} _versionMajor = ofxPlugin->pluginVersionMajor; _versionMinor = ofxPlugin->pluginVersionMinor; } }; /// class that we use to manipulate a plugin class Plugin : public PluginDesc { /// owned by the PluginBinary it lives inside /// Plugins can only be pass about either by pointer or reference private : Plugin(const Plugin&) : PluginDesc() {} ///< hidden Plugin &operator= (const Plugin&) {return *this;} ///< hidden protected : PluginBinary *_binary; ///< the file I live inside int _index; ///< where I live inside that file public : Plugin(); PluginBinary *getBinary() { return _binary; } const PluginBinary *getBinary() const { return _binary; } int getIndex() const { return _index; } /// construct this based on the struct returned by the getNthPlugin() in the binary Plugin(PluginBinary *bin, int idx, OfxPlugin *o) : PluginDesc(o), _binary(bin), _index(idx) { } /// construct me from the cache Plugin(PluginBinary *bin, int idx, const std::string &api, int apiVersion, const std::string &identifier, const std::string &rawIdentifier, int majorVersion, int minorVersion) : PluginDesc(api, apiVersion, identifier, rawIdentifier, majorVersion, minorVersion) , _binary(bin) , _index(idx) { } virtual ~Plugin() { } virtual APICache::PluginAPICacheI &getApiHandler() = 0; bool trumps(Plugin *other) { int myMajor = getVersionMajor(); int theirMajor = other->getVersionMajor(); int myMinor = getVersionMinor(); int theirMinor = other->getVersionMinor(); if (myMajor > theirMajor) { return true; } if (myMajor == theirMajor && myMinor > theirMinor) { return true; } return false; } }; class PluginHandle; /// class that represents a binary file which holds plugins class PluginBinary { /// has a set of plugins inside it and which it owns /// These are owned by a PluginCache friend class PluginHandle; protected : Binary _binary; ///< our binary object, abstracted layer ontop of OS calls, defined in ofxhBinary.h std::string _filePath; ///< full path to the file std::string _bundlePath; ///< path to the .bundle directory std::vector _plugins; ///< my plugins time_t _fileModificationTime; ///< used as a time stamp to check modification times, used for caching off_t _fileSize; ///< file size last time we check, used for caching bool _binaryChanged; ///< whether the timestamp/filesize in this cache is different from that in the actual binary public : /// create one from the cache. this will invoke the Binary() constructor which /// will stat() the file. explicit PluginBinary(const std::string &file, const std::string &bundlePath, time_t mtime, off_t size) : _binary(file) , _filePath(file) , _bundlePath(bundlePath) , _fileModificationTime(mtime) , _fileSize(size) , _binaryChanged(false) { if (isInvalid()) { return; } if (_fileModificationTime != _binary.getTime() || _fileSize != _binary.getSize()) { _binaryChanged = true; } } /// constructor which will open a library file, call things inside it, and then /// create Plugin objects as appropriate for the plugins exported therefrom explicit PluginBinary(const std::string &file, const std::string &bundlePath, PluginCache *cache) : _binary(file) , _filePath(file) , _bundlePath(bundlePath) , _binaryChanged(false) { loadPluginInfo(cache); } /// dtor virtual ~PluginBinary(); time_t getFileModificationTime() const { return _fileModificationTime; } off_t getFileSize() { return _fileSize; } const std::string &getFilePath() const { return _filePath; } const std::string &getBundlePath() const { return _bundlePath; } bool hasBinaryChanged() const { return _binaryChanged; } bool isLoaded() const { return _binary.isLoaded(); } bool isInvalid() const { return _binary.isInvalid(); } void addPlugin(Plugin *pe) { _plugins.push_back(pe); } void loadPluginInfo(PluginCache *); /// how many plugins? int getNPlugins() const {return (int)_plugins.size(); } /// get a plugin Plugin &getPlugin(int idx) {return *_plugins[idx];} /// get a plugin const Plugin &getPlugin(int idx) const {return *_plugins[idx];} }; /// wrapper class for Plugin/PluginBinary. use in a RAIA fashion to make sure the binary gets unloaded when needed and not before. class PluginHandle { PluginBinary *_b; OfxPlugin *_op; public: PluginHandle(Plugin *p, OFX::Host::Host *_host); virtual ~PluginHandle(); OfxPlugin *getOfxPlugin() { return _op; } OfxPlugin *operator->() { return _op; } }; /// for later struct PluginCacheSupportedApi { std::string api; int minVersion; int maxVersion; APICache::PluginAPICacheI *handler; PluginCacheSupportedApi(const std::string &_api, int _minVersion, int _maxVersion, APICache::PluginAPICacheI *_handler) : api(_api), minVersion(_minVersion), maxVersion(_maxVersion), handler(_handler) { } bool matches(const std::string &_api, int _version) const { if (_api == api && _version >= minVersion && _version <= maxVersion) { return true; } return false; } }; /// Where we keep our plugins. class PluginCache { protected : OFX::Host::Property::PropSpec* _hostSpec; std::list _pluginPath; ///< list of directories to look in std::set _nonrecursePath; ///< list of directories to look in (non-recursively) std::list _pluginDirs; ///< list of directories we found std::list _binaries; ///< all the binaries we know about, we own these std::list _plugins; ///< all the plugins inside the binaries, we don't own these, populated from _binaries std::set _knownBinFiles; PluginBinary *_xmlCurrentBinary; Plugin *_xmlCurrentPlugin; std::list _apiHandlers; void scanDirectory(std::set &foundBinFiles, const std::string &dir, bool recurse); bool _ignoreCache; std::string _cacheVersion; bool _dirty; bool _enablePluginSeek; ///< Turn off to make all seekPluginFile() calls return an empty string static PluginCache* gPluginCachePtr; ///< singleton plugin cache public: /// ctor, which inits _pluginPath to default locations and not much else PluginCache(); /// dtor ~PluginCache(); /// get our plugin cache static PluginCache* getPluginCache(); /// clear our plugin cache static void clearPluginCache(); /// get the list in which plugins are sought const std::list &getPluginPath() { return _pluginPath; } /// was the cache outdated? bool dirty() const { return _dirty; } /// add a file to the plugin path void addFileToPath(const std::string &f, bool recurse=true) { _pluginPath.push_back(f); if (!recurse) { _nonrecursePath.insert(f); } } /// prepend a file to the plugin path void prependFileToPath(const std::string &f, bool recurse=true) { _pluginPath.push_front(f); if (!recurse) { _nonrecursePath.insert(f); } } /// specify which subdirectory of /usr/OFX or equivilant /// (as well as 'Plugins') to look in for plugins. void setPluginHostPath(const std::string &hostId); /// set the version string to write to the cache, /// and also that we expect on cachess read in void setCacheVersion(const std::string &cacheVersion) { _cacheVersion = cacheVersion; } // populate the cache. must call scanPluginFiles() after to check for changes. void readCache(std::istream &is); // seek a particular file on the OFX plugin path std::string seekPluginFile(const std::string &baseName) const; /// Sets behaviour of seekPluginFile(). /// Enable (the default): normal operation; disable: returns an empty string instead void setPluginSeekEnabled(bool enabled) { _enablePluginSeek = enabled; } /// scan for plugins void scanPluginFiles(); // write the plugin cache output file to the given stream void writePluginCache(std::ostream &os) const; // callback function for the XML void elementBeginCallback(void *userData, const XML_Char *name, const XML_Char **attrs); void elementCharCallback(void *userData, const XML_Char *data, int len); void elementEndCallback(void *userData, const XML_Char *name); /// register an API cache handler void registerAPICache(const std::string &api, int min, int max, APICache::PluginAPICacheI *apiCache) { _apiHandlers.push_back(PluginCacheSupportedApi(api, min, max, apiCache)); } /// find the API cache handler for the given api/apiverson APICache::PluginAPICacheI* findApiHandler(const std::string &api, int apiver); /// obtain a list of plugins to walk through const std::list &getPlugins() const { return _plugins; } }; } } #endif