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 import std.stdio; 23 import std.string; 24 25 import gtkc.paths; 26 27 public struct Linker 28 { 29 private static void*[string] loadedLibraries; 30 private static string[][string] loadFailures; 31 32 extern(C) static void unsupportedSymbol() 33 { 34 throw new Error("The function you are calling is not pressent in your version of GTK+."); 35 } 36 37 /* 38 * Links the provided symbol 39 * Params: 40 * funct = The function we are linking 41 * symbol = The name of the symbol to link 42 * libraries = One or more libraries to search for the symbol 43 */ 44 public static void link(T)(ref T funct, string symbol, LIBRARY[] libraries ...) 45 { 46 funct = cast(T)getSymbol(symbol, libraries); 47 } 48 49 /* 50 * Links the provided symbol 51 * Params: 52 * funct = The function we are linking 53 * symbol = The name of the symbol to link 54 * libraries = One or more libraries to search for the symbol 55 */ 56 public static void link(T)(ref T funct, string symbol, string[] libraries ...) 57 { 58 funct = cast(T)getSymbol(symbol, libraries); 59 } 60 61 /* 62 * Gets a simbol from one of the provided libraries 63 * Params: 64 * symbol = The name of the symbol to link 65 * libraries = One or more libraries to search for the symbol 66 */ 67 public static void* getSymbol(string symbol, LIBRARY[] libraries ...) 68 { 69 string[] libStr = new string[libraries.length]; 70 71 foreach (i, library; libraries ) 72 { 73 libStr[i] = importLibs[library]; 74 } 75 76 return getSymbol(symbol, libStr); 77 } 78 79 /* 80 * Gets a simbol from one of the provided libraries 81 * Params: 82 * symbol = The name of the symbol to link 83 * libraries = One or more libraries to search for the symbol 84 */ 85 public static void* getSymbol(string symbol, string[] libraries ...) 86 { 87 void* handle; 88 89 foreach ( library; libraries ) 90 { 91 if( !(library in loadedLibraries) ) 92 loadLibrary(library); 93 94 handle = pGetSymbol(loadedLibraries[library], symbol); 95 96 if ( handle !is null ) 97 break; 98 } 99 100 if ( handle is null ) 101 { 102 foreach ( library; libraries ) 103 loadFailures[library] ~= symbol; 104 105 handle = &unsupportedSymbol; 106 } 107 108 return handle; 109 } 110 111 /* 112 * Loads a library 113 */ 114 public static void loadLibrary(string library) 115 { 116 void* handle = pLoadLibrary(library); 117 118 //TODO: A more general way to try more than one version. 119 if ( handle is null && library == importLibs[LIBRARY.GSV] ) 120 handle = pLoadLibrary(importLibs[LIBRARY.GSV1]); 121 122 if ( handle is null ) 123 throw new Exception("Library load failed ("~ library ~"): "~ getErrorMessage()); 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 writefln("Loaded lib = %s", lib); 172 } 173 } 174 175 /** 176 * Checks if a library is loaded. 177 * Returns: true is the library was loaded sucsessfully. 178 */ 179 public static bool isLoaded(LIBRARY library) 180 { 181 return isLoaded(importLibs[library]); 182 } 183 184 /** 185 * Checks if a library is loaded. 186 * Returns: true is the library was loaded sucsessfully. 187 */ 188 public static bool isLoaded(string library) 189 { 190 if ( library in loadedLibraries ) 191 return true; 192 else 193 return false; 194 } 195 196 /** 197 * Gets all the failed loads for a specific library. 198 * returns: An array of the names hat failed to load for a specific library 199 * or null if none was found 200 */ 201 public static string[] getLoadFailures(LIBRARY library) 202 { 203 return getLoadFailures(importLibs[library]); 204 } 205 206 /** 207 * Gets all the failed loads for a specific library. 208 * returns: An array of the names hat failed to load for a specific library 209 * or null if none was found 210 */ 211 public static string[] getLoadFailures(string library) 212 { 213 if ( library in loadFailures ) 214 return loadFailures[library]; 215 else 216 return null; 217 } 218 219 /** 220 * Print all symbols that failed to load 221 */ 222 public static void dumpFailedLoads() 223 { 224 foreach ( library; loadedLibraries.keys ) 225 { 226 foreach ( symbol; getLoadFailures(library) ) 227 { 228 writefln("failed (%s) %s", library, symbol); 229 } 230 } 231 } 232 233 static ~this() 234 { 235 foreach ( library; loadedLibraries.keys ) 236 unloadLibrary(library); 237 } 238 } 239 240 // Platform specific implementation below. 241 242 version(Windows) 243 { 244 extern(Windows) 245 { 246 void* LoadLibraryA(char*); 247 void* GetProcAddress(void*, char*); 248 void FreeLibrary(void*); 249 250 uint GetLastError(); 251 uint FormatMessageA(uint, void*, uint, uint, char*, uint, void* /* va_list */); 252 253 int SetDllDirectoryA(const(char)* path); 254 255 enum MessageFormat 256 { 257 FromSystem = 0x00001000, 258 ArgumentArray = 0x00002000 259 } 260 261 enum Lang 262 { 263 Neutral = 0x00 264 } 265 266 enum MachineType : ushort 267 { 268 UNKNOWN = 0x0, 269 AM33 = 0x1d3, 270 AMD64 = 0x8664, 271 ARM = 0x1c0, 272 EBC = 0xebc, 273 I386 = 0x14c, 274 IA64 = 0x200, 275 M32R = 0x9041, 276 MIPS16 = 0x266, 277 MIPSFPU = 0x366, 278 MIPSFPU16 = 0x466, 279 POWERPC = 0x1f0, 280 POWERPCFP = 0x1f1, 281 R4000 = 0x166, 282 SH3 = 0x1a2, 283 SH3DSP = 0x1a3, 284 SH4 = 0x1a6, 285 SH5 = 0x1a8, 286 THUMB = 0x1c2, 287 WCEMIPSV2 = 0x169, 288 } 289 } 290 291 private void* pLoadLibrary(string libraryName) 292 { 293 setDllPath(); 294 295 return LoadLibraryA(cast(char*)toStringz(libraryName)); 296 } 297 298 private void* pGetSymbol(void* handle, string symbol) 299 { 300 return GetProcAddress(handle, cast(char*)toStringz(symbol)); 301 } 302 303 private alias FreeLibrary pUnloadLibrary; 304 305 private string getErrorMessage() 306 { 307 char[] buffer = new char[2048]; 308 309 FormatMessageA( MessageFormat.FromSystem | MessageFormat.ArgumentArray, 310 null, 311 GetLastError(), 312 Lang.Neutral, 313 buffer.ptr, 314 buffer.length, 315 null); 316 317 return buffer.ptr.fromStringz.idup; 318 } 319 320 private void setDllPath() 321 { 322 static bool isSet; 323 324 if ( isSet ) 325 return; 326 327 string gtkPath = getGtkPath(); 328 329 if ( gtkPath.length > 0 ) 330 SetDllDirectoryA((gtkPath~'\0').ptr); 331 332 isSet = true; 333 } 334 335 private string getGtkPath() 336 { 337 import std.algorithm; 338 import std.path; 339 import std.process; 340 import std.file; 341 342 foreach (path; splitter(environment.get("PATH"), ';')) 343 { 344 string dllPath = buildNormalizedPath(path, "libgtk-3-0.dll"); 345 346 if ( !exists(dllPath) ) 347 continue; 348 349 if ( checkArchitecture(dllPath) ) 350 return path; 351 } 352 353 return null; 354 } 355 356 private bool checkArchitecture(string dllPath) 357 { 358 import std.stdio; 359 360 File dll = File(dllPath); 361 362 dll.seek(0x3c); 363 int offset = dll.rawRead(new int[1])[0]; 364 365 dll.seek(offset); 366 uint peHead = dll.rawRead(new uint[1])[0]; 367 368 //Not a PE Header. 369 if( peHead != 0x00004550) 370 return false; 371 372 MachineType type = dll.rawRead(new MachineType[1])[0]; 373 374 version(Win32) 375 { 376 if ( type == MachineType.I386 ) 377 return true; 378 } 379 else version(Win64) 380 { 381 if ( type == MachineType.AMD64 ) 382 return true; 383 } 384 385 return false; 386 } 387 } 388 else 389 { 390 extern(C) 391 { 392 void* dlopen(char*, int); 393 char* dlerror(); 394 void* dlsym(void*,char*); 395 int dlclose(void*); 396 } 397 398 enum RTLD 399 { 400 LAZY = 0x00001, // Lazy function call binding 401 NOW = 0x00002, // Immediate function call binding 402 NOLOAD = 0x00004, // No object load 403 DEEPBIND = 0x00008, // 404 GLOBAL = 0x00100 // Make object available to whole program 405 } 406 407 version(OSX) 408 { 409 string basePath() 410 { 411 import std.process; 412 413 static string path; 414 415 if (path !is null) 416 return path; 417 418 path = environment.get("GTK_BASEPATH", environment.get("HOMEBREW_ROOT", "")); 419 420 return path; 421 } 422 } 423 else 424 { 425 enum basePath = ""; 426 } 427 428 private void* pLoadLibrary(string libraryName, RTLD flag = RTLD.NOW) 429 { 430 void* handle = dlopen(cast(char*)toStringz(basePath~libraryName), flag | RTLD.GLOBAL); 431 432 // clear the error buffer 433 dlerror(); 434 435 return handle; 436 } 437 438 private void* pGetSymbol(void* libraryHandle, string symbol) 439 { 440 void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol)); 441 442 // clear the error buffer 443 dlerror(); 444 445 return symbolHandle; 446 } 447 448 private int pUnloadLibrary(void* libraryHandle) 449 { 450 return dlclose(libraryHandle); 451 } 452 453 private string getErrorMessage() 454 { 455 return dlerror().fromStringz.idup; 456 } 457 }