:erlang.load_nif/2 finds shared library file inside original project but can't find it if the project gets imported - elixir-mix

I've build a small elixir application that uses NIF functions to execute some c++ code.
The nifs are loaded via:
def load_nifs do
:erlang.load_nif('<relative_path_to_lib>/<lib_name>', 0)
:ok
end
and this works fine.
Now I want to integrate this app into another project. The problem now is that load_nif throws:
Failed to load NIF library: '<relative_path_to_lib>/<lib_name>.so: cannot open shared object file: No such file or directory''
although nothing has changed. I checked the deps folder and the shared library files are exactly where they are supposed to be, so the dependency seems to be loaded correctly. I also tried putting the .so files into the same folder as the module that calls load_nif (and omit <relative_path_to_lib>/) as well as providing an absolute path, all to no avail.
Any help is appreciated, Cheers.
Relevant info regarding my system:
OS: Ubuntu 22.04
Elixir version: Elixir 1.13.0 (compiled with Erlang/OTP 24)
Update:
The issue does not seem to be that files are located at the wrong place, as it finds the files during the first test run after compilation.
However, the error occurs when I repeat the run. It seems that the error message is wrong, since no files are deleted during the test.
If I repeat the function within one test multiple times there's no problem, so the issue is not created because the NIF function is executed multiple times, but because the test that contains the function is repeated multiple times.

Solution:
I still have no idea what causes this behavior but after putting the .so files into a priv directory and accessing them via
:erlang.load_nif(:code.priv_dir(:<app_name>), 0)
the tests pass.

Related

add luasocket to program (bizhawk) shipped with own lua environment

I am trying to get luasocket working in the lua scripting environment of Bizhawk, but so far without luck. I downloaded the vc8 version of luasocket here, made sure I have vc8 installed, and checked the lua version that came with bizhawk: 5.1
But, when I start the script I get the following error:
LuaInterface.LuaScriptException: error loading module 'socket.core' from file './libs/socket\core.dll':
Das angegebene Modul wurde nicht gefunden. (the given module was not found)
The lua code:
package.path = package.path..';./libs/lua/?.lua'
package.cpath = package.cpath..';./libs/?.dll'
local socket = require("socket")
The file system structure:
libs
> lua
> socket
ftp.lua http.lua smtp.lua tp.lua url.lua
ltn12.lua
mime.lua
socket.lua
> socket
core.dll
> mime
core.dll
It seems to find the files, because when I had the file structure wrong it gave me actual file-not-found errors.
My best guess is that there is some kind if incompatibility between the lua that is shipped with Bizhawk and the external luasocket library binaries, but I am out of ideas.
This guy's set it up: https://github.com/antogerva/emuHostUDP (dearchive to emuhawk.exe base directory). His example seems to work, but it might not contain everything you need. Like HTTP for example.
Since project is complex and the luasockets examples are awful, here's a one-liner for testing http:
print(require("socket.http").request{ url = "http://www.google.com" });
Following his model, I applied the following method: contents of lua dir to root; lua5.1.dll to root. Note that we will not be using the core.dlls from luasockets. This is because BizHawk now has them integrated; and this was necessary to work around a bug with luasockets in bizhawk.
More specifically, we have
/emuhawk.exe
/Lua (untouched)
/Socket/ftp.lua,http.lua,etc.
/ltn12.lua,socket.lua,mime.lua
/lua5.1.dll
/mytest.lua
(with files from http://files.luaforge.net/releases/luasocket/luasocket/luasocket-2.0.2/luasocket-2.0.2-lua-5.1.2-Win32-vc8.zip)
I can't say why precisely all of this is necessary, but I think it's miraculous that it works, given that we have a customized lua.
When using lua, if you find a directory structure that works, it's best not to wrestle with it any further.
Update:
(may be out of date) As soon as you do something nontrivial you may find bizhawk crashes. It seems this is due to a conflict with luasocket's "protection" system. Inspect http.lua and observe the code at the end which sends a function through socket.protect to wrap it. Remove the socket.protect wrapper and it should solve this problem.
I suspect that it's because of the dependence on lua51.dll. luasocket core.dll library is linked against lua51.dll (most likely; you can ran depends or similar tool to find out for sure), which is probably not present and this prevents socket.core from being loaded.
Even if you find lua51.dll, it's not likely to work if Bizhawk is statically compiled with lua51.dll as this will lead to two interpreters loaded into the same process, which is a recipe for seg faults.
There are three main options, but they all depend on how Bizhawk project is structured:
Bizhawk is compiled against lua51.dll (and this dll is present as a standalone file). In this case you need to make sure that the socket/core.dll you are using is compiled against the same library and it should work (as long as the run-times are the same and lua51.sll is available in PATH).
Bizhawk is statically compiled with lua51.dll. The simplest option is also statically compile luasocket libraries into the executable.
If option 2 is not available, then you need to use a proxy library and export Lua symbols from the Bizhawk executable as described in this SO answer: https://stackoverflow.com/a/28310666/1442917
If none of this helps you to solve the problem, you'll need to get depends for your Windows platform and run it in the "profiling" mode, which will tell you the exact error that happens when that DLL is loaded.

ng-boilerplate LESS files are not compiling to the CSS file in build directory

The CSS (from LESS) is not rendering or compiling in my ng-boilerplate app.
I'm creating an app using ng-boilerplate and rails using these tutorials here, and we've integrated a working rails server with the angular. The angular is working on any page... as in, we can use:
$scope.animal = "dogs"
and
<div> I like {{animal}}! </div>
to get
I like dogs!
However, the Less and CSS (which was copied and pasted from a functional ng-boilerplate app without rails where it worked and compiled from all the different directories just fine) is not compiling to the build directory nor rendering. In fact, in this new app (the one with the rails), not even the original css/less from the ng-boilerplate template will compile or render.
We tried using the following commands, as the tutorial suggested, which created weird nested build directories (a build inside the build inside the build) and didn't fix the problem.
cd public
ln -s ../build UI
Meanwhile, the UI folder is linked to the build folder using a symlink but...
The LESS (from different files in different directories) is all supposed to compile to one CSS file called ng-boilerplate.css (which it did in the original app without rails), and instead there's a file called ng-boilerplate-0.3.1.css, which is empty. I tried renaming that, but it re-renames itself back to the 0.3.1 one every time grunt builds it.
I'm using rails version 4.2.1 and ruby says it's 2.1.0p0.
I hope that you have already found a solution, as I realise that this post is quite old. However, since this question appears reasonably highly when I was google searching, I felt it should have an answer.
When using ng-boilerplate, you need to be aware that, although it's compile process finds all of your JS files and makes nice compiled JS code, the same is not done for the LESS files in their build process. If you look in the directory 'src/app/less/' and view the 'main.less' file, you can see a small note mentioning that he would like to automate this process. Instead, it is at this point that you unfortunately need to manually import all of your LESS files.
The LESS way to import files is this for anybody who is unsure:
#import '../app/less/fileToImport.less'
The path can be relative or absolute.

How to reload all OTP code when developing an OTP application?

While I'm learning OTP I've been making a lot of changes to the .app and .erl files and re-running my application to see the effect of the changes.
I've tried the following sequence of commands to pick up all my new changes, but it doesn't seem to work:
Compile src files ...
erlc -o ebin src/*.erl
... followed by this is the erlang shell:
application:stop(my_app).
application:unload(my_app).
application:load(my_app).
application:start(my_app).
However, this doesn't seem to work. The only way I have found to work is to exit the erlang shell, recompile the app and then run application:start(my_app)..
Is there an easier way of picking up my changes?
Calling application:load(App) (after stopping and unloading) will reload the .app file but not the modules. As the documentation says: "Note that the function does not load the actual Erlang object code."
If you were to do an upgrade using releases, you would ship an .appup file that specified which modules to reload on upgrade to the new version (no need to reload all of them if only one or two have changed), but if you're just developing and don't want to stop and restart everything, you'll have to set up your own help functions for reloading code.
Edit: Since OTP 20 (2017), the interactive Erlang shell now has the lm() function for loading all modules whose .beam files have changed, so there is no need to roll your own utility function for this anymore. See https://erlang.org/doc/man/c.html#lm-0

Why does LoadLibrary succeed or fail depending on the current directory?

I'm having issues with LoadLibrary, something that never occurred to me before, this is the first time i see this, i'm trying to load a module "nss3.dll" inside "C:\Program Files (x86)\Mozilla Firefox\" with LoadLibrary, in order to import some functions from it, but it fails with 0 return value, i tried hard-coding a .dll from system32 folder and it worked fine, any ideas what's happening ?
Edit: I forgot to mention, a way to overcome this issue is to call SetCurrentDirectory and THEN import the module without specifying the path to the .dll, but that's not the case, i want to find out why it's happening to me.
Edit: Here's some snippet (this works):
var
NSSModule: HModule;
begin
SetCurrentDir('C:\Program Files (x86)\Mozilla Firefox');
NSSModule := LoadLibrary('nss3.dll');
This, does not works, no idea why...:
var
NSSModule: HModule;
begin
NSSModule := LoadLibrary('C:\Program Files (x86)\Mozilla Firefox\nss3.dll');
The dll you are loading is trying to statically load another dll in the same folder (mozglue.dll). That's why when you set the working directory it works. When you don't, since firefox's path is not in the system path, the api cannot find the dll. You can find specifics about dll search here.
The explanation for this is that the DLL itself links to other DLLs that live in the same directory. You cannot load the DLL without also loading these other dependencies. And it is the loader's attempt to load the dependencies that is failing.
The expected host for this DLL is Firefox, which is also found in that same directory. When Firefox loads the DLL, the dependencies are resolved successfuilly because the DLL search path searches the directory containing the executable. But when your program attempts to load the module, the dependent modules cannot be found because the your executable is in a different directory.
The error code that you report is ERROR_MOD_NOT_FOUND. This refers to one of the dependencies rather than the module you loaded. In fact that is common with loader errors. You may encounter hard to believe error codes but usually the explanation is that they refer to a error resolving a dependent module.
Modifying the working directory is not the solution. That does resolve your problem but you should not rely on the working directory for dependency resolution. The system provided mechanisms to influence DLL search are SetDllDirectory or AddDllDirectory. For instance:
SetDllDirectory('C:\Program Files (x86)\Mozilla Firefox');
NSSModule := LoadLibrary('nss3.dll');
SetDllDirectory(nil);
The MSDN topic Dynamic-Link Library Search Order is required reading here.

How do I know which modules to include when packaging with py2app?

I'm trying to package the Mac version of an open source application that I didn't write (I'm not much of a coder). I'm using py2app 0.6.4. The application builds on my system properly, but I'm unsure of what to list for the includes in the setup.py file.
The dependencies include qt4, PyQt, matplotlib, cherrypy, and sip.
When I looked at this article on handling PyQt applications, I noticed the dependencies were not listed simply as PyQt but rather *PyQt4._qt* etc. How can I determine what to insert in the includes statement from the code of the application?
When py2app runs, it's going to look at each of your scripts, automatically grabbing any modules or packages imported by your scripts. In many cases, this will suffice and you won't need to list anything in the includes variable. Some packages have extra files such as data files that aren't used by the import statement, but must be present for the package to run correctly. Then you need to explicitly include it so py2app will grab it as well. Try to use your app; if you get an error that some module or file isn't found then worry about putting it in the includes variable.

Resources