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 }