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 import core.sys.windows.winbase : LoadLibraryA, GetProcAddress, FreeLibrary, GetLastError, FormatMessageA, 245 FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_ARGUMENT_ARRAY; 246 import core.sys.windows.winnt : LANG_NEUTRAL, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386; 247 248 extern(Windows) 249 { 250 int SetDllDirectoryA(const(char)* path); 251 } 252 253 private void* pLoadLibrary(string libraryName) 254 { 255 setDllPath(); 256 257 return LoadLibraryA(cast(char*)toStringz(libraryName)); 258 } 259 260 private void* pGetSymbol(void* handle, string symbol) 261 { 262 return GetProcAddress(handle, cast(char*)toStringz(symbol)); 263 } 264 265 private alias FreeLibrary pUnloadLibrary; 266 267 private string getErrorMessage() 268 { 269 char[] buffer = new char[2048]; 270 buffer[0] = '\0'; 271 272 FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 273 null, 274 GetLastError(), 275 LANG_NEUTRAL, 276 buffer.ptr, 277 cast(uint)buffer.length, 278 cast(char**)["\0".ptr].ptr); 279 280 return buffer.ptr.fromStringz.idup; 281 } 282 283 private void setDllPath() 284 { 285 static bool isSet; 286 287 if ( isSet ) 288 return; 289 290 string gtkPath = getGtkPath(); 291 292 if ( gtkPath.length > 0 ) 293 SetDllDirectoryA((gtkPath~'\0').ptr); 294 295 isSet = true; 296 } 297 298 private string getGtkPath() 299 { 300 import std.algorithm; 301 import std.path; 302 import std.process; 303 import std.file; 304 305 foreach (path; splitter(environment.get("PATH"), ';')) 306 { 307 string dllPath = buildNormalizedPath(path, "libgtk-3-0.dll"); 308 309 if ( !exists(dllPath) ) 310 continue; 311 312 if ( checkArchitecture(dllPath) ) 313 return path; 314 } 315 316 return null; 317 } 318 319 private bool checkArchitecture(string dllPath) 320 { 321 import std.stdio; 322 323 File dll = File(dllPath); 324 325 dll.seek(0x3c); 326 int offset = dll.rawRead(new int[1])[0]; 327 328 dll.seek(offset); 329 uint peHead = dll.rawRead(new uint[1])[0]; 330 331 //Not a PE Header. 332 if( peHead != 0x00004550) 333 return false; 334 335 ushort type = dll.rawRead(new ushort[1])[0]; 336 337 version(Win32) 338 { 339 if ( type == IMAGE_FILE_MACHINE_I386 ) 340 return true; 341 } 342 else version(Win64) 343 { 344 if ( type == IMAGE_FILE_MACHINE_AMD64 ) 345 return true; 346 } 347 348 return false; 349 } 350 } 351 else 352 { 353 import core.sys.posix.dlfcn : dlopen, dlerror, dlsym, dlclose, RTLD_NOW, RTLD_GLOBAL; 354 import std.path : buildPath; 355 356 private string lastError; 357 358 version(OSX) 359 { 360 string basePath() 361 { 362 import std.process; 363 364 static string path; 365 366 if (path !is null) 367 return path; 368 369 path = environment.get("GTK_BASEPATH"); 370 if(!path){ 371 path=environment.get("HOMEBREW_ROOT"); 372 if(path){ 373 path=path.buildPath("lib"); 374 } 375 } 376 return path; 377 } 378 } 379 else 380 { 381 enum basePath = ""; 382 } 383 384 private void* pLoadLibrary(string libraryName, int flag = RTLD_NOW) 385 { 386 void* handle = dlopen(cast(char*)toStringz(basePath.buildPath(libraryName)), flag | RTLD_GLOBAL); 387 388 if(!handle){ 389 lastError = dlerror().fromStringz.idup; 390 } 391 392 // clear the error buffer 393 dlerror(); 394 395 return handle; 396 } 397 398 private void* pGetSymbol(void* libraryHandle, string symbol) 399 { 400 void* symbolHandle = dlsym(libraryHandle, cast(char*)toStringz(symbol)); 401 402 // clear the error buffer 403 dlerror(); 404 405 return symbolHandle; 406 } 407 408 private int pUnloadLibrary(void* libraryHandle) 409 { 410 return dlclose(libraryHandle); 411 } 412 413 private string getErrorMessage() 414 { 415 scope(exit) lastError = null; 416 return lastError; 417 } 418 }