Added easy integration for CMake

This commit is contained in:
alejo committed 2026-10-18 00:08:45 -03:00
1 parent ab21b3f30d
commit ec26261392
10 files changed
+262 -1

No files matched your search

Regular → Executable
View File
File mode changed.
+12 -1
View File
@@ -1,4 +1,15 @@
# Hot Reload # Volkan
<div align="center">
<img src="https://github.com/AcruxDevelopment/Volkan/blob/main/res/icon.png" alt="Logo" width="auto" height="80">
# Volkan
**C++ Hot-Reload library**
</div>
<br>
A cross-platform, cross-compiler C/C++ library that patches a running A cross-platform, cross-compiler C/C++ library that patches a running
process's own code in place, so a new build of a function takes effect process's own code in place, so a new build of a function takes effect
+250
View File
@@ -0,0 +1,250 @@
# =============================================================================
# integration/HotReload.cmake
#
# Everything another CMake project needs to use this library, in one file,
# with no dependency on anything else under this repository's own cmake/
# directory (that's internal build machinery for *this* repo -- see
# CONTRIBUTING.md -- and pulling it into someone else's build would also
# pull in this project's own test/example discovery, output-directory
# layout, and Doxygen integration, none of which a consumer asked for).
# Reading this file top to bottom is reading the whole integration surface;
# there's nothing else to go find.
#
# What you get, after including this file:
# - A `hotreload_lib` target (alias: `HotReload::hotreload_lib`) --
# built from this repository's own source/HotReload/src, so there is
# exactly one implementation, never a second copy to drift out of sync.
# - hot_reload_enable_patchable_functions(<target>) and
# hot_reload_restrict_exports(<target>) -- identical in behavior to
# cmake/HotReload.cmake's own (see that file for the full reasoning
# behind each); duplicated here, not include()'d from there, so this
# one file has zero path dependency on the rest of this repo's layout.
# - hotreload_add_plugin(<target> SOURCES <files...> [RESTRICT_EXPORTS])
# -- the one-call version of "build a SHARED library correctly set up
# to be hot-reloaded", for when you don't need anything custom about
# the target. Skip it and call add_library() yourself, plus the two
# functions above, for anything it doesn't cover (EXTRA_SOURCES,
# target_compile_definitions, DEPENDS, ...).
#
# ---- Three ways to get this file into your own CMake project ----
#
# 1) Vendor a copy of this repository (git submodule, or just copy the
# tree) and include() it directly -- the simplest option, and the one
# this file is tuned for by default:
#
# include(path/to/hotreload/integration/HotReload.cmake)
# target_link_libraries(my_app PRIVATE HotReload::hotreload_lib)
# hotreload_add_plugin(my_plugin SOURCES src/my_plugin.cpp)
#
# With no other configuration, this file finds this repository's own
# source/HotReload next to itself (see HOTRELOAD_SOURCE_DIR below) --
# nothing else to set up.
#
# 2) FetchContent, if you'd rather not vendor a copy. Point SOURCE_SUBDIR
# at this directory (integration/) rather than the repository root --
# it has no CMakeLists.txt of its own, so FetchContent_MakeAvailable()
# populates the source but does NOT add_subdirectory() this
# repository's own top-level CMakeLists.txt (that's the documented
# behavior of SOURCE_SUBDIR pointing at a directory with no
# CMakeLists.txt, CMake 3.18+ -- not a trick or a workaround), so none
# of the internal build system this file exists to let you skip ever
# runs. Verified directly: configuring this way produces no warning,
# deprecated or otherwise, on CMake versions from 3.18 through 4.4.
#
# include(FetchContent)
# FetchContent_Declare(hotreload
# GIT_REPOSITORY <wherever you host this repository>
# GIT_TAG <a pinned tag or commit>
# SOURCE_SUBDIR integration)
# FetchContent_MakeAvailable(hotreload)
# include(${hotreload_SOURCE_DIR}/integration/HotReload.cmake)
# target_link_libraries(my_app PRIVATE HotReload::hotreload_lib)
#
# (Older CMake without SOURCE_SUBDIR support (pre-3.18): use
# FetchContent_Declare() + FetchContent_Populate(<name>) instead of
# MakeAvailable -- the same idea, fetch without add_subdirectory(),
# just via the older API for it.)
#
# 3) Point HOTRELOAD_SOURCE_DIR at source/HotReload yourself (any layout
# at all -- a sparse checkout, a copy with a different folder name,
# anything) and include() this file; see the variable below.
#
# ---- What this deliberately does NOT do ----
#
# No install()/export()/find_package() support -- this is a "build it as
# part of your own project" integration, not an installed system package.
# No BUILD_EXAMPLES/BUILD_TESTS, no Doxygen, no output-directory
# redirection, no warning-flag opinions forced on your own targets, and
# this file never calls project() or cmake_minimum_required() itself (see
# below) -- all deliberate: a consumer's build should only ever notice
# this file added what it explicitly asked for.
# =============================================================================
include_guard(GLOBAL)
# A version check, not a `cmake_minimum_required()` call: this file is
# include()'d into the CALLER's own scope (it's not its own subdirectory
# with a fresh policy scope), so calling cmake_minimum_required() here
# would silently change policy versions for the rest of *your* CMakeLists
# too. 3.16 is the floor this file's own features actually need:
# FetchContent (3.11+), file(GLOB CONFIGURE_DEPENDS) (3.12+),
# target_compile_features(... cxx_std_20) (3.12+), include_guard (3.10+).
if(CMAKE_VERSION VERSION_LESS "3.16")
message(FATAL_ERROR "integration/HotReload.cmake needs CMake 3.16 or newer (found ${CMAKE_VERSION}).")
endif()
# ---- Where source/HotReload actually lives ----
# Defaults to right next to this file, since that's where it is in this
# repository; override by setting this BEFORE including this file if
# you've laid your checkout out some other way.
if(NOT DEFINED HOTRELOAD_SOURCE_DIR)
get_filename_component(HOTRELOAD_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../source/HotReload" ABSOLUTE)
endif()
if(NOT EXISTS "${HOTRELOAD_SOURCE_DIR}/include/hot_reload/hot_reload.h")
message(FATAL_ERROR
"integration/HotReload.cmake: no hot_reload.h under "
"'${HOTRELOAD_SOURCE_DIR}/include/hot_reload/'. Set HOTRELOAD_SOURCE_DIR "
"to wherever source/HotReload actually is before including this file.")
endif()
# =============================================================================
# hotreload_lib -- the library itself.
#
# Mirrors add_lib_module(hotreload_lib TYPE STATIC)'s own behavior
# (cmake/BuildHelpers.cmake + source/HotReload/CMakeLists.txt) exactly,
# source-globbing included, so this file and the real project build
# identical code: src/*.cpp recursively under source/HotReload (including
# src/c-api), include/ as the one PUBLIC include directory, STATIC +
# position-independent so it's safe to end up inside a SHARED library or
# PIE executable later. cxx_std_20 is PRIVATE, not PUBLIC: that's what
# this library's own implementation needs internally, but hot_reload.h
# itself is plain C-compatible (valid C11 and valid C++14 through C++20+,
# confirmed directly by compiling the header alone under each), so this
# deliberately does not force C++20 on your own targets just for calling
# into this one.
# =============================================================================
if(NOT TARGET hotreload_lib)
file(GLOB_RECURSE _hotreload_integration_sources CONFIGURE_DEPENDS "${HOTRELOAD_SOURCE_DIR}/src/*.cpp")
file(GLOB_RECURSE _hotreload_integration_headers CONFIGURE_DEPENDS
"${HOTRELOAD_SOURCE_DIR}/include/*.h" "${HOTRELOAD_SOURCE_DIR}/include/*.hpp")
add_library(hotreload_lib STATIC ${_hotreload_integration_sources} ${_hotreload_integration_headers})
add_library(HotReload::hotreload_lib ALIAS hotreload_lib)
target_include_directories(hotreload_lib PUBLIC "${HOTRELOAD_SOURCE_DIR}/include")
target_compile_features(hotreload_lib PRIVATE cxx_std_20)
set_target_properties(hotreload_lib PROPERTIES
OUTPUT_NAME hotreload
POSITION_INDEPENDENT_CODE ON)
if(NOT WIN32)
# dlopen/dlclose/dlinfo (ModuleHandle.cpp). PUBLIC: for a STATIC
# library this needs to reach whatever executable ultimately
# links hotreload_lib, matching source/HotReload/CMakeLists.txt.
target_link_libraries(hotreload_lib PUBLIC ${CMAKE_DL_LIBS})
endif()
if(MSVC)
# UnDecorateSymbolName (Demangle.cpp) lives in dbghelp.lib, which
# MSVC does not link by default. Guarded on MSVC specifically
# (true for cl.exe and clang-cl, false for MinGW), matching
# source/HotReload/CMakeLists.txt exactly.
target_link_libraries(hotreload_lib PUBLIC dbghelp)
endif()
unset(_hotreload_integration_sources)
unset(_hotreload_integration_headers)
endif()
# =============================================================================
# hot_reload_enable_patchable_functions(<target>) /
# hot_reload_restrict_exports(<target>)
#
# Identical behavior to cmake/HotReload.cmake's versions of the same two
# functions -- see that file for the full design reasoning behind each
# (the 32-byte patchable-entry size, why /hotpatch is the primary safety
# mechanism on MSVC specifically, why restrict_exports can't and doesn't
# try to quiet std::string/std::vector noise). Guarded so this plays
# nicely if a consumer somehow also ends up with the real
# cmake/HotReload.cmake included in the same configure.
# =============================================================================
if(NOT COMMAND hot_reload_enable_patchable_functions)
function(hot_reload_enable_patchable_functions TARGET_NAME)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${TARGET_NAME} PRIVATE -fpatchable-function-entry=32,0)
elseif(MSVC)
target_compile_options(${TARGET_NAME} PRIVATE /hotpatch)
else()
message(WARNING
"hot_reload_enable_patchable_functions(${TARGET_NAME}): "
"unrecognized compiler '${CMAKE_CXX_COMPILER_ID}', no patchable-entry "
"flag applied. Hot-reloading ${TARGET_NAME} will still work for any "
"function whose compiled body happens to be large enough on its own; "
"see HOT_RELOAD_SYMBOL_SKIPPED_TOO_SMALL in hot_reload.h.")
endif()
endfunction()
endif()
if(NOT COMMAND hot_reload_restrict_exports)
function(hot_reload_restrict_exports TARGET_NAME)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${TARGET_NAME} PRIVATE -fvisibility=hidden -fvisibility-inlines-hidden)
elseif(MSVC)
# MSVC already exports nothing by default; simply not setting
# WINDOWS_EXPORT_ALL_SYMBOLS is this function's Windows
# equivalent. Nothing to apply here -- see
# hotreload_add_plugin()'s RESTRICT_EXPORTS branch below,
# which is what actually turns that property off.
endif()
endfunction()
endif()
# =============================================================================
# hotreload_add_plugin(<target> SOURCES <files...> [RESTRICT_EXPORTS])
#
# The one-call version of "build a SHARED library correctly set up to be
# hot-reloaded": add_library(... SHARED), patchable function entries, and
# an export policy -- exactly the three steps source/HotReload/README.md's
# "Using it in your own module" walks through by hand. Does NOT link the
# plugin against hotreload_lib: a plugin loaded via dlopen()/LoadLibrary()
# doesn't call into this library's API at all (the HOST does), so it only
# gets the include directory, for HOT_RELOAD_EXPORT -- matching
# source/examples/HotReloadLiveDemo/Plugin/CMakeLists.txt exactly, which
# does the same thing by hand for the same reason.
#
# Default export policy is "export everything" (GCC/Clang/MinGW's own
# default, plus WINDOWS_EXPORT_ALL_SYMBOLS so MSVC matches it) -- zero
# extra annotation needed to get a working plugin, matching
# source/examples/HotReloadDemo's PluginV1/PluginV2. Pass RESTRICT_EXPORTS
# for the narrower, recommended-for-a-real-project surface instead (every
# intended entry point then needs `extern "C" HOT_RELOAD_EXPORT` --
# hot_reload.h -- or plain `HOT_RELOAD_EXPORT` on a C++ class/method);
# matches HotReloadLiveDemo's plugin.
#
# For anything beyond a plain list of source files -- EXTRA_SOURCES,
# target_compile_definitions, linking your own other libraries into the
# plugin -- call add_library() yourself and the two functions above
# directly instead; this wrapper only covers the common case.
# =============================================================================
function(hotreload_add_plugin TARGET_NAME)
cmake_parse_arguments(ARG "RESTRICT_EXPORTS" "" "SOURCES" ${ARGN})
if(NOT ARG_SOURCES)
message(FATAL_ERROR "hotreload_add_plugin(${TARGET_NAME}): SOURCES is required.")
endif()
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "hotreload_add_plugin(${TARGET_NAME}): unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif()
add_library(${TARGET_NAME} SHARED ${ARG_SOURCES})
target_include_directories(${TARGET_NAME} PRIVATE "${HOTRELOAD_SOURCE_DIR}/include")
hot_reload_enable_patchable_functions(${TARGET_NAME})
if(ARG_RESTRICT_EXPORTS)
hot_reload_restrict_exports(${TARGET_NAME})
set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS OFF)
else()
set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
endfunction()
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.
View File
File mode changed.