What's A Catkin?

A Dialogue

What the heck! I've been using ROS since mango tango, and you're telling me that rosbuild is deprecated in ROS Groovy‽ Explain!

Calm down, friendly developer! It's not the end of the world, and rosbuild will still be supported during the transition to the new buildsystem, called "catkin".

Don't tell me to calm down! I'm outraged and I feel violated by these changes! I demand to know the names of the renegades who made these decisions without my consent!

The phasing-out of rosbuild has been an ongoing process that started only shortly after rosbuild stabilized back in 2010. An experimental version of this new buildsystem was deployed with ROS fuerte and well-advertised at ROSCon. Like most substantial changes to ROS, all users were invited to contribute via the ROS groovy special interest groups (SIGS), and you can see the public discussion about the latest iteration of the new buildsystem here.

Well then, what's the deal?

OK. Here's the deal...

No, wait! I don't care why Catkin is the way it is!, just tell me what I need to do different to use catkin packages!

Alright, here's what you need to know to use catkin packages...

That's fine, but I need to create a new catkin package!

Here's how to go about creating a new catkin package...

I don't need to create a new package! How do I convert my rosbuild package to use catkin?

Here's how to migrate your rosbuild package to catkin...

I've already looked at all the documentation but the world is broken!

Here's how to re-set your workspace...

I don't care about all the catkin flexibility! How do I use catkin like I'm used to using rosbuild?

Here's how to use wrapper CMake macros to make catkin simpler...

Catkin: A New "Buildsystem"

If you're curious about the whole story, read through the following links (there's a lot to read). Otherwise, skip to the next section.

See Catkin documentation.

See Catkin tutorials.

See how sausage is made...

How To Use Catkin Packages

Structured Atomic Workspaces

When using rosbuild, you could scatter your source-based ros packages across your filesystem, but as long as they were under $ROS_PACKAGE_PATH, rosbuild would build each package's libs and bins in-place inside of their respective packages. When you want to build a given package, calling rosmake would build a package and all of its dependencies. rosbuild then calls cmake (configuration) and make (compilation/linking) in each package directory.

You can't build-from-the-hip with Catkin like you did with rosbuild. Instead, all of the packages you wish to build at once need to be collected in a single source directory inside what's called a "Catkin workspace." A Catkin workspace normally looks like this (once it's been initialized and built):

~/my_workspace/
├── build/
├── devel/
└── src/
    ├── CMakeLists.txt -> /opt/ros/$ROS_DISTRO/share/catkin/cmake/toplevel.cmake
    ├── your_awesome_package/
    ├── my_awesome_package/
    └── his_terrible_package/

The layout of a Catkin workspace is designed to be used like a single CMake project. This means that as far as CMake is concerned, for a given Catkin workspace, you have one source directory, one build directory, and only one call to cmake and make.

There are two ways to build this workspace: (1) manually, or (2) with the catkin_make utility.

For the manual way (1), you initialize the workspace, configure the build directory from the src directory, and then call make:

catkin_init_workspace ~/my_workspace/src    # Initialize the workspace (once)
cd ~/my_workspace/build                     # Navigate to the build directory
cmake ../src -DCATKIN_DEVEL_PREFIX=../devel # Configure CMake from the src directory
make                                        # Build all the workspace's packages

For the second way (2) catkin_make will perform these steps for you:

cd ~/my_workspace
catkin_make

SUPER SUBTLE SECRET BEHAVIOR

Each time you build a Catkin workspace for the first time, the contents of the $CMAKE_PREFIX_PATH environment variable is cached in the following files:

build/catkin_generated/installspace/_setup_util.py
devel/_setup_util.py

When you source the setup.sh file generated by a given workspace, it will prefix $CMAKE_PREFIX_PATH with the devel directory of this workspace.

This concatenation of $CMAKE_PREFIX_PATH paths enables chaining of workspaces, and is done AUTOMATICALLY when you build a Catkin workspace.

If you want to truly reset the environment for a workspace after sourcing one or more setup files, you can do so by unsetting $CMAKE_PREFIX_PATH and re-sourcing your installed ROS setup file like so:

rm build/catkin_generated/installspace/_setup_util.py
rm devel/_setup_util.py
unset CMAKE_PREFIX_PATH
source /opt/ros/$ROS_DISTRO/setup.sh
catkin_make --force-cmake

See the Catkin ticket titled "catkin implicit workspace chaining dangerous" for more information.

Locating ROS Resources

With rosbuild, all of your favorite ros* tools (roscd, rosrun, roslaunch, and roswtf etc.) would use $ROS_PACKAGE_PATH to locate ROS resources. But now with Catkin, everything gets built into the single build directory, so none of the libs or bins are going to be in their respective source directories.

If you want to interact with your workspace like you're used to, you need to source the setup.sh file that catkin generates for you. This will add all the appropriate environment variables that would be otherwise too complicated to manage yourself:

source catkin_workspace/devel/setup.sh

How To Create a New Catkin Package

See this lovely tutorial: http://www.ros.org/wiki/catkin/Tutorials/CreatingPackage

How To Migrate Your Rosbuild Package to Catkin

You can start off with the "catkinize" migration scripts, but these will not work in all cases, and currently fail to migrate some things like messages, rosdeps, and python libraries.

See the "catkinize" tutorials here: catkin/migrating_from_rosbuild#Migration_Help_-_.22Catkinize.22_Scripts

Dependencies

Previously, dependencies were only located in one place, a package's manifest.xml, and no distinction was made between dependencies needed at build-time, run-time, nor test-time.

With Catkin, these different classes of dependencies are defined, and need to be specified as so in package.xml and CMakeLists.txt as shown below:

`package.xml`

<package>
  ...
  <build_depends>some-dev-lib</build_depends>
  <run_depends>some-daemon</run_depends>
  <test_depends>some_pkg_supporting_testing</test>
  ...
</package>

`CMakeLists.txt`

All of these dependencies need to be passed into the catkin_package(DEPENDS ...) macro:

...
catkin_package(DEPENDS some-dev-lib some-daemon some_pkg_supporting_testing ... )
...

Then, these dependencies can be further split into two groups:

  1. dependencies which are find_package()-able

    • catkin packages
    • libraries that have Find*.cmake files installed in the system
  2. dependencies which are NOT find_package()-able

    • libraries that DO NOT have Find*.cmake files installed in the system

The first category can be passed into the find_package(catkin DEPENDS ...) macro:

...
find_package(catkin DEPENDS dep1 dep2 dep3 ... )
...

Then the dep*_INCLUDE_DIRS and dep*_LIBRARIES and other CMake variables will be stored in catkin_INCLUDE_DIRS and catkin_LIBRARIES and others, respectively.

rosdeps

Previously, rosbuild made a distinction between rosdep dependencies (system dependencies) and ROS package dependencies. Catkin no longer makes that distinction when declaring dependencies, but it still uses the same rosdep definitions as rosbuild.

These dependencies need to go in your package's package.xml AND CMakeLists.txt. Consider the example where you depend on a system library called MySystemLib and which has rosdeps MySystemLib,MySystemLib-dev, and MySystemLibDaemon needed at either build and runtime. Then you would do the following:

rosdeps in `package.xml`

<package>
  ...
  <build_depend>MySystemLib-dev</build_depend>
  ...
  <run_depend>MySystemLib</run_depend>
  <run_depend>MySystemLibDaemon</run_depend>
  ...
</package>

rosdeps in `CMakeLists.txt`

...
catkin_package(
  DEPENDS ... MySystemLib MySystemLib-dev MySystemLibDaemon ...
  ...
  )
...

Executables and Libraries

Previously, rosbuild-based packages would "push" their compile flags and linker flags via <export> tags to any dependent package. With catkin, however, you need to "pull" all the flags and pass them into build targets explicitly.

Suppose your rosbuild CMake code looked like this:

...
rosbuild_add_executable(my_node src/my_node.cpp)
...

The ROS headers and libraries are no longer automatically added via the rosbuild_add_executable macro, so you need to add them like so:

...
include_directories(${catkin_INCLUDE_DIRS})
add_executable(my_node src/my_node.cpp)
target_link_libraries(my_node ${catkin_LIBRARIES})
...

Messages & Services

Previously, ROS message and service generators were first-class citizens in rosbuild. Now, you need to do the following.

Add `genmsg` deps to `package.xml`

<package>
  ...
  <build_depend>message_generation</build_depend>
  <run_depend>message_runtime</run_depend>
  ...
</package>

Add `genmsg` dep to `CMakeLists.txt`

...
find_package(catkin REQUIRED message_generation)
...
catkin_package(
  DEPENDS ... message_runtime ...
  ...
  )

Add message list and generator to `CMakeLists.txt`

Note: this must be added BEFORE the call to catkin_package()

...
add_message_files(
  FILES
  MyCoolMessage.msg
  SuperCoolMessage.msg
  MessageIDontUseOften.msg)

generate_messages(DEPENDENCIES geometry_msgs std_msgs sensor_msgs)
...

Python Modules (Libraries)

Previously, if you wanted to provide a python module to scripts in your package or to other packages, you would add <export> tags in your manifest.xml, and then client scripts would call roslib.load_manifest(...) to inject these paths into the python search path.

The Catkin setup.sh scripts now modify $PYTHONPATH directly, and Catkin now gets this configuration from the standard python setup.py file. Furthermore, roslib.load_manifest(...) is no longer used in client scripts, since python modules will already be in the search paths defined by setup.sh.

Consider the ROS package with the following python module:

my_package/
├── CMakeLists.txt
├── package.xml
└── src
    └── mymodule
        ├── __init__.py
        └── some_functions.py

Add a `setup.py` file to your Package

Note: catkin_pkg.package.parse_package_for_distutils() is deprecated. Please use catkin_pkg.python_setup.generate_distutils_setup(**kwargs) instead.

   1 #!/usr/bin/env python
   2 
   3 from distutils.core import setup
   4 from catkin_pkg.package import parse_package_for_distutils
   5 
   6 package_info = parse_package_for_distutils()
   7 package_info['packages'] = ['mymodule']
   8 package_info['package_dir'] = {'mymodule': 'src'}
   9 package_info['install_requires'] = []
  10 
  11 setup(**package_info)

Add `catkin_python_setup()` to `CMakeLists.txt`

...
catkin_python_setup()
...

More

More (and fewer) migration details here:

Wiki: catkin/what (last edited 2013-10-07 13:46:23 by JonathanBohren)