Using an executable as library in dune - ocaml

I'm building a project using dune and I'm facing the following situation. Essentially, my project depends on another project, not developed by me, from which I want to use some parts of the source code.
Here is more or less my project tree
my_project/
|---dune-project
|---src/
|---dune
|---extrenal-project/
|---dune-project
|---dune
|---src/
|---dune
|---src-file.ml
The problem is that external-project has a dune file that builds it as an executable, instead of a library. Therefore, I cannot include it as a library in my dune file. Ideally, I don't want to modify the dune file inside of external-project.
I've tried a series of combinations. Here is the current status of my dune file:
(dirs external-project)
(executable
(name myexec)
(libraries containers)
(modules Myexec)
(promote (until-clean) (into ".."))
)
(env (dev (flags (:standard -warn-error -A))))
Which allows me to compile external-project correctly, but then does not allow me to reference any of its files. I also tried (dirs external-project external-project/src) but to no success.
So, my question is: is there a way for me to reference external-project as a library, even though it is build as an executable?
Thanks for your help!

Related

Building "hello world" with ocaml dune

Following the tutorial at https://dune.readthedocs.io/en/stable/quick-start.html, I created a file hello_world.ml containing
print_endline "Hello, world!"
and a file dune containing
(executable
(name hello_world))
and then typed
dune build hello_world.exe
but it complains about errors in other (completely unrelated) files.
Is it possible that dune looks at other files even though they are not mentioned in the dune file (even recursively) ? And how to prevent it ?
Yes, dune will search for all files that have *.ml or *.re files in the current folder. To disable this behavior use the modules stanza and explicitly specify which modules comprise your executable. For example, if it is made of hello_world.ml and utilities.ml compilation units, then the following specification will work for you,
(executable
(name hello_world)
(modules hello_world utilities))

generating OCaml documentation with dune

I'm in an internal package of my project and I ran dune build #doc. It does not complain and runs to completion. I then check the /_build/default directory and no _doc directory exists.
Am I missing something?
You should simply be able to generate docs by running dune build #doc if you have foo.ml, foo.opam, and a dune file containing simple contents. For example,
(library
(name foo))
To answer the question, the package name should be related to the library name or public name (if added as a stanza to the dune file).

Copying a newly builded file as a Dune's stanza

I have written a library in OCaml with all of its sources located in lib folder.
I also prepared "facade" executables in bin folder.
Now I would like to prepare some examples how to use the above mentioned executables.
To do this I need to either copy an executable beforehand or (preferably) tell Dune to use a newly created one after build.
And here is my question.
Dune's copy_files stanza does not allow1 me to copy from _build folder.
Is there any other way to use fresh executables each time after building or do I need to copy them at some point and keep up to date?
Below is the structure of the project (in case verbal description was misleading in any way).
root
lib <- source
bin <- frontend for source
examples <- how to use the above frontend
1 By not allow I mean the following usage of this stanza:
( copy_files %{project_root}/_build/default/bin/program.exe )
A solution, as suggested by #Lhooq, might be to use dune exec command with --root parameter.
In regard to the provided scenario, if we make a script:
dune exec --root=../.. ./bin/some_program.exe
(*
Where 'some_program' is the name of an .ml file located in bin folder.
I assumed here that the program is compiled to native code, not to bytecode (hence the .exe).
*)
and place it in examples directory, then by invoking it we will actually run the latest build of the program defined in some_program.ml located in bin folder.
And just to make things clear: bin folder does NOT contain any compiled files (neither .exe nor .bc) .

File path issue with Meson and Eigen

I cannot make local include paths work in the Meson build system.
This C++ inclusion works correctly:
#include </cygdrive/c/Users/user/project/Third-Party/eigen/Eigen/Dense>
This one does not:
#include "Third-Party/eigen/Eigen/Dense"
fatal error: Eigen/Dense: No such file or directory
In the Meson build file, I tried to add Eigen's path, without success:
# '.' will refer to current build directory
include_dirs = include_directories('include', '.', '../project/Third-Party/eigen')
This is the project tree structure:
project
meson.build
src
meson.build
example.h
example.cpp
Third-Party
eigen (headers only lib)
Eigen
Note: with CMake I do not have this issue.
For dependency management, meson allows you to manually declare include_directories() in your build files. However, there is another way do handle dependencies: using dependency() command.
dependency() is a much better way to handle dependencies, because meson will build it if necessary (if dependency is a shared or a static library) and safely allows you to use includes. That means that you don't have to know where includes for dependency are located physically or care about their paths ever after. The only downside is that this kind of dependency needs it's own meson.build file.
Using dependency() command:
To actually use it, you have to write a wrap file for dependency. Or, if you are lucky enough, there is already a wrap file for you in the Wrap DB -- a community-driven database for meson wrap files. Wrap file is a config of some kind that declare where you can get a dependency and in what form. Wrap file can wrap around zip archives and git repositories.
For your given dependency, there is wrap file in Wrap DB: eigen. All you have to do is download it and place it in the subprojects directory near your meson.build. For example:
$ cd project
$ mkdir subprojects
$ wget "https://wrapdb.mesonbuild.com/v1/projects/eigen/3.3.4/1/get_wrap" \
-O subprojects/eigen.wrap
Now, not every project builds with meson. For the ones that don't, wrap file also specify a patch. Patch is used to just copy appropriate meson.build file into dependency directory (as well as any other files that would be needed for building that particular dependency with meson). Eigen wrap file contains a patch.
To find out how any particular dependency declare itself as a dependency (using declare_dependency() command), you need to investigate meson.build file in dependency source directory (although it's often just name of the dependency plus _dep, e.g. "eigen_dep"). For me, eigen directory was subprojects/eigen-eigen-5a0156e40feb. So, you search for the declare_dependency() command:
$ grep declare_dependency subprojects/eigen-eigen-5a0156e40feb/meson.build
eigen_dep = declare_dependency(
As you can see, eigen declare dependency as eigen_dep. If you want to know what exactly is declared, just scroll down the dependency meson.build file.
Now, to use that eigen_dep in your project, create a dependency object with a dependency() command. Here is a sample project that I used to compile "A simple first program" from Eigen: Getting Started:
project('example', 'cpp')
eigen_dependency = dependency('eigen', fallback: ['eigen', 'eigen_dep'])
executable('example', 'example.cpp', dependencies: eigen_dependency)
Notice arguments for the dependency() command. The first one is system-wide dependency that meson is searching for. If there is no eigen for development installed in your system, then meson uses fallback: first item in fallback is basename of the wrap file, second item is a name of declared dependency.
Then use eigen_dependency variable in whatever you build, passing it to the dependencies argument.
Using include_directories() command:
If you want to just include some files from external directory (such as your "Third-Party" directory) using include_directories() command, that directory has to be relative to the meson.build file where you use it.
To use manually declared includes, you need to call include_directories() command to get the include_directories object. Pass that object to include_directories argument in whatever you build.
Given your example, I assume that root meson.build file is a project build file. Then in that root meson.build, for example, you can write:
# File: project/meson.build
project('example', 'cpp')
eigen_includes = include_directories('Third-Parties/eigen')
executable('example', 'example.cpp', include_directories: eigen_includes)
But if you want to get eigen includes from src/meson.build, then you need to change include_directories to:
# File: project/src/meson.build
eigen_includes = include_directories('../Third-Parties/eigen')
...

OCaml: using Oasis with multiple-level src folders

I am trying to use oasis to compile my project, and my project is organized in this way:
_oasis
src/
main.ml
core_a.ml
core_b.ml
type.ml
plugins/
plugin_a.ml
plugin_b.ml
Note that in the plugin_a.ml, it refers to module type.ml (i.e., open Type).
When I use oasis to compile the project, it reports:
Unbound module Type
Here is the simplified version of my _oasis file:
....
BuildTools: ocamlbuild
BuildDepends: deriving, deriving.syntax, core, batteries
Executable "main"
Path: src
MainIs: main.ml
CompiledObject: best
Install: false
BuildDepends: deriving, deriving.syntax, core, batteries
Am I doing anything wrong here? Or what I am doing is not the best practice to organize a project like this?
I think your project structure is a bit strange. You have a sub directory for plugins, but you should note that there's no namespace or package hierarchy so this is not really useful in practice.
As for whether this is possible, the answer seems to be mixed:
It's not possible to do it with just oasis because it doesn't let you specify multiple values for the Path option.
It should be easily done with ocamlbuild however by tagging everything you need with include (but I don't recommend this).
As your oasis project grows, you should look into defining library sections in oasis and using those to organize inter project dependencies. E.g., in this case you could create a "plugins" library where you include plugin_a and plugin_b. But without some planning ahead here, you will quickly run into circular dependencies.