Running OUnit tests using dune - ocaml

I'm having difficulties running OUnit tests, mostly because I'm new to both dune and OUnit. dune complains when I run dune runtest:
File "test/dune", line 4, characters 13-14:
Error: Library "f" not found.
Hint: try: dune external-lib-deps --missing #runtest
Here's the project structure:
├── dune
├── f.ml # This is the source file.
└── test
├── dune
└── f_test.ml # This is the test.
This is dune:
(executable
(name f))
This is test/dune:
(test
(name f_test)
(libraries oUnit f)) ; <- `f` here causes problems.
I can see that the error appears because dune does not know about f.ml, and hence does not know about f in the dune file.
How can I make dune compile f.ml in such a way that test/dune knows about the f library that I use in test/f_test.ml? How can I run the unit tests properly?

One possibility is to split f into a private library and an executable, and then test the split library.
EDIT:
For instance, the project structure could be updated to
├── dune
├── f.ml # f only contains the I/O glue code.
├── lib
| ├── dune
| └── a.ml # a implements the features that need to be tested.
└── test
├── dune
└── test.ml # This is the test.
with dune
(executable (name main) (libraries Lib))
For the test, test/dune:
(test (name test) (libraries Lib oUnit))
and finally lib/dune
(library (name Lib))
With this setup, the test can be run with dune runtest.

Related

How to specify gcm.cache location using g++ & CMake

I am currently working on a project which requires C++20 (g++ v11) features and CMake. The project tree is similar to the following one:
- Top level
- src
- IO
- IO.cpp
- CMakeLists.txt
- main.cpp
- CMakeLists.txt
CMake compiles IO module without any problem but It generates gcm.cache folder in a following way:
- build
- Some other CMake files and folders
- bin
- lib
- src
- IO
- gcm.cache
- IO.gcm
Therefore, g++ can not find gcm.cache folder and gives me this error:
IO: error: failed to read compiled module: No such file or directory
IO: note: compiled module file is 'gcm.cache/IO.gcm'
IO: note: imports must be built before being imported
IO: fatal error: returning to the gate for a mechanical issue
I would be grateful if anyone tell me that there is a way to specify gcm.cache locations using CMake or force CMake to search gcm files recursively or tell it to create a top level gcm.cache and store everything inside of it. I can not find any answer on anywhere since C++20 documentations are terrible. Thanks in advance...
I have experienced the exact same issue, and without actually discovering a solution have found a workaround. Complete code found here.
In short, I create a symbolic link such that subprojects are all using the gcm.cache/ directory located in the root directory of the project. Create a symlink like so:
ln -fs ../gcm.cache gcm.cache
This is the directory tree of the project:
.
├── engine
│   ├── core
│   │   └── types.cpp
│   ├── engine.cpp
│   ├── gcm.cache -> ../gcm.cache
│   ├── Makefile
│   └── memory
├── gcm.cache
├── init.sh
├── Makefile
└── testgame
├── gamelib.cpp
├── gcm.cache -> ../gcm.cache
├── Makefile
└── test.cpp
So when gcc builds the engine and testgame projects it actually uses the gcm.cache/ from the root directory. Until something better comes along this is my go-to method.

How to automaticaly dowload library from github inside lib folder cmake

I want to create my first big project in c++.
And I need to use some library. So I have made this structure
├── CMakeLists.txt
├── README.md
├── src
│   ├── CMakeLists.txt
│   ├── HelloWorld.cpp
│   ├── HelloWorld.h
│   └── main.cpp
├── tst
| ├── CMakeLists.txt
| ├── HelloWorld-test.cpp
| └── main.cpp
└── lib
and I want to automatically download and place the library in the lib folder. In my case, I want to clone googletest int lib folder. So I have tried that in my main project CMakeLists.txt file:
if(UNIX)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib/googletest)
execute_process(
COMMAND git clone "https://github.com/google/googletest.git" googletest
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
add_subdirectory(lib/googletest)
endif(UNIX)
But when I build I have this error :
CMake Error at CMakeLists.txt:17 (add_subdirectory):
add_subdirectory given source "lib/googletest" which is not an existing
directory.
The issue here is that the repository is cloned to the binary directory, but add_subdirectory looks for the directory relative to the source directory.
You need to use add_subdirectory with an absolute path here and pass a build directory. Furthermore add quotes to avoid issues with spaces in the file path.
set(REPO_PARENT "${CMAKE_CURRENT_BINARY_DIR}/lib")
set(REPO_DIR "${REPO_PARENT}/googletest")
set(REPO_BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest_build")
file(MAKE_DIRECTORY ${REPO_DIR})
file(MAKE_DIRECTORY ${REPO_BIN_DIR})
execute_process(
COMMAND git clone "https://github.com/google/googletest.git" googletest
WORKING_DIRECTORY ${REPO_PARENT})
add_subdirectory(${REPO_DIR} ${REPO_BIN_DIR})

Can OCaml dune build a project that has a flat directory structure?

Suppose I want to use dune to build a project that has a flat directory structure:
my_project/
├── dune
├── basics.ml
├── basics.mli
├── utils.ml
├── utils.mli
└── main.ml
main.ml is the main program (the entry point). main.ml depends on utils.ml, while utils.ml in turn depends on basics.ml. main.ml calls functions in utils.ml using prefix Utils (e.g. Utils.example_function x y).
The question: What should I write in the dune file in order to compile this project?
So far, all the dune examples I have seen use this directory structure instead:
my_project/
├── dune
├── main.ml
└── mylib
├── dune
├── basics.ml
├── basics.mli
├── utils.ml
└── utils.mli
Where my_project/dune is:
(executable
(name main)
(libraries mylib))
and my_project/mylib/dune is:
(library (name mylib))
and main.ml calls functions in utils.ml using prefix Mylib.Utils (e.g. Mylib.Utils.example_function x y).
I do not want this directory structure; I do not want to create a separate directory for utils.ml and basics.ml. I want all source files to be in the same directory as main.ml. Is there a way to do this using dune?
You have to list modules of each library/executable for this to work:
(executable
(name main)
(modules main)
(libraries mylib))
(library
(name mylib)
(modules utils basics))
Or you can just put everything in your executable:
(executable
(name main)
(modules basics main utils)) ; and this line is not needed anymore

Copy the produced executable in my root dir with dune

Let's say I have a simple project with the following structure:
.
├── dune-project
└── src
└── bin
├── dune
└── main.ml
dune-project
(lang dune 2.7)
(name myproject)
(package
(name myproject)
(synopsis "myproject")
(description "Basic project")
)
dune
(executable
(name main)
(public_name myproject)
)
When compiling with dune build I have a new directory _build with the following structure:
_build
├── default
│   ├── dune-project
│   ├── META.myproject
│   ├── myproject.dune-package
│   ├── myproject.install
│   └── src
│   └── bin
│   ├── dune
│   ├── main.exe
│   └── main.ml
├── install
│   └── default
│   ├── bin
│   │   └── myproject -> ../../../default/src/bin/main.exe
│   └── lib
│   └── myproject
│   ├── dune-package -> ../../../../default/myproject.dune-package
│   └── META -> ../../../../default/META.myproject
└── log
So dune is able to create a symlink from main.exe to myproject but I actually want myproject to be in . and not in _build/install/default/bin
I tried three things:
Add the rule (promote (into ../../)) in my dune file. It actually copies main.exe and not myproject in the source directory and dune clean doesn't delete main.exe in the root dir
Add the following rule in my dune file:
(rule
(target myproject)
(deps ../../myproject)
(action (copy %{target} %{deps}))
)
which gives me the following error:
❯ dune build
File "src/bin/dune", line 6, characters 0-88:
6 | (rule
7 | (target myproject)
8 | (deps ../../myproject)
9 | (action (copy %{target} %{deps}))
10 | )
Error: No rule found for myproject
Error: Dependency cycle between the following files:
_build/default/src/bin/myproject
Done: 0/0 (jobs: 0)%
Add the following rule in my dune file:
(rule
(target main.exe)
(deps ../../myproject)
(action (copy %{target} %{deps}))
)
which gives me the following error:
❯ dune build
Error: Multiple rules generated for _build/default/src/bin/main.exe:
- src/bin/dune:6
- src/bin/dune:2
Done: 0/0 (jobs: 0)%
And honestly I don't know what else I can do. I read the manual and can't find a solution. I wouldn't mind if I could just type dune run but since dune allows to build multiple executables, I understand it's not an option. I'd like to be able to write dune run myproject since I'm using the public name of my executables but dune can't find it. So I just want to have myproject in my root directory so I can just type ./myproject and be happy.
Do you know about dune exec? It is what you call dune run I believe.
From the root of your project, you can do dune exec ./bin/main.exe.
you can also use promote. For instance,
(executable
(name my_tool)
(promote
(until-clean)
(into ../bin)))

Run a target from a custom target in CMake

I have the following file structure:
├── Generator/
│   ├── output/
│   └── script.py
│
└── FinalProgram/
├── build/
├── src/
│   └── main.cpp
├── include/
│   └── MyClass.h
└── CMakeLists.txt
The Generator/script.py file is a script that generates c++ files in the Generator/output folder. That script can be launched with two different arguments (SimA and SimB), each one generating a different set of files.
On the other hand, the FinalProgram needs to use that set of generated files, so every time I want to compile the FinalProgram with the SimA set of files, I have to
cd Generator
./script.py SimA
cp output/*.cpp ../FinalProgram/src
cp output/*.h ../FinalProgram/include
cd ../FinalProgram/build && cmake ..
make
What I want is to be able to type
make SimA
Or
make SimB
So everything happens automatically. In both cases the executable must be the same, so obviously I cannot have two different add_executable blocks. I guess I should create two add_custom_target blocks, one for each possible value, do all the work there and finally call the target that compiles everything. Hence, the real question is, how can I run another target from within an add_custom_target block? Of course, I guess I could use
COMMAND make
But that... that makes me cry. Isn't there a better way?
As MadScienceDreams mentioned, you can use add_custom_command to generate the c++ file, then you can use generated file in add_executable command.
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp
COMMAND ${PYTHON_EXECUTABLE} script.py SimA
DEPENDS ${CMAKE_SOURCE_DIR}/../Generator/script.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../Generator)
set_property(SOURCE ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp PROPERTY GENERATED TRUE)
add_executable(SimA ${CMAKE_SOURCE_DIR}/../Generator/output/SimA.cpp ...)
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp
COMMAND ${PYTHON_EXECUTABLE} script.py SimB
DEPENDS ${CMAKE_SOURCE_DIR}/../Generator/script.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../Generator)
set_property(SOURCE ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp PROPERTY GENERATED TRUE)
add_executable(SimB ${CMAKE_SOURCE_DIR}/../Generator/output/SimB.cpp ...)
For more information see
http://www.cmake.org/cmake/help/v3.0/command/add_custom_command.html
http://www.cmake.org/cmake/help/v3.0/prop_sf/GENERATED.html