reflective-rapidjson/lib/cmake/modules/ReflectionGenerator.cmake

218 lines
9.5 KiB
CMake

cmake_minimum_required(VERSION 3.17.0 FATAL_ERROR)
# prevent multiple inclusion
if (DEFINED REFLECTION_GENERATOR_MODULE_LOADED)
return()
endif ()
set(REFLECTION_GENERATOR_MODULE_LOADED YES)
# find code generator
set(DEFAULT_REFLECTION_GENERATOR_EXECUTABLE "${TARGET_PREFIX}reflective_rapidjson_generator${TARGET_SUFFIX}")
set(REFLECTION_GENERATOR_EXECUTABLE
""
CACHE FILEPATH "path to executable of reflection generator")
if (REFLECTION_GENERATOR_EXECUTABLE)
# use custom generator executable
if (NOT EXISTS "${REFLECTION_GENERATOR_EXECUTABLE}" OR IS_DIRECTORY "${REFLECTION_GENERATOR_EXECUTABLE}")
message(FATAL_ERROR "The specified code generator executable \"${REFLECTION_GENERATOR_EXECUTABLE}\" is not a file.")
endif ()
elseif (CMAKE_CROSSCOMPILING OR NOT TARGET "${DEFAULT_REFLECTION_GENERATOR_EXECUTABLE}")
# find native/external "reflective_rapidjson_generator"
find_program(REFLECTION_GENERATOR_EXECUTABLE "${DEFAULT_REFLECTION_GENERATOR_EXECUTABLE}" PATHS "/usr/bin" "/bin")
else ()
# use "reflective_rapidjson_generator" target
set(REFLECTION_GENERATOR_EXECUTABLE "${DEFAULT_REFLECTION_GENERATOR_EXECUTABLE}")
endif ()
if (NOT REFLECTION_GENERATOR_EXECUTABLE)
message(
FATAL_ERROR
"Unable to find executable of generator for reflection code. Set REFLECTION_GENERATOR_EXECUTABLE to specify the path."
)
endif ()
# determine Clang's resource directory
set(REFLECTION_GENERATOR_CLANG_RESOURCE_DIR
""
CACHE PATH "directory containing Clang's builtin headers, usually /usr/lib/clang/version")
if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR)
if (NOT REFLECTION_GENERATOR_CLANG_BIN)
find_program(
REFLECTION_GENERATOR_CLANG_BIN clang
NAMES clang++
PATHS "/usr/bin" "/bin")
if (NOT REFLECTION_GENERATOR_CLANG_BIN)
message(FATAL_ERROR "Unable to find the clang executable to determine Clang's resource directory")
endif ()
endif ()
execute_process(
COMMAND ${REFLECTION_GENERATOR_CLANG_BIN} -print-resource-dir
OUTPUT_VARIABLE REFLECTION_GENERATOR_CLANG_RESOURCE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif ()
if (NOT REFLECTION_GENERATOR_CLANG_RESOURCE_DIR OR NOT IS_DIRECTORY "${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR}")
message(
FATAL_ERROR
"Unable to find Clang's resource directory. Set REFLECTION_GENERATOR_CLANG_RESOURCE_DIR manually to the corresponding path (usually /usr/lib/clang/\$version)."
)
endif ()
message(STATUS "Using ${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR} as Clang's resource directory for Reflective RapidJSON")
# allow to specify a custom include paths (useful for cross-compilation when header files are under custom prefix)
set(REFLECTION_GENERATOR_INCLUDE_DIRECTORIES
""
CACHE FILEPATH "include directories for code generator")
# allow to specify a custom platform tiple (useful for cross-compilation to specify the target platform)
set(REFLECTION_GENERATOR_TRIPLE
""
CACHE STRING "platform triple for code generator")
function (_reflective_rapidjson_set_prop TARGET_NAME PROPERTY_NAME)
if ("${CMAKE_VERSION}" VERSION_LESS "3.15.0")
set(PROP
"$<TARGET_PROPERTY:${TARGET_NAME},${PROPERTY_NAME}>"
PARENT_SCOPE)
message(
WARNING
"Passing empty flags to the code generator for property ${PROPERTY_NAME} of target ${TARGET_NAME} might not be prevented. Consider updating to CMake 3.15.0 or newer."
)
else ()
set(PROP
"$<FILTER:$<TARGET_PROPERTY:${TARGET_NAME},${PROPERTY_NAME}>,EXCLUDE,^$>"
PARENT_SCOPE)
endif ()
endfunction ()
# define helper function to add a reflection generator invocation for a specified list of source files
include(CMakeParseArguments)
function (add_reflection_generator_invocation)
# parse arguments
set(OPTIONAL_ARGS ERROR_RESILIENT)
set(ONE_VALUE_ARGS OUTPUT_DIRECTORY JSON_VISIBILITY BINARY_VISBILITY)
set(MULTI_VALUE_ARGS
INPUT_FILES
GENERATORS
OUTPUT_LISTS
CLANG_OPTIONS
CLANG_OPTIONS_FROM_TARGETS
CLANG_OPTIONS_FROM_DEPENDENCIES
JSON_CLASSES)
cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN})
# determine file name or file path if none specified
if (NOT ARGS_OUTPUT_DIRECTORY)
set(ARGS_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/reflection")
file(MAKE_DIRECTORY "${ARGS_OUTPUT_DIRECTORY}")
endif ()
# specify Clang's resource directory
list(APPEND ARGS_CLANG_OPTIONS -resource-dir "${REFLECTION_GENERATOR_CLANG_RESOURCE_DIR}")
# apply specified REFLECTION_GENERATOR_TRIPLET
if (REFLECTION_GENERATOR_TRIPLE)
list(APPEND ARGS_CLANG_OPTIONS -Xclang -triple -Xclang "${REFLECTION_GENERATOR_TRIPLE}")
endif ()
# apply specified REFLECTION_GENERATOR_INCLUDE_DIRECTORIES
foreach (INCLUDE_DIR ${REFLECTION_GENERATOR_INCLUDE_DIRECTORIES})
if (NOT IS_DIRECTORY "${INCLUDE_DIR}")
message(FATAL_ERROR "Specified include directory \"${INCLUDE_DIR})\" for reflection generator doesn't exists.")
endif ()
list(APPEND ARGS_CLANG_OPTIONS -I "${INCLUDE_DIR}")
endforeach ()
# avoid including headers from host when cross compiling
if (CMAKE_CROSSCOMPILING)
list(APPEND ARGS_CLANG_OPTIONS -nostdinc)
endif ()
# add options to be passed to clang from the specified targets
if (ARGS_CLANG_OPTIONS_FROM_TARGETS)
foreach (TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_TARGETS})
# set c++ standard
list(
APPEND
ARGS_CLANG_OPTIONS
"$<$<BOOL:$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>:-std=c++$<TARGET_PROPERTY:${TARGET_NAME},CXX_STANDARD>>"
)
# add compile flags and options
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_FLAGS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>")
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_OPTIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>")
# add compile definitions
_reflective_rapidjson_set_prop("${TARGET_NAME}" COMPILE_DEFINITIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-D$<JOIN:${PROP},$<SEMICOLON>-D>>")
# add include directories
_reflective_rapidjson_set_prop("${TARGET_NAME}" INCLUDE_DIRECTORIES)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-I$<JOIN:${PROP},$<SEMICOLON>-I>>")
endforeach ()
endif ()
if (ARGS_CLANG_OPTIONS_FROM_DEPENDENCIES)
foreach (TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_DEPENDENCIES})
if (NOT TARGET "${TARGET_NAME}")
continue()
endif ()
# add interface compile options
_reflective_rapidjson_set_prop("${TARGET_NAME}" INTERFACE_COMPILE_OPTIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:$<JOIN:${PROP},$<SEMICOLON>>>")
# add interface compile definitions
_reflective_rapidjson_set_prop("${TARGET_NAME}" INTERFACE_COMPILE_DEFINITIONS)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-D$<JOIN:${PROP},$<SEMICOLON>-D>>")
# add interface include directories
_reflective_rapidjson_set_prop("${TARGET_NAME}" INTERFACE_INCLUDE_DIRECTORIES)
list(APPEND ARGS_CLANG_OPTIONS "$<$<BOOL:${PROP}>:-I$<JOIN:${PROP},$<SEMICOLON>-I>>")
endforeach ()
endif ()
# create a custom command for each input file
foreach (INPUT_FILE ${ARGS_INPUT_FILES})
# determine the output file
get_filename_component(OUTPUT_NAME "${INPUT_FILE}" NAME_WE)
set(OUTPUT_FILE "${ARGS_OUTPUT_DIRECTORY}/${OUTPUT_NAME}.h")
message(STATUS "Adding generator command for ${INPUT_FILE} producing ${OUTPUT_FILE}")
# compose the CLI arguments and actually add the custom command
set(CLI_ARGUMENTS
--output-file
"${OUTPUT_FILE}"
--input-file
"${INPUT_FILE}"
--generators
${ARGS_GENERATORS}
--clang-opt
${ARGS_CLANG_OPTIONS}
--json-classes
${ARGS_JSON_CLASSES})
if (ARGS_JSON_VISIBILITY)
list(APPEND CLI_ARGUMENTS --json-visibility "${ARGS_JSON_VISIBILITY}")
endif ()
if (ARGS_BINARY_VISBILITY)
list(APPEND CLI_ARGUMENTS --binary-visibility "${ARGS_BINARY_VISBILITY}")
endif ()
if (ARGS_ERROR_RESILIENT)
list(APPEND CLI_ARGUMENTS --error-resilient)
endif ()
add_custom_command(
OUTPUT "${OUTPUT_FILE}"
COMMAND "${REFLECTION_GENERATOR_EXECUTABLE}" ARGS ${CLI_ARGUMENTS}
DEPENDS "${INPUT_FILE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Generating reflection code for ${INPUT_FILE}"
VERBATIM)
# prevent Qt's code generator to be executed on the files generated by this code generator
set_property(SOURCE "${OUTPUT_FILE}" PROPERTY SKIP_AUTOGEN ON)
# append the output file to lists specified via OUTPUT_LISTS
if (ARGS_OUTPUT_LISTS)
foreach (OUTPUT_LIST ${ARGS_OUTPUT_LISTS})
list(APPEND "${OUTPUT_LIST}" "${OUTPUT_FILE}")
set("${OUTPUT_LIST}"
"${${OUTPUT_LIST}}"
PARENT_SCOPE)
endforeach ()
endif ()
endforeach ()
endfunction ()