1 /*
2  * This file is part of gtkD.
3  * 
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  * 
10  * gtkD is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  * 
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 module gtkc.Loader;
21 
22 version(Tango)
23 {
24 	import tango.io.Stdout;
25 	import tango.stdc.stringz;
26 
27 	import gtkc.glibtypes;
28 //	private alias char[] string;
29 }
30 else
31 {
32 	import std.stdio;
33 	import std.string;
34 }
35 
36 import gtkc.paths;
37 
38 public struct Linker
39 {
40 	private static void*[string]    loadedLibraries;
41 	private static string[][string] loadFailures;
42 
43 	/*
44 	 * Links the provided symbol
45 	 * Params:
46 	 *     funct     = The function we are linking
47 	 *     symbol    = The name of the symbol to link
48 	 *     libraries = One or more libraries to search for the symbol
49 	 */
50 	public static void link(T)(ref T funct, string symbol, LIBRARY[] libraries ...)
51 	{
52 		funct = cast(T)getSymbol(symbol, libraries);
53 	}
54 
55 	/*
56 	 * Links the provided symbol
57 	 * Params:
58 	 *     funct     = The function we are linking
59 	 *     symbol    = The name of the symbol to link
60 	 *     libraries = One or more libraries to search for the symbol
61 	 */
62 	public static void link(T)(ref T funct, string symbol, string[] libraries ...)
63 	{
64 		funct = cast(T)getSymbol(symbol, libraries);
65 	}
66 
67 	/*
68 	 * Gets a simbol from one of the provided libraries
69 	 * Params:
70 	 *     symbol    = The name of the symbol to link
71 	 *     libraries = One or more libraries to search for the symbol
72 	 */
73 	public static void* getSymbol(string symbol, LIBRARY[] libraries ...)
74 	{
75 		string[] libStr = new string[libraries.length];
76 
77 		foreach (i, library; libraries )
78 		{
79 			libStr[i] = importLibs[library];
80 		}
81 
82 		return getSymbol(symbol, libStr);
83 	}
84 
85 	/*
86 	 * Gets a simbol from one of the provided libraries
87 	 * Params:
88 	 *     symbol    = The name of the symbol to link
89 	 *     libraries = One or more libraries to search for the symbol
90 	 */
91 	public static void* getSymbol(string symbol, string[] libraries ...)
92 	{
93 		void* handle;
94 
95 		foreach ( library; libraries )
96 		{
97 			if( !(library in loadedLibraries) )
98 				loadLibrary(library);
99 
100 			handle = pGetSymbol(loadedLibraries[library], symbol);
101 
102 			if ( handle !is null )
103 				break;
104 		}
105 
106 		if ( handle is null )
107 		{
108 			foreach ( library; libraries )
109 				loadFailures[library] ~= symbol;
110 		}
111 
112 		return handle;
113 	}
114 
115 	/*
116 	 * Loads a library
117 	 */
118 	public static void loadLibrary(string library)
119 	{
120 		void* handle = pLoadLibrary(libPath ~ library);
121 
122 		if ( handle is null )
123 			throw new Exception("Library load failed: " ~ library);
124 
125 		loadedLibraries[library] = handle;
126 	}
127 
128 	/*
129 	 * Unload a library
130 	 */
131 	public static void unloadLibrary(LIBRARY library)
132 	{
133 		unloadLibrary( importLibs[library] );
134 	}
135 
136 	/*
137 	 * Unload a library
138 	 */
139 	public static void unloadLibrary(string library)
140 	{
141 		pUnloadLibrary(loadedLibraries[library]);
142 
143 		loadedLibraries.remove(library);
144 	}
145 
146 	/**
147 	 * Checks if any symbol failed to load
148 	 * Returns: true if ALL symbols are loaded
149 	 */
150 	public static bool isPerfectLoad()
151 	{
152 		return loadFailures.keys.length == 0;
153 	}
154 
155 	/**
156 	 * Gets all libraries loaded.
157 	 * returns: An array with the loaded libraries
158 	 */
159 	public static string[] getLoadLibraries()
160 	{
161 		return loadedLibraries.keys;
162 	}
163 
164 	/**
165 	 * Print all libraries loaded.
166 	 */
167 	public static void dumpLoadLibraries()
168 	{
169 		foreach ( lib; getLoadLibraries() )
170 		{
171 			version(Tango)
172 				Stdout.formatln("Loaded lib = {}", lib);
173 			else
174 				writefln("Loaded lib = %s", lib);
175 		}
176 	}
177 
178 	/**
179 	 * Checks if a library is loaded.
180 	 * Returns: true is the library was loaded sucsessfully.
181 	 */
182 	public static bool isLoaded(LIBRARY library)
183 	{
184 		return isLoaded(importLibs[library]);
185 	}
186 
187 	/**
188 	 * Checks if a library is loaded.
189 	 * Returns: true is the library was loaded sucsessfully.
190 	 */
191 	public static bool isLoaded(string library)
192 	{
193 		if ( library in loadedLibraries )
194 			return true;
195 		else
196 			return false;
197 	}
198 
199 	/**
200 	 * Gets all the failed loads for a specific library.
201 	 * returns: An array of the names hat failed to load for a specific library
202 	 *          or null if none was found
203 	 */
204 	public static string[] getLoadFailures(LIBRARY library)
205 	{
206 		return getLoadFailures(importLibs[library]);
207 	}
208 
209 	/**
210 	 * Gets all the failed loads for a specific library.
211 	 * returns: An array of the names hat failed to load for a specific library
212 	 *          or null if none was found
213 	 */
214 	public static string[] getLoadFailures(string library)
215 	{
216 		if ( library in loadFailures )
217 			return loadFailures[library];
218 		else
219 			return null;
220 	}
221 
222 	/**
223 	 * Print all symbols that failed to load
224 	 */
225 	public static void dumpFailedLoads()
226 	{
227 		foreach ( library; loadedLibraries.keys )
228 		{
229 			foreach ( symbol; getLoadFailures(library) )
230 			{
231 				version(Tango)
232 					Stdout.formatln("failed ({}) {}", library, symbol);
233 				else
234 					writefln("failed (%s) %s", library, symbol);
235 			}
236 		}
237 	}
238 
239 	static ~this()
240 	{
241 		foreach ( library; loadedLibraries.keys )
242 			unloadLibrary(library);
243 	}
244 }
245 
246 // Platform specific implementation below.
247 
248 version(Windows)
249 {
250 	extern(Windows)
251 	{
252 		void* LoadLibraryA(char*);
253 		void* GetProcAddress(void*, char*);
254 		void FreeLibrary(void*);
255 	}
256 
257 	private void* pLoadLibrary(string libraryName)
258 	{
259 		return LoadLibraryA(cast(char*)toStringz(libraryName));
260 	}
261 
262 	private void* pGetSymbol(void* handle, string symbol)
263 	{
264 		return GetProcAddress(handle, cast(char*)toStringz(symbol));
265 	}
266 
267 	private alias FreeLibrary pUnloadLibrary;
268 }
269 else
270 {
271 	extern(C)
272 	{
273 		void* dlopen(char*, int);
274 		char* dlerror();
275 		void* dlsym(void*,char*);
276 		int   dlclose(void*);
277 	}
278 
279 	enum RTLD
280 	{
281 	GDC_BUG_WORKAROUND,
282 		LAZY     = 0x00001,  // Lazy function call binding
283 		NOW      = 0x00002,  // Immediate function call binding
284 		NOLOAD   = 0x00004,  // No object load
285 		DEEPBIND = 0x00008,  //
286 		GLOBAL   = 0x00100   // Make object available to whole program
287 	}
288 
289 	private void* pLoadLibrary(string libraryName, RTLD flag = RTLD.NOW)
290 	{
291 		void* handle = dlopen(cast(char*)toStringz(libraryName), flag);
292 
293 		// clear the error buffer
294 		dlerror();
295 
296 		return handle;
297 	}
298 
299 	private void* pGetSymbol(void* libraryHandle, string symbol)
300 	{
301 		void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol));
302 
303 		// clear the error buffer
304 		dlerror();
305 
306 		return symbolHandle;
307 	}
308 
309 	private int pUnloadLibrary(void* libraryHandle)
310 	{
311 		return dlclose(libraryHandle);
312 	}
313 
314     version(build) version(linux)
315     {
316 			pragma(link, "dl"); // tell dsss to link libdl
317     }
318 }