diff --git a/doomsday/engine/engine.pro b/doomsday/engine/engine.pro index cb63946f0e..1716d6db82 100644 --- a/doomsday/engine/engine.pro +++ b/doomsday/engine/engine.pro @@ -185,6 +185,7 @@ DENG_HEADERS = \ portable/include/gl_texmanager.h \ portable/include/gl_tga.h \ portable/include/image.h \ + portable/include/library.h \ portable/include/lumpdirectory.h \ portable/include/lumpfile.h \ portable/include/lumpinfo.h \ @@ -311,7 +312,6 @@ DENG_HEADERS = \ unix:!win32 { DENG_PLATFORM_HEADERS += \ $$DENG_UNIX_INCLUDE_DIR/dd_uinit.h \ - $$DENG_UNIX_INCLUDE_DIR/sys_dylib.h \ $$DENG_UNIX_INCLUDE_DIR/sys_path.h INCLUDEPATH += $$DENG_UNIX_INCLUDE_DIR @@ -353,7 +353,6 @@ DENG_UNIX_SOURCES += \ unix/src/dd_uinit.c \ unix/src/sys_audiod_loader.c \ unix/src/sys_console.c \ - unix/src/sys_dylib.c \ unix/src/sys_findfile.c \ unix/src/sys_input.c \ unix/src/sys_path.c @@ -433,6 +432,7 @@ SOURCES += \ portable/src/gl_texmanager.c \ portable/src/gl_tga.c \ portable/src/image.c \ + portable/src/library.c \ portable/src/lumpdirectory.c \ portable/src/lumpfile.c \ portable/src/material.c \ diff --git a/doomsday/engine/portable/include/de_base.h b/doomsday/engine/portable/include/de_base.h index 2cff9145e2..6a9af658c5 100644 --- a/doomsday/engine/portable/include/de_base.h +++ b/doomsday/engine/portable/include/de_base.h @@ -43,6 +43,7 @@ #include "dd_input.h" #include "dd_loop.h" #include "dd_help.h" +#include "library.h" #include "reader.h" #include "writer.h" diff --git a/doomsday/engine/portable/include/library.h b/doomsday/engine/portable/include/library.h new file mode 100644 index 0000000000..99c75014f9 --- /dev/null +++ b/doomsday/engine/portable/include/library.h @@ -0,0 +1,84 @@ +/**\file library.h + *\section License + * License: GPL + * Online License Link: http://www.gnu.org/licenses/gpl.html + * + *\author Copyright © 2006-2011 Jaakko Keränen + *\author Copyright © 2009-2011 Daniel Swanson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * Dynamic Libraries. + * + * These functions provide roughly the same functionality as the ltdl + * library. Since the ltdl library appears to be broken on Mac OS X, + * these will be used instead when loading plugin libraries. + */ + +#ifndef LIBDENG_SYSTEM_UTILS_DYNAMIC_LIBRARY_H +#define LIBDENG_SYSTEM_UTILS_DYNAMIC_LIBRARY_H + +struct library_s; // The library instance (opaque). +typedef struct library_s Library; + +/** + * Initializes the library loader. + */ +void Library_Init(void); + +/** + * Release all resources associated with dynamic libraries. Must be called + * when shutting down the engine. + */ +void Library_Shutdown(void); + +/** + * Defines an additional library @a dir where to look for dynamic libraries. + */ +void Library_AddSearchDir(const char* dir); + +/** + * Looks for dynamic libraries and calls @a func for each one. + */ +int Library_IterateAvailableLibraries(int (*func)(const char* fileName, void* data), void* data); + +/** + * Loads a dynamic library. + * + * @param fileName Name of the library to open. + */ +Library* Library_New(const char* fileName); + +void Library_Delete(Library* lib); + +/** + * Looks up a symbol from the library. + * + * @param symbolName Name of the symbol. + * + * @return @c NULL if the symbol is not defined. Otherwise the address of + * the symbol. + */ +void* Library_Symbol(Library* lib, const char* symbolName); + +/** + * Returns the latest error message. + */ +const char* Library_LastError(void); + +#endif /* LIBDENG_SYSTEM_UTILS_DYNAMIC_LIBRARY_H */ diff --git a/doomsday/engine/portable/src/dd_plugin.c b/doomsday/engine/portable/src/dd_plugin.c index e2445fdbcc..3869e3dfb5 100644 --- a/doomsday/engine/portable/src/dd_plugin.c +++ b/doomsday/engine/portable/src/dd_plugin.c @@ -30,7 +30,7 @@ #ifdef UNIX # include -# include "sys_dylib.h" +# include "library.h" #endif #include "de_base.h" @@ -162,12 +162,12 @@ void* DD_FindEntryPoint(pluginid_t pluginId, const char* fn) } return adr; #elif UNIX - lt_dlhandle* handle = &app.hInstPlug[pluginId-1]; - void* adr = (void*)lt_dlsym(*handle, fn); - if(!adr) + void* addr = Library_Symbol(app.hInstPlug[pluginId - 1], fn); + if(!addr) { - Con_Message("DD_FindEntryPoint: Error locating address of \"%s\" (%s).\n", fn, lt_dlerror()); + Con_Message("DD_FindEntryPoint: Error locating address of \"%s\" (%s).\n", fn, + Library_LastError()); } - return adr; + return addr; #endif } diff --git a/doomsday/engine/portable/src/library.c b/doomsday/engine/portable/src/library.c new file mode 100644 index 0000000000..b32ca00fc1 --- /dev/null +++ b/doomsday/engine/portable/src/library.c @@ -0,0 +1,199 @@ +/**\file library.c + *\section License + * License: GPL + * Online License Link: http://www.gnu.org/licenses/gpl.html + * + *\author Copyright © 2006-2011 Jaakko Keränen + *\author Copyright © 2009-2011 Daniel Swanson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "de_base.h" +#include "de_filesys.h" +#include "m_misc.h" +#include "m_args.h" + +#include +#include +#include +#include +#include + +typedef void* handle_t; + +static filename_t appDir; /// @todo Use ddstring_t +static ddstring_t* lastError; + +struct library_s { + ddstring_t* path; + handle_t handle; +}; + +static void getBundlePath(char* path, size_t len) +{ + if(ArgCheckWith("-libdir", 1)) + { + strncpy(path, ArgNext(), len); + return; + } + + if(ArgCheckWith("-appdir", 1)) + { + dd_snprintf(path, len, "%s/%s", appDir, ArgNext()); + return; + } + +#ifdef MACOSX + // This is the default location where bundles are. + dd_snprintf(path, len, "%s/Bundles", appDir); +#endif +#ifdef UNIX +#ifdef DENG_LIBRARY_DIR + strncpy(path, DENG_LIBRARY_DIR, len); +#else + // Assume they are in the cwd. + strncpy(path, appDir, len); +#endif +#endif +} + +void Library_Init(void) +{ + lastError = Str_NewStd(); + getcwd(appDir, sizeof(appDir)); +} + +void Library_Shutdown(void) +{ + Str_Delete(lastError); lastError = 0; + + /// @todo Unload all remaining libraries. +} + +Library* Library_New(const char *fileName) +{ + Library* lib = 0; + handle_t handle; + filename_t bundlePath; /// @todo Use ddstring_t +#ifdef MACOSX + char* ptr; +#endif + + getBundlePath(bundlePath, FILENAME_T_MAXLEN); + if(bundlePath[strlen(bundlePath) - 1] != '/') + strncat(bundlePath, "/", FILENAME_T_MAXLEN); + +#ifdef MACOSX + strncat(bundlePath, fileName, FILENAME_T_MAXLEN); + strncat(bundlePath, "/", FILENAME_T_MAXLEN); +#endif + + strncat(bundlePath, fileName, FILENAME_T_MAXLEN); + +#ifdef MACOSX + { const char* ext = F_FindFileExtension(bundlePath); + if(ext && stricmp(ext, "dylib") && stricmp(ext, "bundle")) { + // Not a dynamic library... We already know this will fail. + Str_Set(lastError, "not a dynamic library"); + return NULL; + }} + // Get rid of the ".bundle" in the end. + if(NULL != (ptr = strrchr(bundlePath, '.'))) + *ptr = '\0'; +#endif + Str_Clear(lastError); + + handle = dlopen(bundlePath, RTLD_NOW); + if(!handle) + { + Str_Set(lastError, dlerror()); + printf("Library_New: Error opening \"%s\" (%s).\n", bundlePath, Library_LastError()); + return 0; + } + + // Create the Library instance. + lib = calloc(1, sizeof(*lib)); + lib->handle = handle; + lib->path = Str_NewStd(); + Str_Set(lib->path, bundlePath); + return lib; +} + +void Library_Delete(Library *lib) +{ + assert(lib); + if(lib->handle) + { + dlclose(lib->handle); + } + Str_Delete(lib->path); + free(lib); +} + +void* Library_Symbol(Library* lib, const char* symbolName) +{ + assert(lib); + void* ptr = dlsym(lib->handle, symbolName); + if(!ptr) + { + Str_Set(lastError, dlerror()); + } + return ptr; +} + +const char* Library_LastError(void) +{ + return Str_Text(lastError); +} + +void Library_AddSearchDir(const char *dir) +{ + /// @todo Implement this (and use it in the lookup) +} + +int Library_IterateAvailableLibraries(int (*func)(const char *, void *), void *data) +{ + struct dirent* entry = NULL; + filename_t bundlePath; + DIR* dir = NULL; + + // This is the default location where bundles are. + getBundlePath(bundlePath, FILENAME_T_MAXLEN); + + dir = opendir(bundlePath); + if(!dir) + { + Str_Set(lastError, strerror(errno)); + printf("Library_IterateAvailableLibraries: Error opening \"%s\" (%s).\n", + bundlePath, Library_LastError()); + return 0; + } + + while((entry = readdir(dir))) + { +#ifdef MACOSX + // Mac plugins are bundled in a subdir. + if(entry->d_type != DT_REG && entry->d_type != DT_DIR) continue; + if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; +#else + if(entry->d_type != DT_REG) continue; +#endif + if(func(entry->d_name, data)) break; + } + closedir(dir); + return 0; +} diff --git a/doomsday/engine/unix/include/dd_uinit.h b/doomsday/engine/unix/include/dd_uinit.h index af107f3ea7..7ef3c274a2 100644 --- a/doomsday/engine/unix/include/dd_uinit.h +++ b/doomsday/engine/unix/include/dd_uinit.h @@ -30,10 +30,10 @@ #define LIBDENG_UINIT_H #include "dd_pinit.h" -#include "sys_dylib.h" +#include "library.h" typedef struct { - lt_dlhandle hInstPlug[MAX_PLUGS]; + Library* hInstPlug[MAX_PLUGS]; GETGAMEAPI GetGameAPI; /// @c true = We are using a custom user dir specified on the command line. diff --git a/doomsday/engine/unix/src/dd_uinit.c b/doomsday/engine/unix/src/dd_uinit.c index ac4f646abf..64b09e0bf0 100644 --- a/doomsday/engine/unix/src/dd_uinit.c +++ b/doomsday/engine/unix/src/dd_uinit.c @@ -38,7 +38,7 @@ #include #ifdef UNIX -# include "sys_dylib.h" +# include "library.h" #endif #include "de_base.h" @@ -56,6 +56,8 @@ // TYPES ------------------------------------------------------------------- +typedef Library* PluginHandle; + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -74,11 +76,10 @@ application_t app; // CODE -------------------------------------------------------------------- -static lt_dlhandle* findFirstUnusedPluginHandle(application_t* app) +static PluginHandle* findFirstUnusedPluginHandle(application_t* app) { int i; assert(app); - for(i = 0; i < MAX_PLUGS; ++i) { if(!app->hInstPlug[i]) @@ -89,26 +90,27 @@ static lt_dlhandle* findFirstUnusedPluginHandle(application_t* app) static int loadPlugin(application_t* app, const char* pluginPath, void* paramaters) { - lt_dlhandle plugin, *handle; + Library* plugin; + PluginHandle* handle; void (*initializer)(void); filename_t name; assert(app && pluginPath && pluginPath[0]); - plugin = lt_dlopenext(pluginPath); + plugin = Library_New(pluginPath); if(!plugin) { - Con_Message("loadPlugin: Error loading \"%s\" (%s).\n", pluginPath, lt_dlerror()); + Con_Message(" loadPlugin: Error loading \"%s\" (%s).\n", pluginPath, Library_LastError()); return 0; // Continue iteration. } - initializer = lt_dlsym(plugin, "DP_Initialize"); + initializer = Library_Symbol(plugin, "DP_Initialize"); if(!initializer) { // Clearly not a Doomsday plugin. #if _DEBUG - Con_Message("loadPlugin: \"%s\" does not export entrypoint DP_Initialize, ignoring.\n", pluginPath); + Con_Message(" loadPlugin: \"%s\" does not export entrypoint DP_Initialize, ignoring.\n", pluginPath); #endif - lt_dlclose(plugin); + Library_Delete(plugin); return 0; // Continue iteration. } @@ -116,15 +118,15 @@ static int loadPlugin(application_t* app, const char* pluginPath, void* paramate if(!handle) { #if _DEBUG - Con_Message("loadPlugin: Failed acquiring new handle for \"%s\", ignoring.\n", pluginPath); + Con_Message(" loadPlugin: Failed acquiring new handle for \"%s\", ignoring.\n", pluginPath); #endif - lt_dlclose(plugin); + Library_Delete(plugin); return 0; // Continue iteration. } // This seems to be a Doomsday plugin. _splitpath(pluginPath, NULL, NULL, name, NULL); - Con_Printf(" %s\n", name); + Con_Message(" %s\n", name); *handle = plugin; initializer(); @@ -137,7 +139,7 @@ typedef struct { application_t* app; } loadpluginparamaters_t; -static int loadPluginWorker(const char* pluginPath, lt_ptr data) +static int loadPluginWorker(const char* pluginPath, void* data) { loadpluginparamaters_t* params = (loadpluginparamaters_t*) data; filename_t name; @@ -159,15 +161,14 @@ static int loadPluginWorker(const char* pluginPath, lt_ptr data) return 0; // Continue search. } -static boolean unloadPlugin(lt_dlhandle* handle) +static boolean unloadPlugin(PluginHandle* handle) { int result; assert(handle); + if(!*handle) return true; - result = lt_dlclose(*handle); + Library_Delete(*handle); *handle = 0; - if(result != 0) - Con_Printf("unloadPlugin: Error unloading plugin (%s)\n", lt_dlerror()); return result; } @@ -178,20 +179,20 @@ static boolean loadAllPlugins(application_t* app) { assert(app); - Con_Printf("Initializing plugins...\n"); + Con_Message("Initializing plugins...\n"); // Try to load all libraries that begin with libj. { loadpluginparamaters_t params; params.app = app; params.loadingGames = true; - lt_dlforeachfile(NULL, loadPluginWorker, (lt_ptr) ¶ms); + Library_IterateAvailableLibraries(loadPluginWorker, ¶ms); } // Try to load all libraries that begin with libdp. { loadpluginparamaters_t params; params.app = app; params.loadingGames = false; - lt_dlforeachfile(NULL, loadPluginWorker, (lt_ptr) ¶ms); + Library_IterateAvailableLibraries(loadPluginWorker, ¶ms); } return true; } @@ -217,10 +218,10 @@ static int initTimingSystem(void) static int initPluginSystem(void) { // Initialize libtool's dynamic library routines. - lt_dlinit(); + Library_Init(); #ifdef DENG_LIBRARY_DIR // The default directory is defined in the Makefile. For instance, "/usr/local/lib". - lt_dladdsearchdir(DENG_LIBRARY_DIR); + Library_AddSearchDir(DENG_LIBRARY_DIR); #endif return true; } @@ -423,5 +424,5 @@ void DD_Shutdown(void) SDL_Quit(); unloadAllPlugins(&app); - lt_dlexit(); + Library_Shutdown(); } diff --git a/doomsday/engine/unix/src/sys_audiod_loader.c b/doomsday/engine/unix/src/sys_audiod_loader.c index 0f8242628b..3dc9367e52 100644 --- a/doomsday/engine/unix/src/sys_audiod_loader.c +++ b/doomsday/engine/unix/src/sys_audiod_loader.c @@ -32,7 +32,7 @@ #include #ifdef UNIX -# include "sys_dylib.h" +# include "library.h" #endif #include "de_console.h" @@ -64,13 +64,13 @@ audiointerface_cd_t audiodExternalICD; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static lt_dlhandle handle = NULL; +static Library* handle = NULL; // CODE -------------------------------------------------------------------- static void* Imp(const char* fn) { - return lt_dlsym(handle, fn); + return Library_Symbol(handle, fn); } void Sys_ShutdownAudioDriver(void) @@ -83,7 +83,7 @@ void Sys_ShutdownAudioDriver(void) if(audioDriver == &audiodExternal) { - lt_dlclose(handle); + Library_Delete(handle); handle = NULL; } } @@ -168,7 +168,7 @@ audiodriver_t* Sys_LoadAudioDriver(const char* name) #endif // Load the audio driver library and import symbols. - handle = lt_dlopenext(Str_Text(&libPath)); + handle = Library_New(Str_Text(&libPath)); if(NULL != handle) { ad = importExternal();