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 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 46 public static void link(T)(ref T funct, string symbol, LIBRARY[] libraries ...) 47 { 48 funct = cast(T)getSymbol(symbol, libraries); 49 } 50 51 /* 52 * Links the provided symbol 53 * Params: 54 * funct = The function we are linking 55 * symbol = The name of the symbol to link 56 * libraries = One or more libraries to search for the symbol 57 */ 58 public static void link(T)(ref T funct, string symbol, const string[] libraries ...) 59 { 60 funct = cast(T)getSymbol(symbol, libraries); 61 } 62 63 /* 64 * Gets a simbol from one of the provided libraries 65 * Params: 66 * symbol = The name of the symbol to link 67 * libraries = One or more libraries to search for the symbol 68 */ 69 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 70 public static void* getSymbol(string symbol, LIBRARY[] libraries ...) 71 { 72 string[] libStr = new string[libraries.length]; 73 74 foreach (i, library; libraries ) 75 { 76 libStr[i] = importLibs[library]; 77 } 78 79 return getSymbol(symbol, libStr); 80 } 81 82 /* 83 * Gets a simbol from one of the provided libraries 84 * Params: 85 * symbol = The name of the symbol to link 86 * libraries = One or more libraries to search for the symbol 87 */ 88 public static void* getSymbol(string symbol, const string[] libraries ...) 89 { 90 void* handle; 91 92 foreach ( library; libraries ) 93 { 94 if( !(library in loadedLibraries) ) 95 loadLibrary(library); 96 97 handle = pGetSymbol(loadedLibraries[library], symbol); 98 99 if ( handle !is null ) 100 break; 101 } 102 103 if ( handle is null ) 104 { 105 foreach ( library; libraries ) 106 loadFailures[library] ~= symbol; 107 108 handle = &unsupportedSymbol; 109 } 110 111 return handle; 112 } 113 114 /* 115 * Loads a library 116 */ 117 public static void loadLibrary(string library) 118 { 119 void* handle; 120 121 if ( library.canFind(';') ) 122 { 123 foreach ( lib; library.split(';') ) 124 { 125 handle = pLoadLibrary(lib); 126 if ( handle ) 127 break; 128 } 129 } 130 else 131 { 132 handle = pLoadLibrary(library); 133 } 134 135 if ( handle is null ) 136 throw new Exception("Library load failed ("~ library ~"): "~ getErrorMessage()); 137 138 loadedLibraries[library] = handle; 139 } 140 141 /* 142 * Unload a library 143 */ 144 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 145 public static void unloadLibrary(LIBRARY library) 146 { 147 unloadLibrary( importLibs[library] ); 148 } 149 150 /* 151 * Unload a library 152 */ 153 public static void unloadLibrary(string library) 154 { 155 pUnloadLibrary(loadedLibraries[library]); 156 157 loadedLibraries.remove(library); 158 } 159 160 ///Ditto 161 public static void unloadLibrary(const string[] libraries) 162 { 163 foreach ( lib; libraries ) 164 { 165 unloadLibrary(lib); 166 } 167 } 168 169 /** 170 * Checks if any symbol failed to load 171 * Returns: true if ALL symbols are loaded 172 */ 173 public static bool isPerfectLoad() 174 { 175 return loadFailures.keys.length == 0; 176 } 177 178 /** 179 * Gets all libraries loaded. 180 * returns: An array with the loaded libraries 181 */ 182 public static string[] getLoadLibraries() 183 { 184 return loadedLibraries.keys; 185 } 186 187 /** 188 * Print all libraries loaded. 189 */ 190 public static void dumpLoadLibraries() 191 { 192 foreach ( lib; getLoadLibraries() ) 193 { 194 writefln("Loaded lib = %s", lib); 195 } 196 } 197 198 /** 199 * Checks if a library is loaded. 200 * Returns: true is the library was loaded sucsessfully. 201 */ 202 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 203 public static bool isLoaded(LIBRARY library) 204 { 205 return isLoaded(importLibs[library]); 206 } 207 208 /** 209 * Checks if a library is loaded. 210 * Returns: true is the library was loaded sucsessfully. 211 */ 212 public static bool isLoaded(string library) 213 { 214 if ( library in loadedLibraries ) 215 return true; 216 else 217 return false; 218 } 219 220 ///Ditto 221 public static bool isLoaded(const string[] libraries) 222 { 223 return isLoaded(libraries[0]); 224 } 225 226 /** 227 * Gets all the failed loads for a specific library. 228 * returns: An array of the names hat failed to load for a specific library 229 * or null if none was found 230 */ 231 deprecated("Use the LIBRARY_* symbols defined for each package, instead of gtkd.paths.LIBRARY") 232 public static string[] getLoadFailures(LIBRARY library) 233 { 234 return getLoadFailures(importLibs[library]); 235 } 236 237 /** 238 * Gets all the failed loads for a specific library. 239 * returns: An array of the names hat failed to load for a specific library 240 * or null if none was found 241 */ 242 public static string[] getLoadFailures(string library) 243 { 244 if ( library in loadFailures ) 245 return loadFailures[library]; 246 else 247 return null; 248 } 249 250 ///Ditto. 251 public static string[] getLoadFailures(const string[] libraries) 252 { 253 string[] failures; 254 255 foreach ( lib; libraries ) 256 { 257 failures ~= getLoadFailures(lib); 258 } 259 260 return failures; 261 } 262 263 /** 264 * Print all symbols that failed to load 265 */ 266 public static void dumpFailedLoads() 267 { 268 foreach ( library; loadedLibraries.keys ) 269 { 270 foreach ( symbol; getLoadFailures(library) ) 271 { 272 writefln("failed (%s) %s", library, symbol); 273 } 274 } 275 } 276 277 static ~this() 278 { 279 foreach ( library; loadedLibraries.keys ) 280 unloadLibrary(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 (path; splitter(environment.get("PATH"), ';')) 350 { 351 string dllPath = buildNormalizedPath(path, "libgtk-3-0.dll"); 352 353 if ( !exists(dllPath) ) 354 continue; 355 356 if ( checkArchitecture(dllPath) ) 357 return path; 358 } 359 360 return null; 361 } 362 363 private bool checkArchitecture(string dllPath) 364 { 365 import std.stdio; 366 367 File dll = File(dllPath); 368 369 dll.seek(0x3c); 370 int offset = dll.rawRead(new int[1])[0]; 371 372 dll.seek(offset); 373 uint peHead = dll.rawRead(new uint[1])[0]; 374 375 //Not a PE Header. 376 if( peHead != 0x00004550) 377 return false; 378 379 ushort type = dll.rawRead(new ushort[1])[0]; 380 381 version(Win32) 382 { 383 if ( type == IMAGE_FILE_MACHINE_I386 ) 384 return true; 385 } 386 else version(Win64) 387 { 388 if ( type == IMAGE_FILE_MACHINE_AMD64 ) 389 return true; 390 } 391 392 return false; 393 } 394 } 395 else 396 { 397 import core.sys.posix.dlfcn : dlopen, dlerror, dlsym, dlclose, RTLD_NOW, RTLD_GLOBAL; 398 import std.path : buildPath; 399 400 private string lastError; 401 402 version(OSX) 403 { 404 string basePath() 405 { 406 import std.process; 407 408 static string path; 409 410 if (path !is null) 411 return path; 412 413 path = environment.get("GTK_BASEPATH"); 414 if(!path){ 415 path=environment.get("HOMEBREW_ROOT"); 416 if(path){ 417 path=path.buildPath("lib"); 418 } 419 } 420 return path; 421 } 422 } 423 else 424 { 425 enum basePath = ""; 426 } 427 428 private void* pLoadLibrary(string libraryName, int flag = RTLD_NOW) 429 { 430 void* handle = dlopen(cast(char*)toStringz(basePath.buildPath(libraryName)), flag | RTLD_GLOBAL); 431 432 if(!handle){ 433 lastError = dlerror().fromStringz.idup; 434 } 435 436 // clear the error buffer 437 dlerror(); 438 439 return handle; 440 } 441 442 private void* pGetSymbol(void* libraryHandle, string symbol) 443 { 444 void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol)); 445 446 // clear the error buffer 447 dlerror(); 448 449 return symbolHandle; 450 } 451 452 private int pUnloadLibrary(void* libraryHandle) 453 { 454 return dlclose(libraryHandle); 455 } 456 457 private string getErrorMessage() 458 { 459 scope(exit) lastError = null; 460 return lastError; 461 } 462 }