From cb78f595594fb8d39d8cb6e0d2d41f5507eb1f99 Mon Sep 17 00:00:00 2001 From: David Tarazi Date: Tue, 28 Jul 2026 14:42:35 -0700 Subject: [PATCH 1/3] added an EXPORT arg to allow for exporting for other packages to use --- dbc_gen_cpp/cmake/generate_dbc_cpp.cmake | 30 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake b/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake index 9ea17d5..eea549a 100644 --- a/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake +++ b/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake @@ -3,7 +3,7 @@ function(generate_dbc_cpp library_name) find_package(Python3 REQUIRED COMPONENTS Interpreter) - set(one_value_args DBC) + set(one_value_args DBC EXPORT) cmake_parse_arguments(ARG "" "${one_value_args}" "" ${ARGN}) if(NOT ARG_DBC) message(FATAL_ERROR "generate_dbc_cpp: Missing required keyword argument DBC") @@ -45,9 +45,31 @@ function(generate_dbc_cpp library_name) add_library(${library_name} STATIC ${generated_c}) add_dependencies(${library_name} ${library_name}_c_sources) target_link_libraries(${library_name} PUBLIC dbc_gen_cpp::dbc_gen_cpp) - target_include_directories(${library_name} - PUBLIC ${gen_basedir} - ) + + if(ARG_EXPORT) + # Exportable target: use generator-expression include dirs so the target can be + # installed into an EXPORT set and linked from other packages. The generated + # headers install to include/${library_name}/, and the .hpp includes its C header + # as "${library_name}/${library_name}.h", so the install-interface root is include/. + target_include_directories(${library_name} + PUBLIC + $ + $ + ) + install( + TARGETS ${library_name} + EXPORT ${ARG_EXPORT} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include + ) + else() + # Default (in-package use): build-tree include dir only, no exported target. + target_include_directories(${library_name} + PUBLIC ${gen_basedir} + ) + endif() # Install the generated files to the install space, just in case they're included in public headers install( From 8b53851f67554dc2e383836467533d9ff7ec6f4b Mon Sep 17 00:00:00 2001 From: David Tarazi Date: Tue, 28 Jul 2026 15:54:53 -0700 Subject: [PATCH 2/3] changed to macro to export deps --- dbc_gen_cpp/cmake/generate_dbc_cpp.cmake | 109 ++++++++++++----------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake b/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake index eea549a..e340ca5 100644 --- a/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake +++ b/dbc_gen_cpp/cmake/generate_dbc_cpp.cmake @@ -1,79 +1,84 @@ # SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc. # SPDX-License-Identifier: Apache-2.0 -function(generate_dbc_cpp library_name) +# +# generate_dbc_cpp( DBC ) +# +# Generates C/C++ CAN message types from a DBC file and builds them into a static +# library named (C++ namespace and C-symbol prefix also , +# headers at include//.hpp). +# +# The library is ALWAYS installed and exported, so a downstream package that does +# find_package() can link it as the imported target +# ::. Within the generating package it is also usable by +# its plain local name. +# +# NOTE: this is a macro, not a function, on purpose. It calls ament_export_targets() +# and ament_export_dependencies(), whose bookkeeping is stored in ordinary variables +# in the current scope; ament_package() reads them from the package's top-level scope, +# so they must not be trapped inside a function scope. +macro(generate_dbc_cpp library_name) find_package(Python3 REQUIRED COMPONENTS Interpreter) - set(one_value_args DBC EXPORT) - cmake_parse_arguments(ARG "" "${one_value_args}" "" ${ARGN}) - if(NOT ARG_DBC) + cmake_parse_arguments(_dbc_arg "" "DBC" "" ${ARGN}) + if(NOT _dbc_arg_DBC) message(FATAL_ERROR "generate_dbc_cpp: Missing required keyword argument DBC") endif() - set(gen_basedir ${CMAKE_CURRENT_BINARY_DIR}/dbc_gen_cpp) - set(gen_dir ${gen_basedir}/${library_name}) + set(_dbc_gen_basedir ${CMAKE_CURRENT_BINARY_DIR}/dbc_gen_cpp) + set(_dbc_gen_dir ${_dbc_gen_basedir}/${library_name}) - set(generated_c ${gen_dir}/${library_name}.c) - set(generated_h ${gen_dir}/${library_name}.h) - set(generated_hpp ${gen_dir}/${library_name}.hpp) - set(generated_files ${generated_c} ${generated_h} ${generated_hpp}) + set(_dbc_generated_c ${_dbc_gen_dir}/${library_name}.c) + set(_dbc_generated_h ${_dbc_gen_dir}/${library_name}.h) + set(_dbc_generated_hpp ${_dbc_gen_dir}/${library_name}.hpp) - # Kind of awkward, but makes the sources get regenerated if the generator tool changes, by depending on the generator sources. - # Finds the python module directory via Python3 import + # Kind of awkward, but makes the sources get regenerated if the generator tool + # changes, by depending on the generator sources. Finds the python module directory. execute_process( COMMAND ${Python3_EXECUTABLE} -c "import dbc_gen_cpp, os; print(os.path.dirname(dbc_gen_cpp.__file__))" - OUTPUT_VARIABLE GENERATOR_DIR + OUTPUT_VARIABLE _dbc_generator_dir OUTPUT_STRIP_TRAILING_WHITESPACE ) - file(GLOB_RECURSE GENERATOR_SOURCES CONFIGURE_DEPENDS - "${GENERATOR_DIR}/*.py" - "${GENERATOR_DIR}/templates/*.j2" + file(GLOB_RECURSE _dbc_generator_sources CONFIGURE_DEPENDS + "${_dbc_generator_dir}/*.py" + "${_dbc_generator_dir}/templates/*.j2" ) - # Generate the source files + # Generate the source files. add_custom_command( - OUTPUT ${generated_files} - COMMAND ${Python3_EXECUTABLE} -m dbc_gen_cpp ${ARG_DBC} -o ${gen_dir} -n ${library_name} - DEPENDS ${ARG_DBC} ${GENERATOR_SOURCES} - COMMENT "Generating C source from DBC ${ARG_DBC} with cantools" + OUTPUT ${_dbc_generated_c} ${_dbc_generated_h} ${_dbc_generated_hpp} + COMMAND ${Python3_EXECUTABLE} -m dbc_gen_cpp ${_dbc_arg_DBC} -o ${_dbc_gen_dir} -n ${library_name} + DEPENDS ${_dbc_arg_DBC} ${_dbc_generator_sources} + COMMENT "Generating C source from DBC ${_dbc_arg_DBC} with cantools" VERBATIM ) add_custom_target(${library_name}_c_sources - DEPENDS ${generated_c} ${generated_h} + DEPENDS ${_dbc_generated_c} ${_dbc_generated_h} ) - # Create the library target from generated sources - add_library(${library_name} STATIC ${generated_c}) + # Create the library target from generated sources. + add_library(${library_name} STATIC ${_dbc_generated_c}) add_dependencies(${library_name} ${library_name}_c_sources) target_link_libraries(${library_name} PUBLIC dbc_gen_cpp::dbc_gen_cpp) + target_include_directories(${library_name} + PUBLIC + $ + $ + ) - if(ARG_EXPORT) - # Exportable target: use generator-expression include dirs so the target can be - # installed into an EXPORT set and linked from other packages. The generated - # headers install to include/${library_name}/, and the .hpp includes its C header - # as "${library_name}/${library_name}.h", so the install-interface root is include/. - target_include_directories(${library_name} - PUBLIC - $ - $ - ) - install( - TARGETS ${library_name} - EXPORT ${ARG_EXPORT} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin - INCLUDES DESTINATION include - ) - else() - # Default (in-package use): build-tree include dir only, no exported target. - target_include_directories(${library_name} - PUBLIC ${gen_basedir} - ) - endif() - - # Install the generated files to the install space, just in case they're included in public headers + # Install + export the target and its headers so other packages can link it as + # ::${library_name}. + install( + TARGETS ${library_name} + EXPORT ${library_name}Targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include + ) install( - FILES ${generated_h} ${generated_hpp} + FILES ${_dbc_generated_h} ${_dbc_generated_hpp} DESTINATION include/${library_name} ) -endfunction() + ament_export_targets(${library_name}Targets HAS_LIBRARY_TARGET) + ament_export_dependencies(dbc_gen_cpp) +endmacro() From 513777bb37fb43268fcf013e41a6447cc96ce0b7 Mon Sep 17 00:00:00 2001 From: David Tarazi Date: Tue, 28 Jul 2026 16:42:18 -0700 Subject: [PATCH 3/3] verify the export works as intended --- test_dbc_gen_cpp/CMakeLists.txt | 14 +++++++++ .../test/check_exported_headers.cmake | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 test_dbc_gen_cpp/test/check_exported_headers.cmake diff --git a/test_dbc_gen_cpp/CMakeLists.txt b/test_dbc_gen_cpp/CMakeLists.txt index bdcf85b..5426f04 100644 --- a/test_dbc_gen_cpp/CMakeLists.txt +++ b/test_dbc_gen_cpp/CMakeLists.txt @@ -48,6 +48,20 @@ if(BUILD_TESTING) ENV CATCH_CONFIG_CONSOLE_WIDTH=120 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) + + # Verify that generate_dbc_cpp() installs the generated headers into the export + # location (include//) so downstream packages can consume them. + # This exercises the install/export path, which the compiled test above does not: + # that test links the libraries in-tree via their BUILD_INTERFACE include dirs. + ament_add_test( + test_dbc_exported_headers + GENERATE_RESULT_FOR_RETURN_CODE_ZERO + COMMAND ${CMAKE_COMMAND} + -DINSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} + "-DLIBRARIES=fake_vehicle_can;fake_j1939_can" + -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_exported_headers.cmake + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) endif() ament_package() diff --git a/test_dbc_gen_cpp/test/check_exported_headers.cmake b/test_dbc_gen_cpp/test/check_exported_headers.cmake new file mode 100644 index 0000000..97ef93d --- /dev/null +++ b/test_dbc_gen_cpp/test/check_exported_headers.cmake @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Verifies that generate_dbc_cpp() exports the generated headers into the install +# space so downstream packages can include them as /.hpp. +# +# Invoked with -DINSTALL_PREFIX= -DLIBRARIES=. + +if(NOT DEFINED INSTALL_PREFIX) + message(FATAL_ERROR "check_exported_headers: INSTALL_PREFIX not set") +endif() +if(NOT DEFINED LIBRARIES) + message(FATAL_ERROR "check_exported_headers: LIBRARIES not set") +endif() + +set(_missing "") +foreach(_lib IN LISTS LIBRARIES) + foreach(_ext h hpp) + set(_header "${INSTALL_PREFIX}/include/${_lib}/${_lib}.${_ext}") + if(EXISTS "${_header}") + message(STATUS "Found exported header: ${_header}") + else() + message(WARNING "Missing exported header: ${_header}") + list(APPEND _missing "${_header}") + endif() + endforeach() +endforeach() + +if(_missing) + message(FATAL_ERROR "generate_dbc_cpp did not export the expected headers:\n ${_missing}") +endif()