TuttleOFX
1
|
00001 /* 00002 * Software License : 00003 * 00004 * Copyright (c) 2007-2009, The Open Effects Association Ltd. All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions are met: 00008 * 00009 * Redistributions of source code must retain the above copyright notice, 00010 * this list of conditions and the following disclaimer. 00011 * Redistributions in binary form must reproduce the above copyright notice, 00012 * this list of conditions and the following disclaimer in the documentation 00013 * and/or other materials provided with the distribution. 00014 * Neither the name The Open Effects Association Ltd, nor the names of its 00015 * contributors may be used to endorse or promote products derived from this 00016 * software without specific prior written permission. 00017 * 00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 00019 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00020 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00021 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 00022 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 00023 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00024 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 00025 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00026 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00027 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00028 */ 00029 00030 // ofx host 00031 #include "property/OfxhSet.hpp" 00032 #include "OfxhBinary.hpp" 00033 #include "OfxhMemory.hpp" 00034 #include "OfxhPluginAPICache.hpp" 00035 #include "OfxhPluginCache.hpp" 00036 #include "OfxhHost.hpp" 00037 #include "OfxhUtilities.hpp" 00038 00039 // ofx 00040 #include <ofxCore.h> 00041 #include <ofxImageEffect.h> 00042 00043 #include <map> 00044 #include <string> 00045 #include <iostream> 00046 #include <fstream> 00047 #include <sstream> 00048 #include <cstring> 00049 #include <cstdlib> 00050 00051 00052 #if defined ( __linux__ ) 00053 00054 #define DIRLIST_SEP_CHARS ":;" 00055 #define DIRSEP "/" 00056 #include <dirent.h> 00057 00058 #define ARCHSTR getArchStr() 00059 00060 #elif defined ( __APPLE__ ) 00061 00062 #define DIRLIST_SEP_CHARS ";:" 00063 #define ARCHSTR "MacOS" 00064 #define DIRSEP "/" 00065 #include <dirent.h> 00066 00067 #elif defined ( WINDOWS ) 00068 #define DIRLIST_SEP_CHARS ";" 00069 #ifdef _WIN64 00070 #define ARCHSTR "win64" 00071 #else 00072 #define ARCHSTR "win32" 00073 #endif 00074 #define DIRSEP "\\" 00075 00076 // CINTERFACE needs to be declared if compiling with VC++ 00077 #include <shlobj.h> 00078 #include <tchar.h> 00079 #ifndef _MSC_VER 00080 #define SHGFP_TYPE_CURRENT 0 00081 #endif 00082 00083 #endif 00084 00085 namespace tuttle { 00086 namespace host { 00087 namespace ofx { 00088 00089 struct PluginCacheSupportedApi 00090 { 00091 APICache::OfxhPluginAPICacheI* _handler; 00092 00093 PluginCacheSupportedApi( APICache::OfxhPluginAPICacheI* handler ) 00094 : _handler( handler ) {} 00095 00096 bool matches( std::string api, int version ) const 00097 { 00098 if( api == _handler->_apiName && version >= _handler->_apiVersionMin && version <= _handler->_apiVersionMax ) 00099 { 00100 return true; 00101 } 00102 return false; 00103 } 00104 }; 00105 00106 00107 #if defined ( __linux__ ) 00108 00109 static const char* getArchStr() 00110 { 00111 if( sizeof( void* ) == 4 ) 00112 { 00113 return "Linux-x86"; 00114 } 00115 else 00116 { 00117 return "Linux-x86-64"; 00118 } 00119 } 00120 00121 #endif 00122 #if defined ( WINDOWS ) 00123 00124 const TCHAR* getStdOFXPluginPath( const std::string& hostId = "Plugins" ) 00125 { 00126 static TCHAR buffer[MAX_PATH]; 00127 static int gotIt = 0; 00128 00129 if( !gotIt ) 00130 { 00131 gotIt = 1; 00132 SHGetFolderPath( NULL, CSIDL_PROGRAM_FILES_COMMON, NULL, SHGFP_TYPE_CURRENT, buffer ); 00133 strncat( buffer, "\\OFX\\Plugins", MAX_PATH ); 00134 } 00135 return buffer; 00136 } 00137 00138 #endif 00139 00140 std::string OFXGetEnv( const char* e ) 00141 { 00142 #if !defined( __GNUC__ ) && defined( WINDOWS ) 00143 std::size_t requiredSize; 00144 getenv_s( &requiredSize, 0, 0, e ); 00145 std::vector<char> buffer( requiredSize ); 00146 if( requiredSize > 0 ) 00147 { 00148 getenv_s( &requiredSize, &buffer.front(), requiredSize, e ); 00149 return &buffer.front(); 00150 } 00151 return ""; 00152 #else 00153 const char* env_value = getenv( e ); 00154 if( env_value == NULL ) 00155 return ""; 00156 return env_value; 00157 #endif 00158 } 00159 00160 OfxhPluginCache::OfxhPluginCache() 00161 : _ignoreCache( false ) 00162 , _cacheVersion( "" ) 00163 , _dirty( false ) 00164 , _enablePluginSeek( true ) 00165 { 00166 std::string s = OFXGetEnv( "OFX_PLUGIN_PATH" ); 00167 00168 while( s.length() ) 00169 { 00170 int spos = int(s.find_first_of( DIRLIST_SEP_CHARS ) ); 00171 00172 std::string path; 00173 00174 if( spos != -1 ) 00175 { 00176 path = s.substr( 0, spos ); 00177 s = s.substr( spos + 1 ); 00178 } 00179 else 00180 { 00181 path = s; 00182 s = ""; 00183 } 00184 00185 _pluginPath.push_back( path ); 00186 } 00187 00188 #if defined( WINDOWS ) 00189 _pluginPath.push_back( getStdOFXPluginPath() ); 00190 _pluginPath.push_back( "C:\\Program Files\\Common Files\\OFX\\Plugins" ); 00191 #endif 00192 #if defined( __linux__ ) 00193 _pluginPath.push_back( "/usr/OFX/Plugins" ); 00194 #endif 00195 #if defined( __APPLE__ ) 00196 _pluginPath.push_back( "/Library/OFX/Plugins" ); 00197 #endif 00198 } 00199 00200 OfxhPluginCache::~OfxhPluginCache() 00201 {} 00202 00203 void OfxhPluginCache::setPluginHostPath( const std::string& hostId ) 00204 { 00205 #if defined( WINDOWS ) 00206 _pluginPath.push_back( getStdOFXPluginPath( hostId ) ); 00207 _pluginPath.push_back( "C:\\Program Files\\Common Files\\OFX\\" + hostId ); 00208 #endif 00209 #if defined( __linux__ ) 00210 _pluginPath.push_back( "/usr/OFX/" + hostId ); 00211 #endif 00212 #if defined( __APPLE__ ) 00213 _pluginPath.push_back( "/Library/OFX/" + hostId ); 00214 #endif 00215 } 00216 00217 void OfxhPluginCache::scanDirectory( std::set<std::string>& foundBinFiles, const std::string& dir, bool recurse ) 00218 { 00219 TUTTLE_LOG_TRACE( "Search plugins" << (recurse?" recursively":"") << " in " << quotes(dir) << "." ); 00220 00221 #if defined ( WINDOWS ) 00222 WIN32_FIND_DATA findData; 00223 HANDLE findHandle; 00224 #else 00225 DIR* d = opendir( dir.c_str() ); 00226 if( !d ) 00227 { 00228 return; 00229 } 00230 #endif 00231 00232 _pluginDirs.push_back( dir ); 00233 00234 #if defined ( UNIX ) 00235 while( dirent * de = readdir( d ) ) 00236 #elif defined ( WINDOWS ) 00237 findHandle = FindFirstFile( ( dir + "\\*" ).c_str(), &findData ); 00238 00239 if( findHandle == INVALID_HANDLE_VALUE ) 00240 { 00241 return; 00242 } 00243 00244 while( 1 ) 00245 #endif 00246 { 00247 #if defined ( UNIX ) 00248 std::string name = de->d_name; 00249 bool isdir = true; 00250 #else 00251 std::string name = findData.cFileName; 00252 bool isdir = ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0; 00253 #endif 00254 if( name.find( ".ofx.bundle" ) != std::string::npos ) 00255 { 00256 const std::string barename = name.substr( 0, name.length() - strlen( ".bundle" ) ); 00257 const std::string bundlepath = dir + DIRSEP + name; 00258 const std::string binpath = bundlepath + DIRSEP "Contents" DIRSEP + ARCHSTR + DIRSEP + barename; 00259 00260 foundBinFiles.insert( binpath ); 00261 00262 if( _knownBinFiles.find( binpath ) == _knownBinFiles.end() ) 00263 { 00264 TUTTLE_LOG_TRACE( "Binary does not exist in the cache: " << quotes(binpath) ); 00265 try 00266 { 00267 // Creating the binary may throw, if there are some missing 00268 // dependencies (like wrong LD_LIBRARY_PATH). 00269 // If it throws, it will not be declared in the plugin cache. 00270 OfxhPluginBinary* pb = new OfxhPluginBinary( binpath, bundlepath, this ); 00271 _binaries.push_back( pb ); 00272 00273 // The binary file has been succesfully loaded, 00274 // so we need to add it into the cache. 00275 setDirty(); // the cache has to be rewrite 00276 _knownBinFiles.insert( binpath ); 00277 00278 TUTTLE_LOG_TRACE( quotes(barename) << " contains " << pb->getNPlugins() << " plugins." ); 00279 00280 // Now, if there is an error that's because the plugin 00281 // is not supported by the host. 00282 for( int j = 0; j < pb->getNPlugins(); ++j ) 00283 { 00284 OfxhPlugin& plug = pb->getPlugin( j ); 00285 APICache::OfxhPluginAPICacheI& api = plug.getApiHandler(); 00286 try 00287 { 00288 api.loadFromPlugin( plug ); 00289 } 00290 catch(... ) 00291 { 00292 TUTTLE_LOG_INFO( "Can't load plugin " 00293 << quotes(plug.getIdentifier()) << " " << plug.getVersionMajor() << "." << plug.getVersionMinor() 00294 << " from file " << quotes(binpath) ); 00295 TUTTLE_LOG_TRACE( boost::current_exception_diagnostic_information() ); 00296 } 00297 } 00298 } 00299 catch(... ) 00300 { 00301 TUTTLE_LOG_INFO( "Can't load plugin file " << quotes(binpath) ); 00302 TUTTLE_LOG_TRACE( boost::current_exception_diagnostic_information() ); 00303 #ifdef __WINDOWS__ 00304 TUTTLE_LOG_TRACE( "PATH: " << std::getenv("PATH") << std::endl ); 00305 #else 00306 TUTTLE_LOG_TRACE( "LD_LIBRARY_PATH: " << std::getenv("LD_LIBRARY_PATH") << std::endl ); 00307 #endif 00308 } 00309 } 00310 else 00311 { 00312 TUTTLE_LOG_TRACE( "Found cached binary " << quotes(binpath) ); 00313 } 00314 } 00315 else 00316 { 00317 if( isdir && ( recurse && name[0] != '@' && name != "." && name != ".." ) ) 00318 { 00319 scanDirectory( foundBinFiles, dir + DIRSEP + name, recurse ); 00320 } 00321 } 00322 #if defined( WINDOWS ) 00323 int rval = FindNextFile( findHandle, &findData ); 00324 00325 if( rval == 0 ) 00326 { 00327 break; 00328 } 00329 #endif 00330 } 00331 00332 #if defined( UNIX ) 00333 closedir( d ); 00334 #else 00335 FindClose( findHandle ); 00336 #endif 00337 } 00338 00339 void OfxhPluginCache::addPlugin( OfxhPlugin* plugin ) 00340 { 00341 // Check if the same plugin has already been loaded 00342 if( _loadedMap.find( plugin->getIdentity() ) == _loadedMap.end() ) 00343 { 00344 _loadedMap[plugin->getIdentity()] = true; 00345 } 00346 else 00347 { 00348 TUTTLE_LOG_INFO( "Plugin: " << plugin->getRawIdentifier() << " loaded twice! (" << plugin->getBinary().getFilePath() << ")" ); 00349 } 00350 _plugins.push_back( plugin ); 00351 00352 if( _pluginsByID.find( plugin->getIdentifier() ) != _pluginsByID.end() ) 00353 { 00354 OfxhPlugin& otherPlugin = *_pluginsByID[plugin->getIdentifier()]; 00355 if( plugin->trumps( otherPlugin ) ) 00356 { 00357 _pluginsByID[plugin->getIdentifier()] = plugin; 00358 } 00359 } 00360 else 00361 { 00362 _pluginsByID[plugin->getIdentifier()] = plugin; 00363 } 00364 } 00365 00366 std::string OfxhPluginCache::seekPluginFile( const std::string& baseName ) const 00367 { 00368 // Exit early if disabled 00369 if( !_enablePluginSeek ) 00370 return ""; 00371 00372 for( std::list<std::string>::const_iterator paths = _pluginDirs.begin(); 00373 paths != _pluginDirs.end(); 00374 ++paths ) 00375 { 00376 std::string candidate = *paths + DIRSEP + baseName; 00377 FILE* f = fopen( candidate.c_str(), "r" ); 00378 if( f ) 00379 { 00380 fclose( f ); 00381 return candidate; 00382 } 00383 } 00384 return ""; 00385 } 00386 00387 void OfxhPluginCache::scanPluginFiles() 00388 { 00389 std::set<std::string> foundBinFiles; 00390 00391 for( std::list<std::string>::iterator paths = _pluginPath.begin(); 00392 paths != _pluginPath.end(); 00393 ++paths ) 00394 { 00395 scanDirectory( foundBinFiles, *paths, _nonrecursePath.find( *paths ) == _nonrecursePath.end() ); 00396 } 00397 00398 OfxhPluginBinaryList::iterator i = _binaries.begin(); 00399 while( i != _binaries.end() ) 00400 { 00401 if( foundBinFiles.find( i->getFilePath() ) == foundBinFiles.end() ) 00402 { 00403 // the binary was in the cache, but was not on the path 00404 setDirty(); 00405 i = _binaries.erase( i ); 00406 } 00407 else 00408 { 00409 const bool binChanged = i->hasBinaryChanged(); 00410 00411 try 00412 { 00413 // the binary was in the cache, but the binary has changed and thus we need to reload 00414 if( binChanged ) 00415 { 00416 i->loadPluginInfo( this ); 00417 setDirty(); 00418 } 00419 00420 for( int j = 0; j < i->getNPlugins(); ++j ) 00421 { 00422 OfxhPlugin& plug = i->getPlugin( j ); 00423 try 00424 { 00425 APICache::OfxhPluginAPICacheI& api = plug.getApiHandler(); 00426 00427 if( binChanged ) 00428 { 00429 api.loadFromPlugin( plug ); // may throw 00430 } 00431 00432 std::string reason; 00433 00434 if( api.pluginSupported( plug, reason ) ) 00435 { 00436 addPlugin( &plug ); 00437 api.confirmPlugin( plug ); 00438 } 00439 else 00440 { 00441 TUTTLE_LOG_INFO( 00442 "Ignoring plugin " << quotes(plug.getIdentifier()) << 00443 ": unsupported, " << reason << "." ); 00444 } 00445 } 00446 catch(...) 00447 { 00448 TUTTLE_LOG_INFO( 00449 "Ignoring plugin " << quotes(plug.getIdentifier()) << 00450 ": loading error." ); 00451 TUTTLE_LOG_TRACE(boost::current_exception_diagnostic_information()); 00452 } 00453 } 00454 } 00455 catch(...) 00456 { 00457 TUTTLE_LOG_INFO( 00458 "Ignoring ofx bundle " << quotes(i->getBundlePath()) << 00459 ": loading error." ); 00460 TUTTLE_LOG_TRACE(boost::current_exception_diagnostic_information()); 00461 } 00462 00463 ++i; 00464 } 00465 } 00466 } 00467 00468 void OfxhPluginCache::clearPluginFiles() 00469 { 00470 setDirty(); 00471 00472 _binaries.clear(); 00473 _plugins.clear(); 00474 _pluginsByID.clear(); 00475 _loadedMap.clear(); 00476 _knownBinFiles.clear(); 00477 } 00478 00479 void OfxhPluginCache::registerAPICache( APICache::OfxhPluginAPICacheI& apiCache ) 00480 { 00481 _apiHandlers.push_back( PluginCacheSupportedApi( &apiCache ) ); 00482 } 00483 00484 APICache::OfxhPluginAPICacheI* OfxhPluginCache::findApiHandler( const std::string& api, int version ) 00485 { 00486 std::list<PluginCacheSupportedApi>::iterator i = _apiHandlers.begin(); 00487 while( i != _apiHandlers.end() ) 00488 { 00489 if( i->matches( api, version ) ) 00490 { 00491 return i->_handler; 00492 } 00493 ++i; 00494 } 00495 return 0; 00496 } 00497 00498 /** 00499 * get the plugin by id. vermaj and vermin can be specified. if they are not it will 00500 * pick the highest found version. 00501 */ 00502 OfxhPlugin* OfxhPluginCache::getPluginById( const std::string& id, int vermaj, int vermin ) 00503 { 00504 if( vermaj == -1 && vermin == -1 ) 00505 return _pluginsByID[id]; 00506 00507 // return the highest version one, which fits the pattern provided 00508 OfxhPlugin* sofar = 0; 00509 00510 for( std::list<OfxhPlugin*>::iterator i = _plugins.begin(); i != _plugins.end(); ++i ) 00511 { 00512 OfxhPlugin* p = *i; 00513 00514 if( p->getIdentifier() != id ) 00515 { 00516 continue; 00517 } 00518 00519 if( vermaj != -1 && p->getVersionMajor() != vermaj ) 00520 { 00521 continue; 00522 } 00523 00524 if( vermin != -1 && p->getVersionMinor() != vermin ) 00525 { 00526 continue; 00527 } 00528 00529 if( !sofar || p->trumps( *sofar ) ) 00530 { 00531 sofar = p; 00532 } 00533 } 00534 return sofar; 00535 } 00536 00537 std::ostream& operator<<( std::ostream& os, const OfxhPluginCache& v ) 00538 { 00539 os << "OfxhPluginCache {" << std::endl; 00540 00541 if( v._pluginsByID.empty() ) 00542 os << "No Plug-ins Found." << std::endl; 00543 00544 os << "________________________________________________________________________________" << std::endl; 00545 for( std::map<std::string, OfxhPlugin*>::const_iterator it = v._pluginsByID.begin(); it != v._pluginsByID.end(); ++it ) 00546 { 00547 os << "Plug-in:" << it->first << std::endl; 00548 os << " " << "Filepath: " << it->second->getBinary().getFilePath(); 00549 os << "(" << it->second->getIndex() << ")" << std::endl; 00550 00551 // os << "Contexts:" << std::endl; 00552 // const std::set<std::string>& contexts = it->second->getContexts(); 00553 // for( std::set<std::string>::const_iterator it2 = contexts.begin(); it2 != contexts.end(); ++it2 ) 00554 // os << " * " << *it2 << std::endl; 00555 // const OfxhImageEffectNodeDescriptor& d = it->second->getDescriptor(); 00556 // os << "Inputs:" << std::endl; 00557 // const std::map<std::string, attribute::OfxhClipImageDescriptor*>& inputs = d.getClips(); 00558 // for( std::map<std::string, attribute::OfxhClipImageDescriptor*>::const_iterator it2 = inputs.begin(); it2 != inputs.end(); ++it2 ) 00559 // os << " * " << it2->first << std::endl; 00560 os << "________________________________________________________________________________" << std::endl; 00561 } 00562 os << "}" << std::endl; 00563 return os; 00564 } 00565 00566 } 00567 } 00568 } 00569 00570