<<TableOfContents(4)>> == Overview == Here we describe how to migrate rosbuild package to catkin, in general. === Prerequisites === Catkin packages cannot depend on rosbuild packages. Therefore before migrating a package from rosbuild to catkin make sure that all package dependencies have already been migrated to catkin. For more information about catkin workspaces please see the [[http://www.ros.org/reps/rep-0128.html|REP 128]]. === Package Layout in Workspace === The layout of workspaces is not very different between rosbuild and catkin packages. A few keys things to notice: * '''Packages no longer contain a manifest.xml file -- this has been superseded by [[catkin/package.xml | package.xml]]''' {{{ my_package/ --> my_package/ manifest.xml --> package.xml }}} * '''Packages are no longer organized within stacks.''' Instead, they are grouped into collections of packages called '''metapackages'''. '''The stack.xml file is replaced with a [[catkin/package.xml | package.xml]] file''' which internally indicates it is a metapackage. This meta package.xml is moved into a folder on the same level as the other packages, from my_stack/stack.xml to my_stack/'''my_stack'''/package.xml. {{{ my_stack/ --> my_metapackage/ my_package_1/ --> my_package_1/ manifest.xml --> package.xml ... my_package_n/ --> my_package_n/ manifest.xml --> package.xml stack.xml --> my_metapackage package.xml (Internally, a tag indicates it's a metapackage) }}} * '''Packages still contain a CMakeLists.txt file, but the content and format of this file has changed drastically.''' * '''Packages no longer contain a Makefile.''' === Converting a rosbuild (Dry) Package to a catkin (Wet) Package === At a high level, the key steps to convert a ROS stack from rosbuild to catkin packages are: 1. Create a catkin workspace to hold your migrated projects 1. Move any project to this catkin workspace once all its dependencies are catkinized 1. Convert your stack into a metapackage, unless it was a unary rosbuild stack (Unary rosbuild stacks are stacks with just one package, those will become one catkin package) 1. Create a catkin package in your old stack folder and name it like the stack, this is going to be the metapackage (as an example, see ros_comm/ros_comm) 1. Add all other packages of the old stack as run_depend tags of the metapackage in the package.xml 1. Remove the `stack.xml`, the information from it moves into the `package.xml` of the metapackage 1. What previously was your ‘stack’ folder should now contain only packages and at most one metapackage. 1. For each folder containing a `manifest.xml` file: 1. Rename the `manifest.xml` to `package.xml` 1. Add a name tag with the name of the package, which should also be the folder name 1. If missing, create a `CMakeLists.txt` file containing a catkin_package() invocation 1. Remove the Makefile 1. In each `CMakeLists.txt`: 1. If rosbuild macros were used, switch from rosbuild macros to the underlying CMake commands 1. Declare how your targets (C++ binaries) shall be installed 1. For each C++ executable or library: 1. Add an `add_dependencies()` call to the targets generating the message headers, e.g. `geometry_msgs_generate_messages_cpp` or using `${catkin_EXPORTED_TARGETS}`. * For example, if your `foo` package that builds a `foo_node` executable depends on `foo_msgs`, then add `add_dependencies(foo_node foo_msgs_gencpp)` at the end of foo's CMakeLists.txt file 1. Add `an `target_link_libraries()` call to ${catkin_LIBRARIES} if [[http://answers.ros.org/question/63656/how-to-solve-undefined-reference-to-rosinit-on-groovy/?answer=63674#post-id-63674|ros is not found]]. 1. For each executable python script in your package 1. install the script using cmake (not setup.py) so that they are installed under "lib/PKGNAME" instead of the global "bin" folder 1. if the script is not only used by `rosrun` but also within launch files and there being found using "$(find pkgname)path/to/script": install the script for backward compatibility into share (within the same folder hierarchy as it is in source). (See this issue with [[https://github.com/ros/xacro/issues/1|xacro]]) You should also create a differently named script which will be used going forward. Such as change xacro.py to xacro and install it into the standard package local path. This will prevent collisions when using rosrun when using the backwards compatible executable. 1. If your package contains python packages, create a setup.py declaring the packages and invoke `catkin_python_setup()` in your `CMakeLists.txt` One confusing aspect of catkin is the multiple source directories and what they hold. The layout of your catkin workspace if you have multiple packages is: {{{ my_workspace/ build/ <-- created by catkin_make devel/ <-- created by catkin_make src/ package_1/ package.xml CMakeLists.txt src/ code.cpp ... package_2/ package.xml CMakeLists.txt src/ code.cpp ... ... }}} Now when you run catkin_make inside my_workspace, cmake will build your packages and drop the binaries inside of the build directory. '''Note''' that catkin_make will create the build and devel directories and all of the files inside of those two directories. If you want to completely rebuild all of the catkin created files and directories, you can safely delete the build and devel directories. Another call of catkin_make will recreate those. === Difference in rosbuild manifest.xml from catkin package.xml === The manifest.xml and package.xml files serve almost the same role between rosbuild and catkin - providing information about the package and its dependencies. The difference is that some of the information that traditionally went into the manifest.xml file such as export information (e.g. compiler cflags) is now explicit in CMakeLists.txt and handled by CMake rather than by the ROS build system. See [[catkin/package.xml]] for more details. === Differences in CMakeLists.txt for rosbuild and catkin === The most significant change between rosbuild and catkin is the differences in the contents of the CMakeLists.txt file that must be provided with each package. The biggest difference is the elimination of rosbuild macros such as ''rosbuild_add_library'' which have been replaced by standard CMake functions or by catkin-specific macros. See [[catkin/CMakeLists.txt]] for details about the structure and contents of this file. Below is a quick guide to showing the mapping between rosbuild CMake macros and catkin CMake macros. ==== Build Macros ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| ||rosbuild_init()|| ''remove'' || ||rosbuild_add_library(...)||add_library(...)|| ||rosbuild_add_executable(...)||add_executable(...)|| ||rosbuild_add_compile_flags(...)||set_target_properties(target PROPERTIES COMPILE_FLAGS new_flags)|| ||rosbuild_remove_compile_flags(...)||set_target_properties(target PROPERTIES COMPILE_FLAGS new_flags)|| ||rosbuild_add_link_flags(...)|| set_target_properties(target PROPERTIES LINK_FLAGS new_flags|| ||rosbuild_remove_link_flags(...)||set_target_properties(target PROPERTIES LINK_FLAGS new_flags)|| ||rosbuild_add_boost_directories(...); rosbuild_link_boost(target components)|| find_package(Boost REQUIRED COMPONENTS components); include_directories(${Boost_INCLUDE_DIRS}); target_link_libraries(target ${Boost_LIBRARIES}) || ||rosbuild_add_openmp_flags(...)|| find_package(OpenMP), then do other stuff (an example is displayed below the table) || ||rosbuild_invoke_rospack(...)||DO NOT DO THIS|| ||rosbuild_find_ros_package(...)||DO NOT DO THIS|| ||rosbuild_find_ros_stack()||DO NOT DO THIS|| ||rosbuild_check_for_sse(...)||look around online and find an example of how to find SSE|| ||rosbuild_include(package module)||include(module) (might require some initial work to find the path to the module)|| ||rosbuild_add_lisp_executable()|| ''no support for this currently''|| ||rosbuild_add_swigpy_library(target lib src1 src2) || - || ===== OpenMP ===== Example usage for OpenMP with catkin: {{{ # let cmake find OpenMP and set some variables find_package(OpenMP REQUIRED) if(OPENMP_FOUND) message(STATUS "OPENMP FOUND") set(OpenMP_FLAGS ${OpenMP_CXX_FLAGS}) # or if you use C: ${OpenMP_C_FLAGS} set(OpenMP_LIBS gomp) endif() ... # the entry in catkin_package could be optional (I am not fully sure about this) catkin_package( DEPENDS OpenMP ) ... # exemplary executable foo using OpenMP add_executable(foo ros/src/foo_node.cpp ) target_compile_options(foo PRIVATE ${OpenMP_FLAGS}) add_dependencies(foo ${catkin_EXPORTED_TARGETS}) target_link_libraries(foo ${catkin_LIBRARIES} ${OpenMP_LIBS} ) }}} ==== Test Macros ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| ||rosbuild_add_gtest(...) || catkin_add_gtest(...)|| ||rosbuild_add_gtest_labeled || if (${LABEL}) || || || catkin_add_gtest(...) || || || endif() || ||rosbuild_add_gtest_future || # comment it out || ||rosbuild_add_gtest_build_flags || use set_target_properties on test target || ||rosbuild_add_pyunit || migrate to catkin_add_nosetests(...) || ||rosbuild_add_pyunit_labeled || similar to rosbuild_add_gtest_labeled || ||rosbuild_add_pyunit_future || # comment it out || ||rosbuild_add_rostest(...) || add_rostest(...) || ||rosbuild_add_rostest_labeled || similar to rosbuild_add_gtest_labeled || ||rosbuild_add_rostest_future || # comment it out || ||rosbuild_add_roslaunch_check || roslaunch_add_file_check (as of roslaunch 1.9.46, requires to find package roslaunch before) || ||rosbuild_declare_test || add_dependencies(tests <test-target>) || ||rosbuild_count_cores || - || ||rosbuild_check_for_display || - || ||rosbuild_check_for_vm || - || Note that the "add_rostest" macro is defined in the rostest package. ==== Message/Service Macros ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| || rosbuild_add_generated_msgs(...) || add_message_files(DIRECTORY msg FILES ...) || || rosbuild_add_generated_srvs(...) || add_service_files(DIRECTORY srv FILES ...) || || rosbuild_genmsg() || generate_messages() (once per CMakeLists.txt)|| || rosbuild_gensrv() || generate_messages() (once per CMakeLists.txt)|| Listing the message files is useful as adding removing files will trigger an automatic reconfigure by cmake. ==== Version Macros ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| || rosbuild_get_stack_version || obsolete || || rosbuild_get_package_version || obsolete || ==== Data Macros ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| || rosbuild_download_data(url filename [md5sum]) || [[http://docs.ros.org/api/catkin/html/dev_guide/generated_cmake_api.html#catkin_download_test_data|catkin_download_test_data]] || || rosbuild_download_test_data || [[http://docs.ros.org/api/catkin/html/dev_guide/generated_cmake_api.html#catkin_download_test_data|catkin_download_test_data]] || || rosbuild_untar_file || - || ==== Special Targets ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| || rosbuild_premsgsrvgen || - || || rosbuild_precompile || - || || rosbuild_make_distribution || - || ==== Actionlib ==== ||<tablestyle="width: 80%">|| ||'''rosbuild'''||'''catkin'''|| || include(${actionlib_msgs_PACKAGE_PATH}/cmake/actionbuild.cmake) || Remove this, Add actionlib_msgs to find_package call || || genaction() || add_action_files(DIRECTORY msg FILES ...) || === Python === Catkin leverages Python distutils. If you have python packages or scripts in your catkin package you will need to add a setup.py: For a standard layout with the python module in the ```src``` subdirectory of your catkin package: {{{#!python #!/usr/bin/env python from distutils.core import setup from catkin_pkg.python_setup import generate_distutils_setup d = generate_distutils_setup( # # don't do this unless you want a globally visible script # scripts=['bin/myscript'], packages=['PYTHON_PACKAGE_NAME'], package_dir={'': 'src'} ) setup(**d) }}} and in your CMakeLists.txt call: {{{ catkin_python_setup() }}} and {{{ catkin_install_python( PROGRAMS scripts/foo_script DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) }}} For more details see: http://ros.org/doc/groovy/api/catkin/html/user_guide/setup_dot_py.html and http://docs.ros.org/indigo/api/catkin/html/howto/format1/installing_python.html === dynamic_reconfigure === For each .cfg file: * Remove "{{{import roslib;roslib.load_manifest(PACKAGE)}}}". * Change "{{{from dynamic_reconfigure.parameter_generator import *}}}" to "{{{from dynamic_reconfigure.parameter_generator_catkin import *}}}" In CMakeLists.txt, add the following before the call to catkin_package() to generate code for the dynamic reconfigure files: {{{ generate_dynamic_reconfigure_options(cfg/DynFile1.cfg cfg/DynFile2.cfg ...) }}} Additionally in the CMakeLists.txt, add the following dependency for each target which uses the generated code: {{{ # make sure configure headers are built before any node using them add_dependencies(example_node ${${PROJECT_NAME}_EXPORTED_TARGETS}) }}} For more details see: [[http://ros.org/doc/groovy/api/catkin/html/howto/dynamic_reconfiguration.html|the catkin dynamic reconfigure how-to]] and [[dynamic_reconfigure/Tutorials/HowToWriteYourFirstCfgFile/catkin#Use_the_cfg_File|dynamic_reconfigure tutorial]] === Migration Help - "Catkinize" Scripts === There is a Python project available that can help you: * Convert your manifest.xml files to package.xml files * Convert your rosbuild-based CMakeLists.txt files to use the updated catkin macros Here is an example of how to use them. First, install the scripts: {{{ $ git clone https://github.com/ros-infrastructure/catkinize $ cd catkinize $ sudo python setup.py install }}} Next, set up a catkin workspace in which to do the migration: {{{ $ mkdir filter_ws $ mkdir filter_ws/src $ cd filter_ws $ catkin_make $ source devel/setup.bash }}} Check out a ROS stack that hasn't yet been converted to catkin into workspace `src` folder: {{{ $ cd filter_ws/src $ hg clone https://kforge.ros.org/common/filters $ catkinize_stack filters 1.0.0 }}} to catkinize single packages only, call: {{{ $ catkinize filters 1.0.0 }}} As an alternative to the catkinize scripts, use catkin_create_pkg to create a new package, and copy & paste or adapt the contents manually. ==== Migration checks ==== Now check and adapt the CMakeLists.txt and package.xml with any text editor. 1. Make sure there is a valid maintainer in package.xml 1. Uncomment dependencies as needed 1. deal with all TODO comments 1. Validate marked changes 1. remove all commented blocks that should now be obsolete 1. list message, service and action files in the `add_message_files`, `add_service_files` and `add_action_files` macro calls 1. remove obsolete `include` calls 1. In the catkin_package macro, export those dependencies required to build against your package, i.e. all that your API header files use, so that client packages do not have to speciy those redundantly 1. adapt target and variable names in CMakeLists.txt to make them likely unique 1. for C++ libraries or executable targets that depend on (custom) ROS messages, add the `add_dependencies()` call * For example, if your `foo` package that builds a `foo_node` executable depends on `foo_msgs`, then add `add_dependencies(foo_node foo_msgs_gencpp)` at the end of foo's CMakeLists.txt file 1. install any scripts (python) using cmake `catkin_install_python()` macro 1. create a setup.py for your own python modules The last point is of particular importance. With catkin, projects are commonly build as one project, meaning cmake target names and global (cached) variables can conflict with each other between projects. See http://ros.org/doc/groovy/api/catkin/html/user_guide/standards.html for details on recommended standards for CMakeLists.txt. So pay particular attention to any items like: {{{ catkin_add_gtest(targetname ...) add_executable(targetname ...) set(varname CACHED ...) option(varname...) }}} and be sure that `targetname` and `varname` are likely to be unique among plenty of projects in the ROS ecosystem. Check the results {{{ $ cd ~/groovy_overlay/build $ cmake ../src $ make }}} === Overlaying Dry and Wet Workspaces === A common problem you will likely run into is having to mix wet packages and dry packages. This is possible with the following constraints: * catkin and rosbuild workspaces need to be in different, isolated directories * Dry packages can depend on wet packages, but wet packages cannot depend on dry That being said, things work in the same way through the use of environment setup files (i.e. setup.* scripts). Both catkin and rosbuild workspaces have this concept; in rosbuild these files are located in the workspace root, in catkin they are located in the devel and install spaces. Here is an example showing the difference between the layouts. {{{ rosbuild_ws/ .rosinstall setup.bash setup.sh setup.py catkin_ws/ devel/ setup.bash setup.sh setup.py install/ setup.bash setup.sh setup.py }}} To overlay a rosbuild workspace on top of a catkin workspace, the rosbuild workspace has to reference the setup.sh file as `setup-file` of either the devel or the install space in the `.rosinstall` file (not both). Which one is up to you, the catkin way would be to use the devel space setup.sh. When not using rosws/rosinstall, just make sure your `ROS_PACKAGE_PATH` includes the `src` folder of your catkin workspace. A tutorial exists for this [[catkin/Tutorials/using_rosbuild_with_catkin|here]] === Changes in Tooling === The [[rosws]] tool was commonly used to setup and manage rosbuild workspaces, but now the [[wstool]] command line tool should be used with catkin packages. The commands and interfaces are very similar. === Updating References === rosbuild packages which are released are indexed in the release repo on code.ros.org while catkin packages are indexed in the git rosdistro repo. When you convert a released package from rosbuild to catkin you need to remove the old indexing entries and add them to the new location. ==== Remove Old Reference ==== To remove the old entry you will need to remove your repository from the appropriate rosdistro file in the release repo. All maintainers have access to this repo to be able to update their packages on release. [release/Setup/Repository|Reverse These Setup Instructions] ==== Add new entry in new rosdistro repo ==== After converting your package to catkin. If you would like it to be released you will need to follow the new releasing instructions. [[https://bloom.readthedocs.io|Instructions for releasing]] you will need to add it to the [[https://github.com/ros/rosdistro| releases folder in the rosdistro repo]]