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(library);
121 
122 		//TODO: A more general way to try more than one version.
123 		if ( handle is null && library == importLibs[LIBRARY.GSV] )
124 			handle = pLoadLibrary(importLibs[LIBRARY.GSV1]);
125 
126 		if ( handle is null )
127 			throw new Exception("Library load failed: " ~ library);
128 
129 		loadedLibraries[library] = handle;
130 	}
131 
132 	/*
133 	 * Unload a library
134 	 */
135 	public static void unloadLibrary(LIBRARY library)
136 	{
137 		unloadLibrary( importLibs[library] );
138 	}
139 
140 	/*
141 	 * Unload a library
142 	 */
143 	public static void unloadLibrary(string library)
144 	{
145 		pUnloadLibrary(loadedLibraries[library]);
146 
147 		loadedLibraries.remove(library);
148 	}
149 
150 	/**
151 	 * Checks if any symbol failed to load
152 	 * Returns: true if ALL symbols are loaded
153 	 */
154 	public static bool isPerfectLoad()
155 	{
156 		return loadFailures.keys.length == 0;
157 	}
158 
159 	/**
160 	 * Gets all libraries loaded.
161 	 * returns: An array with the loaded libraries
162 	 */
163 	public static string[] getLoadLibraries()
164 	{
165 		return loadedLibraries.keys;
166 	}
167 
168 	/**
169 	 * Print all libraries loaded.
170 	 */
171 	public static void dumpLoadLibraries()
172 	{
173 		foreach ( lib; getLoadLibraries() )
174 		{
175 			version(Tango)
176 				Stdout.formatln("Loaded lib = {}", lib);
177 			else
178 				writefln("Loaded lib = %s", lib);
179 		}
180 	}
181 
182 	/**
183 	 * Checks if a library is loaded.
184 	 * Returns: true is the library was loaded sucsessfully.
185 	 */
186 	public static bool isLoaded(LIBRARY library)
187 	{
188 		return isLoaded(importLibs[library]);
189 	}
190 
191 	/**
192 	 * Checks if a library is loaded.
193 	 * Returns: true is the library was loaded sucsessfully.
194 	 */
195 	public static bool isLoaded(string library)
196 	{
197 		if ( library in loadedLibraries )
198 			return true;
199 		else
200 			return false;
201 	}
202 
203 	/**
204 	 * Gets all the failed loads for a specific library.
205 	 * returns: An array of the names hat failed to load for a specific library
206 	 *          or null if none was found
207 	 */
208 	public static string[] getLoadFailures(LIBRARY library)
209 	{
210 		return getLoadFailures(importLibs[library]);
211 	}
212 
213 	/**
214 	 * Gets all the failed loads for a specific library.
215 	 * returns: An array of the names hat failed to load for a specific library
216 	 *          or null if none was found
217 	 */
218 	public static string[] getLoadFailures(string library)
219 	{
220 		if ( library in loadFailures )
221 			return loadFailures[library];
222 		else
223 			return null;
224 	}
225 
226 	/**
227 	 * Print all symbols that failed to load
228 	 */
229 	public static void dumpFailedLoads()
230 	{
231 		foreach ( library; loadedLibraries.keys )
232 		{
233 			foreach ( symbol; getLoadFailures(library) )
234 			{
235 				version(Tango)
236 					Stdout.formatln("failed ({}) {}", library, symbol);
237 				else
238 					writefln("failed (%s) %s", library, symbol);
239 			}
240 		}
241 	}
242 
243 	static ~this()
244 	{
245 		foreach ( library; loadedLibraries.keys )
246 			unloadLibrary(library);
247 	}
248 }
249 
250 // Platform specific implementation below.
251 
252 version(Windows)
253 {
254 	extern(Windows)
255 	{
256 		void* LoadLibraryA(char*);
257 		void* GetProcAddress(void*, char*);
258 		void FreeLibrary(void*);
259 	}
260 
261 	private void* pLoadLibrary(string libraryName)
262 	{
263 		return LoadLibraryA(cast(char*)toStringz(libraryName));
264 	}
265 
266 	private void* pGetSymbol(void* handle, string symbol)
267 	{
268 		return GetProcAddress(handle, cast(char*)toStringz(symbol));
269 	}
270 
271 	private alias FreeLibrary pUnloadLibrary;
272 }
273 else
274 {
275 	extern(C)
276 	{
277 		void* dlopen(char*, int);
278 		char* dlerror();
279 		void* dlsym(void*,char*);
280 		int   dlclose(void*);
281 	}
282 
283 	enum RTLD
284 	{
285 	GDC_BUG_WORKAROUND,
286 		LAZY     = 0x00001,  // Lazy function call binding
287 		NOW      = 0x00002,  // Immediate function call binding
288 		NOLOAD   = 0x00004,  // No object load
289 		DEEPBIND = 0x00008,  //
290 		GLOBAL   = 0x00100   // Make object available to whole program
291 	}
292 
293 	private void* pLoadLibrary(string libraryName, RTLD flag = RTLD.NOW)
294 	{
295 		void* handle = dlopen(cast(char*)toStringz(libraryName), flag);
296 
297 		// clear the error buffer
298 		dlerror();
299 
300 		return handle;
301 	}
302 
303 	private void* pGetSymbol(void* libraryHandle, string symbol)
304 	{
305 		void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol));
306 
307 		// clear the error buffer
308 		dlerror();
309 
310 		return symbolHandle;
311 	}
312 
313 	private int pUnloadLibrary(void* libraryHandle)
314 	{
315 		return dlclose(libraryHandle);
316 	}
317 
318     version(build) version(linux)
319     {
320 			pragma(link, "dl"); // tell dsss to link libdl
321     }
322 }