A Guide to CMakeLists
Since we are using ROS and ROS uses the cmake build system, we have the (dis)pleasure of writing CMakeLists.txt for our packages :tada:. This page should provide a working example you can use for your packages and break down each of the steps
Working Example¶
We will use (a slightly modified version of) the CMakeLists.txt of mpc_controller as an example.
cmake_minimum_required(VERSION 3.0.2)
project(mpc_controller)
set(CMAKE_CXX_STANDARD 17)
find_package(catkin_simple REQUIRED)
find_package(casadi REQUIRED)
catkin_simple()
if(${acados_pacejka_mpcc_solver_FOUND})
add_definitions(-Dacados_pacejka_mpcc_solver_FOUND)
else()
message("Acados Pacejka MPCC Solver not found. Building without AcadosMPCC support")
endif()
#############
# Libraries #
#############
add_library(intermediate_library_target src/source_1.cpp src/source_2.cpp)
target_include_directories(bla PUBLIC some/include/dir)
cs_add_library(${PROJECT_NAME}
src/pacejka_controller/mpcc_pacejka_controller.cpp
src/pacejka_controller/tracking_mpc_pacejka_controller.cpp)
add_dependencies(${PROJECT_NAME} intermediate_library_target)
target_link_libraries(${PROJECT_NAME} intermediate_library_target)
##########
# Tests #
##########
if (CATKIN_ENABLE_TESTING)
catkin_add_gtest(controller_test test/test_controller.cpp WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test/)
target_link_libraries(controller_test ${catkin_LIBRARIES} ${PROJECT_NAME} casadi stdc++fs)
endif()
##########
# Export #
##########
cs_install()
cs_export()
Breakdown¶
Note that this project uses catkin_simple ("docs") to streamline and simplify some aspects of the CMakeLists.txt. You can achieve the same functionality of the project without this package but the resulting file will be more verbose. The catkin_simple macros will be elaborated on and the corresponding pure cmake commands will be listed.
Some nomenclature¶
Generally cmake refers to libraries (e.g. what the mpc_controller package provides) and executables (e.g. the final executable in ros_controllers which is run by ROS) as targets However, a target can also be more general. For instance, a python or bash script can also be a target (for instance if you want to auto generate some code). By specifying dependencies between targets, you tell cmake in which order they should be built. Furthermore, since the targets are compiled as 'separate instances', you will have to link targets to each other if they have use functionalities provided by other targets. E.g. library A implements how to write to a file and application B is your cool application which needs to write to a file. In this case you probably included some headers of Library A in application B and use functionality provided by library A. So you need to tell cmake that B needs to link against A in order for your cool application to work.
cmake_minimum_required¶
This specifies the required version of cmake to build the project. if you use more advanced cmake features you might need a higher version but 3.0.2 should be sufficient for most basic projects.
project¶
This specifies the project name to cmake. Convention is to give it the same name as the top level folder and the name you provided in the package.xml
set¶
This is a standard cmake command to set environment variables for the build. Here we specify that we want to use the C++17 standard for our code, giving us access to C++17 language features.
find_package¶
This is a standard cmake command which finds the provided packages (who would've thought). the REQUIRED argument will result in an error being thrown if the package is not found. Here, we are looking for the catkin_simple and casadi packages.
For nerds: this command searches for a Find<PackageNAme>.cmake file in the CMAKE_MODULE_PATH and in modules provided by cmake or for a <package>-config.cmake (which are provided by the packages). If you're lucky, you will never have to dig any deeper. If you are unlucky, (been there, i feel for you) you have to write such a file by yourself. But these files essentially just provide the <package>_FOUND <package>_INCLUDE_DIRS and <package>_LIBRARIES variables which your project uses to build the targets. If you are one of the poor souls which has to write such a file, you can find an example here. Note that there is (afaik) no clear convention of <package>_INCLUDE_DIRS, <package>_LIBRARIES of <package>_INCLUDE_DIR and <package>_LIBRARY. However, ROS uses the first option. Link for the real nerds.
catkin_simple¶
Our first catkin_simple macro. This "initalizes" catkin_simple and does some magic under the hood:
- It creates a top level target and sets up some environment variables.
- it
find_packages all packages specified in thepackage.xmlfile. - Set the local include dir to `project_dir/include
- Compiles ros messages/services/actions if the folders
project_dir/[msg|srv|action]exist. - Generate dynamic reconfigure files if
project_dir/cfgexists with valid*.cfgfiles.
For the actual catkin macros for these actions please refer to this resource
If all dependencies in package.xml are required for your project you will need to pass the argument ALL_DEPS_REQUIRED to this macro, leading the build to fail if one dependency cannot be found.
if, else & endif¶
Allows us to perform some conditional compilations. In this case, we check whether the relevant solver package has been found in catkin_simples find_package(the_package_name) and if not we compile the project without support for this solver. Note that we did not specify ALL_DEPS_REQUIRED in catkin_simple so this package might not be present.
cs_add_library¶
This catkin_simple macro is responsible for declaring libraries we want to generate and does some bookkeeping under the hood.
- Calls
add_librarywith the same target name and files provided to the macro which generates the library. - Calls
target_link_librarieswith${catkin_LIBRARIES}which links the created library to the catkin libraries (i.e. all packages built using catkin). (for info aboutcatkin_LIBRARIESplease refer to section 5.2
You can also provide the NO_AUTO_LINK if you dont want to link with catkin_LIBRARIES (though there should not really be a reason for this unless your target has no existing dependencies on packages built by catkin). Another noteworthy argument is NO_AUTO_EXPORT which marks the library as not exported by the current package (e.g. if its an internal implementation detail and you only want to export a dependent target).
If you have other dependencies you need to link against (e.g. non catkin packages but system libraries or libraries that were built using the standard cmake function add_library) you need to explicitly call target_link_libraries(target_name library_1 library_2). If you need headers for the compilation of the intermediate libraries, you can add them using target_include_directories with the keyword specifier PUBLIC which makes them available to consuming targets.
A common pitfall is that you need to specify all relevant .cpp files in the cs_add_library macro (or add_library respectively) call for the library to be generated. E.g. If you have a file lib.cpp which implements the lib but you implement some utilities in utils.cpp (with the respective (optional) .h files), then you need to call cs_add_library(my_cool_lib lib.cpp utils.cpp).
cs_add_executable (not mentioned in the CMakeLists.txt)¶
This is another relevant catkin_simple macro. its very similar to cs_add_library apart from that it generates an executable instead of a library target. Use this for your ros nodes, etc.
- Calls
add_executablewith the same target name and files provided to the macro which generates the executable. - Calls
target_link_librarieswith${catkin_LIBRARIES}which links the created library to the catkin libraries (i.e. all packages built using catkin). (for info aboutcatkin_LIBRARIESplease refer to section 5.2
Again, you need to specify all relevant .cpp files in the cs_add_executable macro (or add_executable respectively) call for the library to be generated.
catkin_add_gtest¶
This allows us to add gtests to a package. The calling signature is comparable to add_executable. you first specify the target name, then all the source files and additionally the working directory of the tests (which is relevant for file name resolution if you want to include config files for instance).
cs_install¶
This installs the targets which were defined using cs_add_* macros. Takes care of the header files in include, as well as any launch files etc.
cs_export¶
This macro calls catkin_package. It directly adds the include directory to the package as well as adds any libraries which were declared using cs_add_library. Hence, in dependant packages, you can simply include catkin_INCLUDE_DIRS and link against catkin_LIBRARIES to link against any library defined in this project using the cs macro. You can add further dependencies manually:
INCLUDE_DIRS: any additional directory which includes public header files that are required for dependant packages.LIBRARIES: any additional libraries you wish to make available to other packages that were not added using the cs macro.DEPENDS: Any dependent (system) targets you need to expose for linking purposes.