|
1 | 1 | # SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc. |
2 | 2 | # SPDX-License-Identifier: Apache-2.0 |
3 | | -function(generate_dbc_cpp library_name) |
| 3 | +# |
| 4 | +# generate_dbc_cpp(<library_name> DBC <path/to/file.dbc>) |
| 5 | +# |
| 6 | +# Generates C/C++ CAN message types from a DBC file and builds them into a static |
| 7 | +# library named <library_name> (C++ namespace and C-symbol prefix also <library_name>, |
| 8 | +# headers at include/<library_name>/<library_name>.hpp). |
| 9 | +# |
| 10 | +# The library is ALWAYS installed and exported, so a downstream package that does |
| 11 | +# find_package(<this_package>) can link it as the imported target |
| 12 | +# <this_package>::<library_name>. Within the generating package it is also usable by |
| 13 | +# its plain local name. |
| 14 | +# |
| 15 | +# NOTE: this is a macro, not a function, on purpose. It calls ament_export_targets() |
| 16 | +# and ament_export_dependencies(), whose bookkeeping is stored in ordinary variables |
| 17 | +# in the current scope; ament_package() reads them from the package's top-level scope, |
| 18 | +# so they must not be trapped inside a function scope. |
| 19 | +macro(generate_dbc_cpp library_name) |
4 | 20 | find_package(Python3 REQUIRED COMPONENTS Interpreter) |
5 | 21 |
|
6 | | - set(one_value_args DBC) |
7 | | - cmake_parse_arguments(ARG "" "${one_value_args}" "" ${ARGN}) |
8 | | - if(NOT ARG_DBC) |
| 22 | + cmake_parse_arguments(_dbc_arg "" "DBC" "" ${ARGN}) |
| 23 | + if(NOT _dbc_arg_DBC) |
9 | 24 | message(FATAL_ERROR "generate_dbc_cpp: Missing required keyword argument DBC") |
10 | 25 | endif() |
11 | 26 |
|
12 | | - set(gen_basedir ${CMAKE_CURRENT_BINARY_DIR}/dbc_gen_cpp) |
13 | | - set(gen_dir ${gen_basedir}/${library_name}) |
| 27 | + set(_dbc_gen_basedir ${CMAKE_CURRENT_BINARY_DIR}/dbc_gen_cpp) |
| 28 | + set(_dbc_gen_dir ${_dbc_gen_basedir}/${library_name}) |
14 | 29 |
|
15 | | - set(generated_c ${gen_dir}/${library_name}.c) |
16 | | - set(generated_h ${gen_dir}/${library_name}.h) |
17 | | - set(generated_hpp ${gen_dir}/${library_name}.hpp) |
18 | | - set(generated_files ${generated_c} ${generated_h} ${generated_hpp}) |
| 30 | + set(_dbc_generated_c ${_dbc_gen_dir}/${library_name}.c) |
| 31 | + set(_dbc_generated_h ${_dbc_gen_dir}/${library_name}.h) |
| 32 | + set(_dbc_generated_hpp ${_dbc_gen_dir}/${library_name}.hpp) |
19 | 33 |
|
20 | | - # Kind of awkward, but makes the sources get regenerated if the generator tool changes, by depending on the generator sources. |
21 | | - # Finds the python module directory via Python3 import |
| 34 | + # Kind of awkward, but makes the sources get regenerated if the generator tool |
| 35 | + # changes, by depending on the generator sources. Finds the python module directory. |
22 | 36 | execute_process( |
23 | 37 | COMMAND ${Python3_EXECUTABLE} -c "import dbc_gen_cpp, os; print(os.path.dirname(dbc_gen_cpp.__file__))" |
24 | | - OUTPUT_VARIABLE GENERATOR_DIR |
| 38 | + OUTPUT_VARIABLE _dbc_generator_dir |
25 | 39 | OUTPUT_STRIP_TRAILING_WHITESPACE |
26 | 40 | ) |
27 | | - file(GLOB_RECURSE GENERATOR_SOURCES CONFIGURE_DEPENDS |
28 | | - "${GENERATOR_DIR}/*.py" |
29 | | - "${GENERATOR_DIR}/templates/*.j2" |
| 41 | + file(GLOB_RECURSE _dbc_generator_sources CONFIGURE_DEPENDS |
| 42 | + "${_dbc_generator_dir}/*.py" |
| 43 | + "${_dbc_generator_dir}/templates/*.j2" |
30 | 44 | ) |
31 | 45 |
|
32 | | - # Generate the source files |
| 46 | + # Generate the source files. |
33 | 47 | add_custom_command( |
34 | | - OUTPUT ${generated_files} |
35 | | - COMMAND ${Python3_EXECUTABLE} -m dbc_gen_cpp ${ARG_DBC} -o ${gen_dir} -n ${library_name} |
36 | | - DEPENDS ${ARG_DBC} ${GENERATOR_SOURCES} |
37 | | - COMMENT "Generating C source from DBC ${ARG_DBC} with cantools" |
| 48 | + OUTPUT ${_dbc_generated_c} ${_dbc_generated_h} ${_dbc_generated_hpp} |
| 49 | + COMMAND ${Python3_EXECUTABLE} -m dbc_gen_cpp ${_dbc_arg_DBC} -o ${_dbc_gen_dir} -n ${library_name} |
| 50 | + DEPENDS ${_dbc_arg_DBC} ${_dbc_generator_sources} |
| 51 | + COMMENT "Generating C source from DBC ${_dbc_arg_DBC} with cantools" |
38 | 52 | VERBATIM |
39 | 53 | ) |
40 | 54 | add_custom_target(${library_name}_c_sources |
41 | | - DEPENDS ${generated_c} ${generated_h} |
| 55 | + DEPENDS ${_dbc_generated_c} ${_dbc_generated_h} |
42 | 56 | ) |
43 | 57 |
|
44 | | - # Create the library target from generated sources |
45 | | - add_library(${library_name} STATIC ${generated_c}) |
| 58 | + # Create the library target from generated sources. |
| 59 | + add_library(${library_name} STATIC ${_dbc_generated_c}) |
46 | 60 | add_dependencies(${library_name} ${library_name}_c_sources) |
47 | 61 | target_link_libraries(${library_name} PUBLIC dbc_gen_cpp::dbc_gen_cpp) |
48 | 62 | target_include_directories(${library_name} |
49 | | - PUBLIC ${gen_basedir} |
| 63 | + PUBLIC |
| 64 | + $<BUILD_INTERFACE:${_dbc_gen_basedir}> |
| 65 | + $<INSTALL_INTERFACE:include> |
50 | 66 | ) |
51 | 67 |
|
52 | | - # Install the generated files to the install space, just in case they're included in public headers |
| 68 | + # Install + export the target and its headers so other packages can link it as |
| 69 | + # <this_package>::${library_name}. |
53 | 70 | install( |
54 | | - FILES ${generated_h} ${generated_hpp} |
| 71 | + TARGETS ${library_name} |
| 72 | + EXPORT ${library_name}Targets |
| 73 | + ARCHIVE DESTINATION lib |
| 74 | + LIBRARY DESTINATION lib |
| 75 | + RUNTIME DESTINATION bin |
| 76 | + INCLUDES DESTINATION include |
| 77 | + ) |
| 78 | + install( |
| 79 | + FILES ${_dbc_generated_h} ${_dbc_generated_hpp} |
55 | 80 | DESTINATION include/${library_name} |
56 | 81 | ) |
57 | | -endfunction() |
| 82 | + ament_export_targets(${library_name}Targets HAS_LIBRARY_TARGET) |
| 83 | + ament_export_dependencies(dbc_gen_cpp) |
| 84 | +endmacro() |
0 commit comments