Files
revng-revng/cmake/TupleTreeGenerator.cmake
2022-03-10 13:51:28 +01:00

293 lines
8.9 KiB
CMake

#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
# Extracts definitions and generates C++ headers and implementations from the
# given header files.
# The definitions must be embedded as described in the docs for
# tuple_tree_generator_extract_definitions_from_headers.
function(tuple_tree_generator
# Name of the target to generate the code
TARGET_NAME
# List of C++ headers
HEADERS
# Delimiter used to mark comments embedding type schemas
DELIMITER
NAMESPACE
# Where the schema will be collected
SCHEMA_PATH
# Directory where the headers will be generated
HEADERS_DIR
# Include path prefix
INCLUDE_PATH_PREFIX
# Variable will be filled with the list of generated C++ headers
GENERATED_HEADERS_VARIABLE
# Variable will be filled with the list of generated C++ source files
GENERATED_IMPLS_VARIABLE
# Where the JSON schema will be produced (empty for no schema)
JSONSCHEMA_PATH
# Type to use as the root of the JSON schema
JSONSCHEMA_ROOT_TYPE
# Types equivalent to strings
STRING_TYPES
# Types equivalent to strings which get a separate type definition
SEPARATE_STRING_TYPES
)
#
# Collect all the definitions in a single YAML document
#
tuple_tree_generator_extract_definitions_from_headers(
"${HEADERS}"
"${DELIMITER}"
"${SCHEMA_PATH}"
)
#
# C++ headers and implementation generation
#
tuple_tree_generator_compute_generated_cpp_files(
"${HEADERS}"
"${HEADERS_DIR}"
LOCAL_GENERATED_HEADERS
LOCAL_GENERATED_IMPLS
)
tuple_tree_generator_generate_cpp(
"${SCHEMA_PATH}"
"${NAMESPACE}"
"${HEADERS_DIR}"
"${INCLUDE_PATH_PREFIX}"
"${LOCAL_GENERATED_HEADERS}"
"${LOCAL_GENERATED_IMPLS}"
)
set("${GENERATED_HEADERS_VARIABLE}" ${LOCAL_GENERATED_HEADERS} PARENT_SCOPE)
set("${GENERATED_IMPLS_VARIABLE}" ${LOCAL_GENERATED_IMPLS} PARENT_SCOPE)
#
# Produce JSON schema, if requested
#
if(NOT "${JSONSCHEMA_PATH}" STREQUAL "")
tuple_tree_generator_generate_jsonschema(
"${SCHEMA_PATH}"
"${NAMESPACE}"
"${JSONSCHEMA_ROOT_TYPE}"
"${STRING_TYPES}"
"${SEPARATE_STRING_TYPES}"
"${JSONSCHEMA_PATH}"
)
endif()
add_custom_target(
"${TARGET_NAME}"
DEPENDS
"${SCHEMA_PATH}"
${LOCAL_GENERATED_HEADERS}
${LOCAL_GENERATED_IMPLS}
${JSONSCHEMA_PATH}
)
endfunction()
# Extracts tuple_tree_generator YAML definitions from the given header files
#
# The definitions have to be embedded in a c-style comment marked with the given
# delimiter, like so:
#
# /* DELIMITER
# <definition>
# DELIMITER */
function(tuple_tree_generator_extract_definitions_from_headers HEADERS DELIMITER OUTPUT_FILE)
add_custom_command(
OUTPUT "${OUTPUT_FILE}"
COMMAND "${CMAKE_SOURCE_DIR}/scripts/tuple_tree_generator/extract_yaml.py"
--output "${OUTPUT_FILE}"
"${DELIMITER}"
${HEADERS}
DEPENDS ${HEADERS}
)
endfunction()
# Computes the list of headers and C++ source files that will be generated by
# tuple_tree_generator.
# Note: the output variables will be overwritten
function(tuple_tree_generator_compute_generated_cpp_files
SOURCE_HEADERS
HEADERS_DIR
GENERATED_HEADERS_VARIABLE
GENERATED_IMPLS_VARIABLE
)
# Empty output variables
set(LOCAL_GENERATED_HEADERS_VARIABLE "${HEADERS_DIR}/ForwardDecls.h")
set(LOCAL_GENERATED_IMPLS_VARIABLE)
foreach(HEADER ${SOURCE_HEADERS})
# TODO: we should not be generating /Impl/*.cpp files in an include/* directory
# TODO: this piece of code is tightly coupled with cppheaders.py
get_filename_component(HEADER_FILENAME "${HEADER}" NAME)
get_filename_component(HEADER_FILENAME_WE "${HEADER}" NAME_WE)
set(EARLY_OUTPUT "${HEADERS_DIR}/Early/${HEADER_FILENAME}")
set(LATE_OUTPUT "${HEADERS_DIR}/Late/${HEADER_FILENAME}")
set(IMPL_OUTPUT "${HEADERS_DIR}/Impl/${HEADER_FILENAME_WE}.cpp")
list(APPEND LOCAL_GENERATED_HEADERS_VARIABLE "${EARLY_OUTPUT}")
list(APPEND LOCAL_GENERATED_HEADERS_VARIABLE "${LATE_OUTPUT}")
list(APPEND LOCAL_GENERATED_IMPLS_VARIABLE "${IMPL_OUTPUT}")
endforeach()
set("${GENERATED_HEADERS_VARIABLE}" ${LOCAL_GENERATED_HEADERS_VARIABLE} PARENT_SCOPE)
set("${GENERATED_IMPLS_VARIABLE}" ${LOCAL_GENERATED_IMPLS_VARIABLE} PARENT_SCOPE)
endfunction()
set(TEMPLATES_DIR "${CMAKE_SOURCE_DIR}/scripts/tuple_tree_generator/tuple_tree_generator/templates")
set(TEMPLATES
"${TEMPLATES_DIR}/class_forward_decls.h.tpl"
"${TEMPLATES_DIR}/enum.h.tpl"
"${TEMPLATES_DIR}/struct.h.tpl"
"${TEMPLATES_DIR}/struct_forward_decls.h.tpl"
"${TEMPLATES_DIR}/struct_late.h.tpl"
"${TEMPLATES_DIR}/struct_impl.cpp.tpl"
)
set(SCRIPTS_ROOT_DIR "${CMAKE_SOURCE_DIR}/scripts/tuple_tree_generator")
# The list of Python scripts is build as follows:
#
# find scripts/tuple_tree_generator -name "*.py" | sort | sed 's|scripts/tuple_tree_generator|"\${SCRIPTS_ROOT_DIR}|; s/$/"/'
#
# TODO: detect and warn about extra files in those directories
set(TUPLE_TREE_GENERATOR_SOURCES
"${SCRIPTS_ROOT_DIR}/extract_yaml.py"
"${SCRIPTS_ROOT_DIR}/tuple-tree-generate-cpp.py"
"${SCRIPTS_ROOT_DIR}/tuple-tree-generate-jsonschema.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/__init__.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/generators/__init__.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/generators/cppheaders.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/generators/jinja_utils.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/generators/jsonschema.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/schema/__init__.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/schema/definition.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/schema/enum.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/schema/schema.py"
"${SCRIPTS_ROOT_DIR}/tuple_tree_generator/schema/struct.py"
)
# Generates headers and implementation C++ files
function(tuple_tree_generator_generate_cpp
# Path to the yaml definitions
YAML_DEFINITIONS
# Base namespace of the generated classes (e.g. model)
NAMESPACE
# Output directory
OUTPUT_DIR
# Include path prefix
INCLUDE_PATH_PREFIX
# List of headers that are expected to be generated
EXPECTED_GENERATED_HEADERS
# List of implementation files expected to be generated
EXPECTED_GENERATED_IMPLS
)
add_custom_command(
COMMAND "${SCRIPTS_ROOT_DIR}/tuple-tree-generate-cpp.py"
--namespace "${NAMESPACE}"
--include-path-prefix "${INCLUDE_PATH_PREFIX}"
"${YAML_DEFINITIONS}"
"${OUTPUT_DIR}"
OUTPUT ${EXPECTED_GENERATED_HEADERS} ${EXPECTED_GENERATED_IMPLS}
DEPENDS
"${YAML_DEFINITIONS}"
${TEMPLATES}
"${SCRIPTS_ROOT_DIR}/extract_yaml.py"
${TUPLE_TREE_GENERATOR_SOURCES}
)
endfunction()
# Generates JSON schema files
function(tuple_tree_generator_generate_jsonschema
YAML_DEFINITIONS # Path to the yaml definitions
NAMESPACE # Base namespace of the generated classes (e.g. model)
JSONSCHEMA_ROOT_TYPE # Type to use as the root of the JSON schema
STRING_TYPES # Types equivalent to plain strings
SEPARATE_STRING_TYPES # Types equivalent to plain strings that get a separate type definition
OUTPUT_PATH # Output path
)
set(STRING_TYPE_ARGS)
foreach(ST ${STRING_TYPES})
list(APPEND STRING_TYPE_ARGS --string-type "${ST}")
endforeach()
set(SEPARATE_STRING_TYPE_ARGS)
foreach(ST ${SEPARATE_STRING_TYPES})
list(APPEND SEPARATE_STRING_TYPE_ARGS --separate-string-type "${ST}")
endforeach()
add_custom_command(
COMMAND "${SCRIPTS_ROOT_DIR}/tuple-tree-generate-jsonschema.py"
--namespace "${NAMESPACE}"
--root-type "${JSONSCHEMA_ROOT_TYPE}"
--output "${OUTPUT_PATH}"
${STRING_TYPE_ARGS}
${SEPARATE_STRING_TYPE_ARGS}
"${YAML_DEFINITIONS}"
OUTPUT "${OUTPUT_PATH}"
DEPENDS "${YAML_DEFINITIONS}" ${TUPLE_TREE_GENERATOR_SOURCES}
)
endfunction()
function(
target_tuple_tree_and_schema_generate
HEADER_DIRECTORY
NAMESPACE
SCHEMA_PATH
TARGET_ID
JSONSCHEMA_PATH
JSONSCHEMA_ROOT_TYPE
STRING_TYPES
SEPARATE_STRING_TYPES
)
# Generate C++ headers from the collected YAML
set(OUTPUT_DIR "${CMAKE_BINARY_DIR}/include/revng/${HEADER_DIRECTORY}/Generated")
tuple_tree_generator(
generate-${TARGET_ID}-tuple-tree-code
"${ARGN}"
TUPLE-TREE-YAML
${NAMESPACE}
"${SCHEMA_PATH}"
"${OUTPUT_DIR}"
"revng/${HEADER_DIRECTORY}"
GENERATED_HEADERS
GENERATED_IMPLS
"${JSONSCHEMA_PATH}"
${JSONSCHEMA_ROOT_TYPE}
"${STRING_TYPES}"
"${SEPARATE_STRING_TYPES}"
)
install(
DIRECTORY ${OUTPUT_DIR}
DESTINATION include/revng/${HEADER_DIRECTORY}
)
target_sources(${TARGET_ID} PRIVATE ${GENERATED_IMPLS})
add_dependencies(${TARGET_ID} generate-${TARGET_ID}-tuple-tree-code)
endfunction()
function(
target_tuple_tree_generate
HEADER_DIRECTORY
NAMESPACE
SCHEMA_PATH
TARGET_ID
)
target_tuple_tree_and_schema_generate(
${HEADER_DIRECTORY}
${NAMESPACE}
${SCHEMA_PATH}
${TARGET_ID}
""
${TARGET_ID}_schema
""
""
"${ARGN}")
endfunction()