godot-cpp icon indicating copy to clipboard operation
godot-cpp copied to clipboard

CMake Configuration Exporting

Open grabusr opened this issue 1 year ago • 5 comments

Feature

Allows to build CMake install target that exports GodotCppConfig.cmake allowing to find it by using find_package() function.

Usage

Configure godot-cpp CMake project in build directory:

# example project configuration
cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=<path where you want install godot_cpp>
# install
cmake --build . --target install

Now instead of storing godot_cpp project as submodule in GDExtension CMake project we can:

Configuring GDExtension project in build directory

cmake .. -G Ninja -DCMAKE_PREFIX_PATH=<path where godot_cpp was installed by install target if not in PATH env var>

Finding and linking to godot_cpp library:

find_package(GodotCpp REQUIRED)
add_library(MyExtension MODULE)
target_link_libraries(MyExtension PRIVATE godot::cpp)

Why?

  1. Storing godot-cpp as submodule can be problematic, because it is needed to compile it every time when GDExtension is compiled from scratch, This is painfull especially in CI.
  2. CMake Configuration Exporting is easy way to collect all necessary files to use in CMake based GDExtension project.
  3. Having export configuration mechanism makes creating packages for package managers like vcpkg and conan very easy.
  4. Exporting can be also used as RUN Docker command in Dockerfile. Stored built godot-cpp in Docker image allows to use it in separate environment (eg. on CI) and save time and resources.

Example install godot-cpp in Dockerfile

Tip: /usr/local/ is by default in Linux env in PATH, so when GDExtension CMake project is configured we do not need to provide CMAKE_PREFIX_PATH.

RUN git clone https://github.com/godotengine/godot-cpp.git -b godot-4.2.1-stable \
    && mkdir -p godot-cpp/build \
    && cd godot-cpp/build \
    && cmake .. -G Ninja -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX=/usr/local/ \
    && cmake --build . --target install \
    && cd ../.. \
    && rm -rf godot-cpp

grabusr avatar Nov 14 '23 20:11 grabusr

Thanks!

At a high-level, this makes sense: you can build the necessary version of godot-cpp into a Docker image, and then re-use it during CI. And I tested it using the commands in the description, and it seemed to work fine!

However, personally, I don't know cmake very well, so I can't say if this is the right/best way to accomplish this. Also, because I don't know cmake very well, I'm a little wary of having to maintain this long-term. So, I think it'd be good to get a 2nd opinion from someone on the GDExtension team with more cmake experience.

dsnopek avatar Nov 15 '23 15:11 dsnopek

I actually just did something very similar to this PR last night. I wanted godot-cpp packaged in vcpkg for my project, but I needed these exports to do it.

When we link against a library in CMake, the official and recommended way is to use find_package(CONFIG). It works in all situations, so it's absolutely critical to have proper find_package support. It's not just this Docker situation that benefits. find_package(CONFIG) is suggested when you're using a library as a subdirectory of your project, via a package manager, or in any other situation. It is the way you consume libraries in CMake.

At a glance, I do see some odd things going on here. There should be no reason to add GODOT_CPP_INSTALL as an option. The install commands set up the ability to run cmake --install .... There should be no need to opt in or out.

It is also suggested to use the GNUInstallDirs to set up the install locations.

I can take a deeper look at this when I have some more time if you'd like. It's easy to miss details, so it warrants a few more eyes, but if we get it right the first time, it shouldn't need to be modified often.

notskm avatar Nov 19 '23 12:11 notskm

If you use godot-cpp project as subproject (eg. in your GDExtension CMake project) and you use install target in case:


add_subdirectory(godot-cpp)

#...

add_library(MyExtension)
target_link_libraries(MyExtension PRIVATE godot::cpp)

install(TARGETS MyExtension ...)

then installing GDExtension project will export not only MyExtension.so (or *.dll), but also godot-cpp's library and config which in most of cases is not expected behavior.

To avoid that there is a common practice in CMake for libraries/frameworks exporting config to introduce flag like <Library>_INSTALL, eg. like in Google Test we have a cache flag INSTALL_GTEST.

If we use godot-cpp CMake project as sub-project we set cache flag before calling add_subdirectory(godot_cpp):

set(GODOT_CPP_INSTALL OFF CACHE BOOL "" FORCE)
add_subdirectory(godot_cpp)

Now building target install export only our GDExtension binaries (or other resources if needed) without exporting godot_cpp library and CMake config files.

Regarding install locations, targets by default use GNUInstallDirs, only header files need to be setup here. I try to use it by assigning include directories as PUBLIC_HEADERS next week.

grabusr avatar Nov 30 '23 20:11 grabusr

I have been playing with PUBLIC_HEADER property. It is just adding few commands:

if (GODOT_GDEXTENSION_DIR AND EXISTS ${GODOT_GDEXTENSION_DIR})
	file(GLOB_RECURSE GODOT_GDEXTENSION_HEADERS CONFIGURE_DEPENDS ${GODOT_GDEXTENSION_DIR}/*.h**)
endif ()

set(PUBLIC_HEADERS_LIST
		${GENERATED_FILES_LIST}
		${HEADERS}
		${GODOT_GDEXTENSION_HEADERS}
)
set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS_LIST}")

and it install all header files in CMAKE_INSTALL_INCLUDEDIR. The problem is that it does not keep directory structure, every header file is copied directly under include directory. Also using variable CMAKE_INSTALL_INCLUDEDIR does not solve the problem, because this:

install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/gen/include/
	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

results in CMake project loading error, because during project configuration variable ${CMAKE_INSTALL_INCLUDEDIR} is empty. So if we would like to keep directory structure we need to use install(DIRECTORY ... DESTINATION include) command.

grabusr avatar Dec 23 '23 09:12 grabusr

This is great! I would like something like this, so I can use this library with package managers like vcpkg.

That said I feel like the export name should be be consistent with the alias name, which is godot::cpp.

A library user would expect the library's target name to be the same regardless of how it's consumed.
So I think it's best to upheld that assumption.

Fortunately the fix is only 1 line, just add EXPORT_NAME "cpp" here https://github.com/grabusr/godot-cpp/blob/6b02a58e5a21fa8103abac8953d029e130d750f0/CMakeLists.txt#L212

set_target_properties(${PROJECT_NAME}
	PROPERTIES
		CXX_EXTENSIONS OFF
		POSITION_INDEPENDENT_CODE ON
		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}"
		EXPORT_NAME "cpp"
)

ytnuf avatar Mar 04 '24 23:03 ytnuf