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
|
||||
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