# Copyright (c) 2025-2026, IB-Robot Group & openEuler Embedded SIG & openharmony-robot sig_RoboFrame.
# All rights reserved.
#
# DdsIdlRpcGenCode.cmake - Generate C++ RPC wrapper code from DDS IDL service definitions
#
# This module provides functions to generate C++ RPC service/proxy classes
# from YAML service definition files that reference DDS IDL message types.
#
# The generated code uses ibmw::dds::GetDdsMessageTypeSupport<>() for
# CDR serialization and produces service classes (Sync, Async, Co),
# proxy classes (Sync, Async, Future, Co), CoClient, and CoServer.
#
# Usage:
# add_dds_idl_rpc_gencode_target(
# TARGET_NAME <target>
# SERVICE_FILE <path/to/ExampleService.yaml>
# GENCODE_PATH <output_directory>
# IDL_FILE_STEM <ExampleRpc>
# [DEP_IDL_TARGETS <dep1> [dep2 ...]]
# )
# Generate C++ RPC wrapper code from a DDS IDL service definition YAML file
#
# Arguments:
# TARGET_NAME - Name of the CMake target to create
# SERVICE_FILE - Path to the YAML service definition file
# GENCODE_PATH - Output directory for generated code
# IDL_FILE_STEM - The IDL file stem (e.g., "ExampleRpc" for ExampleRpc.idl)
# DEP_IDL_TARGETS - CMake targets for IDL gencode dependencies (optional)
#
function(add_dds_idl_rpc_gencode_target)
cmake_parse_arguments(ARG "" "TARGET_NAME;SERVICE_FILE;GENCODE_PATH;IDL_FILE_STEM" "DEP_IDL_TARGETS" ${ARGN})
# Ensure Python3::Interpreter is available (may not exist in consumer projects)
if(NOT TARGET Python3::Interpreter)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
endif()
if(NOT EXISTS ${ARG_GENCODE_PATH})
file(MAKE_DIRECTORY ${ARG_GENCODE_PATH})
endif()
# Get the tool path from global property
get_property(DDS_IDL_RPC_GEN_CODE_TOOL_PATH GLOBAL PROPERTY DDS_IDL_RPC_GEN_CODE_TOOL_PATH_PROPERTY)
if(NOT DDS_IDL_RPC_GEN_CODE_TOOL_PATH)
message(FATAL_ERROR "DDS_IDL_RPC_GEN_CODE_TOOL_PATH_PROPERTY not set. "
"Make sure dds_idl_gen_rpc tool is added before calling this function.")
endif()
# Determine output file names
set(GEN_SRC "${ARG_GENCODE_PATH}/${ARG_IDL_FILE_STEM}.ibmw_rpc.dds.cc")
set(GEN_HDR "${ARG_GENCODE_PATH}/${ARG_IDL_FILE_STEM}.ibmw_rpc.dds.h")
# Build IDL gencode path arguments for include resolution
set(IDL_GENCODE_PATHS)
foreach(DEP ${ARG_DEP_IDL_TARGETS})
get_target_property(_dep_gencode_path ${DEP} IDL_GENCODE_PATH)
if(_dep_gencode_path)
list(APPEND IDL_GENCODE_PATHS ${_dep_gencode_path})
endif()
endforeach()
# Build --idl_gencode_path argument if we have any
set(IDL_GENCODE_ARG)
if(IDL_GENCODE_PATHS)
list(GET IDL_GENCODE_PATHS 0 _first_path)
set(IDL_GENCODE_ARG "--idl_gencode_path=${_first_path}")
endif()
# Custom command to run the Python code generator
add_custom_command(
OUTPUT ${GEN_SRC} ${GEN_HDR}
COMMAND Python3::Interpreter ARGS
${DDS_IDL_RPC_GEN_CODE_TOOL_PATH}/dds_idl_gen_rpc.py
--service_file=${ARG_SERVICE_FILE}
--output_path=${ARG_GENCODE_PATH}
${IDL_GENCODE_ARG}
DEPENDS
${ARG_SERVICE_FILE}
${DDS_IDL_RPC_GEN_CODE_TOOL_PATH}/dds_idl_gen_rpc.py
COMMENT "Generating ibmw DDS RPC code for ${ARG_IDL_FILE_STEM}"
VERBATIM
)
# Create a static library target with the generated code
add_library(${ARG_TARGET_NAME} STATIC)
set_target_properties(${ARG_TARGET_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_sources(${ARG_TARGET_NAME} PRIVATE ${GEN_SRC})
target_sources(${ARG_TARGET_NAME}
PUBLIC FILE_SET HEADERS
BASE_DIRS ${ARG_GENCODE_PATH}
FILES ${GEN_HDR}
)
target_include_directories(${ARG_TARGET_NAME}
PUBLIC
$<BUILD_INTERFACE:${ARG_GENCODE_PATH}>
$<INSTALL_INTERFACE:include/${ARG_TARGET_NAME}>
)
# Link against ibmw interfaces and IDL gencode dependencies.
# NOTE: The dds_types dependency here is intentional and legitimate — generated
# RPC code calls ibmw::dds::GetDdsMessageTypeSupport<>() (a real dds_extension
# API). This is unlike DdsIdlGenCode.cmake's WRAP_IDLS path, which only needs
# CDR-only headers and now links ibmw::schema::dds_cdr_helpers instead.
target_link_libraries(${ARG_TARGET_NAME}
PUBLIC
ibmw::api::cpp
ibmw::extensions::dds_types
${ARG_DEP_IDL_TARGETS}
)
endfunction()