Android update and small improvements

-fixed code do cross compiling to android
-fixed build with GCC (workaround)
-added little benchmark
-several small fixes
-updated meson build (demos building, working with GCC, LDC and DMD)
-added some meson options
-added ImGUI bind for OpenGL3
This commit is contained in:
Mergul 2020-06-01 11:24:50 +02:00
parent 86edfa4a57
commit 66860b9042
30 changed files with 1358 additions and 173 deletions

View file

@ -0,0 +1,335 @@
// Copyright Michael D. Parker 2018.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module bindbc.loader.sharedlib;
import core.stdc.stdlib;
import core.stdc.string;
/// Handle to a shared library
struct SharedLib {
private void* _handle;
}
/// Indicates an uninitialized or unassigned handle.
enum invalidHandle = SharedLib.init;
// Contains information about shared library and symbol load failures.
struct ErrorInfo {
private:
char[32] _error;
char[96] _message;
public @nogc nothrow @property:
/**
Returns the string "Missing Symbol" to indicate a symbol load failure, and
the name of a library to indicate a library load failure.
*/
const(char)* error() const { return _error.ptr; }
/**
Returns a symbol name for symbol load failures, and a system-specific error
message for library load failures.
*/
const(char)* message() const { return _message.ptr; }
}
private {
__gshared ErrorInfo[] _errors;
__gshared size_t _errorCount;
}
@nogc nothrow:
/**
Returns an slice containing all errors that have been accumulated by the
`load` and `bindSymbol` functions since the last call to `resetErrors`.
*/
const(ErrorInfo)[] errors()
{
return _errors[0 .. _errorCount];
}
/**
Returns the total number of errors that have been accumulated by the
`load` and `bindSymbol` functions since the last call to `resetErrors`.
*/
size_t errorCount()
{
return _errorCount;
}
/**
Sets the error count to 0 and erases all accumulated errors. This function
does not release any memory allocated for the error list.
*/
void resetErrors()
{
_errorCount = 0;
memset(_errors.ptr, 0, _errors.length * ErrorInfo.sizeof);
}
/*
void freeErrors()
{
free(_errors.ptr);
_errors.length = _errorCount = 0;
}
*/
/**
Loads a symbol from a shared library and assigns it to a caller-supplied pointer.
Params:
lib = a valid handle to a shared library loaded via the `load` function.
ptr = a pointer to a function or variable pointer whose declaration is
appropriate for the symbol being bound (it is up to the caller to
verify the types match).
symbolName = the name of the symbol to bind.
*/
void bindSymbol(SharedLib lib, void** ptr, const(char)* symbolName)
{
// Without this, DMD can hang in release builds
pragma(inline, false);
assert(lib._handle);
auto sym = loadSymbol(lib._handle, symbolName);
if(sym) {
*ptr = sym;
}
else {
addErr("Missing Symbol", symbolName);
}
}
/**
Formats a symbol using the Windows stdcall mangling if necessary before passing it on to
bindSymbol.
Params:
lib = a valid handle to a shared library loaded via the `load` function.
ptr = a pointer to a function or variable pointer whose declaration is
appropriate for the symbol being bound (it is up to the caller to
verify the types match).
symbolName = the name of the symbol to bind.
*/
void bindSymbol_stdcall(Func)(SharedLib lib, ref Func f, const(char)* symbolName)
{
import bindbc.loader.system : bindWindows, bind32;
static if(bindWindows && bind32) {
import core.stdc.stdio : snprintf;
import std.traits : ParameterTypeTuple;
uint paramSize(A...)(A args)
{
size_t sum = 0;
foreach(arg; args) {
sum += arg.sizeof;
// Align on 32-bit stack
if((sum & 3) != 0) {
sum += 4 - (sum & 3);
}
}
return sum;
}
ParameterTypeTuple!f params;
char[128] mangled;
snprintf(mangled.ptr, mangled.length, "_%s@%d", symbolName, paramSize(params));
symbolName = mangled.ptr;
}
bindSymbol(lib, cast(void**)&f, symbolName);
}
/**
Loads a shared library from disk, using the system-specific API and search rules.
libName = the name of the library to load. May include the full or relative
path for the file.
*/
SharedLib load(const(char)* libName)
{
auto handle = loadLib(libName);
if(handle) return SharedLib(handle);
else {
addErr(libName, null);
return invalidHandle;
}
}
/**
Unloads a shared library from process memory.
Generally, it is not necessary to call this function at program exit, as the system will ensure
any shared libraries loaded by the process will be unloaded then. However, any loaded shared
libraries that are no longer needed by the program during runtime, such as those that are part
of a "hot swap" mechanism, should be unloaded to free up resources.
*/
void unload(ref SharedLib lib) {
if(lib._handle) {
unloadLib(lib._handle);
lib = invalidHandle;
}
}
private:
void allocErrs() {
size_t newSize = _errorCount == 0 ? 16 : _errors.length * 2;
auto errs = cast(ErrorInfo*)malloc(ErrorInfo.sizeof * newSize);
if(!errs) exit(EXIT_FAILURE);
if(_errorCount > 0) {
memcpy(errs, _errors.ptr, ErrorInfo.sizeof * _errors.length);
free(_errors.ptr);
}
_errors = errs[0 .. newSize];
}
void addErr(const(char)* errstr, const(char)* message)
{
if(_errors.length == 0 || _errorCount >= _errors.length) {
allocErrs();
}
auto pinfo = &_errors[_errorCount];
strcpy(pinfo._error.ptr, errstr);
if(message) {
strncpy(pinfo._message.ptr, message, pinfo._message.length);
pinfo._message[pinfo._message.length - 1] = 0;
}
else {
sysError(pinfo._message.ptr, pinfo._message.length);
}
++_errorCount;
}
version(Windows)
{
import core.sys.windows.windows;
extern(Windows) @nogc nothrow alias pSetDLLDirectory = BOOL function(const(char)*);
pSetDLLDirectory setDLLDirectory;
void* loadLib(const(char)* name)
{
return LoadLibraryA(name);
}
void unloadLib(void* lib)
{
FreeLibrary(lib);
}
void* loadSymbol(void* lib, const(char)* symbolName)
{
return GetProcAddress(lib, symbolName);
}
void sysError(char* buf, size_t len)
{
char* msgBuf;
enum uint langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
null,
GetLastError(),
langID,
cast(char*)&msgBuf,
0,
null
);
if(msgBuf) {
strncpy(buf, msgBuf, len);
buf[len - 1] = 0;
LocalFree(msgBuf);
}
else strncpy(buf, "Unknown Error\0", len);
}
/**
Adds a path to the default search path on Windows, replacing the path set in a previous
call to the same function.
Any path added to this function will be added to the default DLL search path as documented at
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw.
Generally, when loading DLLs on a path that is not on the search path, e.g., from a subdirectory
of the application, the path should be prepended to the DLL name passed to the load function,
e.g., "dlls\\SDL2.dll". If `setCustomLoaderSearchPath(".\\dlls")` is called first, then the subdirectory
will become part of the DLL search path and the path may be omitted from the load function. (Be
aware that ".\\dlls" is relative to the current working directory, which may not be the application
directory, so the path should be built appropriately.)
Some DLLs may depend on other DLLs, perhaps even attempting to load them dynamically at run time
(e.g., SDL2_image only loads dependencies such as libpng if it is initialized at run time with
PNG support). In this case, if the DLL and its dependencies are placed in a subdirectory and
loaded as e.g., "dlls\\SDL2_image.dll", then it will not be able to find its dependencies; the
system loader will look for them on the regular DLL search path. When that happens, the solution
is to call `setCustomLoaderSearchPath` with the subdirectory before initializing the library.
Calling this function with `null` as the argument will reset the default search path.
When the function returns `false`, the relevant `ErrorInfo` is added to the global error list and can
be retrieved by looping through the array returned by the `errors` function.
When placing DLLs in a subdirectory of the application, it should be considered good practice to
call `setCustomLoaderSearchPath` to ensure all DLLs load properly. It should also be considered good
practice to reset the default search path once all DLLs are loaded.
This function is only available on Windows, so any usage of it should be preceded with
`version(Windows)`.
Params:
path = the path to add to the DLL search path, or `null` to reset the default.
Returns:
`true` if the path was successfully added to the DLL search path, otherwise `false`.
*/
public
bool setCustomLoaderSearchPath(const(char)* path)
{
if(!setDLLDirectory) {
auto lib = load("Kernel32.dll");
if(lib == invalidHandle) return false;
lib.bindSymbol(cast(void**)&setDLLDirectory, "SetDllDirectoryA");
if(!setDLLDirectory) return false;
}
return setDLLDirectory(path) != 0;
}
}
else version(Posix) {
import core.sys.posix.dlfcn;
void* loadLib(const(char)* name)
{
return dlopen(name, RTLD_NOW);
}
void unloadLib(void* lib)
{
dlclose(lib);
}
void* loadSymbol(void* lib, const(char)* symbolName)
{
return dlsym(lib, symbolName);
}
void sysError(char* buf, size_t len)
{
const (char)* msg = dlerror();
strncpy(buf, msg != null ? msg : "Unknown Error", len);
buf[len - 1] = 0;
}
}
else static assert(0, "bindbc-loader is not implemented on this platform.");