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 gtkd.Loader; 21 22 import std.algorithm : canFind; 23 import std.stdio; 24 import std.string; 25 26 import gtkd.paths; 27 28 public struct Linker 29 { 30 private static void*[string] loadedLibraries; 31 private static string[][string] loadFailures; 32 33 extern(C) static void unsupportedSymbol() 34 { 35 throw new Error("The function you are calling is not pressent in your version of GTK+."); 36 } 37 38 /* 39 * Links the provided symbol 40 * Params: 41 * funct = The function we are linking 42 * symbol = The name of the symbol to link 43 * libraries = One or more libraries to search for the symbol 44 */ 45 public static void link(T)(ref T funct, string symbol, const string[] libraries ...) 46 { 47 funct = cast(T)getSymbol(symbol, libraries); 48 } 49 50 /* 51 * Gets a simbol from one of the provided libraries 52 * Params: 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* getSymbol(string symbol, const string[] libraries ...) 57 { 58 void* handle; 59 60 foreach ( library; libraries ) 61 { 62 if( !(library in loadedLibraries) ) 63 loadLibrary(library); 64 65 handle = pGetSymbol(loadedLibraries[library], symbol); 66 67 if ( handle !is null ) 68 break; 69 } 70 71 if ( handle is null ) 72 { 73 foreach ( library; libraries ) 74 loadFailures[library] ~= symbol; 75 76 handle = &unsupportedSymbol; 77 } 78 79 return handle; 80 } 81 82 /* 83 * Loads a library 84 */ 85 public static void loadLibrary(string library) 86 { 87 void* handle; 88 89 if ( library.canFind(';') ) 90 { 91 foreach ( lib; library.split(';') ) 92 { 93 handle = pLoadLibrary(lib); 94 if ( handle ) 95 break; 96 } 97 } 98 else 99 { 100 handle = pLoadLibrary(library); 101 } 102 103 if ( handle is null ) 104 throw new Exception("Library load failed ("~ library ~"): "~ getErrorMessage()); 105 106 loadedLibraries[library] = handle; 107 } 108 109 /* 110 * Unload a library 111 */ 112 public static void unloadLibrary(string library) 113 { 114 pUnloadLibrary(loadedLibraries[library]); 115 116 loadedLibraries.remove(library); 117 } 118 119 ///Ditto 120 public static void unloadLibrary(const string[] libraries) 121 { 122 foreach ( lib; libraries ) 123 { 124 unloadLibrary(lib); 125 } 126 } 127 128 /** 129 * Checks if any symbol failed to load 130 * Returns: true if ALL symbols are loaded 131 */ 132 public static bool isPerfectLoad() 133 { 134 return loadFailures.keys.length == 0; 135 } 136 137 /** 138 * Gets all libraries loaded. 139 * returns: An array with the loaded libraries 140 */ 141 public static string[] getLoadLibraries() 142 { 143 return loadedLibraries.keys; 144 } 145 146 /** 147 * Print all libraries loaded. 148 */ 149 public static void dumpLoadLibraries() 150 { 151 foreach ( lib; getLoadLibraries() ) 152 { 153 writefln("Loaded lib = %s", lib); 154 } 155 } 156 157 /** 158 * Checks if a library is loaded. 159 * Returns: true is the library was loaded sucsessfully. 160 */ 161 public static bool isLoaded(string library) 162 { 163 if ( library in loadedLibraries ) 164 return true; 165 else 166 return false; 167 } 168 169 ///Ditto 170 public static bool isLoaded(const string[] libraries) 171 { 172 return isLoaded(libraries[0]); 173 } 174 175 /** 176 * Gets all the failed loads for a specific library. 177 * returns: An array of the names hat failed to load for a specific library 178 * or null if none was found 179 */ 180 public static string[] getLoadFailures(string library) 181 { 182 if ( library in loadFailures ) 183 return loadFailures[library]; 184 else 185 return null; 186 } 187 188 ///Ditto. 189 public static string[] getLoadFailures(const string[] libraries) 190 { 191 string[] failures; 192 193 foreach ( lib; libraries ) 194 { 195 failures ~= getLoadFailures(lib); 196 } 197 198 return failures; 199 } 200 201 /** 202 * Print all symbols that failed to load 203 */ 204 public static void dumpFailedLoads() 205 { 206 foreach ( library; loadedLibraries.keys ) 207 { 208 foreach ( symbol; getLoadFailures(library) ) 209 { 210 writefln("failed (%s) %s", library, symbol); 211 } 212 } 213 } 214 215 static ~this() 216 { 217 foreach ( library; loadedLibraries.keys ) 218 unloadLibrary(library); 219 } 220 221 /* 222 * Links the provided symbol 223 * Params: 224 * funct = The function we are linking 225 * symbol = The name of the symbol to link 226 * libraries = One or more libraries to search for the symbol 227 */ 228 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 229 public static void link(T)(ref T funct, string symbol, LIBRARY[] libraries ...) 230 { 231 funct = cast(T)getSymbol(symbol, libraries); 232 } 233 234 /* 235 * Gets a simbol from one of the provided libraries 236 * Params: 237 * symbol = The name of the symbol to link 238 * libraries = One or more libraries to search for the symbol 239 */ 240 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 241 public static void* getSymbol(string symbol, LIBRARY[] libraries ...) 242 { 243 string[] libStr = new string[libraries.length]; 244 245 foreach (i, library; libraries ) 246 { 247 libStr[i] = importLibs[library]; 248 } 249 250 return getSymbol(symbol, libStr); 251 } 252 253 /* 254 * Unload a library 255 */ 256 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 257 public static void unloadLibrary(LIBRARY library) 258 { 259 unloadLibrary( importLibs[library] ); 260 } 261 262 /** 263 * Checks if a library is loaded. 264 * Returns: true is the library was loaded sucsessfully. 265 */ 266 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 267 public static bool isLoaded(LIBRARY library) 268 { 269 return isLoaded(importLibs[library]); 270 } 271 272 /** 273 * Gets all the failed loads for a specific library. 274 * returns: An array of the names hat failed to load for a specific library 275 * or null if none was found 276 */ 277 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 278 public static string[] getLoadFailures(LIBRARY library) 279 { 280 return getLoadFailures(importLibs[library]); 281 } 282 } 283 284 // Platform specific implementation below. 285 286 version(Windows) 287 { 288 import core.sys.windows.winbase : LoadLibraryA, GetProcAddress, FreeLibrary, GetLastError, FormatMessageA, 289 FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_ARGUMENT_ARRAY; 290 import core.sys.windows.winnt : LANG_NEUTRAL, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386; 291 292 extern(Windows) 293 { 294 int SetDllDirectoryA(const(char)* path); 295 } 296 297 private void* pLoadLibrary(string libraryName) 298 { 299 setDllPath(); 300 301 return LoadLibraryA(cast(char*)toStringz(libraryName)); 302 } 303 304 private void* pGetSymbol(void* handle, string symbol) 305 { 306 return GetProcAddress(handle, cast(char*)toStringz(symbol)); 307 } 308 309 private alias FreeLibrary pUnloadLibrary; 310 311 private string getErrorMessage() 312 { 313 char[] buffer = new char[2048]; 314 buffer[0] = '\0'; 315 316 FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 317 null, 318 GetLastError(), 319 LANG_NEUTRAL, 320 buffer.ptr, 321 cast(uint)buffer.length, 322 cast(char**)["\0".ptr].ptr); 323 324 return buffer.ptr.fromStringz.idup; 325 } 326 327 private void setDllPath() 328 { 329 static bool isSet; 330 331 if ( isSet ) 332 return; 333 334 string gtkPath = getGtkPath(); 335 336 if ( gtkPath.length > 0 ) 337 SetDllDirectoryA((gtkPath~'\0').ptr); 338 339 isSet = true; 340 } 341 342 private string getGtkPath() 343 { 344 import std.algorithm; 345 import std.path; 346 import std.process; 347 import std.file; 348 349 foreach ( lib; ["libgtk-3-0.dll", "gtk-3-3.0.dll", "gtk-3.dll"] ) 350 { 351 foreach (path; splitter(environment.get("PATH"), ';')) 352 { 353 string dllPath = buildNormalizedPath(path, lib); 354 355 if ( !exists(dllPath) ) 356 continue; 357 358 if ( checkArchitecture(dllPath) ) 359 return path; 360 } 361 } 362 363 return null; 364 } 365 366 private bool checkArchitecture(string dllPath) 367 { 368 import std.stdio; 369 370 File dll = File(dllPath); 371 372 dll.seek(0x3c); 373 int offset = dll.rawRead(new int[1])[0]; 374 375 dll.seek(offset); 376 uint peHead = dll.rawRead(new uint[1])[0]; 377 378 //Not a PE Header. 379 if( peHead != 0x00004550) 380 return false; 381 382 ushort type = dll.rawRead(new ushort[1])[0]; 383 384 version(Win32) 385 { 386 if ( type == IMAGE_FILE_MACHINE_I386 ) 387 return true; 388 } 389 else version(Win64) 390 { 391 if ( type == IMAGE_FILE_MACHINE_AMD64 ) 392 return true; 393 } 394 395 return false; 396 } 397 } 398 else 399 { 400 import core.sys.posix.dlfcn : dlopen, dlerror, dlsym, dlclose, RTLD_NOW, RTLD_GLOBAL; 401 import std.path : buildPath; 402 403 private string lastError; 404 405 version(OSX) 406 { 407 string basePath() 408 { 409 import std.process; 410 411 static string path; 412 413 if (path !is null) 414 return path; 415 416 path = environment.get("GTK_BASEPATH"); 417 if(!path){ 418 path=environment.get("HOMEBREW_ROOT"); 419 if(path){ 420 path=path.buildPath("lib"); 421 } 422 } 423 return path; 424 } 425 } 426 else 427 { 428 enum basePath = ""; 429 } 430 431 private void* pLoadLibrary(string libraryName, int flag = RTLD_NOW) 432 { 433 void* handle = dlopen(cast(char*)toStringz(basePath.buildPath(libraryName)), flag | RTLD_GLOBAL); 434 435 if(!handle){ 436 lastError = dlerror().fromStringz.idup; 437 } 438 439 // clear the error buffer 440 dlerror(); 441 442 return handle; 443 } 444 445 private void* pGetSymbol(void* libraryHandle, string symbol) 446 { 447 void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol)); 448 449 // clear the error buffer 450 dlerror(); 451 452 return symbolHandle; 453 } 454 455 private int pUnloadLibrary(void* libraryHandle) 456 { 457 return dlclose(libraryHandle); 458 } 459 460 private string getErrorMessage() 461 { 462 scope(exit) lastError = null; 463 return lastError; 464 } 465 }