#
# Copyright(c) 2021 to 2022 ZettaScale Technology and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
function(IDLC_GENERATE)
set(options NO_TYPE_INFO WERROR DEFAULT_NON_NESTED)
set(one_value_keywords TARGET DEFAULT_EXTENSIBILITY BASE_DIR OUTPUT_DIR)
set(multi_value_keywords FILES FEATURES INCLUDES WARNINGS DEFINES)
cmake_parse_arguments(
IDLC "${options}" "${one_value_keywords}" "${multi_value_keywords}" "" ${ARGN})
if (TARGET CycloneDDS::libidlc)
set(_idlc_backend "$<TARGET_FILE:CycloneDDS::libidlc>")
if (NOT DEFINED _idlc_generate_skipreport)
message(STATUS "Building internal IDLC backend")
set(_idlc_generate_skipreport 1 CACHE INTERNAL "")
endif()
set(_idlc_depends CycloneDDS::libidlc)
else()
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND NOT ".so" IN_LIST CMAKE_FIND_LIBRARY_SUFFIXES)
# When cross-compiling, find_library looks for libraries using naming conventions of the target,
# But here we're trying to find the idlc backend library to run on the host.
# For now, building for a Windows target on a Linux host with w64-mingw is supported by this workaround.
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".so")
endif()
find_library(_idlc_backend "cycloneddsidlc" NO_CMAKE_FIND_ROOT_PATH)
if (_idlc_backend)
if (NOT DEFINED _idlc_generate_skipreport)
message(STATUS "Using external IDLC backend: ${_idlc_backend}")
set(_idlc_generate_skipreport 1 CACHE INTERNAL "")
endif()
else()
message(FATAL_ERROR "Unable to find IDLC C-backend library: cycloneddsidlc")
endif()
endif()
set(gen_args
BACKEND ${_idlc_backend}
${IDLC_UNPARSED_ARGUMENTS}
TARGET ${IDLC_TARGET}
BASE_DIR ${IDLC_BASE_DIR}
FILES ${IDLC_FILES}
FEATURES ${IDLC_FEATURES}
INCLUDES ${IDLC_INCLUDES}
WARNINGS ${IDLC_WARNINGS}
OUTPUT_DIR ${IDLC_OUTPUT_DIR}
DEFAULT_EXTENSIBILITY ${IDLC_DEFAULT_EXTENSIBILITY}
DEFINES ${IDLC_DEFINES}
DEPENDS ${_idlc_depends})
if(${IDLC_NO_TYPE_INFO})
list(APPEND gen_args NO_TYPE_INFO)
endif()
if(${IDLC_WERROR})
list(APPEND gen_args WERROR)
endif()
if(${IDLC_DEFAULT_NON_NESTED})
list(APPEND gen_args DEFAULT_NON_NESTED)
endif()
idlc_generate_generic(${gen_args})
endfunction()
function(IDLC_GENERATE_GENERIC)
set(options NO_TYPE_INFO WERROR DEFAULT_NON_NESTED)
set(one_value_keywords TARGET BACKEND DEFAULT_EXTENSIBILITY BASE_DIR OUTPUT_DIR)
set(multi_value_keywords FILES FEATURES INCLUDES WARNINGS SUFFIXES DEFINES DEPENDS)
cmake_parse_arguments(
IDLC "${options}" "${one_value_keywords}" "${multi_value_keywords}" "" ${ARGN})
# find idlc binary
if(BUILD_IDLC)
if (CMAKE_PROJECT_NAME STREQUAL "CycloneDDS")
# By using the internal target when building CycloneDDS itself, prevent using an external idlc
# This prevents a problem when an installed cyclone is on your prefix path when building cyclone again
set(_idlc_executable idlc)
set(_idlc_depends idlc)
else()
set(_idlc_executable CycloneDDS::idlc)
set(_idlc_depends CycloneDDS::idlc)
endif()
else()
find_program(_idlc_executable "idlc" NO_CMAKE_FIND_ROOT_PATH REQUIRED)
endif()
if(NOT IDLC_TARGET AND NOT IDLC_FILES)
# assume deprecated invocation: TARGET FILE [FILE..]
list(GET IDLC_UNPARSED_ARGUMENTS 0 IDLC_TARGET)
list(REMOVE_AT IDLC_UNPARSED_ARGUMENTS 0 IDLC_)
set(IDLC_FILES ${IDLC_UNPARSED_ARGUMENTS})
if (IDLC_TARGET AND IDLC_FILES)
message(WARNING " Deprecated use of idlc_generate. \n"
" Consider switching to keyword based invocation.")
endif()
# Java based compiler used to be case sensitive
list(APPEND IDLC_FEATURES "case-sensitive")
endif()
if(NOT IDLC_TARGET)
message(FATAL_ERROR "idlc_generate called without TARGET")
elseif(NOT IDLC_FILES)
message(FATAL_ERROR "idlc_generate called without FILES")
endif()
# remove duplicate features
if(IDLC_FEATURES)
list(REMOVE_DUPLICATES IDLC_FEATURES)
endif()
foreach(_feature ${IDLC_FEATURES})
list(APPEND IDLC_ARGS "-f" ${_feature})
endforeach()
# add macro definitions (-Dname or -Dname=value)
foreach(def ${IDLC_DEFINES})
list(APPEND IDLC_ARGS "-D${def}")
endforeach()
# add directories to include search list
if(IDLC_INCLUDES)
foreach(_dir ${IDLC_INCLUDES})
list(APPEND IDLC_INCLUDE_DIRS "-I" ${_dir})
endforeach()
endif()
# generate using which language (defaults to c)?
if(IDLC_BACKEND)
string(APPEND _language "-l" ${IDLC_BACKEND})
endif()
# set dependencies
if(IDLC_DEPENDS)
list(APPEND _depends ${_idlc_depends} ${IDLC_DEPENDS})
else()
set(_depends ${_idlc_depends})
endif()
if(IDLC_DEFAULT_EXTENSIBILITY)
set(_default_extensibility ${IDLC_DEFAULT_EXTENSIBILITY})
list(APPEND IDLC_ARGS "-x" ${_default_extensibility})
endif()
if(IDLC_WARNINGS)
foreach(_warn ${IDLC_WARNINGS})
list(APPEND IDLC_ARGS "-W${_warn}")
endforeach()
endif()
if(IDLC_NO_TYPE_INFO)
list(APPEND IDLC_ARGS "-t")
endif()
if(IDLC_WERROR)
list(APPEND IDLC_ARGS "-Werror")
endif()
if(IDLC_DEFAULT_NON_NESTED)
list(APPEND IDLC_ARGS "-nfalse")
endif()
if(IDLC_BASE_DIR)
if(${CMAKE_VERSION} VERSION_LESS "3.19.0")
get_filename_component(_base_dir_abs ${IDLC_BASE_DIR} REALPATH)
else()
file(REAL_PATH ${IDLC_BASE_DIR} _base_dir_abs)
endif()
list(APPEND IDLC_ARGS "-b${_base_dir_abs}")
endif()
if(IDLC_OUTPUT_DIR)
set(_dir ${IDLC_OUTPUT_DIR})
else()
set(_dir ${CMAKE_CURRENT_BINARY_DIR})
endif()
list(APPEND IDLC_ARGS "-o${_dir}")
set(_target ${IDLC_TARGET})
foreach(_file ${IDLC_FILES})
get_filename_component(_path ${_file} ABSOLUTE)
list(APPEND _files "${_path}")
endforeach()
# set source suffixes (defaults to .c and .h)
if(NOT IDLC_SUFFIXES)
set(IDLC_SUFFIXES ".c" ".h")
endif()
set(_outputs "")
foreach(_file ${_files})
get_filename_component(_name ${_file} NAME_WLE)
get_filename_component(_name_ext ${_file} NAME)
# Determine middle path for directory reconstruction
if(IDLC_BASE_DIR)
file(RELATIVE_PATH _file_path_rel ${_base_dir_abs} ${_file})
# Hard Fail
if(_file_path_rel MATCHES "^\\.\\.")
message(FATAL_ERROR "Cannot use base dir with different file tree from input file (${_base_dir_abs} to ${_file} yields ${_file_path_rel})")
endif()
string(REPLACE ${_name_ext} "" _mid_dir_path ${_file_path_rel})
string(REGEX REPLACE "[\\/]$" "" _mid_dir_path "${_mid_dir_path}")
endif()
set(_file_outputs "")
foreach(_suffix ${IDLC_SUFFIXES})
if(IDLC_BASE_DIR)
list(APPEND _file_outputs "${_dir}/${_mid_dir_path}/${_name}${_suffix}")
else()
list(APPEND _file_outputs "${_dir}/${_name}${_suffix}")
endif()
endforeach()
list(APPEND _outputs ${_file_outputs})
add_custom_command(
OUTPUT ${_file_outputs}
COMMAND ${_idlc_executable}
ARGS ${_language} ${IDLC_ARGS} ${IDLC_INCLUDE_DIRS} ${_file}
DEPENDS ${_files} ${_depends})
endforeach()
add_custom_target("${_target}_generate" DEPENDS "${_outputs}")
if(${CMAKE_GENERATOR} MATCHES "Xcode")
set_target_properties("${_target}_generate" PROPERTIES XCODE_GENERATE_SCHEME NO)
endif()
add_library(${_target} INTERFACE)
target_sources(${_target} INTERFACE ${_outputs})
target_include_directories(${_target} INTERFACE "${_dir}")
add_dependencies(${_target} "${_target}_generate")
if(${CMAKE_GENERATOR} MATCHES "Xcode")
set_target_properties("${_target}" PROPERTIES XCODE_GENERATE_SCHEME NO)
endif()
endfunction()