Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CMake build to generate Python protobuf bindings #7

Merged
merged 2 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
150 changes: 150 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,153 @@ target_link_libraries(ign_topic_echo
ignition-msgs${IGN_MSGS_VER}::ignition-msgs${IGN_MSGS_VER}
ignition-transport${IGN_TRANSPORT_VER}::ignition-transport${IGN_TRANSPORT_VER}
)

#============================================================================
# generate protobuf Python bindings

# Get the proto files from the installed location of ign-msgs
set(PROTO_PATH ${ignition-msgs${IGN_MSGS_VER}_INCLUDE_DIRS})
file(GLOB PROTO_FILES ${PROTO_PATH}/ignition/msgs/*.proto)

#============================================================================
# Modified from ign-msgs/src/CMakeLists.txt for Python
#============================================================================
# A function that calls protoc on a protobuf file
# Options:
# GENERATE_RUBY - generates ruby code for the message if specified
# GENERATE_CPP - generates c++ code for the message if specified
# GENERATE_PY - generates Python code for the message if specified
# One value arguments:
# PROTO_PACKAGE - Protobuf package the file belongs to (e.g. ".ignition.msgs")
# PROTOC_EXEC - Path to protoc
# INPUT_PROTO - Path to the input .proto file
# OUTPUT_CPP_DIR - Path where C++ files are saved
# OUTPUT_RUBY_DIR - Path where Ruby files are saved
# OUTPUT_PY_DIR - Path where Python files are saved
# OUTPUT_CPP_HH_VAR - A CMake variable name containing a list that the C++ header path should be appended to
# OUTPUT_CPP_CC_VAR - A CMake variable name containing a list that the C++ source path should be appended to
# OUTPUT_RUBY_VAR - A CMake variable name containing a list that the ruby file should be appended to
# OUTPUT_PY_VAR - A CMake variable name containing a list that the Python file should be appended to
# Multi value arguments
# PROTO_PATH - Passed to protoc --proto_path
function(ign_msgs_protoc)
set(options GENERATE_RUBY GENERATE_CPP GENERATE_PY)
set(oneValueArgs PROTO_PACKAGE PROTOC_EXEC INPUT_PROTO OUTPUT_CPP_DIR OUTPUT_RUBY_DIR OUTPUT_PY_DIR OUTPUT_CPP_HH_VAR OUTPUT_CPP_CC_VAR OUTPUT_RUBY_VAR OUTPUT_PY_VAR)
set(multiValueArgs PROTO_PATH)

cmake_parse_arguments(ign_msgs_protoc "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

get_filename_component(ABS_FIL ${ign_msgs_protoc_INPUT_PROTO} ABSOLUTE)
get_filename_component(FIL_WE ${ign_msgs_protoc_INPUT_PROTO} NAME_WE)

set(protoc_args)
set(output_files)

foreach(proto_path ${ign_msgs_protoc_PROTO_PATH})
list(APPEND protoc_args "--proto_path=${proto_path}")
endforeach()

set(proto_package_dir ".")
if(ign_msgs_protoc_PROTO_PACKAGE)
string(REPLACE "." "/" proto_package_dir ${ign_msgs_protoc_PROTO_PACKAGE})
endif()

if(ign_msgs_protoc_GENERATE_CPP)
set(output_header "${ign_msgs_protoc_OUTPUT_CPP_DIR}${proto_package_dir}/${FIL_WE}.pb.h")
set(output_source "${ign_msgs_protoc_OUTPUT_CPP_DIR}${proto_package_dir}/${FIL_WE}.pb.cc")
list(APPEND ${ign_msgs_protoc_OUTPUT_CPP_HH_VAR} ${output_header})
list(APPEND ${ign_msgs_protoc_OUTPUT_CPP_CC_VAR} ${output_source})
list(APPEND output_files ${output_header})
list(APPEND output_files ${output_source})
list(APPEND protoc_args "--plugin=protoc-gen-ignmsgs=${IGN_MSGS_GEN_EXECUTABLE}")
list(APPEND protoc_args "--cpp_out=dllexport_decl=IGNITION_MSGS_VISIBLE:${ign_msgs_protoc_OUTPUT_CPP_DIR}")
list(APPEND protoc_args "--ignmsgs_out" "${ign_msgs_protoc_OUTPUT_CPP_DIR}")
set(${ign_msgs_protoc_OUTPUT_CPP_HH_VAR} ${${ign_msgs_protoc_OUTPUT_CPP_HH_VAR}} PARENT_SCOPE)
set(${ign_msgs_protoc_OUTPUT_CPP_CC_VAR} ${${ign_msgs_protoc_OUTPUT_CPP_CC_VAR}} PARENT_SCOPE)
endif()

if(ign_msgs_protoc_GENERATE_RUBY)
file(MAKE_DIRECTORY ${ign_msgs_protoc_OUTPUT_RUBY_DIR})
set(output_ruby "${ign_msgs_protoc_OUTPUT_RUBY_DIR}${proto_package_dir}/${FIL_WE}_pb.rb")
list(APPEND ${ign_msgs_protoc_OUTPUT_RUBY_VAR} ${output_ruby})
list(APPEND output_files ${output_ruby})
list(APPEND protoc_args "--ruby_out=${ign_msgs_protoc_OUTPUT_RUBY_DIR}")
set(${ign_msgs_protoc_OUTPUT_RUBY_VAR} ${${ign_msgs_protoc_OUTPUT_RUBY_VAR}} PARENT_SCOPE)
endif()

if(ign_msgs_protoc_GENERATE_PY)
file(MAKE_DIRECTORY ${ign_msgs_protoc_OUTPUT_PY_DIR})
set(output_py "${ign_msgs_protoc_OUTPUT_PY_DIR}${proto_package_dir}/${FIL_WE}_pb2.py")
list(APPEND ${ign_msgs_protoc_OUTPUT_PY_VAR} ${output_py})
list(APPEND output_files ${output_py})
list(APPEND protoc_args "--python_out=${ign_msgs_protoc_OUTPUT_PY_DIR}")
set(${ign_msgs_protoc_OUTPUT_PY_VAR} ${${ign_msgs_protoc_OUTPUT_PY_VAR}} PARENT_SCOPE)
endif()

#get_cmake_property(_variableNames VARIABLES)
#foreach (_variableName ${_variableNames})
# message(STATUS "${_variableName}=${${_variableName}}")
#endforeach()

add_custom_command(
OUTPUT
${output_files}
COMMAND
${ign_msgs_protoc_PROTOC_EXEC}
ARGS
${protoc_args}
${ABS_FIL}
DEPENDS
${ABS_FIL}
# ign_msgs_gen
COMMENT "Running protoc on ${ign_msgs_protoc_INPUT_PROTO}"
VERBATIM)
endfunction()

#============================================================================
# run the code generation
foreach(proto_file ${PROTO_FILES})
ign_msgs_protoc(
PROTO_PACKAGE
.ignition.msgs
# GENERATE_CPP
# GENERATE_RUBY
GENERATE_PY
INPUT_PROTO
${proto_file}
PROTOC_EXEC
protobuf::protoc
OUTPUT_CPP_DIR
"${PROJECT_BINARY_DIR}/include"
OUTPUT_RUBY_DIR
"${PROJECT_BINARY_DIR}/ruby"
OUTPUT_PY_DIR
"${PROJECT_BINARY_DIR}/python"
OUTPUT_CPP_HH_VAR
gen_headers
OUTPUT_CPP_CC_VAR
gen_sources
OUTPUT_RUBY_VAR
gen_ruby_scripts
OUTPUT_PY_VAR
gen_py_scripts
PROTO_PATH
"${PROTO_PATH}")

endforeach()

#============================================================================
# add custom target to force .proto compilation

set_source_files_properties(
${gen_headers}
${gen_sources}
${gen_ruby_scripts}
${gen_py_scripts}
PROPERTIES GENERATED TRUE
)

foreach(gen_py_script ${gen_py_scripts})
get_filename_component(FIL_WE ${gen_py_script} NAME_WE)
add_custom_target(${FIL_WE} ALL DEPENDS ${gen_py_script})
endforeach()