Search code examples
pythoncmakevirtualenvros2colcon

ROS2 colcon build fails with ModuleNotFoundError for Python dependency in virtual environment


When trying to build my ROS2 project, I get a ModuleNotFoundError when compiling one of the C modules, due to a missing dependency called em:

% colcon build --cmake-clean-cache
Starting >>> r1_messages
--- stderr: r1_messages                                                                     
CMake Error at /Users/mryall/src/mawson/ros2-iron-build/install/share/rosidl_adapter/cmake/rosidl_adapt_interfaces.cmake:57 (message):
  
  execute_process(/opt/homebrew/Frameworks/Python.framework/Versions/3.12/bin/python3.12
  -m rosidl_adapter --package-name r1_messages --arguments-file
  /Users/mryall/src/mawson/r1-ros/build/r1_messages/rosidl_adapter__arguments__r1_messages.json
  --output-dir
  /Users/mryall/src/mawson/r1-ros/build/r1_messages/rosidl_adapter/r1_messages
  --output-file
  /Users/mryall/src/mawson/r1-ros/build/r1_messages/rosidl_adapter/r1_messages.idls)
  returned error code 1:

  Traceback (most recent call last):

    File "<frozen runpy>", line 198, in _run_module_as_main
    File "<frozen runpy>", line 88, in _run_code
    File "/Users/mryall/src/mawson/ros2-iron-build/install/lib/python3.11/site-packages/rosidl_adapter/__main__.py", line 19, in <module>
      sys.exit(main())
               ^^^^^^
    File "/Users/mryall/src/mawson/ros2-iron-build/install/lib/python3.11/site-packages/rosidl_adapter/main.py", line 53, in main
      abs_idl_file = convert_to_idl(
                     ^^^^^^^^^^^^^^^
    File "/Users/mryall/src/mawson/ros2-iron-build/install/lib/python3.11/site-packages/rosidl_adapter/__init__.py", line 18, in convert_to_idl
      from rosidl_adapter.msg import convert_msg_to_idl
    File "/Users/mryall/src/mawson/ros2-iron-build/install/lib/python3.11/site-packages/rosidl_adapter/msg/__init__.py", line 16, in <module>
      from rosidl_adapter.resource import expand_template
    File "/Users/mryall/src/mawson/ros2-iron-build/install/lib/python3.11/site-packages/rosidl_adapter/resource/__init__.py", line 19, in <module>
      import em

  ModuleNotFoundError: No module named 'em'

Call Stack (most recent call first):
  /Users/mryall/src/mawson/ros2-iron-build/install/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:132 (rosidl_adapt_interfaces)
  CMakeLists.txt:14 (rosidl_generate_interfaces)

---
Failed   <<< r1_messages [4.13s, exited with code 1]

My ROS2 installation was built with a Python virtual environment, which I have activated. But even though the virtual-env is activated and all the required libraries are installed with pip, the ROS modules built with CMake don't seem to be able to find it.

I tried to check that the em library was installed. Running pip install empy confirms that it is already installed:

% pip install empy
Requirement already satisfied: empy in /Users/mryall/src/mawson/ros2-iron/iron_venv/lib/python3.11/site-packages (3.3.4)

Solution

  • CMake modules in ROS2 do not automatically pick up the virtual environment used by the shell which calls colcon. (This is apparently an intentional decision by the ROS2 maintainers, who don't seem to understand the purpose or operation of virtual environments very well.)

    You can fix this by asking colcon to pass the Python3_EXECUTABLE parameter to CMake, using the $VIRTUAL_ENV environment variable from your shell with the active virtualenv:

    % colcon build --cmake-args -DPython3_EXECUTABLE="$VIRTUAL_ENV/bin/python"
    

    Fortunately, once the right binary is known, Python is smart enough to find the libraries which are installed in the virtualenv.

    This enables C++/CMake modules in your ROS2 code to find all their Python dependencies.