Added easy integration for CMake
This commit is contained in:
1 parent
ab21b3f30d
commit
ec26261392
10 files changed
+262
-1
No files matched your search
Regular → Executable
File mode changed.
@@ -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
|
||||||
|
|||||||
@@ -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
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Reference in new issue
Block a user