Author: methylDragon
Reference notes for the CMake-based build system for ROS! I'm using ROS Kinetic at the moment, but these notes should apply for any ROS version Indigo onwards!
Most of the notes will be adapted from: http://wiki.ros.org/catkin/Tutorials
- You need to have Catkin installed! Luckily it comes pre-installed with ROS
- !!! I'll presume you know your way around the Linux Terminal !!!
- If not... play http://web.mit.edu/mprat/Public/web/Terminus/Web/main.html for a start!
- Make sure you've sourced your ROS environment!
source /opt/ros/<DISTRO>/setup.bash
- In the case of Kinetic,
source /opt/ros/kinetic/setup.bash
- It would be good to append said line to your .bashrc file! Access said file using
sudo nano ~/.bashrc
- It would be good to append said line to your .bashrc file! Access said file using
- Introduction
- Basic Catkin Workflow
2.1 Workflow Introduction
2.2 Making a Workspace
2.3 Sourcing the Workspace
2.4 Catkin Packages
2.5 Creating a catkin Package
2.6 Building the Workspace
2.7 More about catkin_make
2.8 Installing Dependencies - Customising your packages: Package.xml
3.1 package.xml
3.2 Example package.xml
3.3 Description
3.4 Maintainers
3.5 License
3.6 Author Info
3.7 Dependencies - Customising your packages: CMakeLists.txt
4.1 CMakeLists.txt
4.2 Required CMake Version
4.3 Package Name
4.4 Declare catkin Dependencies
4.5 Enable Python module support
4.6 Add Messages, Services, and Actions
4.7 Generate Messages, Services, and Actions
4.8 Specify Package Build-info
4.9 Declare Other Dependencies
4.10 Specify Build Targets (and more!)
4.11 Add Unit Test Handlers (Optional)
4.12 Specify Installable Targets (and Python Scripts!)
4.13 Use a Custom Compiler Version)
Long story short, when you're making packages for a huge ecosystem such as ROS, you're bound to end up with a lot of problems managing nested dependencies in its many packages.
Catkin is the next generation of the original answer to that problem (rosbuild.) It helps you manage dependencies, manage workspaces (environments), build ROS packages, and more!
Now that we're ready to go, let's get some context!
The steps you'll generally go through when developing for ROS will go roughly as such
- Make a catkin workspace (and overlaying it into your environment)
catkin_make
- Create catkin packages (ROS packages)
catkin_create_pkg
- Build the catkin workspace and its packages (and overlay them into your environment)
catkin_make
again
- Install the catkin workspace and its packages
catkin_make install
Before we can do anything with Catkin, we need to make a workspace for it. A workspace is a handy way to partition all your required packages, perfect for neatly packaging individual projects together!
Ok. So if you want to make a workspace, navigate to whatever directory you want to make it in using cd
. Then,
$ mkdir -p catkin_ws/src # Create a catkin_ws folder with a src folder in it
$ cd catkin_ws # Navigate into the folder's root
$ catkin_make # Build the required files
Great! Now source into it to allow you to run commands from the workspace in your Terminal. (You're overlaying the workspace's commands into your environment.)
$ source devel/setup.bash # Make sure you run this from your workspace's root!
# It would be good to also append this source command to your .bashrc so you don't have to keep doing it everytime you open a terminal
# Append source <workspace_directory>/devel/setup.bash
To check if you've sourced into it properly, run
$ echo $ROS_PACKAGE_PATH
# Ensure your workspace's directory appears in here
ROS packages are catkin packages!
Every catkin package requires you to have the following files and folders:
- package.xml
- Provides metadata about the package (maintainer, description, etc.)
- CMakeLists.txt
- Describes how to build the code and where to install it
- http://wiki.ros.org/catkin/CMakeLists.txt
- The individual package folders
The simplest package folder structure structure looks like this:
my_package/
CMakeLists.txt
package.xml
So the corresponding workspace folder will look like this
workspace_folder/ -- WORKSPACE
src/ -- SOURCE SPACE
CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin
package_1/
CMakeLists.txt -- CMakeLists.txt file for package_1
package.xml -- Package manifest for package_1
...
package_n/
CMakeLists.txt -- CMakeLists.txt file for package_n
package.xml -- Package manifest for package_n
Go into the src directory of the catkin workspace you want to create a package in
$ cd <workspace_directory>/catkin_ws/src
Run the catkin_create_pkg script to create your package! The command works like this
$ catkin_create_package <package_name> [depend1] [depend2] ...
This will create a package folder with its respective package.xml and CMakeLists.txt, filled somewhat with the information you gave the script.
First-order package dependencies are stored in the package.xml file.
You can also see them using
$ rospack depends1 <package_name>
If you want to see ALL the nested dependencies, use
$ rospack depends <package_name>
Note: You can make catkin_make ignore your package by leaving an empty file with the name CATKIN_IGNORE in the package's directory!
Now all you need to do is build the packages in your workspace!
$ cd <workspace_directory>/catkin_ws
$ catkin_make
catkin_make will now do certain things to certain files:
- Packages in the source space (src) will be built into the build space (/build)
- Source files, scripts, and other static files will remain in the source space (/src)
- Generated files (libraries, executables, etc.) will be placed in the devel space (/devel)
CHOOSE BETWEEN USING THE INSTALL OR DEVEL SPACE
DON'T USE MORE THAN ONE
The development space (devel) is useful for, guess what, developing. When you're using catkin_make
you don't need to invoke the install
argument each time.
The install space (install) is there for when you're ready to distribute.
You know the drill. Find the setup.bash, and source it. Appending it to .bashrc will be useful.
$ source <workspace_directory>/<devel_or_install>/setup.bash
This section will talk about customising the behaviour of catkin_make!
Though, there's another tool that's helpful if you want to trudge through the documentation for that! I'm just going to be (mostly) writing for the official catkin_make tool though! The extra tool I'm talking about is here: http://catkin-tools.readthedocs.io/en/latest/index.html
Sometimes your build might fail at a certain package (eg, at package 100 out of 150).
If you just ran catkin_make again, it'll rebuild everything starting from package 1. How do you fix stuff like that? Turns out there are more parameters you can use for catkin_make! (Actually you can customise it even more by changing your CMakeLists.txt, but if you don't want to do that, there's a lot you can do from the terminal!)
$ catkin_make --pkg <package A> <package B>
$ catkin_make --from-pkg <package>
Now we'll talk about a handy command we can use from the third-party catkin tools!: http://catkin-tools.readthedocs.io/en/latest/verbs/catkin_build.html
This command lets you preview the build order. Very useful for saving time if you know what you want to build or where you had an error!
$ catkin build --dry-run
Sometimes you might find that you don't have dependencies installed from a package you downloaded online. No issues! Use rosdep to install all the dependences!
# I'm assuming you're going to be using ROS Kinetic
# Navigate to your workspace's root and run:
$ rosdep install —from-paths src —ignore-src —rosdistro=kinetic -y
package.xml contains metadata about your package! It's written in XML, so it shouldn't be that hard to understand.
It contains the following data: (Order matters! Play it safe!)
- Description
- Maintainers
- License
- Author Info
- Dependencies
- ...
From the catkin tutorials
<?xml version="1.0"?>
<package format="2">
<name>beginner_tutorials</name>
<version>0.1.0</version>
<description>The beginner_tutorials package</description>
<maintainer email="[email protected]">Your Name</maintainer>
<license>BSD</license>
<url type="website">http://wiki.ros.org/beginner_tutorials</url>
<author email="[email protected]">Jane Doe</author>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
</package>
The description tag can be anything you'd like. But the convention is that the very first sentence should be able to summarise the point of the package.
<description>The beginner_tutorials package</description>
You can mention more than one maintainer. One per line. Make sure you have the email and the name!
<maintainer email="[email protected]">methylDragon</maintainer>
Licenses are required, one per line also.
Common licenses are: BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3.
Don't know what license to use? No worries! https://choosealicense.com/
<license>MIT</license>
Not required, but handy
<url type="website">http://github.com/methylDragon</url>
<author email="[email protected]">methylDragon</author>
REQUIRED. Arguably the most important of them all.
State your dependencies here! For either catkin or system dependencies, there are a couple of ways to state them:
For packages you need at compile time
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
For build tool packages
<buildtool_depend>catkin</buildtool_depend>
For dependencies you need at runtime
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
For dependencies you need for testing only
<test_depend>roscpp</test_depend>
<test_depend>rospy</test_depend>
<test_depend>std_msgs</test_depend>
CMakeLists.txt is what gets passed into the CMake build system when you run catkin_make. It describes where and how to build the code, and where to install the code to.
Your CMakeLists.txt file MUST follow this format otherwise your packages will not build correctly. The order in the configuration DOES count.
- Required CMake Version (
cmake_minimum_required
)- Package Name (
project()
)- Declare catkin Dependencies (
find_package()
)- Enable Python module support (
catkin_python_setup()
)- Add Messages, Services, and Actions (
add_message_files(), add_service_files(), add_action_files()
)- Generate Messages, Services, and Actions (
generate_messages()
)- Specify Package Build-Info (
catkin_package()
)- Declare other dependencies (
add_executable()
,add_dependencies()
,add_action_files()
)- Specify Build Targets (and more!)(
add_library()/add_executable()/target_link_libraries()
)- **Add Unit Test Handlers (Optional) ** (
catkin_add_gtest()
)- Specify Installable Targets (
install()
)
Handy tip:
If you want to set 'variables' for the names or paths of install targets, use
set
# Set the name to use for the executable. set (BINNAME1 pid_configure) set (BINNAME2 pid_listen) # Set the source files to use with the executable. set (SRCS1 ${SRCS1} src/lino_pid_core.cpp) set (SRCS1 ${SRCS1} src/pid_configure.cpp) set (SRCS2 ${SRCS2} src/lino_pid_core.cpp) set (SRCS2 ${SRCS2} src/pid_listen.cpp) # Build the executable that will be used to run this node. add_executable (${BINNAME1} ${SRCS1}) target_link_libraries(${BINNAME1} ${catkin_LIBRARIES}) add_dependencies(${BINNAME1} ${PROJECT_NAME}_gencfg) add_executable (${BINNAME2} ${SRCS2}) target_link_libraries(${BINNAME2} ${catkin_LIBRARIES}) add_dependencies(${BINNAME2} ${PROJECT_NAME}_gencfg)
Requires version 2.8.3 or higher
cmake_minimum_required(VERSION 2.8.3)
Self-Explanatory
project(package_name)
This is where you state what other CMake packages you need to use to build your project.
There is always at least this dependency though! (Since you require catkin in order to build your project)
# This is if you have no dependencies other than catkin
find_package(catkin REQUIRED)
# But if you have more...
# State them as components!
find_package(catkin REQUIRED COMPONENTS package1 package2 package3)
find_package() creates some environment variables once catkin finds the package. These variables tell CMake where the relevant components of the package are (header files, source, libraries, dependencies, paths, etc.)
_FOUND - Set to true if the library is found, otherwise false
_INCLUDE_DIRS or _INCLUDES - The include paths exported by the package
_LIBRARIES or _LIBS - The libraries exported by the package
_DEFINITIONS - ?
If you don't state the component as a component, the paths, libraries, etc. stated in the quoted block above won't get added. So please add them as components.
Use this if your ROS package uses python.
Create a setup.py file, and add
catkin_python_setup()
DO NOT USE THIS ALONE TO INSTALL PYTHON SCRIPTS YOU WANT TO USE AS A PACKAGE IN THE INSTALL SPACE. There's a section for that later on!
4.6 Add Messages, Services, and Actions (add_message_files(), add_service_files(), add_action_files()
)
Messages, Services, and Actions files are hugely important in ROS. Catkin has to prepare them before they can be used by ROS though!
So add them!
# Declare the message files to be built
add_message_files(FILES
MyMessage1.msg
MyMessage2.msg
)
# Declare the service files to be built
add_service_files(FILES
MyService.srv
)
# Declare the action files to be built
add_action_files( FILES
File1
File2
)
Simple enough
generate_messages()
(OPTIONAL) But you MUST have this included before you can add_library() or add_executable()
This is if you need to import modules
CATKIN_DEPENDS are catkin packages
DEPENDS are system dependencies
$(PROJECT_NAME) evaluates to whatever you passed project()
catkin_package(
INCLUDE_DIRS include
LIBRARIES ${PROJECT_NAME}
CATKIN_DEPENDS roscpp nodelet
DEPENDS eigen opencv)
include_directories(include ${catkin_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
link_directories(~/some_lib_dirs)
include_directories
: For C++ library paths (since you include them)
link_directories
: Used to add additional library paths (not recommended)
# define executable to be built using messages etc.
# and the dependencies it needs
add_executable(message_program src/main.cpp)
add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
# define executable not using any messages/services provided by this package
# and the dependencies it needs
add_executable(does_not_use_local_messages_program src/main.cpp)
add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})
There are generally two ways to specify a build target
- Executable: Something you run
- Library: Something that is used by executable targets
set_target_properties(rviz_image_view
PROPERTIES OUTPUT_NAME image_view
PREFIX "")
Sometimes you might want a different directory to install to for certain packages (like python module installs)
set_target_properties(python_module_library
PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
You need to specify the locations of your resources before specifying your targets
- Include paths: Header files, and the like
- Library paths: Libraries, of course
include_directories(include ${Boost_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS})
Executable target
# This will build a program called myProgram from the three source files stated
add_executable(myProgram src/main.cpp src/some_file.cpp src/another_file.cpp)
Library Target
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
Then you link your libraries to your executables!
target_link_libraries(<executableTargetName> <lib1> <lib2> ... <libN>)
catkin_add_gtest(myUnitTest test/utest.cpp)
Make sure catkin_package() is called BEFORE install()!
Normally, catkin_make
places built targets into the devel space (/devel). But if you want to do a proper release where users build using catkin_make install
, use install() to place built targets into the install space (/install).
install() takes the following arguments:
TARGETS
- which targets to installARCHIVE DESTINATION
- Static libraries and DLL (Windows) .lib stubsLIBRARY DESTINATION
- Non-DLL shared libraries and modulesRUNTIME DESTINATION
- Executable targets and DLL (Windows) style shared libraries
For example:
install(TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
Or if you want to install to a specific folder: (In this case, a Python library folder)
install(TARGETS python_module_library
ARCHIVE DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
)
Or if you want to install python scripts
catkin_install_python(PROGRAMS scripts/myscript
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
Or if you want to install header files
install(DIRECTORY include/${PROJECT_NAME}/
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
PATTERN ".svn" EXCLUDE
)
Or if you want to install roslaunch files, or other resources
install(DIRECTORY launch/
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
PATTERN ".svn" EXCLUDE)
Sometimes you might want ROS to compile with a different compiler version to leverage certain features of that compiler.
This one sets the compiler to use to C++11! (Which allows one to use list initialisation! Super useful for making maps.)
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")