Index: source/ps/DllLoader.cpp =================================================================== --- source/ps/DllLoader.cpp (revision 7732) +++ source/ps/DllLoader.cpp (revision 7795) @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -19,19 +19,25 @@ #include "DllLoader.h" +#include "lib/timer.h" #include "lib/posix/posix_dlfcn.h" #include "ps/CStr.h" #include "ps/CLogger.h" +#include "ps/GameSetup/Config.h" void* const HANDLE_UNAVAILABLE = (void*)-1; +// directory to search for libraries (optionally set by --libdir at build-time, +// optionally overridden by -libdir at run-time in the test executable); +// if we don't have an explicit libdir then the linker will look in DT_RUNPATH +// (which we set to $ORIGIN) to find it in the executable's directory +#ifdef INSTALLED_LIBDIR +static CStr g_Libdir = STRINGIZE(INSTALLED_LIBDIR); +#else +static CStr g_Libdir = ""; +#endif -// TODO Use path_util instead, get the actual path to the ps_dbg exe and append -// the library name. - -// note: on Linux, lib is prepended to the SO file name; -// we don't use a path with '/' so the linker will look in DT_RUNPATH -// (which we set to $ORIGIN) to find it in the executable's directory +// note: on Linux, lib is prepended to the SO file name #if OS_UNIX static const char* prefix = "lib"; #else @@ -41,15 +47,26 @@ // but for debugging/performance we prefer to use the same version as the app. // note: on Windows, the extension is replaced with .dll by dlopen. #ifndef NDEBUG -static const char* suffix = "_dbg.so"; +static const char* primarySuffix = "_dbg.so"; +static const char* secondarySuffix = ".so"; #else -static const char* suffix = ".so"; +static const char* primarySuffix = ".so"; +static const char* secondarySuffix = "_dbg.so"; #endif // (This class is currently only used by 'Collada' and 'AtlasUI' which follow // the naming/location convention above - it'll need to be changed if we want // to support other DLLs.) +static CStr GenerateFilename(const CStr& name, const CStr& suffix) +{ + CStr n; + if (!g_Libdir.empty()) + n = g_Libdir + "/"; + n += prefix + name + suffix; + return n; +} + DllLoader::DllLoader(const char* name) : m_Name(name), m_Handle(0) { @@ -72,23 +89,39 @@ // postcondition: m_Handle valid or == HANDLE_UNAVAILABLE. if (m_Handle == 0) { - CStr filename = CStr(prefix) + m_Name + suffix; + TIMER(L"LoadDLL"); // we don't really care when relocations take place, but one of // {RTLD_NOW, RTLD_LAZY} must be passed. go with the former because // it is safer and matches the Windows load behavior. const int flags = RTLD_LOCAL|RTLD_NOW; + CStr filename = GenerateFilename(m_Name, primarySuffix); m_Handle = dlopen(filename, flags); + char* primaryError = NULL; + // open failed (mostly likely SO not found) if (! m_Handle) { - const char* error = dlerror(); - if (error) - LOG(CLogger::Error, L"", L"dlopen error: %hs", error); + primaryError = dlerror(); + if (primaryError) + primaryError = strdup(primaryError); // don't get overwritten by next dlopen + + // Try to open the other debug/release version + filename = GenerateFilename(m_Name, secondarySuffix); + m_Handle = dlopen(filename, flags); + } + + // open still failed; report the first error + if (! m_Handle) + { + if (primaryError) + LOGERROR(L"dlopen error: %hs", primaryError); m_Handle = HANDLE_UNAVAILABLE; } + + free(primaryError); } return (m_Handle != HANDLE_UNAVAILABLE); @@ -117,3 +150,8 @@ if (*fptr == NULL) throw PSERROR_DllLoader_SymbolNotFound(); } + +void DllLoader::OverrideLibdir(const CStr& libdir) +{ + g_Libdir = libdir; +}