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 }