1 module pluginadapter; 2 import plugin; 3 import std.stdio; 4 import std..string; 5 import std.file; 6 import std.path; 7 import std.conv:to; 8 import std.process; 9 10 11 string[][] getExportedFunctions() 12 { 13 if(!exists("plugins")) 14 { 15 writeln("\n\nplugins folder not found! Setting up plugins folder"); 16 mkdir("plugins"); 17 return [[]]; 18 } 19 string[][] exporteds; 20 foreach(string file; dirEntries("plugins", SpanMode.shallow)) 21 { 22 if(isDir(file)) 23 { 24 exporteds~= [file.baseName]; 25 foreach(DirEntry plugin; dirEntries(file, "*.d", SpanMode.shallow)) 26 { 27 if(plugin.isFile) 28 { 29 exporteds[exporteds.length - 1]~= plugin.name.stripExtension.baseName.capitalize; 30 } 31 } 32 } 33 } 34 return exporteds; 35 } 36 37 38 version(Posix) 39 { 40 void* _dlopen(const scope char* dllName) 41 { 42 import core.sys.posix.dlfcn : dlopen, RTLD_LAZY; 43 return dlopen(dllName, RTLD_LAZY); 44 } 45 } 46 47 48 49 class PluginAdapter 50 { 51 static Plugin[string] loadedPlugins; 52 53 static Plugin function()[] loadFuncs; 54 55 56 static void*[] dlls; 57 58 version(Windows) 59 { 60 import core.sys.windows.windows; 61 static alias loadDLL = LoadLibraryA; 62 static const (char)* err; 63 static void* symbolLink(void* dll, const (char)* symbolName) 64 { 65 void* ret = GetProcAddress(dll, symbolName); 66 import std.conv:to; 67 if(!ret) 68 err = ("Could not link symbol "~to!string(symbolName)).ptr; 69 return ret; 70 } 71 static const(char)* dllError() 72 { 73 const(char)* ret = err; 74 err = null; 75 return ret; 76 } 77 } 78 else version(Posix) 79 { 80 import core.sys.posix.dlfcn; 81 static alias loadDLL = _dlopen; 82 static alias symbolLink = dlsym; 83 static alias dllError = dlerror; 84 } 85 else pragma(msg, "Current system does not support dll loading! Implement it yourself or open a new issue!"); 86 87 88 version(Posix)static const (char)* getPackName(string packName) 89 { 90 return ("libplugin"~packName~".so").ptr; 91 } 92 version(Windows)static const (char)* getPackName(string packName) 93 { 94 return ("libplugin"~packName~".dll").ptr; 95 } 96 97 static void loadDLLFunc(void* dll, string pluginName) 98 { 99 void* symbol = symbolLink(dll, ("export"~pluginName).ptr); 100 const(char)* error = dllError(); 101 if(error) 102 writeln("Dynamic Library symbol link error: ", to!string(error)); 103 else 104 { 105 Plugin function() getClass = cast(Plugin function())symbol; 106 Plugin p = getClass(); 107 loadedPlugins[p.target] = p; 108 writeln("Loaded plugin '", p.target, "'"); 109 } 110 } 111 112 static bool compilePluginDLL(string[] files, bool optDebug) 113 { 114 import std.algorithm : countUntil; 115 if(countUntil(files, "Package") == -1) 116 { 117 writeln("package not found, creating it."); 118 string pkg; 119 import std.format : format; 120 import std.uni : toLower; 121 pkg = "module "~ files[0]~";\n"; 122 pkg~="import plugin;"; 123 for(size_t i = 1, len = files.length; i < len; i++) 124 { 125 if(files[i] != "Package") //Remember extension was stripped and it is capitalized 126 pkg~="\npublic import " ~ toLower(files[i])~";"; 127 } 128 pkg~= "\n\nmixin PluginLoad;"; 129 std.file.write("plugins/"~files[0]~"/package.d", pkg); 130 } 131 132 string packName = to!string(getPackName(files[0])); 133 string[] compileCommand = 134 [ 135 "dmd", "-shared", 136 "-odplugins/"~files[0]~"/obj", 137 "-ofplugins/"~files[0]~"/"~packName 138 ]; 139 if(optDebug) 140 compileCommand~="-g"; 141 version(X86){compileCommand~= "-m32";} 142 else version(X86_64){compileCommand~= "-m64";} 143 else 144 { 145 writeln("Architecture unknown, omitting architecture command for compiler"); 146 } 147 version(Windows) 148 { 149 //DLL Specific things. 150 string dllDef = "LIBRARY \"" ~ packName~"\"\n"; 151 dllDef~="EXETYPE NT\n"; 152 dllDef~="SUBSYSTEM WINDOWS\n"; 153 dllDef~="CODE SHARED EXECUTE\n"; 154 dllDef~="DATA WRITE"; 155 string dllDefName = "plugins/"~files[0]~"/"~packName.stripExtension~".def"; 156 if(!exists(dllDefName)) 157 std.file.write(dllDefName, dllDef); 158 compileCommand~=dllDefName; 159 160 } 161 compileCommand~= "source/plugin.d"; 162 string path = "plugins/"~files[0]~"/"; 163 for(size_t i = 1, len = files.length; i < len; i++) 164 { 165 compileCommand~= path~toLower(files[i])~".d"; 166 } 167 writeln("Executing command '", compileCommand, "'"); 168 auto ret = execute(compileCommand); 169 if(ret.status != 0) 170 writeln("DMD Log: \n\n\n", ret.output, "\n\n\n"); 171 return true; 172 } 173 174 static void clean(string dllName) 175 { 176 string bName = dllName.stripExtension; 177 with(std.file) 178 { 179 writeln(bName); 180 if(exists(dllName)) 181 remove(dllName); 182 if(exists(bName~".def")) 183 remove(bName~".def"); 184 if(exists(bName~".exp")) 185 remove(bName~".exp"); 186 if(exists(bName~".lib")) 187 remove(bName~".lib"); 188 if(exists(bName~".pdb")) 189 remove(bName~".pdb"); 190 } 191 192 } 193 194 static string[] loadPlugins(string[] plugins, bool regenerate, bool optDebug) 195 { 196 string[][] funcs = getExportedFunctions(); 197 import std.algorithm : countUntil; 198 199 string[] pluginsLoaded; 200 for(ulong i = 0, len = funcs.length; i < len; i++) 201 { 202 if(plugins.length != 0 && countUntil(plugins, funcs[i][0]) == -1) 203 continue; 204 string packName = to!string(getPackName(funcs[i][0])); 205 string path = "plugins/"~funcs[i][0]~"/"; 206 if(!exists(path~packName) || regenerate) 207 { 208 if(!regenerate) 209 { 210 writeln(packName, " does not exists. Invoke dmd? y/n"); 211 if(readln() == "y\n") 212 compilePluginDLL(funcs[i], optDebug); 213 else 214 { 215 writeln("Compile the dll first!"); 216 continue; 217 } 218 } 219 else 220 { 221 clean(path~packName); 222 compilePluginDLL(funcs[i], optDebug); 223 } 224 225 226 } 227 void* dll = loadDLL((path~packName).ptr); 228 if(dll == null) 229 { 230 writeln("Could not load ", path~packName); 231 continue; 232 } 233 else 234 { 235 pluginsLoaded~= funcs[i][0]; 236 dlls~= dll; 237 } 238 for(ulong j = 1, len2 = funcs[i].length; j < len2; j++) 239 { 240 if(funcs[i][j] == "Package") 241 continue; 242 loadDLLFunc(dll, funcs[i][j]); 243 } 244 245 } 246 return pluginsLoaded; 247 } 248 }