Merge remote-tracking branch 'origin/main' into tphd

This commit is contained in:
Lurs
2026-08-11 17:07:13 +02:00
1291 changed files with 32756 additions and 20945 deletions
+63
View File
@@ -0,0 +1,63 @@
include_guard(GLOBAL)
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
# Android mod linking: symgen scans and filters the game's exports, generating a version script used for the executable
# link step, and generates a shared object stub that mods can link against without having to build the whole game.
function(setup_android_exports target)
include("${_dir}/SymbolManifest.cmake")
ensure_symgen(TRUE)
add_dependencies(${target} symgen)
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
endforeach ()
list(JOIN _rsp_lines "\n" _rsp_content)
set(_rsp "${CMAKE_BINARY_DIR}/dusklight_exports_input.rsp")
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
set(_sdk_args)
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
aurora_os aurora_pad aurora_si aurora_vi)
if (TARGET ${_lib})
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
endif ()
endforeach ()
if (TARGET dawn::webgpu_dawn)
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
if (_dawn_type STREQUAL "STATIC_LIBRARY")
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:dawn::webgpu_dawn>")
endif ()
endif ()
set(_vscript "${CMAKE_BINARY_DIR}/dusklight_exports.ver")
add_custom_command(TARGET ${target} PRE_LINK
COMMAND "${SYMGEN_EXE}" exports
"@${_rsp}"
--out "${_vscript}"
--format version-script
--exclude cmake_pch
--exclude miniz
--exclude asan_options
--exclude src/dusk
# Resolved from the Java side; the SDL ones live in the statically-linked
# SDL archive, outside the provenance scan.
--extra-sym JNI_OnLoad
--extra-sym SDL_main
--extra-sym "Java_*"
${_sdk_args}
COMMENT "Generating dusklight exports"
VERBATIM)
target_link_options(${target} PRIVATE "-Wl,--version-script=${_vscript}")
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _arch)
set(_stub "${CMAKE_BINARY_DIR}/stub-android-${_arch}.so")
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${SYMGEN_EXE}" stub -f elf "$<TARGET_FILE:${target}>" -o "${_stub}"
--soname "$<TARGET_FILE_NAME:${target}>" --arch "${_arch}"
BYPRODUCTS "${_stub}"
COMMENT "Generating dusklight link stub"
VERBATIM)
install(FILES "${_stub}" DESTINATION sdk)
endfunction()
+90
View File
@@ -0,0 +1,90 @@
include_guard(GLOBAL)
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
# Apple mod linking: symgen scans and filters the game's exports, generating an exports list used for the executable
# link step, and generates a MH_EXECUTE Mach-O stub that mods can link against without having to build the whole game.
function(setup_apple_exports target)
include("${_dir}/SymbolManifest.cmake")
ensure_symgen(TRUE)
set(_symgen "${SYMGEN_EXE}")
add_dependencies(${target} symgen)
set(_config_subdir "")
if (CMAKE_CONFIGURATION_TYPES)
set(_config_subdir "$<CONFIG>/")
endif ()
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
endforeach ()
list(JOIN _rsp_lines "\n" _rsp_content)
set(_rsp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports_input.rsp")
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
set(_sdk_args)
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
aurora_os aurora_pad aurora_si aurora_vi)
if (TARGET ${_lib})
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
endif ()
endforeach ()
# Dawn is linked statically on Apple; mods reach wgpu* through the executable.
if (TARGET dawn::webgpu_dawn)
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
if (_dawn_type STREQUAL "STATIC_LIBRARY")
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:dawn::webgpu_dawn>")
endif ()
endif ()
set(_exp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports.exp")
add_custom_command(TARGET ${target} PRE_LINK
COMMAND "${_symgen}" exports
"@${_rsp}"
--out "${_exp}"
--exclude cmake_pch
--exclude miniz
--exclude asan_options
--exclude src/dusk
${_sdk_args}
COMMENT "Generating dusklight exports"
VERBATIM)
target_link_options(${target} PRIVATE -Xlinker -exported_symbols_list -Xlinker "${_exp}")
# Generate the stub executable mods link against via -bundle_loader.
set(_stub_args)
if (IOS)
set(_stub_platform "ios")
list(APPEND _stub_args --platform ios)
elseif (TVOS)
set(_stub_platform "tvos")
list(APPEND _stub_args --platform tvos)
else ()
set(_stub_platform "macos")
endif ()
if (CMAKE_OSX_DEPLOYMENT_TARGET)
list(APPEND _stub_args --min-os "${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif ()
if (CMAKE_OSX_ARCHITECTURES)
set(_archs "${CMAKE_OSX_ARCHITECTURES}")
else ()
set(_archs "${CMAKE_SYSTEM_PROCESSOR}")
endif ()
set(_arch_names "")
foreach (_arch IN LISTS _archs)
string(TOLOWER "${_arch}" _arch)
list(APPEND _stub_args --arch "${_arch}")
list(APPEND _arch_names "${_arch}")
endforeach ()
list(JOIN _arch_names "_" _arch_name)
set(_stub "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight-stub")
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${_symgen}" stub -f macho "${_exp}" -o "${_stub}" ${_stub_args}
BYPRODUCTS "${_stub}"
COMMENT "Generating dusklight link stub"
VERBATIM)
install(FILES "${_stub}" DESTINATION sdk RENAME "stub-${_stub_platform}-${_arch_name}")
endfunction()
+10
View File
@@ -0,0 +1,10 @@
# Copies a mod asset directory for packaging, skipping dotfiles and dot-directories
# (.gitkeep, .DS_Store, ...). Usage: cmake -DSRC=<dir> -DDST=<dir> -P CopyModAssets.cmake
file(MAKE_DIRECTORY "${DST}")
file(GLOB_RECURSE _files RELATIVE "${SRC}" "${SRC}/*")
foreach (_file IN LISTS _files)
if (NOT _file MATCHES "(^|/)\\.")
get_filename_component(_dir "${_file}" DIRECTORY)
file(COPY "${SRC}/${_file}" DESTINATION "${DST}/${_dir}")
endif ()
endforeach ()
+68
View File
@@ -0,0 +1,68 @@
# The game ABI surface shared by the main build and the mod SDK (sdk/CMakeLists.txt)
include_guard(GLOBAL)
get_filename_component(_game_root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
# PARTIAL_DEBUG makes debug and release share one struct/vtable ABI so a mod binary loads into either
set(_game_compile_defs TARGET_PC=1 WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1 PARTIAL_DEBUG=1 DUSK_TPHD=1)
if (ANDROID)
list(APPEND _game_compile_defs TARGET_ANDROID=1)
endif ()
# Public game headers
set(_game_abi_include_dirs
${_game_root}/include
${_game_root}/assets/GZ2E01
${_game_root}/libs/JSystem/include
${_game_root}/extern/aurora/include/dolphin
${_game_root}/extern/aurora/include
${_game_root}/sdk/include
)
# Internal game headers
set(_game_include_dirs
${_game_abi_include_dirs}
${_game_root}/src
${_game_root}/extern
${CMAKE_CURRENT_BINARY_DIR}
)
# Mod API, including services
add_library(dusklight_mod_api INTERFACE)
target_include_directories(dusklight_mod_api INTERFACE ${_game_root}/sdk/include)
# Full internal headers used to build the game
add_library(dusklight_game_headers INTERFACE)
target_include_directories(dusklight_game_headers INTERFACE ${_game_include_dirs})
target_compile_definitions(dusklight_game_headers INTERFACE ${_game_compile_defs})
# Public game ABI for mods
add_library(dusklight_game_abi_headers INTERFACE)
target_include_directories(dusklight_game_abi_headers INTERFACE ${_game_abi_include_dirs})
target_compile_definitions(dusklight_game_abi_headers INTERFACE ${_game_compile_defs})
# Mod feature targets
add_library(dusklight_mod_feature_game INTERFACE)
target_link_libraries(dusklight_mod_feature_game INTERFACE
dusklight_mod_api
dusklight_game_abi_headers)
target_compile_definitions(dusklight_mod_feature_game INTERFACE DUSK_MOD_FEATURE_GAME=1)
# Game headers assume global.h comes first in the translation unit (it defines DUSK_GAME_DATA
# and friends); force-include it so mods don't depend on include order.
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
target_compile_options(dusklight_mod_feature_game INTERFACE
"$<$<COMPILE_LANGUAGE:CXX>:/FIglobal.h>")
else ()
target_compile_options(dusklight_mod_feature_game INTERFACE
"$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include global.h>")
endif ()
target_sources(dusklight_mod_feature_game INTERFACE
${_game_root}/sdk/src/game_feature.cpp)
add_library(dusklight_mod_feature_webgpu INTERFACE)
target_link_libraries(dusklight_mod_feature_webgpu INTERFACE dusklight_mod_api)
target_compile_definitions(dusklight_mod_feature_webgpu INTERFACE DUSK_MOD_FEATURE_WEBGPU=1)
add_library(dusklight_mod_feature_fmt INTERFACE)
target_link_libraries(dusklight_mod_feature_fmt INTERFACE dusklight_mod_api)
target_compile_definitions(dusklight_mod_feature_fmt INTERFACE DUSK_MOD_FEATURE_FMT=1)
+518
View File
@@ -0,0 +1,518 @@
# add_mod(<target> [FEATURES <feature>...] SOURCES <file>... MOD_JSON <mod.json>
# [RUNTIME_LIBRARIES <file>...] [RES_DIR <res>] [OVERLAY_DIR <overlay>]
# [TEXTURES_DIR <textures>] [OUTPUT_DIR <dir>] [BUNDLE])
set(DUSK_MODS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/mods" CACHE PATH "Directory to write mod packages into")
set(DUSKLIGHT_SDK_STUB_URL "https://github.com/encounter/dusklight/releases/download/sdk"
CACHE STRING "Base URL for game link stubs downloaded by out-of-tree mod builds")
function(_mod_lib_info out_platform_var out_name_var)
set(_arch "${CMAKE_SYSTEM_PROCESSOR}")
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
list(LENGTH CMAKE_OSX_ARCHITECTURES _count)
if (_count GREATER 1)
message(FATAL_ERROR "add_mod: universal binaries are not supported")
endif ()
set(_arch "${CMAKE_OSX_ARCHITECTURES}")
elseif (WIN32)
set(_arch_id "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}")
if (NOT _arch_id)
set(_arch_id "${CMAKE_C_COMPILER_ARCHITECTURE_ID}")
endif ()
if (_arch_id MATCHES "^ARM64(EC)?$")
set(_arch "arm64")
elseif (_arch_id STREQUAL "x64")
set(_arch "amd64")
elseif (_arch_id STREQUAL "X86")
set(_arch "x86")
endif ()
endif ()
string(TOLOWER "${CMAKE_SYSTEM_NAME}" _platform)
if (_platform STREQUAL "darwin")
set(_platform "macos")
endif ()
string(TOLOWER "${_arch}" _arch)
if (_arch MATCHES "^(i[3-6]86|x86)$")
set(_arch "x86")
endif ()
if (WIN32)
set(_ext ".dll")
else ()
set(_ext ".so")
endif ()
set(${out_platform_var} "${_platform}-${_arch}" PARENT_SCOPE)
set(${out_name_var} "mod${_ext}" PARENT_SCOPE)
endfunction()
# For out-of-tree builds without a game binary: download the version-independent link stub
# (generated by symgen) for the target platform.
function(_mod_download_link_stub out_var)
_mod_lib_info(_platform _lib_name)
if (WIN32)
set(_asset "${_platform}.lib")
elseif (ANDROID)
set(_asset "stub-${_platform}.so")
else ()
set(_asset "stub-${_platform}")
endif ()
set(_stub "${CMAKE_BINARY_DIR}/dusklight-sdk-stubs/${_asset}")
if (NOT EXISTS "${_stub}")
message(STATUS "Mod SDK: downloading link stub ${_asset}")
file(DOWNLOAD "${DUSKLIGHT_SDK_STUB_URL}/${_asset}" "${_stub}.tmp" STATUS _status)
list(GET _status 0 _code)
if (NOT _code EQUAL 0)
list(GET _status 1 _error)
file(REMOVE "${_stub}.tmp")
message(FATAL_ERROR
"Mod SDK: failed to download ${DUSKLIGHT_SDK_STUB_URL}/${_asset}: ${_error}\n"
"Set DUSK_GAME_EXE to a game binary or link stub to skip the download.")
endif ()
file(RENAME "${_stub}.tmp" "${_stub}")
endif ()
set(${out_var} "${_stub}" PARENT_SCOPE)
endfunction()
function(_mod_resolve_source_path out_var path)
if (IS_ABSOLUTE "${path}")
set(_path "${path}")
else ()
set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${path}")
endif ()
set(${out_var} "${_path}" PARENT_SCOPE)
endfunction()
function(_mod_collect_assets out_var dir)
if (NOT IS_DIRECTORY "${dir}")
message(FATAL_ERROR "add_mod: asset directory does not exist: ${dir}")
endif ()
file(GLOB_RECURSE _files CONFIGURE_DEPENDS LIST_DIRECTORIES false "${dir}/*")
# Dotfiles (.gitkeep, .DS_Store, ...) are not packaged; see CopyModAssets.cmake.
set(_assets "")
foreach (_file IN LISTS _files)
file(RELATIVE_PATH _rel "${dir}" "${_file}")
if (NOT _rel MATCHES "(^|/)\\.")
list(APPEND _assets "${_file}")
endif ()
endforeach ()
set(${out_var} ${_assets} PARENT_SCOPE)
endfunction()
function(_mod_add_webgpu_headers target_name)
if (NOT TARGET dawn::dawncpp_headers AND NOT TARGET dawn::webgpu_dawn)
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../extern/aurora/cmake/AuroraDependencyVersions.cmake")
set(AURORA_DAWN_PROVIDER "package" CACHE STRING "How to provide Dawn for the mod SDK")
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../extern/aurora/cmake/AuroraDawnProvider.cmake")
endif ()
if (TARGET dawn::dawncpp_headers)
target_link_libraries(${target_name} PRIVATE dawn::dawncpp_headers)
elseif (TARGET dawn::webgpu_dawn)
target_link_libraries(${target_name} PRIVATE dawn::webgpu_dawn)
else ()
message(FATAL_ERROR "add_mod: FEATURES webgpu could not provide WebGPU headers")
endif ()
endfunction()
function(_mod_add_fmt target_name)
if (NOT TARGET fmt::fmt-header-only)
find_package(fmt 11 CONFIG QUIET GLOBAL)
endif ()
if (NOT TARGET fmt::fmt-header-only)
include(FetchContent)
message(STATUS "Mod SDK: fetching fmt")
# Keep the fallback version in sync with extern/aurora/extern/CMakeLists.txt.
FetchContent_Declare(fmt
URL https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz
URL_HASH SHA256=ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea
DOWNLOAD_EXTRACT_TIMESTAMP FALSE
EXCLUDE_FROM_ALL)
FetchContent_MakeAvailable(fmt)
endif ()
if (NOT TARGET fmt::fmt-header-only)
message(FATAL_ERROR "add_mod: FEATURES fmt could not provide fmt::fmt-header-only")
endif ()
target_link_libraries(${target_name} PRIVATE fmt::fmt-header-only)
endfunction()
function(add_mod target_name)
cmake_parse_arguments(ARG "BUNDLE" "MOD_JSON;RES_DIR;OVERLAY_DIR;TEXTURES_DIR;OUTPUT_DIR"
"SOURCES;RUNTIME_LIBRARIES;FEATURES" ${ARGN})
if (ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "add_mod: unknown arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif ()
if (NOT ARG_MOD_JSON)
message(FATAL_ERROR "add_mod: MOD_JSON is required")
endif ()
_mod_resolve_source_path(_mod_json "${ARG_MOD_JSON}")
if (NOT EXISTS "${_mod_json}")
message(FATAL_ERROR "add_mod: MOD_JSON does not exist: ${_mod_json}")
endif ()
set(_supported_features fmt game webgpu)
set(_features "")
foreach (_feature IN LISTS ARG_FEATURES)
list(FIND _supported_features "${_feature}" _feature_index)
if (_feature_index EQUAL -1)
list(JOIN _supported_features ", " _supported_features_text)
message(FATAL_ERROR
"add_mod: unknown feature '${_feature}' (supported: ${_supported_features_text})")
endif ()
list(FIND _features "${_feature}" _duplicate_index)
if (NOT _duplicate_index EQUAL -1)
message(FATAL_ERROR "add_mod: duplicate feature '${_feature}'")
endif ()
list(APPEND _features "${_feature}")
endforeach ()
if (_features AND NOT ARG_SOURCES)
message(FATAL_ERROR "add_mod: FEATURES requires SOURCES")
endif ()
set(_has_lib FALSE)
set(_needs_host_link FALSE)
set(_lib_platform "")
set(_lib_name "")
if (ARG_SOURCES)
set(_has_lib TRUE)
add_library(${target_name} MODULE ${ARG_SOURCES})
_mod_lib_info(_lib_platform _lib_name)
set_target_properties(${target_name} PROPERTIES
PREFIX ""
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
WINDOWS_EXPORT_ALL_SYMBOLS OFF)
target_compile_features(${target_name} PRIVATE cxx_std_20)
target_link_libraries(${target_name} PRIVATE dusklight_mod_api)
foreach (_feature IN LISTS _features)
target_link_libraries(${target_name} PRIVATE dusklight_mod_feature_${_feature})
if (_feature STREQUAL "webgpu")
_mod_add_webgpu_headers(${target_name})
endif ()
if (_feature STREQUAL "fmt")
_mod_add_fmt(${target_name})
endif ()
if (_feature STREQUAL "game" OR _feature STREQUAL "webgpu")
set(_needs_host_link TRUE)
endif ()
endforeach ()
if (NOT TARGET dusklight)
# Apply global compile options for out-of-tree mod builds
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
target_compile_options(${target_name} PRIVATE
-Wno-multichar -Wno-trigraphs -Wno-deprecated-declarations)
elseif (APPLE)
target_compile_options(${target_name} PRIVATE
-Wno-declaration-after-statement -Wno-non-pod-varargs)
elseif (MSVC)
target_compile_options(${target_name} PRIVATE
"$<$<COMPILE_LANGUAGE:C,CXX>:/bigobj>"
"$<$<COMPILE_LANGUAGE:C,CXX>:/utf-8>")
endif ()
# Use signed char on ARM to match the original game (and x86)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _mod_arch)
if (_mod_arch MATCHES "^(arm|aarch64)" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
target_compile_options(${target_name} PRIVATE -fsigned-char)
endif ()
endif ()
if (APPLE)
if (_needs_host_link)
if (TARGET dusklight)
set(_game_exe "$<TARGET_FILE:dusklight>")
add_dependencies(${target_name} dusklight)
elseif (DUSK_GAME_EXE)
_mod_resolve_source_path(_game_exe "${DUSK_GAME_EXE}")
else ()
_mod_download_link_stub(_game_exe)
endif ()
target_link_options(${target_name} PRIVATE
-Xlinker -bundle_loader -Xlinker "${_game_exe}")
set_property(TARGET ${target_name} APPEND PROPERTY LINK_DEPENDS "${_game_exe}")
endif ()
set_target_properties(${target_name} PROPERTIES
BUILD_RPATH "@loader_path"
INSTALL_RPATH "@loader_path")
elseif (ANDROID)
if (_needs_host_link)
if (TARGET dusklight)
target_link_libraries(${target_name} PRIVATE dusklight)
else ()
if (DUSK_GAME_EXE)
_mod_resolve_source_path(_game_lib "${DUSK_GAME_EXE}")
else ()
_mod_download_link_stub(_game_lib)
endif ()
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
endif ()
endif ()
set_target_properties(${target_name} PROPERTIES
BUILD_RPATH "$ORIGIN"
INSTALL_RPATH "$ORIGIN")
elseif (UNIX)
if (_needs_host_link)
target_link_options(${target_name} PRIVATE -Wl,--allow-shlib-undefined)
endif ()
set_target_properties(${target_name} PROPERTIES
BUILD_RPATH "$ORIGIN"
INSTALL_RPATH "$ORIGIN")
elseif (WIN32)
# Mods link against the game's import library (sdk/windows-<arch>.lib, generated by
# setup_windows_exports); cl and clang-cl both consume it in MSVC mode. Function
# calls resolve through import thunks; game data is reachable only through
# __declspec(dllimport), i.e. DUSK_GAME_DATA-annotated declarations.
if (_needs_host_link)
if (TARGET dusklight)
if (NOT DUSK_GAME_IMPLIB)
message(FATAL_ERROR
"add_mod: DUSK_GAME_IMPLIB is not set (see setup_windows_exports)")
endif ()
set(_game_lib "${DUSK_GAME_IMPLIB}")
elseif (DUSK_GAME_EXE)
_mod_resolve_source_path(_game_lib "${DUSK_GAME_EXE}")
if (NOT _game_lib MATCHES "\\.lib$")
message(FATAL_ERROR
"add_mod: DUSK_GAME_EXE must be an import library on Windows "
"(sdk/windows-<arch>.lib)")
endif ()
else ()
_mod_download_link_stub(_game_lib)
endif ()
target_link_libraries(${target_name} PRIVATE "${_game_lib}")
endif ()
set_target_properties(${target_name} PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")
target_compile_definitions(${target_name} PRIVATE _ITERATOR_DEBUG_LEVEL=0)
endif ()
endif ()
if (ARG_RUNTIME_LIBRARIES AND NOT _has_lib)
message(FATAL_ERROR "add_mod: RUNTIME_LIBRARIES requires SOURCES")
endif ()
set(_output_dir "${DUSK_MODS_OUTPUT_DIR}")
if (ARG_OUTPUT_DIR)
set(_output_dir "${ARG_OUTPUT_DIR}")
endif ()
set(_stage "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_stage")
set(_out "${_output_dir}/${target_name}.dusk")
set(_zip_args mod.json)
set(_package_deps "${_mod_json}")
set(_package_inputs "${_mod_json}")
set(_extra_cmds "")
set(_lib_copy_cmd "")
set(_target_depend "")
if (_has_lib)
list(APPEND _zip_args lib)
set(_lib_copy_cmd
COMMAND ${CMAKE_COMMAND} -E make_directory "${_stage}/lib/${_lib_platform}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:${target_name}>" "${_stage}/lib/${_lib_platform}/${_lib_name}")
set(_target_depend ${target_name})
endif ()
string(TOLOWER "${_lib_name}" _lib_name_key)
set(_runtime_lib_name_keys "${_lib_name_key}")
foreach (_runtime_lib IN LISTS ARG_RUNTIME_LIBRARIES)
_mod_resolve_source_path(_runtime_lib_path "${_runtime_lib}")
if (NOT EXISTS "${_runtime_lib_path}" OR IS_DIRECTORY "${_runtime_lib_path}")
message(FATAL_ERROR "add_mod: runtime library does not exist or is not a file: ${_runtime_lib_path}")
endif ()
get_filename_component(_runtime_lib_name "${_runtime_lib_path}" NAME)
string(TOLOWER "${_runtime_lib_name}" _runtime_lib_name_key)
list(FIND _runtime_lib_name_keys "${_runtime_lib_name_key}" _runtime_lib_name_index)
if (NOT _runtime_lib_name_index EQUAL -1)
message(FATAL_ERROR "add_mod: duplicate runtime library filename: ${_runtime_lib_name}")
endif ()
list(APPEND _runtime_lib_name_keys "${_runtime_lib_name_key}")
list(APPEND _package_deps "${_runtime_lib_path}")
list(APPEND _package_inputs "${_runtime_lib_path}")
list(APPEND _lib_copy_cmd COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_runtime_lib_path}" "${_stage}/lib/${_lib_platform}/${_runtime_lib_name}")
endforeach ()
if (ARG_RES_DIR)
_mod_resolve_source_path(_res_dir "${ARG_RES_DIR}")
_mod_collect_assets(_res_deps "${_res_dir}")
list(APPEND _package_deps ${_res_deps})
list(APPEND _package_inputs "${_res_dir}" ${_res_deps})
list(APPEND _zip_args res)
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_res_dir}"
"-DDST=${_stage}/res" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
endif ()
if (ARG_OVERLAY_DIR)
_mod_resolve_source_path(_overlay_dir "${ARG_OVERLAY_DIR}")
_mod_collect_assets(_overlay_deps "${_overlay_dir}")
list(APPEND _package_deps ${_overlay_deps})
list(APPEND _package_inputs "${_overlay_dir}" ${_overlay_deps})
list(APPEND _zip_args overlay)
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_overlay_dir}"
"-DDST=${_stage}/overlay" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
endif ()
if (ARG_TEXTURES_DIR)
_mod_resolve_source_path(_textures_dir "${ARG_TEXTURES_DIR}")
_mod_collect_assets(_textures_deps "${_textures_dir}")
list(APPEND _package_deps ${_textures_deps})
list(APPEND _package_inputs "${_textures_dir}" ${_textures_deps})
list(APPEND _zip_args textures)
list(APPEND _extra_cmds COMMAND ${CMAKE_COMMAND} "-DSRC=${_textures_dir}"
"-DDST=${_stage}/textures" -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyModAssets.cmake")
endif ()
set(_bundle_cmds "")
if (ARG_BUNDLE AND TARGET dusklight)
file(READ "${_mod_json}" _mod_json_text)
string(JSON _mod_id GET "${_mod_json_text}" id)
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_TARGETS "${target_name}")
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_IDS "${_mod_id}")
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_STAGES "${_stage}")
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_PACKAGES "${_out}")
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_LIB_PLATFORMS "${_lib_platform}")
set_property(GLOBAL APPEND PROPERTY DUSK_BUNDLED_MOD_LIB_NAMES "${_lib_name}")
set(_bundle_cmds
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/bundled_mods"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_out}" "${CMAKE_BINARY_DIR}/bundled_mods/${target_name}.dusk")
endif ()
set(_package_target "${target_name}_package")
set(_package_inputs_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_package_inputs.txt")
list(SORT _package_inputs)
set(_package_inputs_text "")
foreach (_package_input IN LISTS _package_inputs)
string(APPEND _package_inputs_text "${_package_input}\n")
endforeach ()
file(GENERATE OUTPUT "${_package_inputs_file}" CONTENT "${_package_inputs_text}")
add_custom_command(OUTPUT "${_out}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${_stage}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${_stage}" "${_output_dir}"
${_lib_copy_cmd}
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_mod_json}" "${_stage}/mod.json"
${_extra_cmds}
COMMAND ${CMAKE_COMMAND} -E chdir "${_stage}" ${CMAKE_COMMAND} -E tar cvf "${_out}" --format=zip ${_zip_args}
${_bundle_cmds}
DEPENDS ${_target_depend} ${_package_deps} "${_package_inputs_file}"
COMMENT "Packaging ${target_name} -> ${_out}"
COMMAND_EXPAND_LISTS
VERBATIM
)
add_custom_target(${_package_target} ALL DEPENDS "${_out}")
if (TARGET dusklight_mods)
add_dependencies(dusklight_mods ${_package_target})
endif ()
endfunction()
# Install rules for BUNDLE mods.
# - Windows: the .dusk archives into <install>/mods (the loader extracts native libs to the
# user cache).
# - Linux: pre-extracted stage dirs into <install>/mods so native libs dlopen in place from
# read-only installs.
# - macOS: pre-extracted stage dirs into the installed app's Contents/Resources/mods, native
# libs ad-hoc signed in place, then the whole bundle re-signed.
# - iOS/tvOS: assets into <app>/mods/<id>, the mod library into Frameworks/<id>.so,
# and runtime libraries alongside it.
# - Android: nothing here; gradle packs ${CMAKE_BINARY_DIR}/bundled_mods into APK assets.
function(install_bundled_mods)
get_property(_targets GLOBAL PROPERTY DUSK_BUNDLED_MOD_TARGETS)
if (NOT _targets OR ANDROID)
return ()
endif ()
get_property(_ids GLOBAL PROPERTY DUSK_BUNDLED_MOD_IDS)
get_property(_stages GLOBAL PROPERTY DUSK_BUNDLED_MOD_STAGES)
get_property(_lib_platforms GLOBAL PROPERTY DUSK_BUNDLED_MOD_LIB_PLATFORMS)
get_property(_lib_names GLOBAL PROPERTY DUSK_BUNDLED_MOD_LIB_NAMES)
list(LENGTH _targets _count)
math(EXPR _last "${_count} - 1")
if (APPLE)
get_target_property(_app_name dusklight OUTPUT_NAME)
if (NOT _app_name)
set(_app_name dusklight)
endif ()
set(_bundle_dir "${CMAKE_INSTALL_PREFIX}/${_app_name}.app")
if (IOS OR TVOS)
foreach (_i RANGE ${_last})
list(GET _targets ${_i} _target)
list(GET _ids ${_i} _id)
list(GET _stages ${_i} _stage)
list(GET _lib_platforms ${_i} _lib_platform)
list(GET _lib_names ${_i} _lib_name)
install(DIRECTORY "${_stage}/" DESTINATION "${_bundle_dir}/mods/${_id}"
PATTERN "lib" EXCLUDE)
install(PROGRAMS "$<TARGET_FILE:${_target}>"
DESTINATION "${_bundle_dir}/Frameworks" RENAME "${_id}.so")
install(DIRECTORY "${_stage}/lib/${_lib_platform}/"
DESTINATION "${_bundle_dir}/Frameworks"
PATTERN "${_lib_name}" EXCLUDE)
endforeach ()
if (DUSK_HAS_PREPATCH)
set(_prepatch_executable "${_bundle_dir}/Dusklight")
set(_prepatch_mod_args "")
foreach (_id IN LISTS _ids)
string(APPEND _prepatch_mod_args
" \"${_bundle_dir}/Frameworks/${_id}.so\"")
endforeach ()
install(CODE "
execute_process(
COMMAND \"${SYMGEN_EXE}\" prepatch
--binary \"${_prepatch_executable}\"
--report \"${CMAKE_BINARY_DIR}/prepatch-report-$<CONFIG>.json\"
${_prepatch_mod_args}
COMMAND_ERROR_IS_FATAL ANY)")
endif ()
else ()
foreach (_i RANGE ${_last})
list(GET _ids ${_i} _id)
list(GET _stages ${_i} _stage)
list(GET _lib_platforms ${_i} _lib_platform)
list(GET _lib_names ${_i} _lib_name)
install(DIRECTORY "${_stage}/" DESTINATION "${_bundle_dir}/Contents/Resources/mods/${_id}")
install(CODE "
file(GLOB _mod_libs \"${_bundle_dir}/Contents/Resources/mods/${_id}/lib/${_lib_platform}/*\")
foreach (_mod_lib IN LISTS _mod_libs)
if (NOT IS_DIRECTORY \"\${_mod_lib}\")
execute_process(COMMAND /usr/bin/codesign --force --sign - \"\${_mod_lib}\" COMMAND_ERROR_IS_FATAL ANY)
endif ()
endforeach ()")
endforeach ()
if (DUSK_HAS_PREPATCH)
set(_prepatch_executable "${_bundle_dir}/Contents/MacOS/Dusklight")
set(_prepatch_mod_args "")
foreach (_i RANGE ${_last})
list(GET _ids ${_i} _id)
list(GET _lib_platforms ${_i} _lib_platform)
list(GET _lib_names ${_i} _lib_name)
string(APPEND _prepatch_mod_args
" \"${_bundle_dir}/Contents/Resources/mods/${_id}/lib/${_lib_platform}/${_lib_name}\"")
endforeach ()
install(CODE "
execute_process(
COMMAND \"${SYMGEN_EXE}\" prepatch
--binary \"${_prepatch_executable}\"
--report \"${CMAKE_BINARY_DIR}/prepatch-report-$<CONFIG>.json\"
${_prepatch_mod_args}
COMMAND_ERROR_IS_FATAL ANY)")
endif ()
if (TARGET crashpad_handler)
install(CODE "execute_process(COMMAND /usr/bin/codesign --force --sign - \"${_bundle_dir}/Contents/MacOS/$<TARGET_FILE_NAME:crashpad_handler>\" COMMAND_ERROR_IS_FATAL ANY)")
endif ()
install(CODE "execute_process(COMMAND /usr/bin/codesign --force --sign - --entitlements \"${DUSK_ENTITLEMENTS}\" \"${_bundle_dir}\" COMMAND_ERROR_IS_FATAL ANY)")
endif ()
return ()
endif ()
if (DUSK_PACKAGE_INSTALL)
set(_mods_dest "${CMAKE_INSTALL_DATAROOTDIR}/dusklight/mods")
else ()
set(_mods_dest "${CMAKE_INSTALL_PREFIX}/mods")
endif ()
if (WIN32)
foreach (_target IN LISTS _targets)
install(FILES "${CMAKE_BINARY_DIR}/bundled_mods/${_target}.dusk" DESTINATION "${_mods_dest}")
endforeach ()
else ()
foreach (_i RANGE ${_last})
list(GET _ids ${_i} _id)
list(GET _stages ${_i} _stage)
install(DIRECTORY "${_stage}/" DESTINATION "${_mods_dest}/${_id}")
endforeach ()
endif ()
endfunction()
+13
View File
@@ -0,0 +1,13 @@
# Patches capstone's CMakeLists.txt for compatibility with CMake >= 4.0:
# - Bumps cmake_minimum_required to 3.10 (CMake >= 4.0 dropped < 3.5 support; < 3.10 warns)
# - Removes cmake_policy(SET CMP0048 OLD) (rejected by CMake >= 3.27)
file(READ "${DIR}/CMakeLists.txt" _content)
string(REGEX REPLACE
"cmake_minimum_required[ \t]*\\([ \t]*VERSION[ \t]+[0-9]+\\.[0-9]+(\\.[0-9]+)?[ \t]*\\)"
"cmake_minimum_required(VERSION 3.10)"
_content "${_content}")
string(REGEX REPLACE
"cmake_policy[ \t]*\\([ \t]*SET[ \t]+CMP0048[ \t]+OLD[ \t]*\\)"
"# cmake_policy(SET CMP0048 OLD)"
_content "${_content}")
file(WRITE "${DIR}/CMakeLists.txt" "${_content}")
+60
View File
@@ -0,0 +1,60 @@
file(READ "${SOURCE_DIR}/cmake/capstone.cmake.in" _content)
# Insert PATCH_COMMAND before CONFIGURE_COMMAND in the ExternalProject_Add.
# Bracket args prevent cmake from substituting ${...} while writing this file.
string(REPLACE
" CONFIGURE_COMMAND \"\""
[=[ PATCH_COMMAND "${CMAKE_COMMAND}" -DDIR=${CMAKE_CURRENT_BINARY_DIR}/capstone-src -P "${CAPSTONE_FIX_SCRIPT}"
CONFIGURE_COMMAND ""]=]
_content "${_content}")
file(WRITE "${SOURCE_DIR}/cmake/capstone.cmake.in" "${_content}")
file(READ "${SOURCE_DIR}/src/funchook_unix.c" _unix_content)
# macOS rejects the POSIX mprotect RWX/RW transition for executable image pages on arm64.
# Use Mach VM_PROT_COPY for the short patch window, then restore RX permissions.
if (NOT _unix_content MATCHES "VM_PROT_READ \\| VM_PROT_WRITE \\| VM_PROT_COPY")
string(REPLACE
[=[ rv = mprotect(mstate->addr, mstate->size, prot);]=]
[=[#ifdef __APPLE__
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)mstate->addr,
(vm_size_t)mstate->size, FALSE,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
if (kr == KERN_SUCCESS) {
funchook_log(funchook, " unprotect memory %p (size=%"PRIuPTR", prot=read,write,copy) <- %p (size=%"PRIuPTR")\n",
mstate->addr, mstate->size, start, len);
return 0;
}
funchook_set_error_message(funchook, "Failed to unprotect memory %p (size=%"PRIuPTR", prot=read,write,copy) <- %p (size=%"PRIuPTR", error=%s)",
mstate->addr, mstate->size, start, len,
mach_error_string(kr));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
#endif
rv = mprotect(mstate->addr, mstate->size, prot);]=]
_unix_content "${_unix_content}")
string(REPLACE
[=[ char errbuf[128];
int rv = mprotect(mstate->addr, mstate->size, PROT_READ | PROT_EXEC);]=]
[=[ char errbuf[128];
#ifdef __APPLE__
kern_return_t kr = vm_protect(mach_task_self(), (vm_address_t)mstate->addr,
(vm_size_t)mstate->size, FALSE,
VM_PROT_READ | VM_PROT_EXECUTE);
if (kr == KERN_SUCCESS) {
funchook_log(funchook, " protect memory %p (size=%"PRIuPTR", prot=read,exec)\n",
mstate->addr, mstate->size);
return 0;
}
funchook_set_error_message(funchook, "Failed to protect memory %p (size=%"PRIuPTR", prot=read,exec, error=%s)",
mstate->addr, mstate->size,
mach_error_string(kr));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
#endif
int rv = mprotect(mstate->addr, mstate->size, PROT_READ | PROT_EXEC);]=]
_unix_content "${_unix_content}")
endif ()
file(WRITE "${SOURCE_DIR}/src/funchook_unix.c" "${_unix_content}")
+153
View File
@@ -0,0 +1,153 @@
include_guard(GLOBAL)
get_filename_component(_SYMBOL_MANIFEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
set(_SYMGEN_VERSION "1.3.2")
set(_SYMGEN_RELEASE_BASE_URL "https://github.com/encounter/symgen/releases/download/v${_SYMGEN_VERSION}")
set(SYMGEN_PATH "" CACHE FILEPATH "Path to a symgen executable; empty downloads the pinned release")
mark_as_advanced(SYMGEN_PATH)
function(symgen_host_asset out_name out_hash)
string(TOLOWER "${CMAKE_HOST_SYSTEM_PROCESSOR}" _host_processor)
set(_asset "")
set(_asset_hash "")
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
if (_host_processor MATCHES "^(arm64|aarch64)$")
set(_asset "symgen-macos-arm64")
set(_asset_hash "SHA256=0344838d1674df09c17c3eeddcf26eb89c333fdb85dfd78f68adc436070eccbe")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-macos-x86_64")
set(_asset_hash "SHA256=ae0674f4a1e9d0dedfa02d35939ac28cdd229276b331c1f507df5df809cbec7e")
endif ()
elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
if (_host_processor MATCHES "^(aarch64|arm64)$")
set(_asset "symgen-linux-aarch64")
set(_asset_hash "SHA256=af766de2bfaeb0a06f6d7bc17bb2510b4a9c40f44a56e49bc4a4b798a6223042")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-linux-x86_64")
set(_asset_hash "SHA256=ebd62fb9623acc942b6295609e2306f85a73043b0a8a117f2072b761dd08e68f")
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
set(_asset "symgen-linux-i686")
set(_asset_hash "SHA256=07780a4513fd29726578efc4ff2d88736b22f407ed821b32a68e35ff1e9af5f4")
endif ()
elseif (CMAKE_HOST_WIN32)
if (_host_processor MATCHES "^(arm64|aarch64)$")
set(_asset "symgen-windows-arm64.exe")
set(_asset_hash "SHA256=5bb22b4a4a9b5ad45646af411bfb09b8321a732ff3a077eb4c9de1feaef27d2b")
elseif (_host_processor MATCHES "^(x86_64|amd64)$")
set(_asset "symgen-windows-x86_64.exe")
set(_asset_hash "SHA256=1d1ac087f991a96932d108969e15998becfec643e88f3e7d182ca97ebfc6a46f")
elseif (_host_processor MATCHES "^(i[3-6]86|x86)$")
set(_asset "symgen-windows-x86.exe")
set(_asset_hash "SHA256=c113f4cd05f813efe2b1878dbfcf44d6302aee3974271736e0036430b0cced78")
endif ()
endif ()
set(${out_name} "${_asset}" PARENT_SCOPE)
set(${out_hash} "${_asset_hash}" PARENT_SCOPE)
endfunction()
function(ensure_symgen required)
if (TARGET symgen)
return()
endif ()
if (SYMGEN_PATH)
get_filename_component(_symgen "${SYMGEN_PATH}" ABSOLUTE)
if (NOT EXISTS "${_symgen}")
if (required)
message(FATAL_ERROR "symgen: SYMGEN_PATH does not exist: ${_symgen}")
endif ()
message(STATUS "symgen: SYMGEN_PATH does not exist, symbol manifest generation "
"skipped (by-name hook resolution will be unavailable)")
return()
endif ()
else ()
symgen_host_asset(_asset _asset_hash)
if (_asset STREQUAL "")
if (required)
message(FATAL_ERROR "symgen: no prebuilt binary for host "
"${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR} "
"(configure with -DDUSK_ENABLE_CODE_MODS=OFF)")
endif ()
message(STATUS "symgen: no prebuilt binary for host "
"${CMAKE_HOST_SYSTEM_NAME}/${CMAKE_HOST_SYSTEM_PROCESSOR}; "
"symbol manifest generation skipped (by-name hook resolution will be unavailable)")
return()
endif ()
set(_symgen_dir "${CMAKE_BINARY_DIR}/_deps/symgen")
set(_symgen "${_symgen_dir}/${_asset}")
set(_url "${_SYMGEN_RELEASE_BASE_URL}/${_asset}")
message(STATUS "dusklight: Fetching symgen ${_SYMGEN_VERSION} (${_asset})")
file(MAKE_DIRECTORY "${_symgen_dir}")
file(DOWNLOAD "${_url}" "${_symgen}"
TLS_VERIFY ON
STATUS _download_status
SHOW_PROGRESS
EXPECTED_HASH "${_asset_hash}")
list(GET _download_status 0 _download_code)
if (NOT _download_code EQUAL 0)
list(GET _download_status 1 _download_message)
file(REMOVE "${_symgen}")
if (required)
message(FATAL_ERROR "symgen: failed to download ${_url}: ${_download_message}")
endif ()
message(STATUS "symgen: failed to download ${_url}: ${_download_message}; "
"symbol manifest generation skipped (by-name hook resolution will be unavailable)")
return()
endif ()
if (NOT CMAKE_HOST_WIN32)
file(CHMOD "${_symgen}" PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
endif ()
endif ()
add_custom_target(symgen DEPENDS "${_symgen}")
set(SYMGEN_EXE "${_symgen}" CACHE INTERNAL "symgen executable" FORCE)
endfunction()
function(setup_symbol_manifest target)
ensure_symgen(TRUE)
if (NOT TARGET symgen)
return()
endif ()
add_dependencies(${target} symgen)
# Reserve an ELF program-header entry when the linker supports it (mold).
# symgen can replace the PT_NULL entry without relocating the table, keeping later post-link tools safe.
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
include(CheckLinkerFlag)
check_linker_flag(CXX "LINKER:--spare-program-headers=1" _linker_supports_spare_program_headers)
if (_linker_supports_spare_program_headers)
target_link_options(${target} PRIVATE "LINKER:--spare-program-headers=1")
endif ()
endif ()
if (WIN32)
set(_input --pdb "$<TARGET_PDB_FILE:${target}>")
else ()
set(_input --binary "$<TARGET_FILE:${target}>")
endif ()
if (APPLE)
# Room for the symbol manifest and several prepatch arenas.
target_link_options(${target} PRIVATE "LINKER:-headerpad,0x1000")
# ld64 may update an existing output, which breaks our symdb insertion. Remove it first.
add_custom_command(TARGET ${target} PRE_LINK
COMMAND "${CMAKE_COMMAND}" -E rm -f "$<TARGET_FILE:${target}>"
VERBATIM)
endif ()
# The manifest is embedded into the image as a new section, located at runtime through
# the descriptor manifest.cpp reserves. On Apple platforms this command must stay
# attached before the ad-hoc codesign POST_BUILD command: the patch removes any existing
# signature.
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${SYMGEN_EXE}" manifest ${_input} --embed "$<TARGET_FILE:${target}>"
COMMENT "Embedding symbol manifest"
VERBATIM)
endfunction()
+93
View File
@@ -0,0 +1,93 @@
include_guard(GLOBAL)
get_filename_component(_DUSK_WINDOWS_EXPORTS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
# Windows mod linking: generate the curated export surface for the game executable and the
# import library mods link against. symgen scans the built objects, filters by source, and
# writes a .def used by the main link and import library generation.
function(setup_windows_exports target)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _implib_arch)
if (_implib_arch STREQUAL "arm64")
set(_dlltool_machine "arm64")
set(_lib_machine "arm64")
elseif (_implib_arch MATCHES "^(amd64|x86_64)$")
set(_dlltool_machine "i386:x86-64")
set(_lib_machine "x64")
else ()
message(FATAL_ERROR
"dusklight: no Windows mod linking support for ${CMAKE_SYSTEM_PROCESSOR}")
endif ()
include("${_DUSK_WINDOWS_EXPORTS_CMAKE_DIR}/SymbolManifest.cmake")
ensure_symgen(TRUE)
set(_symgen "${SYMGEN_EXE}")
add_dependencies(${target} symgen)
set(_config_subdir "")
if (CMAKE_CONFIGURATION_TYPES)
set(_config_subdir "$<CONFIG>/")
endif ()
set(_rsp_lines "$<TARGET_OBJECTS:${target}>")
foreach (_lib IN LISTS JSYSTEM_LIBRARIES)
list(APPEND _rsp_lines "$<TARGET_FILE:${_lib}>")
endforeach ()
list(JOIN _rsp_lines "\n" _rsp_content)
set(_rsp "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports_input.rsp")
file(GENERATE OUTPUT "${_rsp}" CONTENT "${_rsp_content}")
set(_sdk_args)
foreach (_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
aurora_os aurora_pad aurora_si aurora_vi)
if (TARGET ${_lib})
list(APPEND _sdk_args --sdk-lib "$<TARGET_FILE:${_lib}>")
endif ()
endforeach ()
set(_forward_args)
if (TARGET dawn::webgpu_dawn)
get_target_property(_dawn_type dawn::webgpu_dawn TYPE)
if (_dawn_type STREQUAL "SHARED_LIBRARY")
list(APPEND _forward_args
--forward-dll "$<TARGET_FILE:dawn::webgpu_dawn>"
--forward-sym-prefix wgpu)
endif ()
endif ()
# Generate curated exports list from the main binary
set(_def "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_exports.def")
add_custom_command(TARGET ${target} PRE_LINK
COMMAND "${_symgen}" def
"@${_rsp}"
--out "${_def}"
--exclude cmake_pch
--exclude miniz
--exclude asan_options
--exclude src/dusk
--max-exports 58000
${_sdk_args}
${_forward_args}
COMMENT "Generating dusklight exports"
VERBATIM)
target_link_options(${target} PRIVATE "/DEF:${_def}")
# Generate import library for mods to link against.
set(_implib "${CMAKE_BINARY_DIR}/${_config_subdir}dusklight_imports.lib")
get_filename_component(_compiler_dir "${CMAKE_CXX_COMPILER}" DIRECTORY)
find_program(DUSK_LLVM_DLLTOOL llvm-dlltool HINTS "${_compiler_dir}")
if (DUSK_LLVM_DLLTOOL)
set(_implib_cmd "${DUSK_LLVM_DLLTOOL}" -d "${_def}" -D dusklight.exe
-m "${_dlltool_machine}" -l "${_implib}")
else ()
set(_implib_cmd "${CMAKE_AR}" /nologo "/def:${_def}" "/machine:${_lib_machine}"
/name:dusklight.exe "/out:${_implib}")
endif ()
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${_implib_cmd}
BYPRODUCTS "${_implib}"
COMMENT "Generating dusklight import library"
VERBATIM)
set(DUSK_GAME_IMPLIB "${_implib}" CACHE INTERNAL "Import library for Windows mod linking")
install(FILES "${_implib}" DESTINATION sdk RENAME "windows-${_implib_arch}.lib")
endfunction()