mirror of
https://github.com/pocoproject/poco.git
synced 2025-12-07 18:24:01 +01:00
fix(SharedLibrary): Missing DLLs not reported #5069
This commit is contained in:
30
.gitignore
vendored
30
.gitignore
vendored
@@ -175,3 +175,33 @@ node_modules
|
||||
# Debug files #
|
||||
##############
|
||||
*.core
|
||||
|
||||
# Build-time symlinks to bundled libraries #
|
||||
############################################
|
||||
Foundation/src/adler32.c
|
||||
Foundation/src/compress.c
|
||||
Foundation/src/crc32.c
|
||||
Foundation/src/crc32.h
|
||||
Foundation/src/deflate.c
|
||||
Foundation/src/deflate.h
|
||||
Foundation/src/gzguts.h
|
||||
Foundation/src/infback.c
|
||||
Foundation/src/inffast.c
|
||||
Foundation/src/inffast.h
|
||||
Foundation/src/inffixed.h
|
||||
Foundation/src/inflate.c
|
||||
Foundation/src/inflate.h
|
||||
Foundation/src/inftrees.c
|
||||
Foundation/src/inftrees.h
|
||||
Foundation/src/pcre2.h
|
||||
Foundation/src/pcre2_*.c
|
||||
Foundation/src/pcre2_*.h
|
||||
Foundation/src/trees.c
|
||||
Foundation/src/trees.h
|
||||
Foundation/src/utf8proc.c
|
||||
Foundation/src/utf8proc.h
|
||||
Foundation/src/utf8proc_data.c
|
||||
Foundation/src/zconf.h
|
||||
Foundation/src/zlib.h
|
||||
Foundation/src/zutil.c
|
||||
Foundation/src/zutil.h
|
||||
|
||||
@@ -64,8 +64,8 @@ public:
|
||||
NObserver(C& object, Handler method, Matcher matcher = nullptr, SyncHandler syncMethod = nullptr):
|
||||
_pObject(&object),
|
||||
_handler(method),
|
||||
_matcher(matcher),
|
||||
_syncHandler(syncMethod)
|
||||
_syncHandler(syncMethod),
|
||||
_matcher(matcher)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
15
Foundation/include/Poco/SharedLibrary.h
Normal file → Executable file
15
Foundation/include/Poco/SharedLibrary.h
Normal file → Executable file
@@ -19,6 +19,8 @@
|
||||
|
||||
|
||||
#include "Poco/Foundation.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
#if defined(hpux) || defined(_hpux)
|
||||
@@ -130,6 +132,19 @@ public:
|
||||
/// SetDllDirectory(). On all other platforms, does not
|
||||
/// do anything and returns false.
|
||||
|
||||
static std::vector<std::string> findMissingDependencies(const std::string& path);
|
||||
/// Parses the shared library at the given path and returns
|
||||
/// a list of dependent libraries that cannot be found.
|
||||
/// If the library itself whose dependencies are checked is not found,
|
||||
/// it returns the library itself.
|
||||
///
|
||||
/// Useful for diagnosing why a library fails to load.
|
||||
/// On Windows, parses the PE import table. On Linux, parses the
|
||||
/// ELF dynamic section. On macOS, parses the Mach-O load commands.
|
||||
///
|
||||
/// Returns an empty vector if the file cannot be parsed or
|
||||
/// on unsupported platforms.
|
||||
|
||||
private:
|
||||
SharedLibrary(const SharedLibrary&);
|
||||
SharedLibrary& operator = (const SharedLibrary&);
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "Poco/Foundation.h"
|
||||
#include "Poco/Mutex.h"
|
||||
#include <dl.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@@ -38,6 +40,7 @@ protected:
|
||||
const std::string& getPathImpl() const;
|
||||
static std::string suffixImpl();
|
||||
static bool setSearchPathImpl(const std::string& path);
|
||||
static std::vector<std::string> findMissingDependenciesImpl(const std::string& path);
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "Poco/Foundation.h"
|
||||
#include "Poco/Mutex.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@@ -43,6 +45,7 @@ protected:
|
||||
const std::string& getPathImpl() const;
|
||||
static std::string suffixImpl();
|
||||
static bool setSearchPathImpl(const std::string& path);
|
||||
static std::vector<std::string> findMissingDependenciesImpl(const std::string& path);
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "Poco/Foundation.h"
|
||||
#include "Poco/Mutex.h"
|
||||
#include <moduleLib.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@@ -38,6 +40,7 @@ protected:
|
||||
const std::string& getPathImpl() const;
|
||||
static std::string suffixImpl();
|
||||
static bool setSearchPathImpl(const std::string& path);
|
||||
static std::vector<std::string> findMissingDependenciesImpl(const std::string& path);
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "Poco/Foundation.h"
|
||||
#include "Poco/Mutex.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@@ -37,6 +39,7 @@ protected:
|
||||
const std::string& getPathImpl() const;
|
||||
static std::string suffixImpl();
|
||||
static bool setSearchPathImpl(const std::string& path);
|
||||
static std::vector<std::string> findMissingDependenciesImpl(const std::string& path);
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
|
||||
@@ -110,4 +110,10 @@ bool SharedLibrary::setSearchPath(const std::string& path)
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibrary::findMissingDependencies(const std::string& path)
|
||||
{
|
||||
return findMissingDependenciesImpl(path);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
@@ -98,4 +98,11 @@ bool SharedLibraryImpl::setSearchPathImpl(const std::string&)
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string&)
|
||||
{
|
||||
// not implemented
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
#include "Poco/SharedLibrary_UNIX.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include <dlfcn.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
#elif defined(__linux__)
|
||||
#include <elf.h>
|
||||
#endif
|
||||
|
||||
|
||||
// Note: cygwin is missing RTLD_LOCAL, set it to 0
|
||||
@@ -54,7 +65,21 @@ void SharedLibraryImpl::loadImpl(const std::string& path, int flags)
|
||||
if (!_handle)
|
||||
{
|
||||
const char* err = dlerror();
|
||||
throw LibraryLoadException(err ? std::string(err) : path);
|
||||
std::string errMsg = err ? std::string(err) : path;
|
||||
|
||||
// Try to identify missing dependencies
|
||||
std::vector<std::string> missingDeps = findMissingDependenciesImpl(path);
|
||||
if (!missingDeps.empty())
|
||||
{
|
||||
errMsg += "; missing dependencies: ";
|
||||
for (size_t i = 0; i < missingDeps.size(); ++i)
|
||||
{
|
||||
if (i > 0) errMsg += ", ";
|
||||
errMsg += missingDeps[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw LibraryLoadException(errMsg);
|
||||
}
|
||||
_path = path;
|
||||
}
|
||||
@@ -134,4 +159,358 @@ bool SharedLibraryImpl::setSearchPathImpl(const std::string&)
|
||||
}
|
||||
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string& path)
|
||||
{
|
||||
std::vector<std::string> missingDeps;
|
||||
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file)
|
||||
{
|
||||
missingDeps.push_back(path);
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
uint32_t magic;
|
||||
file.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
bool is64 = false;
|
||||
bool needsSwap = false;
|
||||
uint32_t ncmds = 0;
|
||||
uint32_t sizeofcmds = 0;
|
||||
|
||||
if (magic == FAT_MAGIC || magic == FAT_CIGAM)
|
||||
{
|
||||
// Fat binary - skip to first architecture
|
||||
bool fatSwap = (magic == FAT_CIGAM);
|
||||
fat_header fatHeader;
|
||||
file.seekg(0);
|
||||
file.read(reinterpret_cast<char*>(&fatHeader), sizeof(fatHeader));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
uint32_t narch = fatSwap ? __builtin_bswap32(fatHeader.nfat_arch) : fatHeader.nfat_arch;
|
||||
if (narch == 0)
|
||||
return missingDeps;
|
||||
|
||||
fat_arch arch;
|
||||
file.read(reinterpret_cast<char*>(&arch), sizeof(arch));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
uint32_t offset = fatSwap ? __builtin_bswap32(arch.offset) : arch.offset;
|
||||
file.seekg(offset);
|
||||
file.read(reinterpret_cast<char*>(&magic), sizeof(magic));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64)
|
||||
{
|
||||
is64 = true;
|
||||
needsSwap = (magic == MH_CIGAM_64);
|
||||
file.seekg(-static_cast<std::streamoff>(sizeof(magic)), std::ios::cur);
|
||||
mach_header_64 header;
|
||||
file.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
ncmds = needsSwap ? __builtin_bswap32(header.ncmds) : header.ncmds;
|
||||
sizeofcmds = needsSwap ? __builtin_bswap32(header.sizeofcmds) : header.sizeofcmds;
|
||||
}
|
||||
else if (magic == MH_MAGIC || magic == MH_CIGAM)
|
||||
{
|
||||
is64 = false;
|
||||
needsSwap = (magic == MH_CIGAM);
|
||||
file.seekg(-static_cast<std::streamoff>(sizeof(magic)), std::ios::cur);
|
||||
mach_header header;
|
||||
file.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
ncmds = needsSwap ? __builtin_bswap32(header.ncmds) : header.ncmds;
|
||||
sizeofcmds = needsSwap ? __builtin_bswap32(header.sizeofcmds) : header.sizeofcmds;
|
||||
}
|
||||
else
|
||||
{
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
// Read all load commands
|
||||
std::vector<char> cmdsBuffer(sizeofcmds);
|
||||
file.read(cmdsBuffer.data(), sizeofcmds);
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
const char* ptr = cmdsBuffer.data();
|
||||
const char* end = ptr + sizeofcmds;
|
||||
|
||||
for (uint32_t i = 0; i < ncmds && ptr < end; ++i)
|
||||
{
|
||||
const load_command* cmd = reinterpret_cast<const load_command*>(ptr);
|
||||
uint32_t cmdType = needsSwap ? __builtin_bswap32(cmd->cmd) : cmd->cmd;
|
||||
uint32_t cmdSize = needsSwap ? __builtin_bswap32(cmd->cmdsize) : cmd->cmdsize;
|
||||
|
||||
if (cmdSize < sizeof(load_command) || ptr + cmdSize > end)
|
||||
break;
|
||||
|
||||
if (cmdType == LC_LOAD_DYLIB || cmdType == LC_LOAD_WEAK_DYLIB || cmdType == LC_REEXPORT_DYLIB)
|
||||
{
|
||||
const dylib_command* dylibCmd = reinterpret_cast<const dylib_command*>(ptr);
|
||||
uint32_t nameOffset = needsSwap ? __builtin_bswap32(dylibCmd->dylib.name.offset) : dylibCmd->dylib.name.offset;
|
||||
|
||||
if (nameOffset < cmdSize)
|
||||
{
|
||||
const char* libPath = ptr + nameOffset;
|
||||
std::string libName(libPath);
|
||||
|
||||
// Check if library can be loaded
|
||||
void* handle = dlopen(libName.c_str(), RTLD_LAZY | RTLD_NOLOAD);
|
||||
if (!handle)
|
||||
{
|
||||
handle = dlopen(libName.c_str(), RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
// Extract just the library name for cleaner output
|
||||
size_t lastSlash = libName.rfind('/');
|
||||
if (lastSlash != std::string::npos)
|
||||
missingDeps.push_back(libName.substr(lastSlash + 1));
|
||||
else
|
||||
missingDeps.push_back(libName);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr += cmdSize;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we get an exception while parsing, just return what we have
|
||||
}
|
||||
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string& path)
|
||||
{
|
||||
std::vector<std::string> missingDeps;
|
||||
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file)
|
||||
{
|
||||
missingDeps.push_back(path);
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
unsigned char ident[EI_NIDENT];
|
||||
file.read(reinterpret_cast<char*>(ident), EI_NIDENT);
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
|
||||
ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3)
|
||||
return missingDeps;
|
||||
|
||||
bool is64 = (ident[EI_CLASS] == ELFCLASS64);
|
||||
file.seekg(0);
|
||||
|
||||
std::vector<std::string> neededLibs;
|
||||
|
||||
if (is64)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
file.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
// Find the dynamic section and string table
|
||||
Elf64_Off shoff = ehdr.e_shoff;
|
||||
uint16_t shnum = ehdr.e_shnum;
|
||||
uint16_t shentsize = ehdr.e_shentsize;
|
||||
|
||||
if (shoff == 0 || shnum == 0)
|
||||
return missingDeps;
|
||||
|
||||
// Read section headers
|
||||
std::vector<Elf64_Shdr> sections(shnum);
|
||||
file.seekg(shoff);
|
||||
for (uint16_t i = 0; i < shnum; ++i)
|
||||
{
|
||||
file.read(reinterpret_cast<char*>(§ions[i]), sizeof(Elf64_Shdr));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
if (shentsize > sizeof(Elf64_Shdr))
|
||||
file.seekg(shentsize - sizeof(Elf64_Shdr), std::ios::cur);
|
||||
}
|
||||
|
||||
// Find .dynamic and .dynstr sections
|
||||
Elf64_Shdr* dynSection = nullptr;
|
||||
Elf64_Shdr* dynstrSection = nullptr;
|
||||
|
||||
for (auto& sec : sections)
|
||||
{
|
||||
if (sec.sh_type == SHT_DYNAMIC)
|
||||
dynSection = &sec;
|
||||
else if (sec.sh_type == SHT_STRTAB && dynstrSection == nullptr)
|
||||
dynstrSection = &sec;
|
||||
}
|
||||
|
||||
// Use the string table linked from .dynamic section
|
||||
if (dynSection && dynSection->sh_link < shnum)
|
||||
dynstrSection = §ions[dynSection->sh_link];
|
||||
|
||||
if (!dynSection || !dynstrSection)
|
||||
return missingDeps;
|
||||
|
||||
// Read string table
|
||||
std::vector<char> strtab(dynstrSection->sh_size);
|
||||
file.seekg(dynstrSection->sh_offset);
|
||||
file.read(strtab.data(), dynstrSection->sh_size);
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
// Read dynamic entries
|
||||
file.seekg(dynSection->sh_offset);
|
||||
size_t numEntries = dynSection->sh_size / sizeof(Elf64_Dyn);
|
||||
for (size_t i = 0; i < numEntries; ++i)
|
||||
{
|
||||
Elf64_Dyn dyn;
|
||||
file.read(reinterpret_cast<char*>(&dyn), sizeof(dyn));
|
||||
if (!file)
|
||||
break;
|
||||
|
||||
if (dyn.d_tag == DT_NULL)
|
||||
break;
|
||||
|
||||
if (dyn.d_tag == DT_NEEDED)
|
||||
{
|
||||
if (dyn.d_un.d_val < strtab.size())
|
||||
neededLibs.push_back(&strtab[dyn.d_un.d_val]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Elf32_Ehdr ehdr;
|
||||
file.read(reinterpret_cast<char*>(&ehdr), sizeof(ehdr));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
Elf32_Off shoff = ehdr.e_shoff;
|
||||
uint16_t shnum = ehdr.e_shnum;
|
||||
uint16_t shentsize = ehdr.e_shentsize;
|
||||
|
||||
if (shoff == 0 || shnum == 0)
|
||||
return missingDeps;
|
||||
|
||||
std::vector<Elf32_Shdr> sections(shnum);
|
||||
file.seekg(shoff);
|
||||
for (uint16_t i = 0; i < shnum; ++i)
|
||||
{
|
||||
file.read(reinterpret_cast<char*>(§ions[i]), sizeof(Elf32_Shdr));
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
if (shentsize > sizeof(Elf32_Shdr))
|
||||
file.seekg(shentsize - sizeof(Elf32_Shdr), std::ios::cur);
|
||||
}
|
||||
|
||||
Elf32_Shdr* dynSection = nullptr;
|
||||
Elf32_Shdr* dynstrSection = nullptr;
|
||||
|
||||
for (auto& sec : sections)
|
||||
{
|
||||
if (sec.sh_type == SHT_DYNAMIC)
|
||||
dynSection = &sec;
|
||||
else if (sec.sh_type == SHT_STRTAB && dynstrSection == nullptr)
|
||||
dynstrSection = &sec;
|
||||
}
|
||||
|
||||
if (dynSection && dynSection->sh_link < shnum)
|
||||
dynstrSection = §ions[dynSection->sh_link];
|
||||
|
||||
if (!dynSection || !dynstrSection)
|
||||
return missingDeps;
|
||||
|
||||
std::vector<char> strtab(dynstrSection->sh_size);
|
||||
file.seekg(dynstrSection->sh_offset);
|
||||
file.read(strtab.data(), dynstrSection->sh_size);
|
||||
if (!file)
|
||||
return missingDeps;
|
||||
|
||||
file.seekg(dynSection->sh_offset);
|
||||
size_t numEntries = dynSection->sh_size / sizeof(Elf32_Dyn);
|
||||
for (size_t i = 0; i < numEntries; ++i)
|
||||
{
|
||||
Elf32_Dyn dyn;
|
||||
file.read(reinterpret_cast<char*>(&dyn), sizeof(dyn));
|
||||
if (!file)
|
||||
break;
|
||||
|
||||
if (dyn.d_tag == DT_NULL)
|
||||
break;
|
||||
|
||||
if (dyn.d_tag == DT_NEEDED)
|
||||
{
|
||||
if (dyn.d_un.d_val < strtab.size())
|
||||
neededLibs.push_back(&strtab[dyn.d_un.d_val]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check which libraries are missing
|
||||
for (const auto& lib : neededLibs)
|
||||
{
|
||||
void* handle = dlopen(lib.c_str(), RTLD_LAZY | RTLD_NOLOAD);
|
||||
if (!handle)
|
||||
{
|
||||
handle = dlopen(lib.c_str(), RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
missingDeps.push_back(lib);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we get an exception while parsing, just return what we have
|
||||
}
|
||||
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string&)
|
||||
{
|
||||
// Unsupported platform - return empty list
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
@@ -139,4 +139,11 @@ bool SharedLibraryImpl::setSearchPathImpl(const std::string&)
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string&)
|
||||
{
|
||||
// not implemented
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
176
Foundation/src/SharedLibrary_WIN32U.cpp
Normal file → Executable file
176
Foundation/src/SharedLibrary_WIN32U.cpp
Normal file → Executable file
@@ -19,6 +19,8 @@
|
||||
#include "Poco/Error.h"
|
||||
#include "Poco/Format.h"
|
||||
#include "Poco/String.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Poco {
|
||||
@@ -56,6 +58,19 @@ void SharedLibraryImpl::loadImpl(const std::string& path, int /*flags*/)
|
||||
DWORD errn = Error::last();
|
||||
std::string err;
|
||||
Poco::format(err, "Error %lu while loading [%s]: [%s]", errn, path, Poco::trim(Error::getMessage(errn)));
|
||||
|
||||
// Try to identify missing dependencies
|
||||
std::vector<std::string> missingDeps = findMissingDependenciesImpl(path);
|
||||
if (!missingDeps.empty())
|
||||
{
|
||||
err += "; missing dependencies: ";
|
||||
for (size_t i = 0; i < missingDeps.size(); ++i)
|
||||
{
|
||||
if (i > 0) err += ", ";
|
||||
err += missingDeps[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw LibraryLoadException(err);
|
||||
}
|
||||
_path = path;
|
||||
@@ -122,4 +137,165 @@ bool SharedLibraryImpl::setSearchPathImpl(const std::string& path)
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> SharedLibraryImpl::findMissingDependenciesImpl(const std::string& path)
|
||||
{
|
||||
std::vector<std::string> missingDeps;
|
||||
|
||||
std::wstring wpath;
|
||||
UnicodeConverter::toUTF16(path, wpath);
|
||||
|
||||
HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
missingDeps.push_back(path);
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (!hMapping)
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
LPVOID pBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!pBase)
|
||||
{
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
auto parseImports = [&]()
|
||||
{
|
||||
PIMAGE_DOS_HEADER pDosHeader = static_cast<PIMAGE_DOS_HEADER>(pBase);
|
||||
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
return;
|
||||
|
||||
PIMAGE_NT_HEADERS pNtHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||
static_cast<BYTE*>(pBase) + pDosHeader->e_lfanew);
|
||||
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
|
||||
return;
|
||||
|
||||
DWORD importDirRVA = 0;
|
||||
DWORD importDirSize = 0;
|
||||
|
||||
if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
|
||||
{
|
||||
PIMAGE_NT_HEADERS32 pNtHeaders32 = reinterpret_cast<PIMAGE_NT_HEADERS32>(pNtHeaders);
|
||||
if (pNtHeaders32->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
{
|
||||
importDirRVA = pNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
importDirSize = pNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
|
||||
}
|
||||
}
|
||||
else if (pNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
||||
{
|
||||
PIMAGE_NT_HEADERS64 pNtHeaders64 = reinterpret_cast<PIMAGE_NT_HEADERS64>(pNtHeaders);
|
||||
if (pNtHeaders64->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
{
|
||||
importDirRVA = pNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
importDirSize = pNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
|
||||
}
|
||||
}
|
||||
|
||||
if (importDirRVA == 0 || importDirSize == 0)
|
||||
return;
|
||||
|
||||
// Convert RVA to file offset by finding the section containing the import directory
|
||||
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
|
||||
DWORD importDirOffset = 0;
|
||||
for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i)
|
||||
{
|
||||
if (importDirRVA >= pSectionHeader[i].VirtualAddress &&
|
||||
importDirRVA < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)
|
||||
{
|
||||
importDirOffset = importDirRVA - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (importDirOffset == 0)
|
||||
return;
|
||||
|
||||
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
|
||||
static_cast<BYTE*>(pBase) + importDirOffset);
|
||||
|
||||
// Get the directory containing the DLL for search path
|
||||
Path dllPath(path);
|
||||
std::string dllDir = dllPath.parent().toString();
|
||||
std::wstring wdllDir;
|
||||
UnicodeConverter::toUTF16(dllDir, wdllDir);
|
||||
|
||||
while (pImportDesc->Name != 0)
|
||||
{
|
||||
// Convert the Name RVA to file offset
|
||||
DWORD nameRVA = pImportDesc->Name;
|
||||
DWORD nameOffset = 0;
|
||||
for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i)
|
||||
{
|
||||
if (nameRVA >= pSectionHeader[i].VirtualAddress &&
|
||||
nameRVA < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)
|
||||
{
|
||||
nameOffset = nameRVA - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nameOffset != 0)
|
||||
{
|
||||
const char* dllName = reinterpret_cast<const char*>(static_cast<BYTE*>(pBase) + nameOffset);
|
||||
|
||||
// Skip API set DLLs - these are virtual DLLs resolved by Windows at runtime
|
||||
// They start with "api-" or "ext-" (per Microsoft documentation)
|
||||
if (_strnicmp(dllName, "api-", 4) == 0 || _strnicmp(dllName, "ext-", 4) == 0)
|
||||
{
|
||||
++pImportDesc;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the DLL can be found
|
||||
std::wstring wDllName;
|
||||
UnicodeConverter::toUTF16(std::string(dllName), wDllName);
|
||||
|
||||
// Try to find the DLL using the standard search order
|
||||
HMODULE hMod = LoadLibraryExW(wDllName.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
||||
if (!hMod)
|
||||
{
|
||||
// Try in the same directory as the original DLL
|
||||
std::wstring fullPath = wdllDir + wDllName;
|
||||
hMod = LoadLibraryExW(fullPath.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE);
|
||||
}
|
||||
|
||||
if (!hMod)
|
||||
{
|
||||
missingDeps.push_back(dllName);
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeLibrary(hMod);
|
||||
}
|
||||
}
|
||||
|
||||
++pImportDesc;
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
parseImports();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we get an exception while parsing, just return what we have
|
||||
}
|
||||
|
||||
UnmapViewOfFile(pBase);
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
|
||||
return missingDeps;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Poco
|
||||
|
||||
@@ -5,6 +5,7 @@ file(GLOB SRCS_G "src/*.cpp")
|
||||
file(GLOB SRCS_G_REMOVE
|
||||
src/TestApp.cpp
|
||||
src/TestLibrary.cpp
|
||||
src/TestLibraryMissingDeps.cpp
|
||||
src/TestPlugin.cpp
|
||||
)
|
||||
list(REMOVE_ITEM SRCS_G ${SRCS_G_REMOVE})
|
||||
@@ -20,6 +21,7 @@ POCO_SOURCES_AUTO_PLAT(TEST_SRCS OFF
|
||||
)
|
||||
|
||||
add_executable(Foundation-testrunner ${TEST_SRCS})
|
||||
set_target_properties(Foundation-testrunner PROPERTIES DEBUG_POSTFIX "d")
|
||||
if(ANDROID)
|
||||
add_test(
|
||||
NAME Foundation
|
||||
@@ -54,20 +56,52 @@ endif()
|
||||
# TestApp
|
||||
add_executable(TestApp src/TestApp.cpp)
|
||||
# The test is run in the runtime directory. So the TestApp is built there too because it is used by the tests
|
||||
set_target_properties(TestApp PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
set_target_properties(TestApp PROPERTIES
|
||||
DEBUG_POSTFIX "d"
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
)
|
||||
target_link_libraries(TestApp PUBLIC Poco::Foundation)
|
||||
|
||||
# TestLibrary
|
||||
add_library(TestLibrary SHARED src/TestLibrary.cpp src/TestPlugin.cpp src/TestPlugin.h)
|
||||
set_target_properties(TestLibrary PROPERTIES DEBUG_POSTFIX "d")
|
||||
set_target_properties(TestLibrary PROPERTIES RELEASE_POSTFIX "")
|
||||
set_target_properties(TestLibrary PROPERTIES CMAKE_MINSIZEREL_POSTFIX "")
|
||||
set_target_properties(TestLibrary PROPERTIES CMAKE_RELWITHDEBINFO_POSTFIX "")
|
||||
# The test requires the library named TestLibrary. By default it is prefixed with lib.
|
||||
set_target_properties(TestLibrary PROPERTIES PREFIX "")
|
||||
set_target_properties(TestLibrary PROPERTIES
|
||||
DEBUG_POSTFIX "d"
|
||||
# The test requires the library named TestLibrary. By default it is prefixed with lib.
|
||||
PREFIX ""
|
||||
)
|
||||
|
||||
# The test is run in the runtime directory. So the TestLibrary is built there too because it is used by the tests
|
||||
set_target_properties(TestLibrary PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
target_link_libraries(TestLibrary PUBLIC Poco::Foundation)
|
||||
|
||||
add_dependencies(Foundation-testrunner TestApp TestLibrary)
|
||||
|
||||
# TestLibraryMissingDeps - a library with missing dependencies for testing findMissingDependencies
|
||||
# Build a stub library that TestLibraryMissingDeps links against
|
||||
# The test will delete it at runtime to simulate a missing dependency
|
||||
add_library(NonExistentLibrary SHARED src/NonExistentStub.c)
|
||||
set_target_properties(NonExistentLibrary PROPERTIES
|
||||
DEBUG_POSTFIX "d"
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
|
||||
)
|
||||
# For multi-config generators (Visual Studio), set per-configuration archive output
|
||||
foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
|
||||
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
|
||||
set_target_properties(NonExistentLibrary PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER}}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_library(TestLibraryMissingDeps SHARED src/TestLibraryMissingDeps.cpp)
|
||||
set_target_properties(TestLibraryMissingDeps PROPERTIES
|
||||
DEBUG_POSTFIX "d"
|
||||
PREFIX ""
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
)
|
||||
target_link_libraries(TestLibraryMissingDeps PUBLIC Poco::Foundation NonExistentLibrary)
|
||||
add_dependencies(TestLibraryMissingDeps NonExistentLibrary)
|
||||
|
||||
add_dependencies(Foundation-testrunner TestLibraryMissingDeps)
|
||||
|
||||
@@ -10,5 +10,7 @@ projects:
|
||||
$(MAKE) -f Makefile-Driver $(MAKECMDGOALS)
|
||||
ifneq ($(LINKMODE),STATIC)
|
||||
$(MAKE) -f Makefile-TestLibrary $(MAKECMDGOALS)
|
||||
$(MAKE) -f Makefile-NonExistentLibrary $(MAKECMDGOALS)
|
||||
$(MAKE) -f Makefile-TestLibraryMissingDeps $(MAKECMDGOALS)
|
||||
endif
|
||||
$(MAKE) -f Makefile-TestApp $(MAKECMDGOALS)
|
||||
|
||||
15
Foundation/testsuite/Makefile-NonExistentLibrary
Normal file
15
Foundation/testsuite/Makefile-NonExistentLibrary
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile
|
||||
#
|
||||
# Makefile for NonExistentLibrary stub (used by TestLibraryMissingDeps)
|
||||
#
|
||||
|
||||
include $(POCO_BASE)/build/rules/global
|
||||
|
||||
objects = NonExistentStub
|
||||
|
||||
target = NonExistentLibrary
|
||||
target_version = 1
|
||||
target_libs =
|
||||
|
||||
include $(POCO_BASE)/build/rules/lib
|
||||
15
Foundation/testsuite/Makefile-TestLibraryMissingDeps
Normal file
15
Foundation/testsuite/Makefile-TestLibraryMissingDeps
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile
|
||||
#
|
||||
# Makefile for TestLibraryMissingDeps
|
||||
#
|
||||
|
||||
include $(POCO_BASE)/build/rules/global
|
||||
|
||||
objects = TestLibraryMissingDeps
|
||||
|
||||
target = TestLibraryMissingDeps
|
||||
target_version = 1
|
||||
target_libs = PocoFoundation NonExistentLibrary
|
||||
|
||||
include $(POCO_BASE)/build/rules/dylib
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/Format.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include "TestPlugin.h"
|
||||
|
||||
|
||||
@@ -28,6 +29,7 @@ using Poco::NotFoundException;
|
||||
using Poco::InvalidAccessException;
|
||||
using Poco::Path;
|
||||
using Poco::File;
|
||||
using Poco::Environment;
|
||||
|
||||
|
||||
ClassLoaderTest::ClassLoaderTest(const std::string& name): CppUnit::TestCase(name)
|
||||
@@ -196,27 +198,33 @@ void ClassLoaderTest::testClassLoader3()
|
||||
|
||||
std::string ClassLoaderTest::getFullName(const std::string& libName)
|
||||
{
|
||||
std::string name = Path::expand("$POCO_BASE");
|
||||
char c = Path::separator();
|
||||
std::string OSNAME = Path::expand("$OSNAME");
|
||||
std::string OSARCH = Path::expand("$OSARCH");
|
||||
name.append(1, c)
|
||||
.append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c))
|
||||
.append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c))
|
||||
.append(libName).append(SharedLibrary::suffix());
|
||||
// Get the directory where the test executable is located
|
||||
Path selfPath(Path::self());
|
||||
selfPath.setFileName("");
|
||||
std::string name = selfPath.toString() + libName + SharedLibrary::suffix();
|
||||
|
||||
// CMake
|
||||
if (!File(name).exists())
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
|
||||
// Fallback: make build layout
|
||||
std::string pocoBase = Environment::get("POCO_BASE", "");
|
||||
if (!pocoBase.empty())
|
||||
{
|
||||
name = Path::expand("$POCO_BASE");
|
||||
name.append(Poco::format("%ccmake-build%cbin%c", c, c, c))
|
||||
char c = Path::separator();
|
||||
std::string OSNAME = Environment::get("OSNAME", "");
|
||||
std::string OSARCH = Environment::get("OSARCH", "");
|
||||
name = pocoBase;
|
||||
name.append(1, c)
|
||||
.append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c))
|
||||
.append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c))
|
||||
.append(libName).append(SharedLibrary::suffix());
|
||||
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
}
|
||||
|
||||
if (!File(name).exists())
|
||||
name = libName + SharedLibrary::suffix();
|
||||
|
||||
return name;
|
||||
// Last resort: just the library name
|
||||
return libName + SharedLibrary::suffix();
|
||||
}
|
||||
|
||||
|
||||
|
||||
15
Foundation/testsuite/src/NonExistentStub.c
Normal file
15
Foundation/testsuite/src/NonExistentStub.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* NonExistentStub.c
|
||||
*
|
||||
* Stub library that provides nonExistentFunction.
|
||||
* This library is built and then deleted to create a missing dependency
|
||||
* for TestLibraryMissingDeps.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int nonExistentFunction()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
161
Foundation/testsuite/src/SharedLibraryTest.cpp
Normal file → Executable file
161
Foundation/testsuite/src/SharedLibraryTest.cpp
Normal file → Executable file
@@ -15,7 +15,14 @@
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/Glob.h"
|
||||
#include "Poco/Format.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
using Poco::SharedLibrary;
|
||||
@@ -24,6 +31,8 @@ using Poco::LibraryLoadException;
|
||||
using Poco::LibraryAlreadyLoadedException;
|
||||
using Poco::Path;
|
||||
using Poco::File;
|
||||
using Poco::Glob;
|
||||
using Poco::Environment;
|
||||
|
||||
|
||||
typedef int (*GimmeFiveFunc)();
|
||||
@@ -129,30 +138,149 @@ void SharedLibraryTest::testSharedLibrary3()
|
||||
}
|
||||
|
||||
|
||||
void SharedLibraryTest::testMissingDependencies()
|
||||
{
|
||||
// Test with a valid library - should return empty list (all dependencies found)
|
||||
std::string libraryPath = getFullName("TestLibrary");
|
||||
assertTrue (File(libraryPath).exists());
|
||||
std::vector<std::string> missing = SharedLibrary::findMissingDependencies(libraryPath);
|
||||
// TestLibrary should have all its dependencies available
|
||||
for (const auto& dep : missing)
|
||||
std::cout << "TestLibrary missing: " << dep << std::endl;
|
||||
assertTrue (missing.empty());
|
||||
|
||||
// Test with non-existent file - should return the file itself as missing
|
||||
missing = SharedLibrary::findMissingDependencies("NonexistentFile.dll");
|
||||
assertTrue (missing.size() == 1);
|
||||
assertTrue (missing[0] == "NonexistentFile.dll");
|
||||
|
||||
// Test with empty path - should return empty path as missing
|
||||
missing = SharedLibrary::findMissingDependencies("");
|
||||
assertTrue (missing.size() == 1);
|
||||
assertTrue (missing[0].empty());
|
||||
|
||||
// Test with a library that has missing dependencies
|
||||
std::string missingDepsLib = getFullName("TestLibraryMissingDeps");
|
||||
if (File(missingDepsLib).exists())
|
||||
{
|
||||
// Delete all NonExistentLibrary files (including versioned ones and symlinks) to ensure the test is valid
|
||||
// Try both with and without "lib" prefix since CMake and make builds differ
|
||||
// Also check the lib/ directory for make builds (libraries have absolute paths embedded)
|
||||
std::set<std::string> libFiles;
|
||||
Path selfPath(Path::self());
|
||||
selfPath.setFileName("");
|
||||
std::string baseDir = selfPath.toString();
|
||||
std::replace(baseDir.begin(), baseDir.end(), '\\', '/');
|
||||
Glob::glob(baseDir + "NonExistentLibrary*", libFiles);
|
||||
Glob::glob(baseDir + "libNonExistentLibrary*", libFiles);
|
||||
|
||||
// For make builds, also check POCO_BASE/lib/OSNAME/OSARCH/
|
||||
std::string pocoBase = Environment::get("POCO_BASE", "");
|
||||
if (!pocoBase.empty())
|
||||
{
|
||||
std::string OSNAME = Environment::get("OSNAME", "");
|
||||
std::string OSARCH = Environment::get("OSARCH", "");
|
||||
std::string libDir = pocoBase + "/lib/" + OSNAME + "/" + OSARCH + "/";
|
||||
Glob::glob(libDir + "NonExistentLibrary*", libFiles);
|
||||
Glob::glob(libDir + "libNonExistentLibrary*", libFiles);
|
||||
}
|
||||
|
||||
for (const auto& libFile : libFiles)
|
||||
{
|
||||
File f(libFile);
|
||||
if (f.exists() || f.isLink())
|
||||
f.remove();
|
||||
}
|
||||
|
||||
missing = SharedLibrary::findMissingDependencies(missingDepsLib);
|
||||
// Should report NonExistentLibrary as missing
|
||||
assertTrue (!missing.empty());
|
||||
bool foundNonExistent = false;
|
||||
for (const auto& dep : missing)
|
||||
{
|
||||
if (dep.find("NonExistentLibrary") != std::string::npos)
|
||||
{
|
||||
foundNonExistent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue (foundNonExistent);
|
||||
for (const auto& dep : missing)
|
||||
std::cout << "Missing library: " << dep;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string SharedLibraryTest::getFullName(const std::string& libName)
|
||||
{
|
||||
// make
|
||||
std::string name = Path::expand("$POCO_BASE");
|
||||
char c = Path::separator();
|
||||
std::string OSNAME = Path::expand("$OSNAME");
|
||||
std::string OSARCH = Path::expand("$OSARCH");
|
||||
name.append(1, c)
|
||||
.append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c))
|
||||
.append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c))
|
||||
.append(libName).append(SharedLibrary::suffix());
|
||||
// Get the directory where the test executable is located
|
||||
Path selfPath(Path::self());
|
||||
selfPath.setFileName("");
|
||||
std::string name = selfPath.toString() + libName + SharedLibrary::suffix();
|
||||
|
||||
// CMake
|
||||
if (!File(name).exists())
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
|
||||
// Fallback: make build layout
|
||||
std::string pocoBase = Environment::get("POCO_BASE", "");
|
||||
if (!pocoBase.empty())
|
||||
{
|
||||
name = Path::expand("$POCO_BASE");
|
||||
name.append(Poco::format("%ccmake-build%cbin%c", c, c, c))
|
||||
char c = Path::separator();
|
||||
std::string OSNAME = Environment::get("OSNAME", "");
|
||||
std::string OSARCH = Environment::get("OSARCH", "");
|
||||
name = pocoBase;
|
||||
name.append(1, c)
|
||||
.append(Poco::format("Foundation%ctestsuite%cbin%c", c, c, c))
|
||||
.append(Poco::format("%s%c%s%c", OSNAME, c, OSARCH, c))
|
||||
.append(libName).append(SharedLibrary::suffix());
|
||||
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
}
|
||||
|
||||
if (!File(name).exists())
|
||||
name = libName + SharedLibrary::suffix();
|
||||
// Last resort: just the library name
|
||||
return libName + SharedLibrary::suffix();
|
||||
}
|
||||
|
||||
return name;
|
||||
|
||||
std::string SharedLibraryTest::getLibFullName(const std::string& libName)
|
||||
{
|
||||
std::string suffix = SharedLibrary::suffix();
|
||||
|
||||
// Get the directory where the test executable is located
|
||||
Path selfPath(Path::self());
|
||||
selfPath.setFileName("");
|
||||
|
||||
#if defined(_WIN32)
|
||||
std::string name = selfPath.toString() + libName + suffix;
|
||||
#else
|
||||
// On Unix, shared libraries have a "lib" prefix
|
||||
std::string name = selfPath.toString() + "lib" + libName + suffix;
|
||||
#endif
|
||||
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
|
||||
#if !defined(_WIN32)
|
||||
// Fallback: make build layout - libraries in lib/ directory with lib prefix
|
||||
std::string pocoBase = Environment::get("POCO_BASE", "");
|
||||
if (!pocoBase.empty())
|
||||
{
|
||||
char c = Path::separator();
|
||||
std::string OSNAME = Environment::get("OSNAME", "");
|
||||
std::string OSARCH = Environment::get("OSARCH", "");
|
||||
name = pocoBase;
|
||||
name.append(1, c)
|
||||
.append(Poco::format("lib%c%s%c%s%c", c, OSNAME, c, OSARCH, c))
|
||||
.append("lib").append(libName).append(suffix);
|
||||
|
||||
if (File(name).exists())
|
||||
return name;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Last resort: just the library name
|
||||
return libName + suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +301,7 @@ CppUnit::Test* SharedLibraryTest::suite()
|
||||
CppUnit_addTest(pSuite, SharedLibraryTest, testSharedLibrary1);
|
||||
CppUnit_addTest(pSuite, SharedLibraryTest, testSharedLibrary2);
|
||||
CppUnit_addTest(pSuite, SharedLibraryTest, testSharedLibrary3);
|
||||
CppUnit_addTest(pSuite, SharedLibraryTest, testMissingDependencies);
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
void testSharedLibrary1();
|
||||
void testSharedLibrary2();
|
||||
void testSharedLibrary3();
|
||||
void testMissingDependencies();
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
@@ -35,6 +36,7 @@ public:
|
||||
|
||||
private:
|
||||
static std::string getFullName(const std::string& libName);
|
||||
static std::string getLibFullName(const std::string& libName);
|
||||
};
|
||||
|
||||
|
||||
|
||||
26
Foundation/testsuite/src/TestLibraryMissingDeps.cpp
Normal file
26
Foundation/testsuite/src/TestLibraryMissingDeps.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// TestLibraryMissingDeps.cpp
|
||||
//
|
||||
// A test library that depends on a non-existent library.
|
||||
// Used to test findMissingDependencies functionality.
|
||||
//
|
||||
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
|
||||
// and Contributors.
|
||||
//
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/ClassLibrary.h"
|
||||
|
||||
|
||||
// Declare an external function from a non-existent library
|
||||
// This will cause the linker to add a dependency on the fake library
|
||||
extern "C" int nonExistentFunction();
|
||||
|
||||
|
||||
extern "C" POCO_LIBRARY_API int gimmeSix()
|
||||
{
|
||||
// Reference the non-existent function to ensure the dependency is kept
|
||||
return 6 + (nonExistentFunction() * 0);
|
||||
}
|
||||
Reference in New Issue
Block a user