foreign non-C library - ocaml

I want to use a library built as a .a file in my dune project (exposing some needed functions...), but the dune documentation specifies only how to use foreign C code.
How can one use a non-C built library with dune?
What I am currently trying to do (I can't see how to apply the process of the documentation with a non-C generated program):
Part of my dune file:
(foreign_archives ../bin_jasmin/TESTS)
My main program:
module ForeignFuncs = struct
open Ctypes
open Foreign
let test_ADCX =
foreign "test_ADCX"
(ocaml_bytes #-> ocaml_bytes #-> uint64_t #-> uint64_t #-> uint8_t
#-> returning void)
end
p.s.: I already used this library with Ctypes, compiling with ocamlopt and it worked just fine. For those curious, this is about using Jasmin

You seem to be using dune, so you should be able to have a link_flags field in your executable stanza. Something like
(link_flags (-ccopt -Lpath/to/archive -cclib -lmy-library))
You still need a rule to generate the archive of course. My dune foo is not great so what I have done in the past is to use a Makefile (which I am more familiar with) to build the archive and have the Makefile call up dune when building the OCaml executable, which has the archive as a dependency. Note that if using a relative path to the archive, the path in the dune file is relative (by default) to the _build/default directory. Your non-C built library may in turn require its own support library to be linked in and it would have to be prepared using C linkage and calling conventions.

Related

Dune: build library and access it in another project and hide or make inaccessible private or implementation modules

I am using Dune for building OCaml projects and I'd like to build a standalone library for reusing it in other projects.
The root folder of the library is mylib and the library was initialized with dune init lib mylib src (after cd mylib).
The directory tree of mylib, the root of the project, is the following:
mylib
+---dune-project
│
+---src
dune
priv.ml
mymodule.ml
mymodule.mli
Here follows the content of the files.
mylib/dune-project:
(lang dune 2.7)
mylib/src/dune:
(library
(name mylib))
mylib/src/priv.ml:
let rec loop a accu i =
let n = Array.length a in
if i = n then
accu
else
loop a (accu + Array.unsafe_get a i) (succ i)
mylib/src/mymodule.mli:
val sum : int array -> int
mylib/src/mymodule.ml:
let sum a =
Priv.loop a 0 0
After giving context and showing the content of each file of this toy example, the question follows:
How can I build the library mylib and use it in another, separate project?
(For example, in another project, I'd consume the library with the following:
Open Mylib
let () =
print_int (Mymodule.sum [1;2;3])
OR
let () =
print_int (Mylib.Mymodule.sum [1;2;3])
)
With Dune, given the executable main, you would write the following dune file to use library mylib if it was published on Opam.
dune:
(executable
(name main)
(libraries mylib))
After successfully building the library and linking it in another project, how can I hide or not expose some modules? For example, given the toy library of before, mylib, I would like to not expose and make inaccessible the module Priv (such that Priv would only be usable inside modules of mylib - like a protected/internal class in Java/C#).
​
I tried searching on Real World OCaml, Cornell CS3110 textbook, Dune documentation, OCaml Learn but, unless deeply nested, I found nothing.
Thank you very much for your help, if I didn't explain something in a clear way, please ask and I will try to explain better.
In the root directory, these should be a file called mylib.opam with content similar to this one. Then, the (public_name mylib) s-expression should be added to library in mylib/src/dune. Finally, making sure to be in the root of the project, the library shall be built with dune build and installed with opam install ..
For actually using the library in another project, even in a different dune workspace, all it's needed is to add (libraries mylib) to the dune file of project that will use the library.
The (private_modules priv) s-expression should be added to library in mylib/src/dune. Then, mylib.ml needs to be created with the following content: module Mymodule = Mymodule: this will make sure that only Mymodule will be exposed under the package Mylib.

Locate system wide libraries in Rcpp Makevars

I wrote a C++ package "P". It has an R interface package "RP", built using Rcpp. P used Make directly for compilation, but was switched to CMake for portability. CMake is used to find headers (let's call their collection HF) and static libraries (SL in the rest of the present post) for system wide libraries.
I want to update RP to be able to depend on the CMake evolution of P. In the C++ sources RP/src/*.cpp, HF elements are included, and of course SL are statically linked.
What is the best way to call CMake in RP/src/Makevars to retrieve the locations of HF and SL ? The point here is not to replace the build system of Rcpp, but to leverage the search capabilities of CMake.
At the moment, P (CMake version) and RP build on my machine, using absolute path references in RP/src/Makevars such as:
INC_NLOPT = /usr/local/Cellar/nlopt/2.4.2_2/include
LIB_NLOPT = /usr/local/Cellar/nlopt/2.4.2_2/lib/libnlopt.a
Since we use RP internaly at the moment, we can expect CMake, HF and SL to be installed on every machine we will deploy to.
The solution was to create a CMakeFileLists.txt file in RP/src. In this file, a Libvar file containing the required library paths is written using CMake file command. Libvar is then included in Makevars using include.
A configure file at the root of the package is executed to ensure that Libvar is generated before every call to make by R.

C++ linux executable keeps trying to use library that does not exist

I am trying to write a simple application with GLFW on Linux. Right now the main file (the only file) is basically just a few lines of code to make sure the dynamic library linked correctly. Here it is:
#include <GLFW/glfw3.h>
#include <iostream>
int main()
{
glfwInit();
std::cout << "It works this far!" << std::endl;
glfwTerminate();
}
The include files are stored in a directory labelled "include" and the library files are stored in a directory labelled "lib". As of right now, I am compiling the program with the following line:
g++ -Wl,-Rlib -Iinclude -Llib test.cpp -o test -lglfw.3.2
It compiles and links just fine, but when I try to execute it, I get the following error:
./test: error while loading shared libraries: libglfw.so.3: cannot open shared object file: No such file or directory
Now, before you rush to downvote this question into oblivion and mark it as a duplicate, at least allow me to explain why I believe my question is different enough to not be a duplicate. I already attempted the solutions that the other questions presented, but it was unsuccessful. As you can see, I tried setting the path to the library during linking with the -Wl,-Rlib tag. I also tried setting LD_LIBRARY_PATH to point to the location of my libraries (the 'lib' folder), but it still threw the same error. (It didn't matter if the path was relative or absolute.)
So, the next thing I tried was running the ldd command on the executable. I got some other dependencies that were working just fine, but importantly, I got this:
libglfw.so.3 => not found
For some reason, it insists on looking for libglfw.so.3. It will not have it any other way. Upon renaming the library from libglfw.3.2.so to libglfw.so.3, the program executed just fine and printed It works this far! as if there were no problems at all.
Why would this happen?
For some reason, it insists on looking for libglfw.so.3. ... Upon renaming the library from libglfw.3.2.so to libglfw.so.3 ...
The ELF executables contain the exact name of the dynamic libraries used.
If the executable contains the library name "libglfw.so.3" the file must be named exactly like this.
The file naming scheme is intentionally done in a way that not the "full" version is coded into the file name: This way a later version ("libglfw.so.3.15") will work with the executable.
Normally there should be a symbolic link to the latest version of the library installed:
libglfw.so.3 -> libglfw.so.3.2
This symbolic link seems to be missing on your computer. I would say that this is an installation problem!
EDIT
The question could be: Why is the file name stored in the executable file not libglfw.3.2.so but libglfw.so.3?
The answer has to do with the backward compatibility when a new version of a library is installed:
Normally you would use the switch -lglfw and a symbolic link named libglfw.so is looked up.
If you stored the file name libglfw.so in the executable file and a new, incompatible version if this library (libglfw.so.4) is installed you would have no chance to get the program running by having both versions of the library installed.
To enable backward compatibility by having both versions of the library installed the "real" symbolic link name of the library (libglfw.so.3) must be stored in the executable file.
Therefore the "expected" file name of a library is stored in the library itself: Inside the file libglfw.so.3.2 you'll find some information that the file expects itself to be stored as libglfw.so.3.
The linker will use this information about the file name because it assumes that the library name given in the linker switch (-lglfw) is less "precise" than the name stored in the library itself.
For some reason, it insists on looking for libglfw.so.3. It will not have it any other way.
This is the Linux convention for shared libraries which is described here among other places. For Linux libfoo.so.x.y.z is considered to have the same ABI as libfoo.so.x. Usually when shared libraries are installed (e.g. via rpm, dpkg, etc.) there's an invocation of ldconfig that happens so that the just installed libraries have a symlink following the convention installed that references the library. Also these libs (if installed to a "trusted location"), are added to a linker cache for performance reasons.
It compiles and links just fine, but when I try to execute it, I get the following error:
./test: error while loading shared libraries: libglfw.so.3: cannot open shared object file: No such file or directory
libglfw.so.3 isn't on ld-linux.so's path.
As you can see, I tried setting the path to the library during linking with the -Wl,-Rlib
Still won't find it -- libglfw.so.3 isn't on ld-linux.so's path. You can add it by doing something like:
ldconfig -n /path/to/lib
Which should output the requisite libglfw.so.3 symlink for your lib.
IIRC setting the rpath might require a full path.
I also tried setting LD_LIBRARY_PATH to point to the location of my libraries
Again, libglfw.so.3 isn't on ld-linux.so's path.

ocamlmktop with oasis

I am having trouble adding adding a library to ocamlmktop.
I have a directory com, with an object file com/com.cma.
If I run ocamlmktop com.cma -o top within the com directory, then the resulting executable top seems to have the library; i.e, I can enter "Com.foo;;" and it will give the type signature of foo in the module Com.
However, if I run ocamlmktop com/com.cma -o top within the directory above com, then the resulting executable does not seem to have the library; i.e, it responds to "Com.foo;;" with "Error: Unbound module Com".
Is there a way to include libraries from different folders, or do I need to put all the .cma files in the same folder?
Also, I'm using the OASIS build system; can I inform OASIS that I want a toplevel with these libraries?
Edit:
I've found a partial solution: ocamlc -pack a/a.cmo b/b.cmo -o everything.cmo, and then ocamlmktop everything.cmo -o top; however, this requires duplicating all the libraries and forces them to be submodules of a single supermodule.
The reason that you cannot use toplevel from the directory above is that toplevels do not include interface files (.cmi) and the toplevel needs to find them on disk when the user accesses some module. So, either load toplevel with -I com switch or after loading issue #directory "com";;.
NB OASIS should support building toplevels natively in the next release (0.4.0).

Using "ocamlfind" to make the OCaml compiler and toplevel find (project specific) libraries

I'm trying to use ocamlfind with both the OCaml compiler and toplevel. From what I understood, I need to place the required libraries in the _tags file at the root of my project, so that the ocamlfind tool will take care of loading them - allowing me to open them in my modules like so :
open Sdl
open Sdlvideo
open Str
Currently, my _tags file looks like this :
<*>: pkg_sdl,pkg_str
I can apparently launch the ocamlfind command with the ocamlc or ocamlopt argument, provided I wan't to compile my project, but I did not see an option to launch the toplevel in the same manner. Is there any way to do this (something like "ocamlfind ocaml")?
I also don't know how to place my project specific modules in the _tags file : imagine I have a module name Land. I am currently using the #use "land.ml" directive to open the file and load the module, but it has been suggested that this is not good practice. What syntax should I use in _tags to specify it should be loaded by ocamlfind (considering land.ml is not in the ocamlfind search path) ?
Thank you,
Charlie P.
Edit : According to the first answer of this post, the _tags file is not to be used with ocamlfind. The questions above still stand, there is just a new one to the list : what is the correct way to specify the libraries to ocamlfind ?
try this:
$ cat >> .ocamlinit
#use "topfind";;
#require "sdl";;
#require "sdlvideo";;
open Sdl
open Sdlvideo;;
open Str;;
.ocamlinit is sourced from the current directory, falling back to /home/user/.ocamlinit. you can explicitly override it via ocaml -init <filename>
One should distinguish ocamlfind packages, module names and file names. Source code references only module names. Modules are provided by .cma .cmo .cmx (and .cmi) files. Ocamlfind packages are named collections of such files (binaries built from some ocaml library sources). Ocamlfind is not strictly necessary to build a project - just specify the paths to all used libraries via -I. But if the project is distributed in source form - another people will have troubles building it cause used libraries are placed in different places. Then one seeks the way to specify the names of used "third party code pieces" and some external tool to resolve those names to actual paths. This tool is ocamlfind.
First find the ocamlfind package that provides the modules you need (in this case Sdl and Sdlvideo - just run ocamlfind list, most probably the package is named sdl.
Compile with ocamlfind ocamlc -package <package name> -linkpkg source.ml -o program
Alternatively use ocamlfind to extract the path to the .cma file (and others) provided by the package (ocamlfind query <package name>) and use this path with your build tool (plain Makefile, ocamlbuild, etc).
Moreover ocamlfind (is intended to) take away the burden of remembering how the actual .cma files are named and what matching compiler options are required and in what order the packages are depending on each other. One just needs to specify the package name.
Keep in mind that there is no one-to-one strict formal relationship between library name (purely human-targeted name e.g. ocaml-extlib), module names (ExtLib, Enum, IO, etc), file names (extLib.cma) and ocamlfind package name (extlib), though for convenience package maintainers and library authors usually choose guessable names :)
The _tags file is not for ocamlfind, but for ocamlbuild.
Exemple :
ocamlbuild land.native