diff --git a/cmake/godotcpp.cmake b/cmake/godotcpp.cmake index 89fafc45f..494806990 100644 --- a/cmake/godotcpp.cmake +++ b/cmake/godotcpp.cmake @@ -30,6 +30,7 @@ include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux.cmake) include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos.cmake) include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/web.cmake) include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows.cmake) +include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/python_callouts.cmake) # Detect number of processors include(ProcessorCount) @@ -109,7 +110,9 @@ function( godotcpp_options ) #TODO threads #TODO compiledb #TODO compiledb_file - #TODO build_profile + + set( GODOT_BUILD_PROFILE "" CACHE PATH + "Path to a file containing a feature build profile" ) set(GODOT_USE_HOT_RELOAD "" CACHE BOOL "Enable the extra accounting required to support hot reload. (ON|OFF)") @@ -193,40 +196,47 @@ function( godotcpp_generate ) set(GODOT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) endif () - #[[ Generate Bindings ]] - if(NOT DEFINED BITS) - set(BITS 32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(BITS 64) - endif(CMAKE_SIZEOF_VOID_P EQUAL 8) + #[[ Configure Binding Variables ]] + # Generate Binding Parameters (True|False) + set( USE_TEMPLATE_GET_NODE "False" ) + if( GODOT_GENERATE_TEMPLATE_GET_NODE ) + set( USE_TEMPLATE_GET_NODE "True" ) endif() + # Bits (32|64) + math( EXPR BITS "${CMAKE_SIZEOF_VOID_P} * 8" ) # CMAKE_SIZEOF_VOID_P refers to target architecture. + + # API json File set(GODOT_GDEXTENSION_API_FILE "${GODOT_GDEXTENSION_DIR}/extension_api.json") - if (NOT "${GODOT_CUSTOM_API_FILE}" STREQUAL "") # User-defined override. + if( GODOT_CUSTOM_API_FILE ) # User-defined override. set(GODOT_GDEXTENSION_API_FILE "${GODOT_CUSTOM_API_FILE}") endif() - # Code Generation option - if(GODOT_GENERATE_TEMPLATE_GET_NODE) - set(GENERATE_BINDING_PARAMETERS "True") - else() - set(GENERATE_BINDING_PARAMETERS "False") + # Build Profile + if( GODOT_BUILD_PROFILE ) + message( STATUS "Using build profile to trim api file") + message( "\tBUILD_PROFILE = '${GODOT_BUILD_PROFILE}'") + message( "\tAPI_SOURCE = '${GODOT_GDEXTENSION_API_FILE}'") + build_profile_generate_trimmed_api( + "${GODOT_BUILD_PROFILE}" + "${GODOT_GDEXTENSION_API_FILE}" + "${CMAKE_CURRENT_BINARY_DIR}/extension_api.json" ) + set( GODOT_GDEXTENSION_API_FILE "${CMAKE_CURRENT_BINARY_DIR}/extension_api.json" ) endif() - execute_process(COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.print_file_list('${GODOT_GDEXTENSION_API_FILE}', '${CMAKE_CURRENT_BINARY_DIR}', headers=True, sources=True)" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GENERATED_FILES_LIST - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + message( STATUS "GODOT_GDEXTENSION_API_FILE = '${GODOT_GDEXTENSION_API_FILE}'") - add_custom_command(OUTPUT ${GENERATED_FILES_LIST} - COMMAND "${Python3_EXECUTABLE}" "-c" "import binding_generator; binding_generator.generate_bindings('${GODOT_GDEXTENSION_API_FILE}', '${GENERATE_BINDING_PARAMETERS}', '${BITS}', '${GODOT_PRECISION}', '${CMAKE_CURRENT_BINARY_DIR}')" - VERBATIM - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - MAIN_DEPENDENCY ${GODOT_GDEXTENSION_API_FILE} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/binding_generator.py - COMMENT "Generating bindings" - ) + # generate the file list to use + binding_generator_get_file_list( GENERATED_FILES_LIST + "${GODOT_GDEXTENSION_API_FILE}" + "${CMAKE_CURRENT_BINARY_DIR}" ) + + binding_generator_generate_bindings( + "${GODOT_GDEXTENSION_API_FILE}" + "${USE_TEMPLATE_GET_NODE}" + "${BITS}" + "${GODOT_PRECISION}" + "${CMAKE_CURRENT_BINARY_DIR}" ) ### Platform is derived from the toolchain target # See GeneratorExpressions PLATFORM_ID and CMAKE_SYSTEM_NAME diff --git a/cmake/python_callouts.cmake b/cmake/python_callouts.cmake new file mode 100644 index 000000000..8e2262112 --- /dev/null +++ b/cmake/python_callouts.cmake @@ -0,0 +1,99 @@ +#[=======================================================================[.rst: +python_callouts.cmake +--------------------- + +This file contains functions which which rely on calling Python + +* Generate Trimmed API +* Generate File List +* Generate Bindings +]=======================================================================] + + +#[[ Generate Trimmed API + +The build_profile.py has a __main__ and is used as a tool +Its usage is listed as: + $ python build_profile.py BUILD_PROFILE INPUT_JSON [OUTPUT_JSON] +]] +function( build_profile_generate_trimmed_api BUILD_PROFILE INPUT_JSON OUTPUT_JSON ) + execute_process( + COMMAND "${Python3_EXECUTABLE}" "build_profile.py" "${BUILD_PROFILE}" "${INPUT_JSON}" "${OUTPUT_JSON}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endfunction( ) + + +#[[ Generate File List + +Use the binding_generator.py Python script to determine the list of files that +will be passed to the code generator using extension_api.json. +NOTE: This happens for every configure.]] +function( binding_generator_get_file_list OUT_VAR_NAME API_FILEPATH OUTPUT_DIR ) + + # This code snippet will be squashed into a single line + # The two strings make this a list, in CMake lists are semicolon delimited strings. + set( PYTHON_SCRIPT +"from binding_generator import print_file_list" +"print_file_list( api_filepath='${API_FILEPATH}', + output_dir='${OUTPUT_DIR}', + headers=True, + sources=True)") + message( DEBUG "Python:\n${PYTHON_SCRIPT}" ) + + # Strip newlines and whitespace to make it a one-liner. + string( REGEX REPLACE "\n *" " " PYTHON_SCRIPT "${PYTHON_SCRIPT}" ) + + execute_process( COMMAND "${Python3_EXECUTABLE}" "-c" "${PYTHON_SCRIPT}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GENERATED_FILES_LIST + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Debug output + message( DEBUG "FileList-Begin" ) + foreach( PATH ${GENERATED_FILES_LIST} ) + message( DEBUG ${PATH} ) + endforeach() + + # Error out if the file list generator returned no files. + list( LENGTH GENERATED_FILES_LIST LIST_LENGTH ) + if( NOT LIST_LENGTH GREATER 0 ) + message( FATAL_ERROR "File List Generation Failed") + endif() + message( STATUS "There are ${LIST_LENGTH} Files to generate" ) + + set( ${OUT_VAR_NAME} ${GENERATED_FILES_LIST} PARENT_SCOPE ) +endfunction( ) + + +#[[ Generate Bindings + +Using the generated file list, use the binding_generator.py to generate the +godot-cpp bindings. This will run at build time only if there are files +missing. ]] +function( binding_generator_generate_bindings API_FILE USE_TEMPLATE_GET_NODE, BITS, PRECISION, OUTPUT_DIR ) + # This code snippet will be squashed into a single line + set( PYTHON_SCRIPT +"from binding_generator import generate_bindings" +"generate_bindings( + api_filepath='${API_FILE}', + use_template_get_node='${USE_TEMPLATE_GET_NODE}', + bits='${BITS}', + precision='${PRECISION}', + output_dir='${OUTPUT_DIR}')") + + message( DEBUG "Python:\n${PYTHON_SCRIPT}" ) + + # Strip newlines and whitespace to make it a one-liner. + string( REGEX REPLACE "\n *" " " PYTHON_SCRIPT "${PYTHON_SCRIPT}" ) + + add_custom_command(OUTPUT ${GENERATED_FILES_LIST} + COMMAND "${Python3_EXECUTABLE}" "-c" "${PYTHON_SCRIPT}" + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + MAIN_DEPENDENCY ${GODOT_GDEXTENSION_API_FILE} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/binding_generator.py + COMMENT "Generating bindings" + ) +endfunction( )