From 8534e2104fce6bee62e0ff18e15fc013b7b7e432 Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 21 Nov 2024 11:00:48 +1030 Subject: [PATCH] Modernise Existing CMakeLists.txt - Added to .gitignore CMakeUserPresets.json ### Configuration: - Changed python command to use single quotes to make build output log more legible. - Added GODOT_DEV_BUILD to allow differentiation of debug or Release builds. - Added find logic for macos Cocoa library ### godot-cpp Changes - godot-cpp-test is changed to be incorporated into the cmake build as a target. - Duplicated godot-cpp target into [template_release, template_debug, editor] - Created {platform}.cmake files mirroring the style of the SCons build. CMake has a feature called generator expressions for its configuration variables that are evaluated at build time. This allows multi-configuration build systems to properly evaulate options. for msvc, xcode and nijna multi-config. - Moved configuration options to generator expressions with the notable exclusion of OSX_ARCHITECTURES. - Remove CMAKE_BUILD_TYPE from msvc CI target as Multi-Config generators ignore it ### godot-cpp-test Changes - Removed majority of the cmake code, now that the godot-cpp project is setup, the majority of the flags will be propagated as transient dependencies - Marked with EXCLUDE_FROM_ALL so that it isn't built as part of the 'all' target - Updated ci to build the godot-cpp-test target from the root directory using cmake - Tests passing for Windows, Linux, and Macos builds. ### Documentation Updated with new information Added Emscripten example Added Android example --- .github/workflows/ci.yml | 46 +--- .gitignore | 5 +- CMakeLists.txt | 108 +++++++-- cmake/android.cmake | 41 ++++ cmake/common_compiler_flags.cmake | 242 ++++++++++++------- cmake/emsdkHack.cmake | 40 ++++ cmake/godotcpp.cmake | 372 +++++++++++++++++------------- cmake/ios.cmake | 22 ++ cmake/linux.cmake | 22 ++ cmake/macos.cmake | 59 +++++ cmake/web.cmake | 42 ++++ cmake/windows.cmake | 63 +++++ doc/cmake.md | 57 ----- doc/cmake.rst | 331 ++++++++++++++++++++++++++ test/CMakeLists.txt | 187 +++++---------- 15 files changed, 1147 insertions(+), 490 deletions(-) create mode 100644 cmake/android.cmake create mode 100644 cmake/emsdkHack.cmake create mode 100644 cmake/ios.cmake create mode 100644 cmake/linux.cmake create mode 100644 cmake/macos.cmake create mode 100644 cmake/web.cmake create mode 100644 cmake/windows.cmake delete mode 100644 doc/cmake.md create mode 100644 doc/cmake.rst diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 246689ae..33b2450d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -207,30 +207,6 @@ jobs: path: ${{ matrix.artifact-path }} if-no-files-found: error - linux-cmake: - name: 🐧 Build (Linux, GCC, CMake) - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install dependencies - run: | - sudo apt-get update -qq - sudo apt-get install -qqq build-essential pkg-config cmake - - - name: Build godot-cpp - run: | - cmake -DCMAKE_BUILD_TYPE=Release . - make -j $(nproc) VERBOSE=1 - - - name: Build test GDExtension library - run: | - cd test && cmake -DCMAKE_BUILD_TYPE=Release -DGODOT_HEADERS_PATH="../godot-headers" -DCPP_BINDINGS_PATH=".." . - make -j $(nproc) VERBOSE=1 - linux-cmake-ninja: name: 🐧 Build (Linux, GCC, CMake Ninja) runs-on: ubuntu-22.04 @@ -245,15 +221,12 @@ jobs: sudo apt-get update -qq sudo apt-get install -qqq build-essential pkg-config cmake ninja-build - - name: Build godot-cpp - run: | - cmake -DCMAKE_BUILD_TYPE=Release -GNinja . - cmake --build . -j $(nproc) --verbose - - name: Build test GDExtension library run: | - cd test && cmake -DCMAKE_BUILD_TYPE=Release -DGODOT_HEADERS_PATH="../godot-headers" -DCPP_BINDINGS_PATH=".." -GNinja . - cmake --build . -j $(nproc) --verbose + mkdir cmake-build + cd cmake-build + cmake ../ -DTEST_TARGET=template_release + cmake --build . --verbose -j $(nproc) -t godot-cpp-test --config Release windows-msvc-cmake: name: 🏁 Build (Windows, MSVC, CMake) @@ -264,12 +237,9 @@ jobs: with: submodules: recursive - - name: Build godot-cpp - run: | - cmake -DCMAKE_BUILD_TYPE=Release -G"Visual Studio 16 2019" . - cmake --build . --verbose --config Release - - name: Build test GDExtension library run: | - cd test && cmake -DCMAKE_BUILD_TYPE=Release -DGODOT_HEADERS_PATH="../godot-headers" -DCPP_BINDINGS_PATH=".." -G"Visual Studio 16 2019" . - cmake --build . --verbose --config Release + mkdir cmake-build + cd cmake-build + cmake ../ -DTEST_TARGET=template_release + cmake --build . --verbose -t godot-cpp-test --config Release diff --git a/.gitignore b/.gitignore index ee9a3f75..9bb08ed3 100644 --- a/.gitignore +++ b/.gitignore @@ -198,4 +198,7 @@ venv # Clion Configuration .idea/ -cmake-build-* +cmake-build*/ + +# CMake related +CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index ff77368b..926946d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,98 @@ -cmake_minimum_required(VERSION 3.13) -project(godot-cpp LANGUAGES CXX) +cmake_minimum_required(VERSION 3.17) -# Configure CMake -# https://discourse.cmake.org/t/how-do-i-remove-compile-options-from-target/5965 -# https://stackoverflow.com/questions/74426638/how-to-remove-rtc1-from-specific-target-or-file-in-cmake -if(${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) - if(NOT CMAKE_BUILD_TYPE MATCHES Debug) - STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) +#[=======================================================================[.rst: + +CMake Version requirements +-------------------------- + +To enable use of the emscripten emsdk hack for pseudo shared library support +without polluting options for consumers we need to use the +CMAKE_PROJECT__INCLUDE which was introduced in version 3.17 + +Scons Compatibility +------------------- + +As we are attempting to maintain feature parity, and ease of maintenance, these +CMake scripts are built to resemble the SCons build system. + +The file structure and file content are made to match, if not in content then +in spirit. The closer the two build systems look the easier they will be to +maintain. + +Where the SCons additional scripts in the tools directory, The CMake scripts +are in the cmake directory. + +For example, the tools/godotcpp.py is sourced into SCons, and the 'options' +function is run. + +.. highlight:: python + + cpp_tool = Tool("godotcpp", toolpath=["tools"]) + cpp_tool.options(opts, env) + + +The CMake equivalent is below. +]=======================================================================] + +include( cmake/godotcpp.cmake ) +godotcpp_options() + +#[=======================================================================[.rst: + +Configurations +-------------- + +There are two build main configurations, 'Debug' and 'Release', these are not +related to godot's DEBUG_FEATURES flag. Build configurations change the default +compiler and linker flags present when building the library, things like debug +symbols, optimization. + +The Scons build scripts don't have this concept, you can think of it like the +SCons solution has a single default configuration. In both cases overriding the +defaults is controlled by options on the command line, or in preset files. + +Because of this added configuration and that it can be undefined, it becomes +important to set a default, considering the SCons solution that does not enable +debug symbols by default, it seemed appropriate to set the default to 'Release' +if unspecified. This can always be overridden like below. + +.. highlight:: shell + + cmake -DCMAKE_BUILD_TYPE:STRING=Debug + +.. caution:: + +A complication arises from `Multi-Config Generators`_ that cannot have +their configuration set at configure time. This means that the configuration +must be set on the build command. This is especially important for Visual +Studio Generators which default to 'Debug' + +.. highlight:: shell + + cmake --build . --config Release + +.. _Multi-Config Generators:https://cmake.org/cmake/help/latest/prop_gbl/GENERATOR_IS_MULTI_CONFIG.html +]=======================================================================] +get_property( IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) +if( NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE ) + if( GODOT_DEV_BUILD ) + set( CMAKE_BUILD_TYPE Debug ) + else () + set( CMAKE_BUILD_TYPE Release ) endif () endif () -include( ${PROJECT_SOURCE_DIR}/cmake/godotcpp.cmake ) +#[[ Python is required for code generation ]] +find_package(Python3 3.4 REQUIRED) # pathlib should be present -# I know this doesn't look like a typical CMakeLists.txt, but as we are -# attempting mostly feature parity with SCons, and easy maintenance, the closer -# the two build systems look the easier they will be to keep in lockstep. - -# The typical target definitions are in ${PROJECT_SOURCE_DIR}/cmake/godotcpp.cmake - -godotcpp_options() +# Define our project. +project( godot-cpp + VERSION 4.4 + DESCRIPTION "C++ bindings for the Godot Engine's GDExtensions API." + HOMEPAGE_URL "https://github.com/godotengine/godot-cpp" + LANGUAGES CXX) godotcpp_generate() + +# Test Example +add_subdirectory( test ) diff --git a/cmake/android.cmake b/cmake/android.cmake new file mode 100644 index 00000000..2227f89c --- /dev/null +++ b/cmake/android.cmake @@ -0,0 +1,41 @@ +#[=======================================================================[.rst: +Android +------- + +This file contains functions for options and configuration for targeting the +Android platform + +Configuration of the Android toolchain is done using toolchain files, +CMakePresets, or variables on the command line. + +The `Android SDK`_ provides toolchain files to help with configuration. + +CMake has its own `built-in support`_ for cross compiling to the +Android platforms. + +.. warning:: + + Android does not support or test the CMake built-in workflow, recommend + using their toolchain file. + +.. _Android SDK:https://developer.android.com/ndk/guides/cmake + +.. _built-in support:https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android + +There is further information and examples in the doc/cmake.rst file. + +]=======================================================================] +function( android_options ) + # Android Options +endfunction() + +function( android_generate TARGET_NAME ) + + target_compile_definitions(${TARGET_NAME} + PUBLIC + ANDROID_ENABLED + UNIX_ENABLED + ) + + common_compiler_flags( ${TARGET_NAME} ) +endfunction() diff --git a/cmake/common_compiler_flags.cmake b/cmake/common_compiler_flags.cmake index 94556415..1b185fe2 100644 --- a/cmake/common_compiler_flags.cmake +++ b/cmake/common_compiler_flags.cmake @@ -1,94 +1,162 @@ -# Add warnings based on compiler & version -# Set some helper variables for readability -set( compiler_less_than_v8 "$,8>" ) -set( compiler_greater_than_or_equal_v9 "$,9>" ) -set( compiler_greater_than_or_equal_v11 "$,11>" ) -set( compiler_less_than_v11 "$,11>" ) -set( compiler_greater_than_or_equal_v12 "$,12>" ) +#[=======================================================================[.rst: +Common Compiler Flags +--------------------- -# These compiler options reflect what is in godot/SConstruct. -target_compile_options( ${PROJECT_NAME} PRIVATE - # MSVC only - $<${compiler_is_msvc}: - /W4 +This file contains a single function to configure platform agnostic compiler +flags like optimization levels, warnings, and features. For platform specific +flags look to each of the ``cmake/.cmake`` files. - # Disable warnings which we don't plan to fix. - /wd4100 # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. - /wd4127 # C4127 (conditional expression is constant) - /wd4201 # C4201 (non-standard nameless struct/union): Only relevant for C89. - /wd4244 # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. - /wd4245 - /wd4267 - /wd4305 # C4305 (truncation): double to float or real_t, too hard to avoid. - /wd4514 # C4514 (unreferenced inline function has been removed) - /wd4714 # C4714 (function marked as __forceinline not inlined) - /wd4820 # C4820 (padding added after construct) - > +]=======================================================================] +#Generator Expression Helpers +set( IS_CLANG "$" ) +set( IS_APPLECLANG "$" ) +set( IS_GNU "$" ) +set( IS_MSVC "$" ) +set( NOT_MSVC "$>" ) - # Clang and GNU common options - $<$: - -Wall - -Wctor-dtor-privacy - -Wextra - -Wno-unused-parameter - -Wnon-virtual-dtor - -Wwrite-strings - > +set( GNU_LT_V8 "$,8>" ) +set( GNU_GE_V9 "$,9>" ) +set( GNU_GT_V11 "$,11>" ) +set( GNU_LT_V11 "$,11>" ) +set( GNU_GE_V12 "$,12>" ) - # Clang only - $<${compiler_is_clang}: - -Wimplicit-fallthrough - -Wno-ordered-compare-function-pointers - > +set( HOT_RELOAD-UNSET "$") - # GNU only - $<${compiler_is_gnu}: - -Walloc-zero - -Wduplicated-branches - -Wduplicated-cond - -Wno-misleading-indentation - -Wplacement-new=1 - -Wshadow-local - -Wstringop-overflow=4 - > - $<$: - # Bogus warning fixed in 8+. - -Wno-strict-overflow - > - $<$: - -Wattribute-alias=2 - > - $<$: - # Broke on MethodBind templates before GCC 11. - -Wlogical-op - > - $<$: - # Regression in GCC 9/10, spams so much in our variadic templates that we need to outright disable it. - -Wno-type-limits - > - $<$: - # False positives in our error macros, see GH-58747. - -Wno-return-type - > -) +set( DISABLE_EXCEPTIONS "$") + + +function( common_compiler_flags TARGET_NAME ) + set( IS_RELEASE "$") + set( DEBUG_FEATURES "$,$>" ) + set( HOT_RELOAD "$,$>" ) + set( DEBUG_SYMBOLS "$" ) + + target_compile_features(${TARGET_NAME} + PUBLIC + cxx_std_17 + ) + + # These compiler options reflect what is in godot/SConstruct. + target_compile_options( ${TARGET_NAME} + PUBLIC + # Disable exception handling. Godot doesn't use exceptions anywhere, and this + # saves around 20% of binary size and very significant build time. + $<${DISABLE_EXCEPTIONS}: + $<${NOT_MSVC}:-fno-exceptions> + > + $<$: + $<${IS_MSVC}:/EHsc> + > + + # Enabling Debug Symbols + $<${DEBUG_SYMBOLS}: + $<${IS_MSVC}: /Zi /FS> + + # Adding dwarf-4 explicitly makes stacktraces work with clang builds, + # otherwise addr2line doesn't understand them. + $<${NOT_MSVC}: + -gdwarf-4 + $ + > + > + + $<${IS_DEV}: + $<${NOT_MSVC}:-fno-omit-frame-pointer -O0 -g> + > + + $<${HOT_RELOAD}: + $<${IS_GNU}:-fno-gnu-unique> + > + + # MSVC only + $<${IS_MSVC}: + "/MP ${PROC_N}" + /W4 + + # Disable warnings which we don't plan to fix. + /wd4100 # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. + /wd4127 # C4127 (conditional expression is constant) + /wd4201 # C4201 (non-standard nameless struct/union): Only relevant for C89. + /wd4244 # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. + /wd4245 + /wd4267 + /wd4305 # C4305 (truncation): double to float or real_t, too hard to avoid. + /wd4514 # C4514 (unreferenced inline function has been removed) + /wd4714 # C4714 (function marked as __forceinline not inlined) + /wd4820 # C4820 (padding added after construct) + + /utf-8 + > + + # Clang and GNU common options + $<$: + -Wall + -Wctor-dtor-privacy + -Wextra + -Wno-unused-parameter + -Wnon-virtual-dtor + -Wwrite-strings + > + + # Clang only + $<${IS_CLANG}: + -Wimplicit-fallthrough + -Wno-ordered-compare-function-pointers + > + + # GNU only + $<${IS_GNU}: + -Walloc-zero + -Wduplicated-branches + -Wduplicated-cond + -Wno-misleading-indentation + -Wplacement-new=1 + -Wshadow-local + -Wstringop-overflow=4 + + # Bogus warning fixed in 8+. + $<${GNU_LT_V8}:-Wno-strict-overflow> + + $<${GNU_GE_V9}:-Wattribute-alias=2> + + # Broke on MethodBind templates before GCC 11. + $<${GNU_GT_V11}:-Wlogical-op> + + # Regression in GCC 9/10, spams so much in our variadic templates that we need to outright disable it. + $<${GNU_LT_V11}:-Wno-type-limits> + + # False positives in our error macros, see GH-58747. + $<${GNU_GE_V12}:-Wno-return-type> + > + ) + + target_compile_definitions(${TARGET_NAME} + PUBLIC + GDEXTENSION + + # features + $<${DEBUG_FEATURES}:DEBUG_ENABLED DEBUG_METHODS_ENABLED> + + $<${HOT_RELOAD}:HOT_RELOAD_ENABLED> + + $<$:REAL_T_IS_DOUBLE> + + $<${IS_MSVC}:$<${DISABLE_EXCEPTIONS}:_HAS_EXCEPTIONS=0>> + ) + + target_link_options( ${TARGET_NAME} + PUBLIC + $<${IS_MSVC}: + /WX # treat link warnings as errors. + /MANIFEST:NO # We dont need a manifest + > + + $<${DEBUG_SYMBOLS}:$<${IS_MSVC}:/DEBUG:FULL>> + $<$: + $<${IS_GNU}:-s> + $<${IS_CLANG}:-s> + $<${IS_APPLECLANG}:-Wl,-S -Wl,-x -Wl,-dead_strip> + > + ) -# Treat warnings as errors -function( set_warning_as_error ) - message( STATUS "[${PROJECT_NAME}] Treating warnings as errors") - if ( CMAKE_VERSION VERSION_GREATER_EQUAL "3.24" ) - set_target_properties( ${PROJECT_NAME} - PROPERTIES - COMPILE_WARNING_AS_ERROR ON - ) - else() - target_compile_options( ${PROJECT_NAME} - PRIVATE - $<${compiler_is_msvc}:/WX> - $<$:-Werror> - ) - endif() endfunction() - -if ( GODOT_WARNING_AS_ERROR ) - set_warning_as_error() -endif() diff --git a/cmake/emsdkHack.cmake b/cmake/emsdkHack.cmake new file mode 100644 index 00000000..6981a379 --- /dev/null +++ b/cmake/emsdkHack.cmake @@ -0,0 +1,40 @@ +#[=======================================================================[.rst: +emsdkHack +--------- + +The Emscripten platform doesn't support the use of shared libraries as known by cmake. + +* https://github.com/emscripten-core/emscripten/issues/15276 +* https://github.com/emscripten-core/emscripten/issues/17804 + +This workaround only works due to the way the cmake scripts are loaded. + +Prior to the use of ``project( ... )`` directive we need to set +``CMAKE_PROJECT_INCLUDE=cmake/emscripten.cmake``. +This file will be loaded after the toolchain overriding the settings that +prevent shared library building. + +CMAKE_PROJECT_INCLUDE was Added in version 3.15. +``CMAKE_PROJECT__INCLUDE`` was Added in version 3.17: + +More information on cmake's `code injection`_ + +.. _code injection:https://cmake.org/cmake/help/latest/command/project.html#code-injection + +Overwrite Shared Library Properties to allow shared libs to be generated. +]=======================================================================] +if( EMSCRIPTEN ) + set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE) + set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-sSIDE_MODULE=1") + set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS "-sSIDE_MODULE=1") + set(CMAKE_SHARED_LIBRARY_SUFFIX) # remove the suffix from the shared lib + set(CMAKE_STRIP FALSE) # used by default in pybind11 on .so modules + + # The Emscripten toolchain sets the default value for EMSCRIPTEN_SYSTEM_PROCESSOR to x86 + # and CMAKE_SYSTEM_PROCESSOR to this value. I don't want that. + set(CMAKE_SYSTEM_PROCESSOR "wasm32" ) + # the above prevents the need for logic like: + #if( ${CMAKE_SYSTEM_NAME} STREQUAL Emscripten ) + # set( SYSTEM_ARCH wasm32 ) + #endif () +endif () diff --git a/cmake/godotcpp.cmake b/cmake/godotcpp.cmake index a5c66779..736d1e3e 100644 --- a/cmake/godotcpp.cmake +++ b/cmake/godotcpp.cmake @@ -1,13 +1,85 @@ -function( godotcpp_options ) +#[=======================================================================[.rst: +godotcpp.cmake +-------------- - #TODO platform - #TODO target +Because these files are included into the top level CMakelists.txt before the +project directive, it means that + +* ``CMAKE_CURRENT_SOURCE_DIR`` is the location of godot-cpp's CMakeLists.txt +* ``CMAKE_SOURCE_DIR`` is the location where any prior ``project(...)`` + directive was + +]=======================================================================] +include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/common_compiler_flags.cmake) +include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/android.cmake) +include( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ios.cmake) +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) + +#Silence warning from unused CMAKE_C_COMPILER from toolchain +if( CMAKE_C_COMPILER ) +endif () + +include(ProcessorCount) +ProcessorCount(PROC_MAX) +message( "Auto-detected ${PROC_MAX} CPU cores available for build parallelism." ) + +# List of known platforms +set( PLATFORM_LIST linux macos windows android ios web ) + +# List of known architectures +set( ARCH_LIST universal x86_32 x86_64 arm32 arm64 rv64 ppc32 ppc64 wasm32 ) + +# Function to map processors to known architectures +function( godot_arch_map ALIAS PROC ) + string( TOLOWER "${PROC}" PROC ) + + if( "${PROC}" IN_LIST ARCH_LIST ) + set( ${ALIAS} "${PROC}" PARENT_SCOPE) + return() + endif() + + set( x86_64 "w64;amd64" ) + set( arm32 "armv7" ) + set( arm64 "armv8;arm64v8;aarch64" ) + set( rv64 "rv;riscv;riscv64" ) + set( ppc32 "ppcle;ppc" ) + set( ppc64 "ppc64le" ) + + if( PROC IN_LIST x86_64 ) + set(${ALIAS} "x86_64" PARENT_SCOPE ) + + elseif( PROC IN_LIST arm32 ) + set(${ALIAS} "arm32" PARENT_SCOPE ) + + elseif( PROC IN_LIST arm64 ) + set(${ALIAS} "arm64" PARENT_SCOPE ) + + elseif( PROC IN_LIST rv64 ) + set(${ALIAS} "rv64" PARENT_SCOPE ) + + elseif( PROC IN_LIST ppc32 ) + set(${ALIAS} "ppc32" PARENT_SCOPE ) + + elseif( PROC IN_LIST ppc64 ) + set(${ALIAS} "ppc64" PARENT_SCOPE ) + + else() + set(${ALIAS} "unknown" PARENT_SCOPE ) + endif () +endfunction() + +# Function to define all the options. +function( godotcpp_options ) + #NOTE: platform is managed using toolchain files. # Input from user for GDExtension interface header and the API JSON file set(GODOT_GDEXTENSION_DIR "gdextension" CACHE PATH "Path to a custom directory containing GDExtension interface header and API JSON file ( /path/to/gdextension_dir )" ) set(GODOT_CUSTOM_API_FILE "" CACHE FILEPATH - "Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`) ( /path/to/custom_api_file )") + "Path to a custom GDExtension API JSON file (takes precedence over `GODOT_GDEXTENSION_DIR`) ( /path/to/custom_api_file )") #TODO generate_bindings @@ -19,15 +91,22 @@ function( godotcpp_options ) set(GODOT_PRECISION "single" CACHE STRING "Set the floating-point precision level (single|double)") - #TODO arch + # The arch is typically set by the toolchain + # however for Apple multi-arch setting it here will override. + set( GODOT_ARCH "" CACHE STRING "Target CPU Architecture") + set_property( CACHE GODOT_ARCH PROPERTY STRINGS ${ARCH_LIST} ) + #TODO threads #TODO compiledb #TODO compiledb_file - #TODO build_profile aka cmake preset + + #NOTE: build_profile's equivalent in cmake is CMakePresets.json set(GODOT_USE_HOT_RELOAD "" CACHE BOOL "Enable the extra accounting required to support hot reload. (ON|OFF)") + # Disable exception handling. Godot doesn't use exceptions anywhere, and this + # saves around 20% of binary size and very significant build time (GH-80513). option(GODOT_DISABLE_EXCEPTIONS "Force disabling exception handling code (ON|OFF)" ON ) set( GODOT_SYMBOL_VISIBILITY "hidden" CACHE STRING @@ -36,48 +115,67 @@ function( godotcpp_options ) #TODO optimize #TODO debug_symbols - #TODO dev_build + option( GODOT_DEBUG_SYMBOLS "" OFF ) + option( GODOT_DEV_BUILD "Developer build with dev-only debugging code (DEV_ENABLED)" OFF ) # FIXME These options are not present in SCons, and perhaps should be added there. - option(GODOT_SYSTEM_HEADERS "Expose headers as SYSTEM." ON) - option(GODOT_WARNING_AS_ERROR "Treat warnings as errors" OFF) + option( GODOT_SYSTEM_HEADERS "Expose headers as SYSTEM." OFF ) + option( GODOT_WARNING_AS_ERROR "Treat warnings as errors" OFF ) - # Run options commands on the following to populate cache for all platforms. - # This type of thing is typically done conditionally - # But as scons shows all options so shall we. - #TODO ios_options() - #TODO linux_options() - #TODO macos_options() - #TODO web_options() - #TODO windows_options() + # Run options commands on the following to populate cache for all + # platforms. This type of thing is typically done conditionally But as + # scons shows all options so shall we. + android_options() + ios_options() + linux_options() + macos_options() + web_options() + windows_options() endfunction() - +# Function to configure and generate the targets function( godotcpp_generate ) - # Set some helper variables for readability - set( compiler_is_clang "$,$>" ) - set( compiler_is_gnu "$" ) - set( compiler_is_msvc "$" ) + #[[ Multi-Threaded MSVC Compilation - # CXX_VISIBILITY_PRESET supported values are: default, hidden, protected, and internal - # which is inline with the gcc -fvisibility= - # https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html - # To match the scons options we need to change the text to match the -fvisibility flag - # it is probably worth another PR which changes both to use the flag options + When using the MSVC compiler the build command -j only specifies + parallel jobs or targets, and not multi-threaded compilation To speed up + compile times on msvc, the /MP flag can be set. But we need to set it + at configure time. + + MSVC is true when the compiler is some version of Microsoft Visual C++ or + another compiler simulating the Visual C++ cl command-line syntax. ]] + if( MSVC ) + math( EXPR PROC_N "(${PROC_MAX}-1) | (${X}-2)>>31 & 1" ) + message( "Using ${PROC_N} cores for multi-threaded compilation.") + # TODO You can override it at configure time with ...." ) + else () + message( "Using ${CMAKE_BUILD_PARALLEL_LEVEL} cores, You can override" + " it at configure time by using -j or --parallel on the build" + " command.") + message( " eg. cmake --build . -j 7 ...") + endif () + + #[[ GODOT_SYMBOL_VISIBLITY + To match the SCons options, the allowed values are "auto", "visible", and "hidden" + This effects the compiler flag -fvisibility=[default|internal|hidden|protected] + The corresponding CMake option CXX_VISIBILITY_PRESET accepts the compiler values. + + TODO: It is probably worth a pull request which changes both to use the compiler values + https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fvisibility + + This performs the necessary conversion + ]] if( ${GODOT_SYMBOL_VISIBILITY} STREQUAL "auto" OR ${GODOT_SYMBOL_VISIBILITY} STREQUAL "visible" ) set( GODOT_SYMBOL_VISIBILITY "default" ) endif () - # Default build type is Debug in the SConstruct - if("${CMAKE_BUILD_TYPE}" STREQUAL "") - set(CMAKE_BUILD_TYPE Debug) - endif() - - # Hot reload is enabled by default in Debug-builds - if( GODOT_USE_HOT_RELOAD STREQUAL "" AND NOT CMAKE_BUILD_TYPE STREQUAL "Release") - set(GODOT_USE_HOT_RELOAD ON) - endif() + # Setup variable to optionally mark headers as SYSTEM + set(GODOT_SYSTEM_HEADERS_ATTRIBUTE "") + if (GODOT_SYSTEM_HEADERS) + set(GODOT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) + endif () + #[[ Generate Bindings ]] if(NOT DEFINED BITS) set(BITS 32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -85,67 +183,26 @@ function( godotcpp_generate ) endif(CMAKE_SIZEOF_VOID_P EQUAL 8) endif() - set(GODOT_GDEXTENSION_API_FILE "${GODOT_GDEXTENSION_DIR}/extension_api.json") if (NOT "${GODOT_CUSTOM_API_FILE}" STREQUAL "") # User-defined override. set(GODOT_GDEXTENSION_API_FILE "${GODOT_CUSTOM_API_FILE}") endif() - if ("${GODOT_PRECISION}" STREQUAL "double") - add_definitions(-DREAL_T_IS_DOUBLE) - endif() - - set( GODOT_COMPILE_FLAGS ) - - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # using Visual Studio C++ - set(GODOT_COMPILE_FLAGS "/utf-8") # /GF /MP - - if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy - endif(CMAKE_BUILD_TYPE MATCHES Debug) - - add_definitions(-DNOMINMAX) - else() # GCC/Clang - if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0 -g") - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") - endif(CMAKE_BUILD_TYPE MATCHES Debug) - endif() - - # Disable exception handling. Godot doesn't use exceptions anywhere, and this - # saves around 20% of binary size and very significant build time (GH-80513). - if (GODOT_DISABLE_EXCEPTIONS) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0") - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions") - endif() - else() - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc") - endif() - endif() - - # Generate source from the bindings file - find_package(Python3 3.4 REQUIRED) # pathlib should be present + # Code Generation option if(GODOT_GENERATE_TEMPLATE_GET_NODE) set(GENERATE_BINDING_PARAMETERS "True") else() set(GENERATE_BINDING_PARAMETERS "False") 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)" + 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 ) 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}\")" + 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} @@ -153,88 +210,87 @@ function( godotcpp_generate ) COMMENT "Generating bindings" ) - # Get Sources - # As this cmake file was added using 'include(godotcpp)' from the root CMakeLists.txt, - # the ${CMAKE_CURRENT_SOURCE_DIR} is still the root dir. - file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS src/*.c**) - file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS include/*.h**) - - # Define our godot-cpp library - add_library(${PROJECT_NAME} STATIC - ${SOURCES} - ${HEADERS} - ${GENERATED_FILES_LIST} + ### Platform is derived from the toolchain target + # See GeneratorExpressions PLATFORM_ID and CMAKE_SYSTEM_NAME + set( SYSTEM_NAME + $<$:android.${ANDROID_ABI}> + $<$:ios> + $<$:linux> + $<$:macos> + $<$:web> + $<$:windows> + $<$:windows> ) - add_library(godot::cpp ALIAS ${PROJECT_NAME}) + string(REPLACE ";" "" SYSTEM_NAME "${SYSTEM_NAME}") - include(${PROJECT_SOURCE_DIR}/cmake/common_compiler_flags.cmake) - - target_compile_features(${PROJECT_NAME} - PRIVATE - cxx_std_17 - ) - - if(GODOT_USE_HOT_RELOAD) - target_compile_definitions(${PROJECT_NAME} PUBLIC HOT_RELOAD_ENABLED) - target_compile_options(${PROJECT_NAME} PUBLIC $<${compiler_is_gnu}:-fno-gnu-unique>) - endif() - - target_compile_definitions(${PROJECT_NAME} PUBLIC - $<$: - DEBUG_ENABLED - DEBUG_METHODS_ENABLED - > - $<${compiler_is_msvc}: - TYPED_METHOD_BIND - > - ) - - target_link_options(${PROJECT_NAME} PRIVATE - $<$: - -static-libgcc - -static-libstdc++ - -Wl,-R,'$$ORIGIN' - > - ) - - # Optionally mark headers as SYSTEM - set(GODOT_SYSTEM_HEADERS_ATTRIBUTE "") - if (GODOT_SYSTEM_HEADERS) - set(GODOT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) - endif () - - target_include_directories(${PROJECT_NAME} ${GODOT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC - include - ${CMAKE_CURRENT_BINARY_DIR}/gen/include - ${GODOT_GDEXTENSION_DIR} - ) - - # Add the compile flags - set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS}) - - # Create the correct name (godot.os.build_type.system_bits) - string(TOLOWER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME) - string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE) - - if(ANDROID) - # Added the android abi after system name - set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI}) - - # Android does not have the bits at the end if you look at the main godot repo build - set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}") + ### Use the arch from the toolchain if it isn't set manually + if( GODOT_ARCH ) + set(SYSTEM_ARCH ${GODOT_ARCH}) else() - set(OUTPUT_NAME "godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}.${BITS}") + godot_arch_map( SYSTEM_ARCH ${CMAKE_SYSTEM_PROCESSOR} ) endif() - set_target_properties(${PROJECT_NAME} - PROPERTIES - CXX_EXTENSIONS OFF - POSITION_INDEPENDENT_CODE ON - CXX_VISIBILITY_PRESET ${GODOT_SYMBOL_VISIBILITY} - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" - OUTPUT_NAME "${OUTPUT_NAME}" - ) + ### Define our godot-cpp library targets + foreach ( TARGET_NAME template_debug template_release editor ) + + # Useful genex snippits used in subsequent genex's + set( IS_RELEASE "$") + set( IS_DEV "$") + set( DEBUG_FEATURES "$,$>" ) + set( HOT_RELOAD "$,$>" ) + + # the godot-cpp.* library targets + add_library( ${TARGET_NAME} STATIC ${EXCLUDE} ) + add_library( godot-cpp::${TARGET_NAME} ALIAS ${TARGET_NAME} ) + + file( GLOB_RECURSE GODOTCPP_SOURCES LIST_DIRECTORIES NO CONFIGURE_DEPENDS src/*.cpp ) + + target_sources( ${TARGET_NAME} + PRIVATE + ${GODOTCPP_SOURCES} + ${GENERATED_FILES_LIST} + ) + + target_include_directories( ${TARGET_NAME} ${GODOT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC + include + ${CMAKE_CURRENT_BINARY_DIR}/gen/include + ${GODOT_GDEXTENSION_DIR} + ) + + set_target_properties( ${TARGET_NAME} + PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET ${GODOT_SYMBOL_VISIBILITY} + + COMPILE_WARNING_AS_ERROR ${GODOT_WARNING_AS_ERROR} + POSITION_INDEPENDENT_CODE ON + BUILD_RPATH_USE_ORIGIN ON + + PREFIX lib + OUTPUT_NAME "${PROJECT_NAME}.${SYSTEM_NAME}.${TARGET_NAME}.${SYSTEM_ARCH}" + ARCHIVE_OUTPUT_DIRECTORY "$<1:${CMAKE_BINARY_DIR}/bin>" + + # Things that are handy to know for dependent targets + GODOT_PLATFORM "${SYSTEM_NAME}" + GODOT_TARGET "${TARGET_NAME}" + GODOT_ARCH "${SYSTEM_ARCH}" + ) + + if( CMAKE_SYSTEM_NAME STREQUAL Android ) + android_generate( ${TARGET_NAME} ) + elseif ( CMAKE_SYSTEM_NAME STREQUAL iOS ) + ios_generate( ${TARGET_NAME} ) + elseif ( CMAKE_SYSTEM_NAME STREQUAL Linux ) + linux_generate( ${TARGET_NAME} ) + elseif ( CMAKE_SYSTEM_NAME STREQUAL Darwin ) + macos_generate( ${TARGET_NAME} ) + elseif ( CMAKE_SYSTEM_NAME STREQUAL Emscripten ) + web_generate( ${TARGET_NAME} ) + elseif ( CMAKE_SYSTEM_NAME STREQUAL Windows ) + windows_generate( ${TARGET_NAME} ) + endif () + + endforeach () endfunction() diff --git a/cmake/ios.cmake b/cmake/ios.cmake new file mode 100644 index 00000000..bb964221 --- /dev/null +++ b/cmake/ios.cmake @@ -0,0 +1,22 @@ +#[=======================================================================[.rst: +Ios +--- + +This file contains functions for options and configuration for targeting the +Ios platform + +]=======================================================================] +function(ios_options) + # iOS options +endfunction() + +function(ios_generate TARGET_NAME) + + target_compile_definitions(${TARGET_NAME} + PUBLIC + IOS_ENABLED + UNIX_ENABLED + ) + + common_compiler_flags(${TARGET_NAME}) +endfunction() diff --git a/cmake/linux.cmake b/cmake/linux.cmake new file mode 100644 index 00000000..fae620e0 --- /dev/null +++ b/cmake/linux.cmake @@ -0,0 +1,22 @@ +#[=======================================================================[.rst: +Linux +----- + +This file contains functions for options and configuration for targeting the +Linux platform + +]=======================================================================] +function( linux_options ) + # Linux Options +endfunction() + +function( linux_generate TARGET_NAME ) + + target_compile_definitions( ${TARGET_NAME} + PUBLIC + LINUX_ENABLED + UNIX_ENABLED + ) + + common_compiler_flags( ${TARGET_NAME} ) +endfunction() diff --git a/cmake/macos.cmake b/cmake/macos.cmake new file mode 100644 index 00000000..1bb4dbcf --- /dev/null +++ b/cmake/macos.cmake @@ -0,0 +1,59 @@ +#[=======================================================================[.rst: +MacOS +----- + +This file contains functions for options and configuration for targeting the +MacOS platform + +]=======================================================================] + +# Find Requirements +IF(APPLE) + set( CMAKE_OSX_SYSROOT $ENV{SDKROOT} ) + find_library( COCOA_LIBRARY REQUIRED + NAMES Cocoa + PATHS ${CMAKE_OSX_SYSROOT}/System/Library + PATH_SUFFIXES Frameworks + NO_DEFAULT_PATH) +ENDIF (APPLE) + + +function( macos_options ) + # macos options here +endfunction() + + +function( macos_generate TARGET_NAME ) + + # OSX_ARCHITECTURES does not support generator expressions. + if( NOT GODOT_ARCH OR GODOT_ARCH STREQUAL universal ) + set( OSX_ARCH "x86_64;arm64" ) + set( SYSTEM_ARCH universal ) + else() + set( OSX_ARCH ${GODOT_ARCH} ) + endif() + + set_target_properties( ${TARGET_NAME} + PROPERTIES + + OSX_ARCHITECTURES "${OSX_ARCH}" + ) + + target_compile_definitions(${TARGET_NAME} + PUBLIC + MACOS_ENABLED + UNIX_ENABLED + ) + + target_link_options( ${TARGET_NAME} + PUBLIC + -Wl,-undefined,dynamic_lookup + ) + + target_link_libraries( ${TARGET_NAME} + INTERFACE + ${COCOA_LIBRARY} + ) + + common_compiler_flags( ${TARGET_NAME} ) +endfunction() diff --git a/cmake/web.cmake b/cmake/web.cmake new file mode 100644 index 00000000..b7d6c13a --- /dev/null +++ b/cmake/web.cmake @@ -0,0 +1,42 @@ +#[=======================================================================[.rst: +Web +--- + +This file contains functions for options and configuration for targeting the +Web platform + +]=======================================================================] + +# Emscripten requires this hack for use of the SHARED option +set( CMAKE_PROJECT_godot-cpp_INCLUDE cmake/emsdkHack.cmake ) + +function( web_options ) + # web options +endfunction() + + +function( web_generate TARGET_NAME ) + + target_compile_definitions(${TARGET_NAME} + PUBLIC + WEB_ENABLED + UNIX_ENABLED + ) + + target_compile_options( ${TARGET_NAME} + PUBLIC + -sSIDE_MODULE + -sSUPPORT_LONGJMP=wasm + -fno-exceptions + ) + + target_link_options( ${TARGET_NAME} + INTERFACE + -sWASM_BIGINT + -sSUPPORT_LONGJMP=wasm + -fvisibility=hidden + -shared + ) + + common_compiler_flags( ${TARGET_NAME} ) +endfunction() diff --git a/cmake/windows.cmake b/cmake/windows.cmake new file mode 100644 index 00000000..c46519e8 --- /dev/null +++ b/cmake/windows.cmake @@ -0,0 +1,63 @@ +#[=======================================================================[.rst: +Windows +------- + +This file contains functions for options and configuration for targeting the +Windows platform + +]=======================================================================] + +function( windows_options ) + + option( GODOT_USE_STATIC_CPP "Link MinGW/MSVC C++ runtime libraries statically" ON ) + + # The below scons variables are controlled via toolchain files instead + # "mingw_prefix" "MinGW prefix" + # "use_llvm" "Use the LLVM compiler (MVSC or MinGW depending on the use_mingw flag)" + # "use_mingw" "Use the MinGW compiler instead of MSVC - only effective on Windows" +endfunction() + +function( windows_generate TARGET_NAME ) + set( IS_MSVC "$" ) + set( IS_CLANG "$,$>" ) + set( NOT_MSVC "$" ) + set( STATIC_CPP "$") + set( DISABLE_EXCEPTIONS "$") + + set_target_properties( ${TARGET_NAME} + PROPERTIES + PDB_OUTPUT_DIRECTORY "$<1:${CMAKE_SOURCE_DIR}/bin>" + ) + + target_compile_definitions( ${TARGET_NAME} + PUBLIC + WINDOWS_ENABLED + $<${IS_MSVC}: + TYPED_METHOD_BIND + NOMINMAX + > + ) + + target_compile_options( ${TARGET_NAME} + PUBLIC + $<${IS_MSVC}: + $$<${IS_DEV}:d> # Link microsoft runtime + > + ) + target_link_options( ${TARGET_NAME} + PUBLIC + + $<${NOT_MSVC}: + -Wl,--no-undefined + $<${STATIC_CPP}: + -static + -static-libgcc + -static-libstdc++ + > + > + + $<${IS_CLANG}:-lstdc++> + ) + + common_compiler_flags( ${TARGET_NAME} ) +endfunction() diff --git a/doc/cmake.md b/doc/cmake.md deleted file mode 100644 index 3dd77f58..00000000 --- a/doc/cmake.md +++ /dev/null @@ -1,57 +0,0 @@ -## CMake - -### cmake arguments - -`CMAKE_BUILD_TYPE`: Compilation target (Debug or Release defaults to Debug) - -### godot-cpp cmake arguments -- `GODOT_GDEXTENSION_DIR`: Path to the directory containing GDExtension interface header and API JSON file -- `GODOT_SYSTEM_HEADERS`: Mark the header files as SYSTEM. This may be useful to suppress warnings in projects including this one. -- `GODOT_WARNING_AS_ERROR`: Treat any warnings as errors -- `GODOT_USE_HOT_RELOAD`: Build with hot reload support. Defaults to YES for Debug-builds and NO for Release-builds. -- `GODOT_CUSTOM_API_FILE`: Path to a custom GDExtension API JSON file (takes precedence over `gdextension_dir`) -- `GODOT_PRECISION`: Floating-point precision level ("single", "double") - -### Android cmake arguments -- `CMAKE_TOOLCHAIN_FILE`: The path to the android cmake toolchain ($ANDROID_NDK/build/cmake/android.toolchain.cmake) -- `ANDROID_NDK`: The path to the android ndk root folder -- `ANDROID_TOOLCHAIN_NAME`: The android toolchain (arm-linux-androideabi-4.9 or aarch64-linux-android-4.9 or x86-4.9 or x86_64-4.9) -- `ANDROID_PLATFORM`: The android platform version (android-23) - -- More info [here](https://godot.readthedocs.io/en/latest/development/compiling/compiling_for_android.html) - -## Examples -```shell -Builds a debug version: -cmake . -cmake --build . -``` -Builds a release version with clang - -```shell -CC=/usr/bin/clang CXX=/usr/bin/clang++ cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" . -cmake --build . -``` -Builds an android armeabi-v7a debug version: - -``` shell -cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_NDK=$ANDROID_NDK \ - -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 -DANDROID_PLATFORM=android-23 -DCMAKE_BUILD_TYPE=Debug . -cmake --build . -``` - -## Protip -Generate the buildfiles in a sub directory to not clutter the root directory with build files: - -```shell -mkdir build && cd build && cmake -G "Unix Makefiles" .. && cmake --build . -``` - -Ensure that you avoid exposing godot-cpp symbols - this might lead to hard to debug errors if you ever load multiple -plugins using difference godot-cpp versions. Use visibility hidden whenever possible: -```cmake -set_target_properties( PROPERTIES CXX_VISIBILITY_PRESET hidden) -``` - -## Todo -Test build for Windows, Mac and mingw. diff --git a/doc/cmake.rst b/doc/cmake.rst new file mode 100644 index 00000000..d47dadd8 --- /dev/null +++ b/doc/cmake.rst @@ -0,0 +1,331 @@ +CMake +===== + +.. warning:: + + The CMake scripts do not have feature parity with the SCons ones at this + stage and are still a work in progress. There are a number of people who + have been working on alternative CMake solutions that are frequently + referenced in the discord chats: Ivan's cmake-rewrite_ branch and + Vorlac's godot-roguelite_ Project + +.. _cmake-rewrite: https://github.com/IvanInventor/godot-cpp/tree/cmake-rewrite +.. _godot-roguelite: https://github.com/vorlac/godot-roguelite + +Introduction +------------ + +Compiling godot-cpp independently of an extension project is mainly for +godot-cpp developers, package maintainers, and CI/CD. Look to the +godot-cpp-template_ for a practical example on how to consume the godot-cpp +library as part of a Godot extension. + +Configuration examples are listed at the bottom of the page. + +.. _godot-cpp-template: https://github.com/godotengine/godot-cpp-template + +Basic walkthrough +----------------- + +.. topic:: Clone the git repository + + .. code-block:: + + git clone https://github.com/godotengine/godot-cpp.git + Cloning into 'godot-cpp'... + ... + cd godot-cpp + + +.. topic:: Out-of-tree build directory + + Create a build directory for CMake to put caches and build artifacts in and + change directory to it. This is typically as a sub-directory of the project + root but can be outside the source tree. This is so that generated files do + not clutter up the source tree. + + .. code-block:: + + mkdir cmake-build + cd cmake-build + +.. topic:: Configure the build + + CMake doesn't build the code, it generates the files that another tool uses + to build the code. To see the list of generators run ``cmake --help``. The + first phase of which is running through the configuration scripts. + + Configure and generate Ninja build files. + + .. code-block:: + + cmake ../ -G "Ninja" + + To list the available options CMake use the ``-L[AH]`` option. ``A`` is for + advanced, and ``H`` is for help strings. + + .. code-block:: + + cmake ../ -LH + + Options are specified on the command line when configuring + + .. code-block:: + + cmake ../ -DGODOT_USE_HOT_RELOAD:BOOL=ON \ + -DGODOT_PRECISION:STRING=double \ + -DCMAKE_BUILD_TYPE:STRING=Debug + + Review setting-build-variables_ and build-configurations_ for more information. + + .. _setting-build-variables: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables + .. _build-configurations: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations + + A non-exhaustive list of options: + + .. code-block:: + + // Path to a custom GDExtension API JSON file (takes precedence over `GODOT_GDEXTENSION_DIR`) ( /path/to/custom_api_file ) + `GODOT_CUSTOM_API_FILE:FILEPATH=` + + // Force disabling exception handling code (ON|OFF) + GODOT_DISABLE_EXCEPTIONS:BOOL=ON + + // Path to a custom directory containing GDExtension interface header and API JSON file ( /path/to/gdextension_dir ) + GODOT_GDEXTENSION_DIR:PATH=gdextension + + // Generate a template version of the Node class's get_node. (ON|OFF) + GODOT_GENERATE_TEMPLATE_GET_NODE:BOOL=ON + + // Set the floating-point precision level (single|double) + GODOT_PRECISION:STRING=single + + // Symbols visibility on GNU platforms. Use 'auto' to apply the default value. (auto|visible|hidden) + GODOT_SYMBOL_VISIBILITY:STRING=hidden + + // Expose headers as SYSTEM. + GODOT_SYSTEM_HEADERS:BOOL=ON + + // Enable the extra accounting required to support hot reload. (ON|OFF) + GODOT_USE_HOT_RELOAD:BOOL= + + // Treat warnings as errors + GODOT_WARNING_AS_ERROR:BOOL=OFF + + +.. topic:: Compiling + + A target and a configuration is required, as the default ``all`` target does + not include anything and when using multi-config generators like ``Ninja + Multi-Config``, ``Visual Studio *`` or ``Xcode`` the build configuration + needs to be specified at build time. Build in Release mode unless you need + debug symbols. + + .. code-block:: + + cmake --build . -t template_debug --config Release + +Examples +-------- + +Windows and MSVC +~~~~~~~~~~~~~~~~ +So long as CMake is installed from the `CMake Downloads`_ page and in the PATH, +and Microsoft Visual Studio is installed with c++ support, CMake will detect +the MSVC compiler. + +.. _CMake downloads: https://cmake.org/download/ + +Assuming the current working directory is the godot-cpp project root: + +.. code-block:: + + mkdir build-msvc + cd build-msvc + cmake ../ + cmake --build . -t godot-cpp-test --config Release + + +MSys2/clang64, "Ninja", godot-cpp-test target with debug symbols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Assumes the ming-w64-clang-x86_64-toolchain is installed + +Using the msys2/clang64 shell + +.. code-block:: + + mkdir build-clang + cd build-clang + cmake ../ -G"Ninja" -DCMAKE_BUILD_TYPE:STRING=Debug + cmake --build . -t godot-cpp-test + +MSys2/clang64, "Ninja Multi-Config", godot-cpp-test target with GODOT_DEV_BUILD +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Assumes the ming-w64-clang-x86_64-toolchain is installed + +Using the msys2/clang64 shell + +.. code-block:: + + mkdir build-clang + cd build-clang + cmake ../ -G"Ninja Multi-Config" -DGODOT_DEV_BUILD:BOOL=ON + cmake --build . -t godot-cpp-test --config Debug + +Emscripten for web, template_release target +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +I've only tested this on windows so far. + +I cloned, installed, and activating the latest Emscripten tools(for me it was +3.1.69) to ``c:\emsdk`` + +From a terminal running the ``c:\emsdk\emcmdprompt.bat`` puts me in a cmdprompt +context which I dislike, so after that I run pwsh to get my powershell 7.4.5 +context back. + +using the ``emcmake.bat`` command adds the emscripten toolchain to the CMake +command + +.. code-block:: + + C:\emsdk\emcmdprompt.bat + pwsh + cd + mkdir build-wasm32 + cd build-wasm32 + emcmake.bat cmake ../ + cmake --build . --verbose -t template_release + +Android Cross Compile from Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There are two separate paths you can choose when configuring for android. + +Use the ``CMAKE_ANDROID_*`` variables specified on the commandline or in your +own toolchain file as listed in the cmake-toolchains_ documentation + +.. _cmake-toolchains: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk + +Or use the toolchain and scripts provided by the Android SDK and make changes +using the ``ANDROID_*`` variables listed there. Where ```` is whatever +ndk version you have installed ( tested with `23.2.8568313`) and ```` +is for android sdk platform, (tested with ``android-29``) + +.. warning:: + + The Android SDK website explicitly states that they do not support using + the CMake built-in method, and recommends you stick with their toolchain + files. + +.. topic:: Using your own toolchain file as described in the CMake documentation + + .. code-block:: + + mkdir build-android + cd build-android + cmake ../ --toolchain my_toolchain.cmake + cmake --build . -t template_release + + Doing the equivalent on just using the command line + + .. code-block:: + + mkdir build-android + cd build-android + cmake ../ \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_SYSTEM_VERSION= \ + -DCMAKE_ANDROID_ARCH_ABI= \ + -DCMAKE_ANDROID_NDK=/path/to/android-ndk + cmake --build . -t template_release + +.. topic:: Using the toolchain file from the Android SDK + + Defaults to minimum supported version( android-16 in my case) and armv7-a. + + .. code-block:: + + mkdir build-android + cd build-android + cmake ../ --toolchain $ANDROID_HOME/ndk//build/cmake/android.toolchain.cmake + cmake --build . -t template_release + + Specify Android platform and ABI + + .. code-block:: + + mkdir build-android + cd build-android + cmake ../ --toolchain $ANDROID_HOME/ndk//build/cmake/android.toolchain.cmake \ + -DANDROID_PLATFORM:STRING=android-29 \ + -DANDROID_ABI:STRING=armeabi-v7a + cmake --build . -t template_release + + +Toolchains +---------- +This section attempts to list the host and target combinations that have been +at tested. + +Info on cross compiling triplets indicates that the naming is a little more +freeform that expected, and tailored to its use case. Triplets tend to have the +format ``[sub][-vendor][-OS][-env]`` + +* `osdev.org `_ +* `stack overflow `_ +* `LLVM `_ +* `clang target triple `_ +* `vcpkg `_ +* `wasm32-unknown-emscripten `_ + +Linux Host +~~~~~~~~~~ + +:Target: x86_64-linux + +Macos Host +~~~~~~~~~~ + +:System: Mac Mini +:OS Name: Sequoia 15.0.1 +:Processor: Apple M2 + +Windows Host +~~~~~~~~~~~~ + +:OS Name: Microsoft Windows 11 Home, 10.0.22631 N/A Build 22631 +:Processor: AMD Ryzen 7 6800HS Creator Edition + +`Microsoft Visual Studio 17 2022 `_ + :Target: x86_64-w64 + +`LLVM `_ + :Target: x86_64-pc-windows-msvc + +`AndroidSDK `_ + armv7-none-linux-androideabi16 + +`Emscripten `_ + :Compiler: Emscripten + :Target: wasm32-unknown-emscripten + +`MinGW-w64 `_ based toolchains + + `MSYS2 `_ + Necessary reading about MSYS2 `environments `_ + + ucrt64 + :Compiler: gcc version 14.2.0 (Rev1, Built by MSYS2 project) + :Target: x86_64-w64-mingw32 + + clang64 + :Compiler: clang version 18.1.8 + :Target: x86_64-w64-windows-gnu + + `LLVM-MinGW `_ + + `MinGW-W64-builds `_ + :Compiler: gcc + :Target: x86_64-w64-mingw32-ucrt + + `Jetbrains-CLion `_ + :Target: x86_64-w64-mingw32-msvcrt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f1c4c0d7..ab7397fa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,144 +1,67 @@ -cmake_minimum_required(VERSION 3.13) -project(godot-cpp-test) +# Testing Extension +# This is only linked to the template_release config. +# so it requires the template_release version of godot to run. -set(GODOT_GDEXTENSION_DIR ../gdextension/ CACHE STRING "Path to GDExtension interface header directory") -set(CPP_BINDINGS_PATH ../ CACHE STRING "Path to C++ bindings") +add_library( godot-cpp-test SHARED EXCLUDE_FROM_ALL ) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(TARGET_PATH x11) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(TARGET_PATH win64) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(TARGET_PATH macos) -else() - message(FATAL_ERROR "Not implemented support for ${CMAKE_SYSTEM_NAME}") -endif() - -# Change the output directory to the bin directory -set(BUILD_PATH ${CMAKE_SOURCE_DIR}/bin/${TARGET_PATH}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${BUILD_PATH}") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BUILD_PATH}") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BUILD_PATH}") -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${BUILD_PATH}") -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${BUILD_PATH}") - -# Set the c++ standard to c++17 -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -set(GODOT_COMPILE_FLAGS ) -set(GODOT_LINKER_FLAGS ) - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # using Visual Studio C++ - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /WX") # /GF /MP - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /DTYPED_METHOD_BIND") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /utf-8") - - if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MDd") # /Od /RTC1 /Zi - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /MD /O2") # /Oy /GL /Gy - STRING(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - endif(CMAKE_BUILD_TYPE MATCHES Debug) - - # Disable conversion warning, truncation, unreferenced var, signed mismatch - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /wd4244 /wd4305 /wd4101 /wd4018 /wd4267") - - add_definitions(-DNOMINMAX) - - # Unkomment for warning level 4 - #if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") - # string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - #endif() - -else() - - set(GODOT_LINKER_FLAGS "-static-libgcc -static-libstdc++ -Wl,-R,'$$ORIGIN'") - - set(GODOT_COMPILE_FLAGS "-fPIC -g -Wwrite-strings") - - if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-omit-frame-pointer -O0") - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -O3") - endif(CMAKE_BUILD_TYPE MATCHES Debug) -endif() - -# Disable exception handling. Godot doesn't use exceptions anywhere, and this -# saves around 20% of binary size and very significant build time (GH-80513). -option(GODOT_DISABLE_EXCEPTIONS ON "Force disabling exception handling code") -if (GODOT_DISABLE_EXCEPTIONS) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -D_HAS_EXCEPTIONS=0") - else() - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} -fno-exceptions") - endif() -else() - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(GODOT_COMPILE_FLAGS "${GODOT_COMPILE_FLAGS} /EHsc") - endif() -endif() - -# Get Sources -file(GLOB_RECURSE SOURCES src/*.c**) -file(GLOB_RECURSE HEADERS include/*.h**) - -# Define our godot-cpp library -add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS}) - -target_include_directories(${PROJECT_NAME} SYSTEM - PRIVATE - ${CPP_BINDINGS_PATH}/include - ${CPP_BINDINGS_PATH}/gen/include - ${GODOT_GDEXTENSION_DIR} +target_sources( godot-cpp-test + PRIVATE + src/example.cpp + src/example.h + src/register_types.cpp + src/register_types.h + src/tests.h ) -# Create the correct name (godot.os.build_type.system_bits) -# Synchronized with godot-cpp's CMakeLists.txt +set( TEST_TARGET "template_debug" CACHE STRING "Which godot-cpp::target to link against" ) +set_property( CACHE TEST_TARGET PROPERTY STRINGS "template_debug;template_release;editor" ) -set(BITS 32) -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(BITS 64) -endif(CMAKE_SIZEOF_VOID_P EQUAL 8) +target_link_libraries( godot-cpp-test + PRIVATE + godot-cpp::${TEST_TARGET} ) -if(CMAKE_BUILD_TYPE MATCHES Debug) - set(GODOT_CPP_BUILD_TYPE Debug) -else() - set(GODOT_CPP_BUILD_TYPE Release) -endif() +### Get useful properties of the library +get_target_property( GODOT_PLATFORM godot-cpp::${TEST_TARGET} GODOT_PLATFORM ) +get_target_property( GODOT_TARGET godot-cpp::${TEST_TARGET} GODOT_TARGET ) +get_target_property( GODOT_ARCH godot-cpp::${TEST_TARGET} GODOT_ARCH ) -string(TOLOWER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME) -string(TOLOWER ${GODOT_CPP_BUILD_TYPE} BUILD_TYPE) +set( OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/project/bin/" ) -if(ANDROID) - # Added the android abi after system name - set(SYSTEM_NAME ${SYSTEM_NAME}.${ANDROID_ABI}) -endif() +set_target_properties( godot-cpp-test + PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF + CXX_VISIBILITY_PRESET ${GODOT_SYMBOL_VISIBILITY} -if(CMAKE_VERSION VERSION_GREATER "3.13") - target_link_directories(${PROJECT_NAME} - PRIVATE - ${CPP_BINDINGS_PATH}/bin/ - ) + POSITION_INDEPENDENT_CODE ON + BUILD_RPATH_USE_ORIGIN ON + LINK_SEARCH_START_STATIC ON + LINK_SEARCH_END_STATIC ON - target_link_libraries(${PROJECT_NAME} - godot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}$<$>:.${BITS}> - ) -else() - target_link_libraries(${PROJECT_NAME} - ${CPP_BINDINGS_PATH}/bin/libgodot-cpp.${SYSTEM_NAME}.${BUILD_TYPE}$<$>:.${BITS}>.a - ) -endif() + # NOTE: Wrapping the output variables inside a generator expression + # prevents msvc generator from adding addition Config Directories + LIBRARY_OUTPUT_DIRECTORY "$<1:${OUTPUT_DIR}>" + RUNTIME_OUTPUT_DIRECTORY "$<1:${OUTPUT_DIR}>" + PDB_OUTPUT_DIRECTORY "$<1:${OUTPUT_DIR}>" #MSVC Only, ignored on other platforms -# Add the compile flags -set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS ${GODOT_COMPILE_FLAGS}) -set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS ${GODOT_LINKER_FLAGS}) + PREFIX "lib" + OUTPUT_NAME "gdexample.${GODOT_PLATFORM}.${GODOT_TARGET}.${GODOT_ARCH}" +) -set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_NAME "gdexample") +if( CMAKE_SYSTEM_NAME STREQUAL Darwin ) + get_target_property( OSX_ARCH godot-cpp::${TEST_TARGET} OSX_ARCHITECTURES ) + + set( OUTPUT_DIR "${OUTPUT_DIR}/libgdexample.macos.${TEST_TARGET}.framework") + + set_target_properties( godot-cpp-test + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "$<1:${OUTPUT_DIR}>" + RUNTIME_OUTPUT_DIRECTORY "$<1:${OUTPUT_DIR}>" + + OUTPUT_NAME "gdexample.macos.${TEST_TARGET}" + SUFFIX "" + + #macos options + OSX_ARCHITECTURES "${OSX_ARCH}" + ) +endif ()