I'm trying to get some Nim bindings for this neat little game programming library called raylib. But well, I'm having some difficulties trying to do so.
After cleaning up raylib.h, I was able to use c2nim to auto-generate a .nim. I commented out a few things and added in the {.importc.} pragmas manually. You can see it thus far here.
If you want to help me out, you'll need to build your own copy of raylib, off of the develop branch, using the SHARED_RAYLIB=YES option at make. There are some extra instructions on their Wiki.
On Linux:
I can't seem to get the library to load at runtime. It compiles fine. But when I try to run the core_basic_window in examples, I keep on getting could not load: libraylib.so messages. I tried compiling with the extra information and it only said that it couldn't find the .so file. I tried placing the .so in /usr/local/lib, and the current working directory (with setting LD_LIBRARY_PATH). Still nothing. Does anyone see what's wrong here?
EDIT: It seems to be an issue with the .so that is generated by raylib. I'm trying to figure out a fix for it. If anyone would like to help: https://github.com/raysan5/raylib/issues/332
2nd EDIT: I got a simple Nim binding to work under linux using the .so`s, but it's been a little bit of a pain since I think I also might have found an issue with the `.so that GLFW makes for the platform. I'll document what I've done to make it work on post #5 of this thread.
On OS X:
It's in a partial working state. For now, the .dylib needs to be in the same folder as the .nim you want to compile, as well as for to run the executable. There's a bit of a problem though. It's failing to load some of the functions from the .dylib, thus making the application crash if I don't have that {.deadCodeElim: on.} pragma at the top of src/raylib.nim. For example, if I remove that pragma, it will compile, but at runtime it will say could not import: LoadMeshEx. The proc id in Nim looks fine to me. Anyone know what's going wrong?
For Windows:
I haven't tried yet. If someone else to try this out and see how they are going, I'd be very appreciative.
So at this point, you can compile the core_basic_window.nim example I have in examples. But the problem is that if I try to run it, I get this:
ben@linux:examples$ ./core_basic_window
/usr/local/lib/libraylib.so: undefined symbol: glfwSetWindowIcon
could not load: libraylib.so
But when I did export LD_LIBRARY_PATH=/usr/local/lib. then it would run fine. What the hell is going on here? Isn't /usr/local/lib one of the default library search paths? That's where the .so files are put.
Add it through /etc/ld.so.conf.d/ example here. Relevant thread in Archlinux forum.
You might want to file a bug to your linux distro.
I think I figured out why setting export LD_LIBRARY_PATH=/usr/local/lib was necessary to make it work. The dlopen() function is used under the hood for the dynlib macro on Linux. dlopen() will not search /usr/local/lib by default. It needs to be manually specified...
Is this something that maybe should be fixed in the Nim source to also search /usr/local/lib for .so files on unix systems?
It is not Nim's problem.
From the dlopen doco, you definitely need to either use ldconfig or set LD_LIBRARY_PATH, because dlopen does not check /usr/local/lib by default:
o (ELF only) If the executable file for the calling program
contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
then the directories listed in the DT_RPATH tag are searched.
o If, at the time that the program was started, the environment
variable LD_LIBRARY_PATH was defined to contain a colon-separated
list of directories, then these are searched. (As a security
measure, this variable is ignored for set-user-ID and set-group-
ID programs.)
o (ELF only) If the executable file for the calling program
contains a DT_RUNPATH tag, then the directories listed in that
tag are searched.
o The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is
checked to see whether it contains an entry for filename.
o The directories /lib and /usr/lib are searched (in that order).
It might be aadded to a FAQ but this is clearly a distribution issue.
Searching "ubuntu /usr/local/lib" yields 700k+ results according to Google. The first page all have at least one correct answer (export ...).
Also no regular package installs in /usr/local/lib, only things that you compile from source. It's a well known location for user compiled libraries but I think it's fair to expect people compiling from source to manage their custom $PATH
I consider it something that Nim should be concerned about.
I am concerned. Now what? ;-) How can we improve the situation?
Taking a look at the man page for dlopen(3), I think the easiest thing to do would to be to wrap the dlopen() call in the Nim code in something that sets and then resets the LD_LIBRARY_PATH environment variable. We wouldn't want it polluting anything else.
Taking a look at the dlopen() imports in dylib.nim and dyncalls.nim, we could use the os modules' getEnv() and putEnv() functions.
E.g.:
from os import getEnv, putEnv
proc real_dlopen(path: cstring, mode: cint): LibHandle
{.importc: "dlopen", header: "<dlfcn.h>".}
proc dlopen(path: cstring, mode: cint): LibHandle=
# Modify the library search path
var
origLibPath = getEnv("LD_LIBRARY_PATH")
newLibPath = originalLibPath & ":/usr/local/lib"
setEnv("LD_LIBRARY_PATH", newLibPath)
result = real_dlopen(path, mode)
setEnv("LD_LIBRARY_PATH", origLibPath)
I think that would do it. Looking also at OS X's dlopen(), it might be best to add in a block that will get the correct environment variable to get, set, and reset. Interestingly enough, OS X already searches /usr/local/lib by defualt. But I'm not sure about /usr/local/Cellar, where is where homebrew likes to put stuff.
I'll leave the details up to you, but I think this needs to be the case for Linux at least.
So this is a fun issue now...
I've gotten the bindings to work, but when you want to do something with Textures, they won't display on the screen. The raylib tracelog says everything is okay when it came to loading the assets. I'm tracking the issue here: https://gitlab.com/define-private-public/raylib-Nim/issues/1
I've tried:
The only thing I can think of is that there is some issue with how c2Nim translated the Image data structure. I think it has to do with that void * pointer.
typedef struct Image {
void *data; // Image raw data
int width; // Image base width
int height; // Image base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (TextureFormat type)
} Image;
type
Image* = object
data*: pointer ## Image raw data
width*: cint ## Image base width
height*: cint ## Image base height
mipmaps*: cint ## Mipmap levels, 1 by default
format*: cint ## Data format (TextureFormat type)
Anyone got any ideas?