refactor(eventsub): generate entire files (#5897)
This commit is contained in:
@@ -1,54 +1,190 @@
|
||||
function(generate_json_impls)
|
||||
set(_one_value_opts SRC_TARGET GEN_TARGET)
|
||||
cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH _eventsub_lib_root)
|
||||
|
||||
# _validate_all_args_are_passed(<prefix> [<required-args>...])
|
||||
function(_validate_all_args_are_parsed prefix)
|
||||
if(DEFINED ${prefix}_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown arguments: (${${prefix}_UNPARSED_ARGUMENTS})")
|
||||
endif()
|
||||
|
||||
foreach(_name ${ARGN})
|
||||
if(NOT DEFINED ${prefix}_${_name})
|
||||
message(FATAL_ERROR "Missing required argument ${_name}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(_make_and_use_venv)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"" "${_one_value_opts}" ""
|
||||
"" "VENV_PATH;REQUIREMENTS;OUT_PYTHON_EXE" ""
|
||||
)
|
||||
if (DEFINED arg_KEYWORDS_MISSING_VALUES)
|
||||
message(FATAL_ERROR "generate_json_impls: Missing ${arg_KEYWORDS_MISSING_VALUES}")
|
||||
_validate_all_args_are_parsed(arg VENV_PATH REQUIREMENTS OUT_PYTHON_EXE)
|
||||
|
||||
if(NOT Python3_EXECUTABLE)
|
||||
message(FATAL_ERROR "Python must be available to create a venv")
|
||||
endif()
|
||||
|
||||
set(_venv_path "${CMAKE_CURRENT_BINARY_DIR}/eventsub-venv")
|
||||
if(WIN32)
|
||||
set(_venv_python_path "${_venv_path}/Scripts/python.exe")
|
||||
set(_venv_pip_path "${_venv_path}/Scripts/pip.exe")
|
||||
else()
|
||||
set(_venv_python_path "${_venv_path}/bin/python3")
|
||||
set(_venv_pip_path "${_venv_path}/bin/pip3")
|
||||
endif()
|
||||
|
||||
cmake_path(SET _requirements NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/ast/requirements.txt")
|
||||
cmake_path(SET _gen_script NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/ast/generate-and-replace-dir.py")
|
||||
cmake_path(SET _search_dir NORMALIZE "${CMAKE_CURRENT_LIST_DIR}/include/twitch-eventsub-ws")
|
||||
|
||||
get_target_property(_inc_dirs ${arg_SRC_TARGET} INCLUDE_DIRECTORIES)
|
||||
list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS})
|
||||
list(APPEND _inc_dirs ${OPENSSL_INCLUDE_DIR})
|
||||
list(APPEND _inc_dirs ${Qt${MAJOR_QT_VERSION}Core_INCLUDE_DIRS})
|
||||
list(JOIN _inc_dirs ";" _inc_dir)
|
||||
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
if(NOT Python3_FOUND)
|
||||
message(STATUS "Creating venv in ${arg_VENV_PATH}")
|
||||
execute_process(
|
||||
COMMAND "${Python3_EXECUTABLE}" -m venv "${arg_VENV_PATH}"
|
||||
RESULT_VARIABLE _venv_output
|
||||
)
|
||||
if(NOT _venv_output EQUAL 0)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_custom_target(${arg_GEN_TARGET}_python_venv_create
|
||||
COMMENT "Creating python virtual environment"
|
||||
COMMAND "${Python3_EXECUTABLE}" -m venv "${_venv_path}"
|
||||
VERBATIM
|
||||
)
|
||||
# update the environment with VIRTUAL_ENV variable (mimic the activate script)
|
||||
set(ENV{VIRTUAL_ENV} "${arg_VENV_PATH}")
|
||||
# change the context of the search
|
||||
set(Python3_FIND_VIRTUALENV FIRST)
|
||||
# unset Python3_EXECUTABLE because it is also an input variable
|
||||
unset(Python3_EXECUTABLE)
|
||||
unset(Python3_FOUND)
|
||||
unset(Python3_Interpreter_FOUND)
|
||||
set(Python3_FIND_REGISTRY NEVER)
|
||||
# Launch a new search
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
add_custom_target(${arg_GEN_TARGET}_python_venv_install
|
||||
COMMENT "Installing python dependencies"
|
||||
COMMAND "${_venv_pip_path}" install -r "${_requirements}"
|
||||
DEPENDS "${_requirements}" ${arg_GEN_TARGET}_python_venv_create
|
||||
VERBATIM
|
||||
message(STATUS "Installing requirements from ${arg_REQUIREMENTS}")
|
||||
execute_process(
|
||||
COMMAND "${Python3_EXECUTABLE}" -m pip install -r "${arg_REQUIREMENTS}"
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${arg_REQUIREMENTS}")
|
||||
|
||||
add_custom_target(${arg_GEN_TARGET}
|
||||
COMMENT "Generating JSON deserializers"
|
||||
DEPENDS ${arg_GEN_TARGET}_python_venv_install
|
||||
COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/messages" --includes "${_inc_dir}"
|
||||
COMMAND "${_venv_python_path}" "${_gen_script}" "${_search_dir}/payloads" --includes "${_inc_dir}"
|
||||
VERBATIM
|
||||
)
|
||||
set(${arg_OUT_PYTHON_EXE} "${Python3_EXECUTABLE}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_setup_and_check_venv)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"" "INCLUDES;OUT_PYTHON_EXE;OUT_CHECK" ""
|
||||
)
|
||||
_validate_all_args_are_parsed(arg OUT_PYTHON_EXE OUT_CHECK INCLUDES)
|
||||
_make_and_use_venv(
|
||||
VENV_PATH "${CMAKE_CURRENT_BINARY_DIR}/eventsub-venv"
|
||||
REQUIREMENTS "${_eventsub_lib_root}/ast/requirements.txt"
|
||||
OUT_PYTHON_EXE _python3_path
|
||||
)
|
||||
if(NOT _python3_path)
|
||||
return() # venv failed
|
||||
endif()
|
||||
|
||||
cmake_path(SET _check_script NORMALIZE "${_eventsub_lib_root}/ast/check-clang.py")
|
||||
execute_process(
|
||||
COMMAND "${_python3_path}" "${_check_script}" "${arg_INCLUDES}"
|
||||
OUTPUT_VARIABLE _check_output
|
||||
ERROR_VARIABLE _check_output
|
||||
RESULT_VARIABLE _check_result
|
||||
WORKING_DIRECTORY "${arg_BASE_DIRECTORY}"
|
||||
)
|
||||
|
||||
set(${arg_OUT_PYTHON_EXE} "${_python3_path}" PARENT_SCOPE)
|
||||
if (NOT _check_result EQUAL 0)
|
||||
set(${arg_OUT_CHECK} "exit code: ${_check_result}\noutput:\n${_check_output}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# generate_json_impls(
|
||||
# [OUTPUT_SOURCES <out-var>]
|
||||
# [BASE_DIRECTORY <path>]
|
||||
# [FORCE <force>]
|
||||
# HEADERS <header>...
|
||||
# )
|
||||
#
|
||||
# `OUTPUT_SOURCES`
|
||||
# A variable to store a list of the generated sources in.
|
||||
# `BASE_DIRECTORY`
|
||||
# The directory to which paths in `HEADERS` are relative to. By default,
|
||||
# they're relative to the directory of the calling file.
|
||||
# `FORCE`
|
||||
# Always generate sources and error if it's not possible.
|
||||
# `HEADERS`
|
||||
# A list of header files for which implmenetations will be generated.
|
||||
function(generate_json_impls)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg
|
||||
"" "OUTPUT_SOURCES;BASE_DIRECTORY;FORCE" "HEADERS"
|
||||
)
|
||||
_validate_all_args_are_parsed(arg HEADERS)
|
||||
if(NOT DEFINED arg_BASE_DIRECTORY)
|
||||
set(arg_BASE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}")
|
||||
endif()
|
||||
|
||||
cmake_path(SET _gen_script NORMALIZE "${_eventsub_lib_root}/ast/generate.py")
|
||||
|
||||
if(arg_FORCE AND NOT Python3_EXECUTABLE)
|
||||
message(FATAL_ERROR "Missing python3")
|
||||
endif()
|
||||
|
||||
# get includes
|
||||
get_target_property(_qt_inc_dirs Qt${MAJOR_QT_VERSION}::Core INCLUDE_DIRECTORIES)
|
||||
get_target_property(_qt_iinc_dirs Qt${MAJOR_QT_VERSION}::Core INTERFACE_INCLUDE_DIRECTORIES)
|
||||
get_target_property(_qt_isinc_dirs Qt${MAJOR_QT_VERSION}::Core INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
|
||||
list(APPEND _inc_dirs ${_qt_inc_dirs} ${_qt_iinc_dirs} ${_qt_isinc_dirs})
|
||||
list(APPEND _inc_dirs "${_eventsub_lib_root}/include/twitch-eventsub-ws")
|
||||
list(APPEND _inc_dirs ${Boost_INCLUDE_DIRS})
|
||||
list(APPEND _inc_dirs ${OPENSSL_INCLUDE_DIR})
|
||||
list(JOIN _inc_dirs ";" _inc_dir)
|
||||
|
||||
# setup venv (<build>/lib/twitch-eventsub-ws/eventsub-venv)
|
||||
if(Python3_EXECUTABLE)
|
||||
_setup_and_check_venv(
|
||||
INCLUDES "${_inc_dirs}"
|
||||
OUT_PYTHON_EXE _python3_path
|
||||
OUT_CHECK _check_output
|
||||
)
|
||||
|
||||
if(NOT _check_output AND _python3_path)
|
||||
set(_generation_supported On)
|
||||
else()
|
||||
set(_generation_supported Off)
|
||||
if (arg_FORCE)
|
||||
message(FATAL_ERROR "Generation of JSON implementation not supported, because the parser can't parse a simple TU:\n${_check_output}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(_generation_supported Off)
|
||||
endif()
|
||||
|
||||
if(_generation_supported)
|
||||
message(STATUS "Generation of JSON implementation is supported")
|
||||
else()
|
||||
message(STATUS "Generation of JSON implementation is not supported (use FORCE_JSON_GENERATION=On to get more information)")
|
||||
endif()
|
||||
|
||||
# get all dependencies
|
||||
file(GLOB _gen_deps "${_eventsub_lib_root}/ast/lib/*.py")
|
||||
file(GLOB _ast_templates "${_eventsub_lib_root}/ast/lib/templates/*.tmpl")
|
||||
list(APPEND _gen_deps ${_ast_templates} "${_gen_script}")
|
||||
|
||||
foreach(_header ${arg_HEADERS})
|
||||
# keep in sync with generate.py
|
||||
string(REGEX REPLACE \.hpp$ .inc _def_path "${_header}")
|
||||
string(REGEX REPLACE \.hpp$ .cpp _source_path "${_header}")
|
||||
string(REGEX REPLACE \.hpp$ .timestamp _timestamp_path "${_header}")
|
||||
string(REGEX REPLACE include[/\\]twitch-eventsub-ws[/\\] src/generated/ _source_path "${_source_path}")
|
||||
|
||||
cmake_path(ABSOLUTE_PATH _timestamp_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/eventsub-deps")
|
||||
cmake_path(ABSOLUTE_PATH _header BASE_DIRECTORY "${arg_BASE_DIRECTORY}")
|
||||
cmake_path(ABSOLUTE_PATH _def_path BASE_DIRECTORY "${arg_BASE_DIRECTORY}")
|
||||
cmake_path(ABSOLUTE_PATH _source_path BASE_DIRECTORY "${arg_BASE_DIRECTORY}")
|
||||
list(APPEND _all_sources "${_source_path}")
|
||||
|
||||
if(_generation_supported)
|
||||
add_custom_command(
|
||||
OUTPUT "${_timestamp_path}" "${_source_path}" "${_def_path}"
|
||||
DEPENDS ${_gen_deps} "${_header}"
|
||||
COMMENT "Generating implementation for ${_header}"
|
||||
COMMAND "${_python3_path}" "${_gen_script}" "${_header}" --includes "${_inc_dirs}" --timestamp "${_timestamp_path}"
|
||||
WORKING_DIRECTORY "${arg_BASE_DIRECTORY}"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(arg_OUTPUT_SOURCES)
|
||||
set(${arg_OUTPUT_SOURCES} "${_all_sources}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
Reference in New Issue
Block a user