diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1b1d2..470a3a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,10 @@ if(AKGL_COVERAGE) find_program(GCOVR_EXECUTABLE gcovr REQUIRED) endif() -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - -# Vendored projects own their test suites. Suppress their CTest registration -# while embedded so the top-level suite contains only targets built here. +# Vendored projects own their test suites. Suppress their CTest registration so +# the suite that runs contains only the targets built here. The override is +# lifted again below, before this project registers its own tests, so an +# embedding consumer's add_test() still reaches CTest. set(AKGL_SUPPRESS_DEPENDENCY_TESTS TRUE) function(add_test) if(NOT AKGL_SUPPRESS_DEPENDENCY_TESTS) @@ -34,53 +34,79 @@ set(JANSSON_WITHOUT_TESTS ON CACHE BOOL "Do not build vendored Jansson tests" FO set(JANSSON_EXAMPLES OFF CACHE BOOL "Do not build vendored Jansson examples" FORCE) set(JANSSON_BUILD_DOCS OFF CACHE BOOL "Do not build vendored Jansson docs" FORCE) -add_subdirectory(deps/jansson EXCLUDE_FROM_ALL) -add_subdirectory(deps/libakerror EXCLUDE_FROM_ALL) -add_subdirectory(deps/libakstdlib EXCLUDE_FROM_ALL) -add_subdirectory(deps/SDL EXCLUDE_FROM_ALL) -add_subdirectory(deps/SDL_image EXCLUDE_FROM_ALL) -add_subdirectory(deps/SDL_mixer EXCLUDE_FROM_ALL) -add_subdirectory(deps/SDL_ttf EXCLUDE_FROM_ALL) +# Add one vendored dependency, if nobody has already declared it and the +# submodule is actually checked out. +# +# A macro rather than a function on purpose: add_subdirectory() inside a +# function runs with that function's variable scope, so every cache-ish variable +# these projects set for their own subdirectories would be discarded on return. +macro(akgl_add_vendored_dependency target dir) + if(NOT TARGET ${target}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt") + add_subdirectory(${dir} EXCLUDE_FROM_ALL) + set(AKGL_VENDORED_DEPENDENCIES TRUE) + endif() + endif() +endmacro() + +# Added on both paths, not just when this is the top-level project. Embedded +# with add_subdirectory(), the submodules under deps/ are sitting right there -- +# the recursive clone the consumer just did put them there -- and refusing to +# configure until SDL3 is installed system-wide is a failure with its own answer +# three directories away. if(NOT TARGET ...) means a consumer that has already +# declared one of these wins; the EXISTS check means a checkout without +# submodules falls through to find_package below. +akgl_add_vendored_dependency(jansson::jansson deps/jansson) +akgl_add_vendored_dependency(akerror::akerror deps/libakerror) +akgl_add_vendored_dependency(akstdlib::akstdlib deps/libakstdlib) +akgl_add_vendored_dependency(SDL3::SDL3 deps/SDL) +akgl_add_vendored_dependency(SDL3_image::SDL3_image deps/SDL_image) +akgl_add_vendored_dependency(SDL3_mixer::SDL3_mixer deps/SDL_mixer) +akgl_add_vendored_dependency(SDL3_ttf::SDL3_ttf deps/SDL_ttf) # libakerror 1.0.0 sizes its own status-name registry; consumers no longer do. # libakgl claims its status codes at runtime in akgl_heap_init() instead. set(AKGL_SUPPRESS_DEPENDENCY_TESTS FALSE) -else() - +# Anything the vendored block did not supply has to come from the system. +if(NOT (TARGET SDL3::SDL3 AND TARGET SDL3_image::SDL3_image AND + TARGET SDL3_mixer::SDL3_mixer AND TARGET SDL3_ttf::SDL3_ttf AND + TARGET akerror::akerror AND TARGET akstdlib::akstdlib AND + TARGET jansson::jansson)) + # Only needed to locate installed copies; a fully vendored build does not + # require pkg-config to be present at all. find_package(PkgConfig REQUIRED) +endif() - if(NOT TARGET SDL3::SDL3) - find_package(SDL3 REQUIRED) - endif() - if(NOT TARGET SDL3_image::SDL3_image) - find_package(SDL3_image REQUIRED) - endif() - if(NOT TARGET SDL3_mixer::SDL3_mixer) - find_package(SDL3_mixer REQUIRED) - endif() - if(NOT TARGET SDL3_ttf::SDL3_ttf) - find_package(SDL3_ttf REQUIRED) - endif() - # No version here: libakerror ships no akerrorConfigVersion.cmake, so asking - # for one makes find_package reject every install. The floor is enforced by - # the #error in include/akgl/error.h instead, which feature-tests - # AKERR_FIRST_CONSUMER_STATUS. - if(NOT TARGET akerror::akerror) - find_package(akerror REQUIRED) - endif() - # 0.2 rather than bare: libakstdlib 0.2.0 ships an akstdlibConfigVersion.cmake - # with SameMinorVersion compatibility, mirroring its soname, so this accepts - # any 0.2.x and refuses 0.3 and 1.0. Unversioned, this path would silently - # accept an ABI-incompatible libakstdlib. - if(NOT TARGET akstdlib::akstdlib) - find_package(akstdlib 0.2 REQUIRED) - endif() - if(NOT TARGET jansson::jansson) - find_package(jansson) - endif() - +if(NOT TARGET SDL3::SDL3) + find_package(SDL3 REQUIRED) +endif() +if(NOT TARGET SDL3_image::SDL3_image) + find_package(SDL3_image REQUIRED) +endif() +if(NOT TARGET SDL3_mixer::SDL3_mixer) + find_package(SDL3_mixer REQUIRED) +endif() +if(NOT TARGET SDL3_ttf::SDL3_ttf) + find_package(SDL3_ttf REQUIRED) +endif() +# No version here: libakerror ships no akerrorConfigVersion.cmake, so asking +# for one makes find_package reject every install. The floor is enforced by +# the #error in include/akgl/error.h instead, which feature-tests +# AKERR_FIRST_CONSUMER_STATUS. +if(NOT TARGET akerror::akerror) + find_package(akerror REQUIRED) +endif() +# 0.2 rather than bare: libakstdlib 0.2.0 ships an akstdlibConfigVersion.cmake +# with SameMinorVersion compatibility, mirroring its soname, so this accepts +# any 0.2.x and refuses 0.3 and 1.0. Unversioned, this path would silently +# accept an ABI-incompatible libakstdlib. +if(NOT TARGET akstdlib::akstdlib) + find_package(akstdlib 0.2 REQUIRED) +endif() +if(NOT TARGET jansson::jansson) + find_package(jansson) endif() set(GAMECONTROLLERDB_H "include/akgl/SDL_GameControllerDB.h") @@ -261,8 +287,9 @@ target_link_libraries(charviewer PRIVATE akstdlib::akstdlib akerror::akerror akg # project subdirectories that are not on the loader's default search path, so a # freshly built test aborts before main() with "cannot open shared object file". # Bake those directories into the build-tree RPATH. Installed builds resolve the -# same libraries through find_package and need no help. -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) +# same libraries through find_package and need no help, so this keys on whether +# anything was actually vendored rather than on being the top-level project. +if(AKGL_VENDORED_DEPENDENCIES) set(AKGL_VENDORED_RPATH "$" "$"