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