How to use CMake with Catch2? - c++

From Catch2's example, I tried to run this example with cmake where structure of my project is like this:
/factorial
+-- CMakeLists.txt
+-- /bin
+-- /include
| +-- catch.hpp
| +-- fact.hpp
+-- /src
| +-- CMakeLists.txt
| +-- fact.cpp
+-- /test
+-- CMakeLists.txt
+-- test_fact.cpp
fact.cpp:
unsigned int factorial( unsigned int number ) {
return number <= 1 ? number : factorial(number-1)*number;
}
fact.hpp:
#ifndef FACT_H
#define FACT_H
unsigned int factorial(unsigned int);
#endif
test_fact.cpp:
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "fact.hpp"
TEST_CASE( "factorials are computed", "[factorial]" ) {
REQUIRE( factorial(1) == 1 );
REQUIRE( factorial(2) == 2 );
REQUIRE( factorial(3) == 6 );
REQUIRE( factorial(10) == 3628800 );
}
I tried several ways already to build this project with cmake but it's failed. Sometimes I got an error:
cpp:X:XX: fatal error: 'fact.hpp' file not found
...
and sometimes I got:
Undefined symbols for architecture x86_64:
"_main", referenced from:
...
when I run make.
What should I have in factorial/CMakeLists.txt,factorial/src/CMakeLists.txt and factorial/test/CMakeLists.txt, if I want to have my execution files in factorial/bin?
Additional:
This is my CMakeLists.txts (I think they are completely wrong).
factorial/CMakeLists.txt:
project(factorial)
cmake_minimum_required(VERSION 2.8.12)
add_definitions("-std=c++11")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
add_subdirectory(src)
add_subdirectory(test)
factorial/src/CMakeLists.txt:
project(factorial)
cmake_minimum_required(VERSION 2.8.12)
add_executable(fact fact.cpp)
factorial/test/CMakeLists.txt:
project(factorial)
cmake_minimum_required(VERSION 2.8.12)
add_executable(test_fact test_fact.cpp)
target_include_directories(test_fact PRIVATE ${CMAKE_SOURCE_DIR}/include)

Many problems here:
add_executable(fact fact.cpp)
The call should be using add_library (You could also specify STATIC or SHARED), since you are only defining a factorial function, not an executable with a main function.
add_executable(fact fact.cpp)
The file should be test_fact.cpp and the target should have a different name to avoid conflict with the previous library you created. Also, your fact.cpp doesn't include fact.hpp. Last but not least, instead of doing target_include_directories, just write the following in your top-level CMakeLists.txt:
include_directories(include)
Now, all subdirectories should be able to access the header files. Beware that this removes control of the scoping of the header files (PRIVATE vs PUBLIC vs INTERFACE) and allows all subdirectories to access the header files. If you want to restrict this behavior, then use target_include_direcories for all targets (Your library and the test executable). For this example, since everything needs to access the header files, there is no problem with the statement above.
More problems:
project(factorial)
cmake_minimum_required(VERSION 2.8.12)
Either switch the order of these statements, or remove both of them. (You only need them in your top level CMake file)

If you look at the CMake documentation, the PROJECT_SOURCE_DIR variable is define as that:
Top level source directory for the current project.
This is the source directory of the most recent project() command.
Since you called project many times, that variable will constantly change. I would suggest you to remove your project directive, or to use CMAKE_SOURCE_DIR, which always point to the source directory of the whole project.
As a side note, I suggest to use set(CMAKE_CXX_STANDARD 11) instead of add_definition

Related

Add header-only library to executable in CMake

I am trying to include the Windows GNU GSL headers-only library (downloaded from https://gnuwin32.sourceforge.net/packages/gsl.htm) to an example code in C++ but have lots of errors unfortunately.
Here is the structure of my repository:
folder/
gsl/
gsl_sf_bessel.h
gsl_mode.h
*.h # other header files
main.cpp
CMakeLists.txt
main.cpp is as such:
#include <stdio.h>
#include <gsl_sf_bessel.h>
int main (void)
{
double x = 5.0;
double y = gsl_sf_bessel_J0 (x);
printf ("J0(%g) = %.18e\n", x, y);
return 0;
}
and CMakeLists.txt:
cmake_minimum_required(VERSION 3.0.0)
project(demoproject VERSION 0.1.0)
add_executable(
demoexecutable
main.cpp
)
target_include_directories(
demoexecutable
PUBLIC
gsl/
)
The error I get when compiling is main.cpp is:
fatal error: gsl/gsl_mode.h: No such file or directory
[build] 26 | #include <gsl/gsl_mode.h>
It looks like it managed to find gsl_sf_bessel.h from gsl/ but gsl_sf_bessel.h needs in its turn gsl_mode.h which the compiler cannot find. Any ideas on how to solve this issue?
I tried different combinations in CMakeLists.txt of functions such as add_library, include_directories, target_link_libraries but nothing worked unfortunately.
Try adding ${CMAKE_CURRENT_SOURCE_DIR} as an include directory. That is the directory that contains gsl/gsl_mode.h. The gsl directory does not contain itself, so adding it as an include directory will not make that error go away.
I've adopted the following practice in my CMake projects:
1) Project's folder structure.
Have an include/${PROJECT_NAME} subfolder (i.e., include subfolder and, within that, another subfolder named after the project's name). For example, in your case:
demoproject_or_whatever
|- include
| \- demoproject
| \- gsl
| |- gsl_sf_bessel.h
| \- gsl_mode.h
|- src
\- test
2) src/CMakeLists.txt.
Set an include_dir var at the top of the file, then use it when setting the executable's target_include_directories.
set(include_dir ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME})
add_executable(${PROJECT_NAME} ${app_sources})
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${include_dir}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
3. Including header files from source files.
Use paths relative to include/${PROJECT_NAME}.
#include <gsl/gsl_sf_bessel.h>

Make all files in a folder accessible to all projects (subdirectories)

I have multiple projects (subdirectories) inside my repository. All projects have only one executable file named main.cpp and they all use libraries from the common folder with #include statements. The folder structure looks like this:
root
|
├────common
| ├──── example.h
| ├──── example.cpp
| ├──── *.h # many header files
| └──── *.cpp # many source files
|
├────Project_A
| ├──── CMakeLists.txt
| └──── main.cpp
|
├────Project_B
| ├──── CMakeLists.txt
| └──── main.cpp
|
└──── CMakeLists.txt
This is my attempt at writing the root's CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
project ("root")
file(GLOB_RECURSE CommonLibs ${CMAKE_SOURCE_DIR}/common/*.cpp)
link_libraries(${CommonLibs})
add_subdirectory ("Project_A")
add_subdirectory ("Project_B")
Project_A's CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
add_executable (Project_A "main.cpp")
Project_B's CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
add_executable (Project_B "main.cpp")
However when running any of the projects I get this error:
LNK1107 invalid or corrupt file: cannot read at 0x7AC, file example.h
I don't believe the file is corrupted because before I tried to use link_libraries() in root's CMakeLists.txt I was getting other error:
Unresolved external symbol SomeNamespace::ExampleClass::ExampleClass(bool)
Possible duplicates
Other questions such as this one don't solve my problem because they usually work with a more complex folder structure. Also there are questions that attempt to target only single project like this one, but I have multiple projects.
Can you please provide a concise solution?
Add a CMakeLists.txt to your common/ directory:
root
|
├────common
| ├──── CMakeLists.txt <-------- Add this
| ├──── example.h
....
common/CMakeLists.txt
file(GLOB_RECURSE CommonLibsFiles ${CMAKE_CURRENT_SRC_DIR}/*.cpp)
add_library(CommonLibs ${CommonLibsFiles})
...
root's CMakeLists.txt
...
### NO NEED FOR THESE
file(GLOB_RECURSE CommonLibs ${CMAKE_SOURCE_DIR}/common/*.cpp)
link_libraries(${CommonLibs})
###
add_subdirectory(common) #CommonLibs will now be visible to children directories
...
Now link the libraries as required. For example for project A:
Project_A's CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
add_executable (Project_A "main.cpp")
target_link_libraries(Project_A PRIVATE CommonLibs) #link to common libs here,
All the targets/variables that are created or are visible to parent cmake file, are visible to children. The reverse is not true though. For children to expose their variables to parent, they need to explicitly specify PARENT_SCOPE
The following lines in your top-level CMake do not make much sense:
file(GLOB_RECURSE CommonLibs ${CMAKE_SOURCE_DIR}/common/*.cpp)
link_libraries(${CommonLibs})
The link_libraries() command accepts libraries or library targets, not variables (such as CommonLibs). You can use the CommonLibs variable to define a new library target, then link the library target to your executables using link_libraries():
file(GLOB_RECURSE CommonLibs ${CMAKE_SOURCE_DIR}/common/*.cpp)
# Add this line.
add_library(MyCommonLib SHARED ${CommonLibs})
link_libraries(MyCommonLib)
Please note that the use of link_libraries() in general is discouraged, even in CMake documentation itself. You should prefer linking your MyCommonLib to the specific executables that require it using target_link_libraries.
target_link_libraries(Project_A PRIVATE MyCommonLib)

Link up src/ with CMake as library #include'd with some `libname/` prefix

For my research project I am setting up a project (coom) to benchmark a set of algorithms on a data structure. For unit testing I settled on Bandit, which leaves me with a project structure that looks as follows:
+ root
|-- CMakeLists.txt
|-+ external/
| \-- bandit/
|-+ src/
| |-- CMakeLists.txt
| |-- node.cpp
| \-- node.h
\-+ test/
|-- CMakeLists.txt
|-- test.cpp
\-- test_node.cpp
From my experience with other languages, this seems to me a standard project structure? The test/ folder contains unit tests for the logic in src/ and no dependencies are intermixed with the source and test code, but are instead in external/.
The testing files I want to look as follows (with irrelevant parts removed)
// test/test.cpp
#include <bandit/bandit.h>
(...)
#include "test_node.cpp"
int main(int argc, char* argv[]) {
(...)
}
// test/test_node.cpp
#include <coom/node.h>
(...)
But my problem is, that when I try to compile with cmake .. and the subsequent Makefile, they are unable to find the source code in src/ where I get the compiler error:
fatal error: coom/node.h: No such file or directory.
I would expect the test/CMakeLists.txt should look somewhat like the following:
# test/CMakeLists.txt
add_executable (test_unit test.cpp)
target_link_libraries(test_unit coom)
I cannot figure out how to setup the CMakeLists.txt and src/CMakeLists.txt to ensure I get the desired outcome above. Currently they look as follows:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project (coom VERSION 0.1)
# ============================================================================ #
# Dependencies
(...)
# ============================================================================ #
# COOM project
add_subdirectory (src)
add_subdirectory (test)
# src/CMakeLists.txt
# ============================================================================ #
# Link up files for the library
set(HEADERS
node.h
)
set(SOURCES
node.cpp
)
add_library(coom ${HEADERS} ${SOURCES})
I can see from other projects, that it is possible to link the src/ directory with some libname/ prefix, but I cannot discern from their CMakeLists.txt files what I am doing wrong. I have looked at writing a coom.pc.in file and providing an install-target, and tried to set_target_properties with either FOLDER coom or PREFIX coom, but neither worked. I can hack an include_directory(../src) into the test/CMakeLists.txt to be able to include the file via an #include <node.cpp>, but that screams I'm doing something inherently wrong.
I'm at this point very much pulling my hairs out, and the CMake documentation is of very little help to me.
Your coom target has no include directories defined. You can define the include directories to use for this target (with target_include_directories()), and propagate these include directories so they are visible to the consuming test_unit target (by using PUBLIC):
# src/CMakeLists.txt
# ============================================================================ #
# Link up files for the library
set(HEADERS
node.h
)
set(SOURCES
node.cpp
)
add_library(coom ${HEADERS} ${SOURCES})
target_include_directories(coom PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Furthermore, the file path to the node.h header is coom/src/node.h, not coom/node.h. But, because you now have coom/src as an public include directory, you can use the following to include the node.h header in your test file:
#include <node.h>

Library headers not visible in headers but perfectly visible inside .cpp file under cmake build. Why?

I have a C++ project containing several modules, some built as libraries, with such structure:
/MyProject
+---/build
/ModuleA
+---CMakeLists.txt <- module level CMakeLists
+---/src
| +--CMakeLists.txt <- src level CMakeLists
| +--FileA1.cpp
| +--FileA2.cpp
+---/include
| +--FileA1.h
| +--FileA2.h
| +--FileA3.h
/ModuleB
+---CMakeLists.txt
+---/src
| +--CMakeLists.txt
| +--FileB1.cpp
| +--FileB2.cpp
+---/include
| +--FileB1.h
| +--FileB2.h
| +--FileB3.h
main.cpp
CMakeLists.txt <- project level CMakeLists
CMakeLists.txt files look as follow:
project level:
cmake_minimum_required(VERSION 3.05)
project(MyProject)
subdirs(ModuleA ModuleB)
set(CMAKE_CXX_STANDARD 11)
add_executable(MyProject main.cpp)
target_link_libraries(MyProject ModuleA ModuleB)
module level:
subdirs(src)
src level:
FIND_PACKAGE(SomePackage REQUIRED)
INCLUDE_DIRECTORIES(
${SomePackage_INCLUDE_DIR}
${MyProject_SOURCE_DIR}/ModuleA/include
)
SET(SOURCE_FILES <all files from ModuleA/src goes here>)
ADD_LIBRARY(ModuleA STATIC ${SOURCE_FILES})
TARGET_LINK_LIBRARIES(ModuleA
${SomePackage_LIBRARIES}
)
The problem is: when I include header files from 'SomePackage' in my ModuleA header files (i.e. SomePackageFile.hpp in FileA1.h) I get an error while running a build with make:
fatal error: SomePackageFile.hpp: No such file or directory
When I include them in cpp files they are visible and project compiles correctly. I assume that is sth wrong with CMakeLists on src level or entire hierarchy of files is missing something.
I have a github project to be used as a skeleton for other projects:
https://github.com/gnyiri/cmake-sandbox
If you follow this layout, you will not need to add ${SomePackage_INCLUDE_DIR} to INCLUDE_DIRECTORIES which is not the best way to add directories to the include path otherwise.
In a nutshell, you should define a new library like this:
project(module_a)
set(sources
src/source_a_1.cc
)
add_library(library_a
${sources}
)
target_include_directories(library_a
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
Then, if you define another library (library_b), you only need to add library_a in target_link_libraries:
project(module_b)
# set list of sources, needs to be extended when new source arrives
set(sources
src/source_b_1.cc
)
# define a library (static by default -> liblibrary_b.a or library_a.lib will be generated)
add_library(library_b
${sources}
)
# include directories
target_include_directories(library_b
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
# link library_b
target_link_libraries(library_b
library_a
)
Note that in this source tree, all the header files are located in
<module>/include/<module>
This way you will include a header file from like this:
#include "<module>/<module_header.h>"
This is simply because /include will be on include path.
Switching from INCLUDE_DIRECTORIES() to TARGET_INCLUDE_DIRECTORIES() was the case, no change to the structure of the project was needed.

CMakeLists.txt add subfolder which represents namespace (just for organization)

Consider the following setup:
-project
--src
---CMakeLists.txt
---main.cpp
---Application.cpp
---Application.hpp
---subfolder
----SomeClass.cpp
----SomeClass.hpp
--bin
And consider this CMakeLists.txt
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
# Include directories
INCLUDE_DIRECTORIES("subfolder")
# Executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
Now, as long as I had all my classes in the same folder (src) everything worked perfectly fine.
But now I want to restructure my application a bit. I want to build a folder-hierarchy that represents the namespaces.
Of course in my includes I would then use
#include "subfolder/SomeClass.hpp"
but it doesn't work that way. I had a look at the manpage but there are so many options in CMake and it's often talking about standalone libraries that have their own CMakeLists.txt... I'm not that far yet. I just want to add a subfolder, that's all.
Until now I've used QMake for my C++ projects, but I wanted to dive into CMake now.
Are there any useful tutorials out there? I've found a few, but they they don't cover the basics.
The recommened way is to use a CMakeLists.txt for every subdirectory in existence. If you want to have subdirectories and organize them without having to create multiple CMakeLists.txt files, you can create one in the main directory with those contents:
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
include_directories(src)
file(GLOB_RECURSE SRC_LIST *.c* *.h*)
# Executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
Using aux_source_directory is used mainly for template related things. Also it's common practice to use a toplevel CMakeLists.txt, which includes the further files, has common project settings etc.:
<project>
|
+- CMakeLists.txt
|
+- src/
|
+-- CMakeLists.txt
|
+-- main.cpp
|
…
So this would look like:
CMakeLists.txt (Project dir):
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
include_directories(src) # Add 'src' to include paths
subdirs(src) # Includes the 'src' directory and its cmake file
# ...
Now you can use the include path as expected.
CMakeLists.txt (src dir):
# Better add src files this way:
add_executable(${PROJECT_NAME} main.cpp Application.cpp)
subdirs(subfolder) # TODO: handle subfolder
The subfolder can be added through an additional library target, that is linked to your executable. Usually there's another CMakeLists.txt file in there.
Also make sure your cmake cache is updated; best you recreate it.