Building ROS Noetic on Ubuntu 22.04

Lukas Reisinger
7 min readAug 3, 2023

--

What is this article about ?

This article enables you to self-build a roscore and ROS tools like rostopic, roslaunch and rosparam, by providing a step-by-step description of how to build ROS Noetic on Ubuntu 22.04 from sources.

ROS offers different software bundles in order to serve different use cases, as can be seen here. To keep the efforts limited, this article’s focus is on the ROS Noetic Base bundle. In order to have a green field and no unexpected errors, we will create a docker image including all the necessary dependencies for building and running the code.

Performing all of the following steps gives the same result as running
sudo apt install ros-noetic-ros-base as described at the
ROS Installation Guide.

Why do we want to do this ?

As mentioned here, ROS Noetic is primarily targeted at Ubuntu 20.04 and doesn’t officially support any newer distribution. Further, ROS Noetic is the last available ROS 1 version and is going to be discontinued in May 2025.

Thus, if you want to use ROS Noetic after end of life on Ubuntu 22.04 or any newer Ubuntu distribution, there is actually almost no way around building the required packages on your own.

How can we do it ?

First of all, please make sure you have the following packages installed on your native system to be able to run the commands without error interruptions:

  • docker
  • python-3
  • pip3
  • git

ROS Noetic base packages

The first thing we need to do is to figure out which packages are part of the original ros-noetic-ros-base package. The easiest way to get this information, is to make use of the rosinstall_generator as it is described here.

To get a list of required packages and version tags we simply run the following two commands within the terminal:

pip3 install -U rosinstall_generator 
rosinstall_generator ros_base --rosdistro noetic --deps --tar > noetic-base.rosinstall

The resulting file noetic-base.rosinstall should contain something like:

- tar:
local-name: actionlib/actionlib
uri: https://github.com/ros-gbp/actionlib-release/archive/release/noetic/actionlib/1.14.0-1.tar.gz
version: actionlib-release-release-noetic-actionlib-1.14.0-1
- tar:
local-name: bond_core/bond
uri: https://github.com/ros-gbp/bond_core-release/archive/release/noetic/bond/1.8.6-1.tar.gz
version: bond_core-release-release-noetic-bond-1.8.6-1
- tar:
local-name: bond_core/bond_core
uri: https://github.com/ros-gbp/bond_core-release/archive/release/noetic/bond_core/1.8.6-1.tar.gz
version: bond_core-release-release-noetic-bond_core-1.8.6-1
- tar:
local-name: bond_core/bondcpp
uri: https://github.com/ros-gbp/bond_core-release/archive/release/noetic/bondcpp/1.8.6-1.tar.gz
version: bond_core-release-release-noetic-bondcpp-1.8.6-1
...

By inspecting noetic-base.rosinstall and researching for the original GitHub repositories, we get the following list of packages and version tags:

actionlib-1.14.0 | bond_core-1.8.6 | catkin-0.8.10 | class_loader-0.5.0 | cmake_modules-0.5.0 | common_msgs-1.13.1 | dynamic_reconfigure-1.7.3 | gencpp-0.7.0 | geneus-3.0.0 | genlisp-0.4.18 | genmsg-0.6.0 | gennodejs-2.0.1 | genpy-0.6.16 | message_generation-0.4.1 | message_runtime-0.4.13 | nodelet_core-1.10.2 | pluginlib-1.13.0 | ros-1.15.18 | ros_comm-1.16.0 | ros_comm_msgs-1.11.3 | ros_environment-1.3.2 | rosbag_migration_rule-1.0.1 | rosconsole-1.14.3 | rosconsole_bridge-0.5.4 | roscpp_core-0.7.2 | roslisp-1.9.25 | rospack-2.6.2 | std_msgs-0.5.13

Cloning the repositories

Since we are dealing with ROS packages and make use of catkin as a build tool, we need to follow the ROS package structure.

Therefore, we simply create a root directory called ros_noetic_base_2204 and a catkin workspace by executing

mkdir -p ros_noetic_base_2204/catkin_ws/src
ros_noetic_base_2204
└── catkin_ws
└── src

Now it is time to clone all the necessary GitHub repositories. This can be achieved by executing the following commands within the directory ros_noetic_base_2204/catkin_ws/src

git clone https://github.com/ros/actionlib.git -b 1.14.0
git clone https://github.com/ros/bond_core.git -b 1.8.6
git clone https://github.com/ros/catkin.git -b 0.8.10
git clone https://github.com/ros/class_loader.git -b 0.5.0
git clone https://github.com/ros/cmake_modules.git -b 0.5.0
git clone https://github.com/ros/common_msgs.git -b 1.13.1
git clone https://github.com/ros/dynamic_reconfigure.git -b 1.7.3
git clone https://github.com/ros/gencpp.git -b 0.7.0
git clone https://github.com/jsk-ros-pkg/geneus.git -b 3.0.0
git clone https://github.com/ros/genlisp.git -b 0.4.18
git clone https://github.com/ros/genmsg.git -b 0.6.0
git clone https://github.com/RethinkRobotics-opensource/gennodejs.git -b 2.0.1
git clone https://github.com/ros/genpy.git -b 0.6.16
git clone https://github.com/ros/message_generation.git -b 0.4.1
git clone https://github.com/ros/message_runtime.git -b 0.4.13
git clone https://github.com/ros/nodelet_core.git -b 1.10.2
git clone https://github.com/ros/pluginlib.git -b 1.13.0
git clone https://github.com/ros/ros.git -b 1.15.8
git clone https://github.com/ros/ros_comm.git -b 1.16.0
git clone https://github.com/ros/ros_comm_msgs.git -b 1.11.3
git clone https://github.com/ros/ros_environment.git -b 1.3.2
git clone https://github.com/ros/rosbag_migration_rule.git -b 1.0.1
git clone https://github.com/ros/rosbag_migration_rule.git -b 1.0.1
git clone https://github.com/ros/rosconsole.git -b 1.14.3
git clone https://github.com/ros/rosconsole_bridge.git -b 0.5.4
git clone https://github.com/ros/roscpp_core.git -b 0.7.2
git clone https://github.com/ros/roslisp.git -b 1.9.25
git clone https://github.com/ros/rospack.git -b 2.6.2
git clone https://github.com/ros/std_msgs.git -b 0.5.13

Additionally to the repositories listed above, we also need to clone catkin_pkg as well as rospkg. Those libraries are non catkin packages and therefore need to stay outside the catkin_ws in order to avoid build errors later on. It is ok to just put it into the root directory

git clone https://github.com/ros-infrastructure/catkin_pkg.git -b 0.5.2
git clone https://github.com/ros-infrastructure/rospkg.git -b 1.5.0
ros_noetic_base_2204
├── catkin_pkg
├── catkin_ws
└── rospkg

Creating the Docker environment

To build the previously cloned packages, a docker environment is used. We start with a minimum Dockerfile based on Ubuntu 22.04 containing some build essentials and add in the course of the following steps further dependencies.

ros_noetic_base_2204
├── catkin_pkg
├── catkin_ws
├── Dockerfile
└── rospkg

Dockerfile:

FROM ubuntu:22.04

RUN apt-get update && \
apt-get install -y \
cmake \
build-essential \
python3 \
pip

The docker image can be build by run the following command inside the root directory:

docker build -t ros_noetic_base_2204 .

In order to run the docker image and mount the current directory (ros_noetic_base_2204), we need to execute:

docker run -it --rm -v .:/ros_noetic_base_2204 ros_noetic_base_2204 bash

Building

The easiest way to build ROS Noetic base from scratch successfully, is to use a trial and error approach.

This simply means that we loop through the following steps over and over again until finally everything is building without errors:

  • execute the build command
  • add missing dependencies and python packages directly to the Dockerfile or adapt code
  • rebuild and rerun the docker image
  • start over again

Lets do it !

At first we need to build and run the docker image

docker build -t ros_noetic_base_2204 .
docker run -it --rm -v .:/ros_noetic_base_2204 ros_noetic_base_2204 bash

and then start the build by using the following commands

cd /ros_noetic_base_2204/catkin_pkg && python3 setup.py install
cd /ros_noetic_base_2204/rospkg && python3 setup.py install
cd /ros_noetic_base_2204/catkin_ws
./src/catkin/bin/catkin_make install \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_EXECUTABLE=/usr/bin/python3

after a few seconds we get the following error

...

-- Could NOT find PY_em (missing: PY_EM)
CMake Error at catkin/cmake/empy.cmake:30 (message):
Unable to find either executable 'empy' or Python module 'em'... try
installing the package 'python3-empy'
Call Stack (most recent call first):
catkin/cmake/all.cmake:164 (include)
CMakeLists.txt:29 (include)


-- Configuring incomplete, errors occurred!

In order to resolve this error, we need to add the missing pyton3-empy dependency to the Dockerfile and redo the execution loop:

FROM ubuntu:22.04

RUN apt-get update && \
apt-get install -y \
cmake \
build-essential \
python3 \
pip

RUN apt-get install -y \
python3-empy
# Execute within the native terminal
docker build -t ros_noetic_base_2204 .
docker run -it --rm -v .:/ros_noetic_base_2204 ros_noetic_base_2204 bash
# Execute inside the docker image
cd /ros_noetic_base_2204/catkin_pkg && python3 setup.py install
cd /ros_noetic_base_2204/rospkg && python3 setup.py install
cd /ros_noetic_base_2204/catkin_ws
./src/catkin/bin/catkin_make install \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_EXECUTABLE=/usr/bin/python3

Et voilà! The above error is gone, but we run right away into the next one

CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
Could NOT find Boost (missing: Boost_INCLUDE_DIR thread system)
Call Stack (most recent call first):
/usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
/usr/share/cmake-3.22/Modules/FindBoost.cmake:2360 (find_package_handle_standard_args)
class_loader/CMakeLists.txt:12 (find_package)


-- Configuring incomplete, errors occurred!
See also "/ros_noetic_base_2204/catkin_ws/build/CMakeFiles/CMakeOutput.log".

In this case the boost-thread library is missing. Therefore, we also add this dependency to the Dockerfile and redo the execution loop.

This procedure is now done over and over again until all dependency issues are resolved.

In the end, the final Dockerfile looks like:

FROM ubuntu:22.04

RUN apt-get update && \
apt-get install -y \
cmake \
build-essential

RUN apt-get install -y \
libboost-thread-dev \
libboost-system-dev \
libboost-filesystem-dev \
libboost-regex-dev \
libboost-program-options-dev \
libconsole-bridge-dev \
libpoco-dev \
libtinyxml2-dev \
liblz4-dev \
libbz2-dev \
uuid-dev \
liblog4cxx-dev \
libgpgme-dev \
libgtest-dev

RUN apt-get install -y \
python3 \
python3-pip \
python3-setuptools \
python3-empy \
python3-nose \
python3-pycryptodome \
python3-defusedxml \
python3-mock \
python3-netifaces \
python3-gnupg \
python3-numpy \
python3-psutil

Once we run the build again after creating the new Docker image, we get the following error:

/ros_noetic_base_2204/catkin_ws/src/rosconsole/src/rosconsole/impl/rosconsole_log4cxx.cpp: In function ‘void ros::console::impl::initialize()’:                                                             
/ros_noetic_base_2204/catkin_ws/src/rosconsole/src/rosconsole/impl/rosconsole_log4cxx.cpp:169:23: error: cannot convert ‘ros::console::impl::ROSConsoleStdioAppender*’ to ‘log4cxx::AppenderPtr’ {aka ‘std::
shared_ptr<log4cxx::Appender>’}
169 | logger->addAppender(new ROSConsoleStdioAppender);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| ros::console::impl::ROSConsoleStdioAppender*

...

This error is caused by a change of the log4cxx API from version 0.10.0 to 0.12.1.

log4cxx is used within the ROS packages ros_comm as well as rosconsole. In order to resolve these errors, we need to apply the following patches to the packages ros_comm and rosconsole.

The easiest way to apply the patches, is to save the diff in a file and make use of the command git apply --ignore-whitespace <patch-file> within the appropriate packages.

After applying the patches we run the build again and IT BUILDS!

Testing

Unfortunately, we are not completely done yet, because we should also check if all the unit tests are passing.

In order to do so, we simply add the command run_tests to the end of our build command and execute it

cd /ros_noetic_base_2204/catkin_ws 
./src/catkin/bin/catkin_make install \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_EXECUTABLE=/usr/bin/python3 \
run_tests

and unfortunately, there is a build error again

In file included from /usr/include/log4cxx/log4cxx.h:45,                                                                                                                                                    
from /usr/include/log4cxx/logstring.h:28,
from /usr/include/log4cxx/level.h:22,
from /ros_noetic_base_2204/catkin_ws/src/rosconsole/include/ros/console.h:46,
from /ros_noetic_base_2204/catkin_ws/src/pluginlib/pluginlib/include/pluginlib/class_loader.hpp:42,
from /ros_noetic_base_2204/catkin_ws/src/pluginlib/pluginlib/test/unique_ptr_test.cpp:32:
/usr/include/log4cxx/boost-std-configuration.h:10:18: error: ‘shared_mutex’ in namespace ‘std’ does not name a type
10 | typedef std::shared_mutex shared_mutex;

but this one is rather easy to fix.

By having a look at the CMakeLists file of the package pluginlib we can see that the target ${PROJECT_NAME}_unique_ptr_test is explicitly compiled with c++ standard 11. pluginlib is making use of the library log4cxx, which is using the new shared_mutex feature only available within c++ standard 17 upwards.

Thus, to make this work we need to change the CMakeLists file so that ${PROJECT_NAME}_unique_ptr_test is compiled with c++17 instead of c++11:

diff --git a/pluginlib/CMakeLists.txt b/pluginlib/CMakeLists.txt
index 289c5af..1776c02 100644
--- a/pluginlib/CMakeLists.txt
+++ b/pluginlib/CMakeLists.txt
@@ -24,12 +24,12 @@ if(CATKIN_ENABLE_TESTING)
endif()

include(CheckCXXCompilerFlag)
- check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11)
- if(COMPILER_SUPPORTS_CXX11)
+ check_cxx_compiler_flag("-std=c++17" COMPILER_SUPPORTS_CXX17)
+ if(COMPILER_SUPPORTS_CXX17)
catkin_add_gtest(${PROJECT_NAME}_unique_ptr_test test/unique_ptr_test.cpp)
if(TARGET ${PROJECT_NAME}_unique_ptr_test)
target_link_libraries(${PROJECT_NAME}_unique_ptr_test ${TinyXML2_LIBRARIES} ${catkin_LIBRARIES} ${Boost_LIBRARIES})
- set_target_properties(${PROJECT_NAME}_unique_ptr_test PROPERTIES COMPILE_FLAGS -std=c++11 LINK_FLAGS -std=c++11)
+ set_target_properties(${PROJECT_NAME}_unique_ptr_test PROPERTIES COMPILE_FLAGS -std=c++17 LINK_FLAGS -std=c++17)
add_dependencies(${PROJECT_NAME}_unique_ptr_test test_plugins)
endif()
endif()
cd /ros_noetic_base_2204/catkin_ws 
./src/catkin/bin/catkin_make install \
-DCMAKE_BUILD_TYPE=Release \
-DPYTHON_EXECUTABLE=/usr/bin/python3 \
run_tests

Congratulations, you managed to build ROS Noetic base from sources!

Since everything is building now, we are ready to start a roscore. Therefore we need to source, the ROS setup file and run the command roscore.

source /ros_noetic_base_2204/catkin_ws/devel/setup.bash
roscore

References

--

--