1 module mutils.plugin.plugin; 2 3 import std.datetime : SysTime; 4 import std.file : timeLastModified; 5 import std.format : format; 6 import std.process : executeShell; 7 import std.stdio : writeln; 8 9 import mutils.plugin.load_lib; 10 import mutils.plugin.safe_executor; 11 import mutils.container.vector; 12 13 struct PluginManager{ 14 version(DigitalMars){ 15 enum compiler="dmd"; 16 }else{ 17 enum compiler="ldc"; 18 } 19 20 Vector!string includes; 21 22 alias PluginFunction=void function(); 23 static struct PluginData{ 24 string path; 25 SysTime lastChange; 26 SharedLibHandle lib; 27 PluginFunction initialize; 28 PluginFunction run; 29 PluginFunction dispose; 30 } 31 32 PluginData[string] plugins; 33 SafeExecutor queue; 34 35 void initialize(){ 36 queue.initialize(); 37 } 38 39 void end(){ 40 queue.end(); 41 foreach(ref plugin;plugins.byValue){ 42 unloadPlugin(&plugin); 43 } 44 } 45 46 bool runPlugin(string pluginPath){ 47 auto plugin=loadPlugin(pluginPath); 48 if(plugin is null){ 49 return false; 50 } 51 return runPlugin(plugin); 52 } 53 54 PluginData* loadPlugin(string pluginPath){ 55 PluginData* plugin; 56 plugin = pluginPath in plugins; 57 SysTime lastChange; 58 try{ 59 lastChange=timeLastModified(pluginPath); 60 }catch(Exception e){ 61 return null; 62 } 63 if(plugin !is null){ 64 if(plugin.lastChange!=lastChange){ 65 unloadPlugin(plugin); 66 bool ok=compilePlugin(plugin); 67 if(!ok)return null; 68 ok=loadPluginData(plugin); 69 if(!ok)return null; 70 plugin.lastChange=lastChange; 71 }else{ 72 return (plugin.run is null)?null:plugin; 73 } 74 }else{ 75 PluginData pluginData; 76 pluginData.path=pluginPath; 77 pluginData.lastChange=lastChange; 78 bool ok=compilePlugin(&pluginData); 79 if(!ok)return null; 80 ok=loadPluginData(&pluginData); 81 if(!ok)return null; 82 plugins[pluginPath]=pluginData; 83 plugin = pluginPath in plugins;//TODO double lookup 84 85 } 86 return plugin; 87 88 } 89 90 bool compilePlugin(PluginData* plugin){ 91 string pluginName=plugin.path~".so"; 92 string command; 93 if(compiler=="dmd"){ 94 command=format("%s %s -g -of%s -boundscheck=on -shared -fPIC -defaultlib=libphobos2.so", compiler, plugin.path, pluginName); 95 }else{ 96 command=format("%s %s -g -of%s -boundscheck=on -shared -relocation-model=pic -defaultlib=/usr/lib/libphobos2-ldc.so.73 -defaultlib=/usr/lib/libdruntime-ldc.so.73", compiler, plugin.path, pluginName); 97 } 98 99 foreach(string include;includes){ 100 command~=" -I"~include; 101 } 102 auto output = executeShell(command); 103 if(output.status != 0){ 104 writeln("Error compiling plugin."); 105 writeln("Output: ", output.output); 106 return false; 107 } 108 return true; 109 } 110 111 static bool loadPluginData(PluginData* plugin){ 112 plugin.lib = LoadSharedLib(plugin.path~".so"); 113 if(plugin.lib is null){ 114 writeln("Error loading plugin: ", GetErrorStr()); 115 return false ; 116 } 117 plugin.initialize=cast(void function())GetSymbol(plugin.lib, "plugin_initialize"); 118 plugin.run= cast(void function())GetSymbol(plugin.lib, "plugin_run"); 119 plugin.dispose= cast(void function())GetSymbol(plugin.lib, "plugin_dispose"); 120 121 if(plugin.run is null){ 122 writeln("Error loading plugin main function."); 123 unloadPlugin(plugin); 124 return false; 125 } 126 if(plugin.initialize !is null){ 127 plugin.initialize(); 128 } 129 return true; 130 131 } 132 133 static void unloadPlugin(PluginData* plugin){ 134 if(plugin.dispose !is null){ 135 plugin.dispose(); 136 } 137 if(plugin.lib !is null){ 138 UnloadSharedLib(plugin.lib); 139 } 140 plugin.initialize=null; 141 plugin.run=null; 142 plugin.dispose=null; 143 } 144 145 146 bool runPlugin(PluginData* plugin){ 147 if(plugin.run is null){ 148 return false; 149 } 150 return queue.execute(plugin.run); 151 } 152 153 } 154 155 unittest{ 156 /*import mutils.plugin.plugin; 157 immutable string include="./source"; 158 import core.thread; 159 160 PluginManager pluginManager; 161 pluginManager.initialize(); 162 pluginManager.includes.add(include); 163 164 string pluginPath="./dll.d"; 165 auto plugin=pluginManager.loadPlugin(pluginPath); 166 if(plugin is null){ 167 writeln("Error compiling plugin"); 168 }else{ 169 pluginManager.runPlugin(plugin); 170 } 171 pluginManager.end();*/ 172 173 }