Skip to content

Commit

Permalink
CMake: Support using build_profile.json
Browse files Browse the repository at this point in the history
Add python_callouts.py to hold functions which call python utilities
- generate trimmed API
- generate file list
- generate bindings

if GODOT_BUILD_PROFILE is specified, a trimmed API file is created in the CMAKE_CURRENT_BINARY_DIR and used as the source for binding generation

Simplify Code Generation Variables
- use generator expressions
- use math for bits
- simplify if statements
  • Loading branch information
enetheru committed Jan 11, 2025
1 parent 012b8ff commit ae198fe
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 26 deletions.
62 changes: 36 additions & 26 deletions cmake/godotcpp.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)")
Expand Down Expand Up @@ -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
Expand Down
99 changes: 99 additions & 0 deletions cmake/python_callouts.cmake
Original file line number Diff line number Diff line change
@@ -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( )

0 comments on commit ae198fe

Please sign in to comment.