Comparison

catkin vs colcon: Common Mistakes When Moving Your ROS 1 Workspace to ROS 2

The Kopik team7 min read

In ROS 2, colcon replaces catkin_make, catkin_make_isolated and catkin_tools as the build tool. The first surprise for ROS 1 developers is that a colcon workspace has no devel directory. colcon builds out of source and creates build, install and log directories next to src, and you source install/setup.bash instead of devel/setup.bash. Most migration headaches come from habits carried over from catkin: expecting devel, rebuilding the whole workspace, mixing apt repositories, or keeping format 1 package.xml files.

This guide collects the mistakes that come up most often when a US lab or startup ports a ROS 1 codebase. Each fix comes from the ROS 2 Lyrical documentation, including its migration guides, which you can query in the ROS 2 knowledge base.

Mistake 1: looking for the devel folder

After your first colcon build, ls shows build, install, log and src. The colcon tutorial says plainly: compared to catkin, there is no devel directory. Nothing went wrong. The three directories have distinct jobs:

  • build: intermediate files, with one subfolder per package where, for example, CMake is invoked.
  • install: where each package is installed, by default into its own subdirectory. This is what you source.
  • log: logging information about each colcon invocation.

What replaces the convenience of devel space? Build types such as ament_cmake do not support a devel space and require packages to be installed. colcon therefore offers --symlink-install, so edits to non-compiled files such as Python scripts take effect without a rebuild. On Windows, the tutorial uses --merge-install instead, because Windows does not allow long paths. Symbolic links also require administrator rights or developer mode there.

Mistake 2: sourcing the wrong script in the wrong terminal

With catkin you sourced devel/setup.bash. With colcon you source the generated scripts in install, but the order and the terminal matter:

  1. Source the underlay first, which is your ROS 2 installation: source /opt/ros/lyrical/setup.bash.
  2. Build the workspace (your overlay) with colcon build.
  3. Open a new terminal before sourcing the overlay. The docs warn that sourcing an overlay in the terminal where you built, or building where an overlay is sourced, may create complex issues.
  4. In the new terminal, source the underlay, then run source install/local_setup.bash. local_setup adds only the overlay's packages, whereas setup sources the overlay plus the underlay it was built on.

Mistake 3: rebuilding the whole workspace every time

catkin_make users are used to building everything. colcon lets you target packages, and the two most useful flags are easy to confuse:

Useful colcon build options (from the ROS 2 tutorials)

OptionWhat it does
--packages-up-to <pkg>Builds the package you want plus all its dependencies, but not the whole workspace
--packages-select <pkg>Builds only the package(s) you name, e.g. colcon build --packages-select my_package
--symlink-installAvoids rebuilding every time you tweak Python scripts
--event-handlers console_direct+Shows console output while building (otherwise found in the log directory)
--executor sequentialBuilds packages one by one instead of in parallel

Use --packages-up-to when you have just cloned a package and its dependencies are not built yet. Use --packages-select when you are iterating on one package whose dependencies are already in place. The tutorial also recommends --executor sequential on CPU-, RAM- and I/O-limited systems such as a Raspberry Pi, where a parallel colcon build may freeze the screen and mouse.

Mistake 4: keeping catkin in package.xml and CMakeLists.txt

ROS 2 only supports package.xml format 2 and higher. If your file starts with <package> or <package format="1">, it must be migrated. The migration guide lists the changes:

  • Set <package format="2">.
  • Replace run_depend, which is no longer allowed, with exec_depend (needed when your package runs) and/or build_export_depend (needed by packages that build against yours). Use both if unsure.
  • Move test-only dependencies from build_depend to test_depend, and reference them in CMake only inside an if(BUILD_TESTING) block.
  • Use doc_depend for documentation tools such as doxygen or python3-sphinx, and collapse build_depend, build_export_depend and exec_depend for the same package into a single depend.
  • Remove <buildtool_depend>catkin</buildtool_depend>, add <buildtool_depend>ament_cmake_ros</buildtool_depend>, and set <build_type>ament_cmake</build_type> in the export section.
  • Remove the <metapackage /> tag. ROS 2 has no special metapackage type, so metapackages become regular packages with only runtime dependencies.

In CMakeLists.txt, replace find_package(catkin REQUIRED COMPONENTS ...) with individual find_package() calls. Delete include_directories(include ${catkin_INCLUDE_DIRS}) in favor of target_include_directories(). Link modern targets such as std_msgs::std_msgs, add ament_package() at the bottom, and map install destinations. For example, CATKIN_PACKAGE_BIN_DESTINATION becomes lib/${PROJECT_NAME} and CATKIN_GLOBAL_BIN_DESTINATION becomes bin.

Mistake 5: mixing ROS 1 and ROS 2 apt repositories

This is the most confusing failure, and the documentation describes it for Ubuntu 22.04 (Jammy), the first platform with a ROS 2 release (Humble) but no official ROS 1 release. The version of catkin-pkg-modules in the Ubuntu repository conflicts with the one in the ROS 2 package repository. If the ROS 2 apt repository is enabled, no ROS 1 packages can be installed. apt install ros-core-dev fails with “ros-core-dev : Depends: catkin but it is not installable”.

The documented fix for running ros1_bridge on Jammy is to remove packages.ros.org from your sources (delete /etc/apt/sources.list.d/ros2.list if you followed the install guide). Then build ROS 2 from source, install colcon with python3 -m pip install -U colcon-common-extensions vcstool, install the upstream ROS 1 packages with sudo apt install -y ros-core-dev, and build ros1_bridge in its own workspace with colcon build after sourcing the ROS 2 workspace. Note that this guide targets Ubuntu 22.04. The base does not document a ros1_bridge procedure for Lyrical on Ubuntu 26.04, so check the ros1_bridge project before planning a bridge on the new LTS.

Mistake 6: copying roslaunch files and assuming a parameter server

Once the workspace builds, the next trap is at run time. ROS 1 launch files were always XML. ROS 2 supports XML and YAML launch files plus Python launch scripts, and the migration guide says XML and YAML should be preferred for typical use cases. An XML file looks familiar, but several node attributes changed:

  • type becomes exec, and ns becomes namespace.
  • required="true" becomes on_exit="shutdown".
  • machine, respawn_delay and clear_params are not available.

Parameters changed even more. In ROS 1, roscore acted as a global parameter blackboard. ROS 2 has no central roscore, so parameters belong to individual nodes and are configured at run time through ROS services. In YAML parameter files, node names must address the parameters, and the ros__parameters key marks where a node's parameters start. If you really need a global blackboard, the docs suggest a dedicated node. ROS 2 ships one, which you start with ros2 run demo_nodes_cpp parameter_blackboard.

Quick reference: catkin habits and their colcon equivalents

  • catkin_create_package becomes ros2 pkg create.
  • Resolve dependencies with rosdep install -i --from-path src --rosdistro lyrical -y.
  • Run tests with colcon test, or a single test with colcon test --packages-select YOUR_PKG_NAME --ctest-args -R YOUR_TEST_IN_PKG.
  • colcon_cd some_ros_package jumps to a package directory once set up in your shell startup script.
  • Automatic helpers exist, such as the Magical ROS 2 Conversion Tool, a launch file migrator and rospy2, but the docs note they are not exhaustive.

Before you port anything

The migration guide's first prerequisite is that all of a package's dependencies must already be available in ROS 2. Map the dependency tree first, then port from the leaves up.

Get migration answers with the source passage

Kopik's ROS 2 base indexes the official migration guides, the colcon tutorials and the release notes. Ask “After building my workspace with colcon, I don't see a 'devel' folder like I had with catkin. Did something go wrong?” or paste your package.xml question.

Browse other technical bases in the catalog, or bring the ROS 2 base into your editor through the developer API.

Frequently asked questions

Why is there no devel folder after colcon build?

That is expected. colcon creates build, install and log directories as peers of src, and the documentation notes that, compared to catkin, there is no devel directory. Source install/setup.bash instead.

What is the difference between --packages-up-to and --packages-select?

--packages-up-to builds the package you want plus all its dependencies, but not the whole workspace. --packages-select builds only the package or packages you name.

What replaces run_depend in ROS 2?

run_depend is not allowed in package.xml format 2. Use exec_depend for runtime dependencies and/or build_export_depend for dependencies needed by packages that build against yours.

Why can't I install ROS 1 packages after adding the ROS 2 apt repository?

On Ubuntu 22.04, the catkin-pkg-modules version in the Ubuntu repository conflicts with the one in the ROS 2 repository, so no ROS 1 packages are installable while the ROS 2 apt source is enabled. The documented workaround is to remove that source and build ROS 2 from source.

Does colcon support catkin-style package.xml files?

colcon uses the package.xml specification from REP 149 and also supports format 2. Format 1 files must be migrated before ROS 2 can use them.

Get the Kopik newsletter

New knowledge bases, RAG guides and product news. One email every week or two, unsubscribe in one click.

By subscribing you agree to receive our newsletter. We never share your address.