Ask Your Question
2

Using catkin to generate and use protobuf messages

asked 2014-09-17 10:26:39 -0500

dustingooding gravatar image

updated 2014-09-17 14:43:33 -0500

Using fuerte/rosbuild, I was able to "cheat" and both generate cpp/py protobuf files and link/import them from other projects. I say "cheat" because the generated artifacts ended up in the package source directory and was easy to find. But hydro/catkin does things "right" and I can't cheat anymore.

Say I have two packages: my_package_msgs and my_package_nodes. In my_package_msgs, there's a proto directory with *.proto message files. I use an add_custom_command() call to generate the protobuf artifacts and output directories. Here's basically how its written right now:

cmake_minimum_required(VERSION 2.8.3)
project(my_package_msgs)

find_package(catkin REQUIRED)

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
find_package(ProtocolBuffers REQUIRED)

set(proto_dir ${PROJECT_SOURCE_DIR}/proto)
set(proto_files ${proto_dir}/Message0.proto
                ${proto_dir}/Message1.proto
                ${proto_dir}/Message2.proto)
message(STATUS "Proto Source Dir: ${proto_dir}")
message(STATUS "Proto Source Files: ${proto_files}")

# Set up destination directories
catkin_destinations()
set(proto_gen_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_INCLUDE_DESTINATION}/proto_gen)
set(proto_gen_cpp_dir ${proto_gen_dir}/cpp/include/${PROJECT_NAME})
set(proto_gen_python_dir ${proto_gen_dir}/python)
file(MAKE_DIRECTORY ${proto_gen_dir})
file(MAKE_DIRECTORY ${proto_gen_cpp_dir})
file(MAKE_DIRECTORY ${proto_gen_python_dir})
set(protogen_include_dirs ${proto_gen_cpp_dir}/../ ${proto_gen_python_dir})
message(STATUS "Proto Include Dirs: ${protogen_include_dirs}")

# Create lists of files to be generated.
set(proto_gen_cpp_files "")
set(proto_gen_python_files "")
foreach(proto_file ${proto_files})
    get_filename_component(proto_name ${proto_file} NAME_WE)
    list(APPEND proto_gen_cpp_files ${proto_gen_cpp_dir}/${proto_name}.pb.h ${proto_gen_cpp_dir}/${proto_name}.pb.cc)
    list(APPEND proto_gen_python_files ${proto_gen_python_dir}/${proto_name}_pb2.py)
endforeach(proto_file ${proto_files})

# Run protoc and generate language-specific headers.
add_custom_command(
    COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${proto_dir} --cpp_out=${proto_gen_cpp_dir} --python_out=${proto_gen_python_dir} ${proto_files}
    DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files}
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    OUTPUT ${proto_gen_cpp_files} ${proto_gen_python_files}
)

# Create proto library for linking.
include_directories(${PROTOBUF_INCLUDE_DIR} ${PROTOBUF_INCLUDE_DIR}/../../)
add_library(${PROJECT_NAME}_proto ${proto_gen_cpp_files})
target_link_libraries(${PROJECT_NAME}_proto ${PROTOBUF_LIBRARY})

catkin_package(
  INCLUDE_DIRS ${protogen_include_dirs}
  LIBRARIES ${PROJECT_NAME}_proto
)

install(TARGETS ${PROJECT_NAME}_proto
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(DIRECTORY ${proto_gen_cpp_dir}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  FILES_MATCHING PATTERN "*.h"
)

install(DIRECTORY ${proto_gen_python_dir}/
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

I tried using catkin_install_python(), but it complains about files not existing (because they haven't been generated). I haven't tried using catkin_python_setup() because of the same problem, and because the output isn't a normal python library layout.

Then, my_package_nodes 'run_depend's on my_package_msgs. But, when I attempt to rosrun one of the python executables in my_package_nodes, I get errors that say python modules aren't found:

Traceback (most recent call last):
  File "/home/dgooding/catkin_ws/src/my_packages/my_package_nodes/src/Node2.py", line 8, in <module>
    import Message2_pb2
ImportError: No module named Message2_pb2

So what's the correct (not cheating) way to generate cpp and python files in one catkin package, and have a second package access them?

edit: I've adopted some techniques from http://answers.ros.org/question/12322... that have helped... but not completely done yet.

edit2: I tried making use of catkin_python_setup() (using some configure_file() magic to find the generated python files), but I think catkin_python_setup() is being executed before the code generation happens because I end up with an empty directory in the dist-packages area in devel space.

edit retag flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
1

answered 2014-09-23 12:37:10 -0500

dustingooding gravatar image

Here's what I came up with.

cmake_minimum_required(VERSION 2.8.3)
project(my_package_msgs)

find_package(catkin REQUIRED COMPONENTS rospy)

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
find_package(ProtocolBuffers REQUIRED)

set(proto_dir ${PROJECT_SOURCE_DIR}/proto)
file(GLOB proto_files "${proto_dir}/*.proto")
message(STATUS "Proto Source Dir: ${proto_dir}")
message(STATUS "Proto Source Files: ${proto_files}")

# Set up destination directories
catkin_destinations()
set(proto_gen_cpp_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_INCLUDE_DESTINATION})
set(proto_gen_python_dir ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION})
file(MAKE_DIRECTORY ${proto_gen_cpp_dir})
file(MAKE_DIRECTORY ${proto_gen_python_dir})
set(protogen_include_dirs ${proto_gen_cpp_dir}/../ ${proto_gen_python_dir})
message(STATUS "Proto Include Dirs: ${protogen_include_dirs}")

catkin_python_setup()

catkin_package(
  INCLUDE_DIRS ${protogen_include_dirs}
  LIBRARIES ${PROJECT_NAME}_proto
)

# Create lists of files to be generated.
set(proto_gen_cpp_files "")
set(proto_gen_python_files "")
foreach(proto_file ${proto_files})
    get_filename_component(proto_name ${proto_file} NAME_WE)
    list(APPEND proto_gen_cpp_files ${proto_gen_cpp_dir}/${proto_name}.pb.h ${proto_gen_cpp_dir}/${proto_name}.pb.cc)
    list(APPEND proto_gen_python_files ${proto_gen_python_dir}/${proto_name}_pb2.py)
endforeach(proto_file ${proto_files})

# Run protoc and generate language-specific headers.
add_custom_command(
    OUTPUT ${proto_gen_cpp_files} ${proto_gen_python_files}
    COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --proto_path=${proto_dir} --cpp_out=${proto_gen_cpp_dir} --python_out=${proto_gen_python_dir} ${proto_files}
    DEPENDS ${PROTOBUF_PROTOC_EXECUTABLE} ${proto_files}
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

set_source_files_properties(${proto_gen_cpp_files} PROPERTIES GENERATED TRUE)
set_source_files_properties(${proto_gen_python_files} PROPERTIES GENERATED TRUE)

add_custom_target(${PROJECT_NAME}_generate_headers
    DEPENDS ${proto_gen_cpp_files} ${proto_gen_python_files}
)

# Create proto library for linking.
include_directories(${PROTOBUF_INCLUDE_DIR} ${PROTOBUF_INCLUDE_DIR}/../../)
add_library(${PROJECT_NAME}_proto ${proto_gen_cpp_files})
target_link_libraries(${PROJECT_NAME}_proto ${PROTOBUF_LIBRARY})
add_dependencies(${PROJECT_NAME}_proto ${PROJECT_NAME}_generate_headers)

install(TARGETS ${PROJECT_NAME}_proto
  ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(DIRECTORY ${proto_gen_cpp_dir}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  FILES_MATCHING PATTERN "*.h"
)

install(DIRECTORY ${proto_gen_python_dir}/
  DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}
  FILES_MATCHING PATTERN "*.py"
)
edit flag offensive delete link more

Comments

You should not use file(GLOB to collect a list of source files (in your case .proto files). See http://www.cmake.org/cmake/help/v2.8....

Dirk Thomas gravatar imageDirk Thomas ( 2014-09-23 13:03:44 -0500 )edit

Agreed. Thanks @dirk-thomas.

dustingooding gravatar imagedustingooding ( 2015-06-24 10:18:25 -0500 )edit
1

answered 2017-10-05 18:32:21 -0500

Shengye Wang gravatar image

Just FYI, we published a "grpc" package that compiles proto files for C++ and Python. Check: https://github.com/CogRob/catkin_grpc

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2014-09-17 10:26:39 -0500

Seen: 3,379 times

Last updated: Sep 23 '14