Skip to content

Commit

Permalink
feat: add add_commonlibsf_plugin CMake command (#49)
Browse files Browse the repository at this point in the history
`add_commonlibsf_plugin` creates a `Plugin.h` in
`${CMAKE_CURRENT_BINARY_DIR}` which contains a plugin declaration as
well as the exported `SFSEPluginVersion` function with all the
appropriate fields filled in. `Plugin.h` can be included anywhere in a
project for easy access to plugin metadata. The new command also takes
care of some housekeeping tasks like linking CommonLibSF to the project.
Lifted mostly verbatim from CLibNG, tested it and it seems to work fine
for me. Example call:
```cmake
add_commonlibsf_plugin(${PROJECT_NAME} AUTHOR ThirdEyeSqueegee SOURCES ${headers} ${sources})
```
See `CommonLibSF.cmake` for other arguments the command can accept 

Closes #8
  • Loading branch information
ThirdEyeSqueegee authored Sep 15, 2023
1 parent 8ac1a3c commit e76b1e7
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
CMakeUserPresets.json
CMakeFiles
CMakeCache.txt
*.cmake
/.vs*
/.vscode*
/.idea
Expand Down
157 changes: 157 additions & 0 deletions CommonLibSF/cmake/CommonLibSF.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
function(commonlibsf_parse_version VERSION)
message("${version_match_count}")
string(REGEX MATCHALL "^([0-9]+)(\\.([0-9]+)(\\.([0-9]+)(\\.([0-9]+))?)?)?$" version_match "${VERSION}")
unset(COMMONLIBSF_VERSION_MAJOR PARENT_SCOPE)
unset(COMMONLIBSF_VERSION_MINOR PARENT_SCOPE)
unset(COMMONLIBSF_VERSION_PATCH PARENT_SCOPE)
unset(COMMONLIBSF_VERSION_TWEAK PARENT_SCOPE)

if("${version_match} " STREQUAL " ")
set(COMMONLIBSF_VERSION_MATCH FALSE PARENT_SCOPE)
return()
endif()

set(COMMONLIBSF_VERSION_MATCH TRUE PARENT_SCOPE)
set(COMMONLIBSF_VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
set(COMMONLIBSF_VERSION_MINOR "0" PARENT_SCOPE)
set(COMMONLIBSF_VERSION_PATCH "0" PARENT_SCOPE)
set(COMMONLIBSF_VERSION_TWEAK "0" PARENT_SCOPE)

if(DEFINED CMAKE_MATCH_3)
set(COMMONLIBSF_VERSION_MINOR "${CMAKE_MATCH_3}" PARENT_SCOPE)
endif()
if(DEFINED CMAKE_MATCH_5)
set(COMMONLIBSF_VERSION_PATCH "${CMAKE_MATCH_5}" PARENT_SCOPE)
endif()
if(DEFINED CMAKE_MATCH_7)
set(COMMONLIBSF_VERSION_TWEAK "${CMAKE_MATCH_7}" PARENT_SCOPE)
endif()
endfunction()

function(target_commonlibsf_properties TARGET)
# EXCLUDE_FROM_ALL and SOURCES are supported here to simplify passing arguments from add_commonlibsf_plugin.
set(options OPTIONAL USE_ADDRESS_LIBRARY USE_SIGNATURE_SCANNING STRUCT_DEPENDENT EXCLUDE_FROM_ALL)
set(oneValueArgs NAME AUTHOR EMAIL VERSION MINIMUM_SFSE_VERSION)
set(multiValueArgs COMPATIBLE_RUNTIMES SOURCES)

cmake_parse_arguments(PARSE_ARGV 1 ADD_COMMONLIBSF_PLUGIN "${options}" "${oneValueArgs}"
"${multiValueArgs}")

set(commonlibsf_plugin_file "${CMAKE_CURRENT_BINARY_DIR}/Plugin.h")

# Set the plugin name.
set(commonlibsf_plugin_name "${TARGET}")
if(DEFINED ADD_COMMONLIBSF_PLUGIN_NAME)
set(commonlibsf_plugin_name "${ADD_COMMONLIBSF_PLUGIN_NAME}")
endif()

# Setup version number of the plugin.
set(commonlibsf_plugin_version "${PROJECT_VERSION}")
if(DEFINED ADD_COMMONLIBSF_PLUGIN_VERSION)
set(commonlibsf_plugin_version "${ADD_COMMONLIBSF_PLUGIN_VERSION}")
endif()

commonlibsf_parse_version("${commonlibsf_plugin_version}")

if(NOT DEFINED COMMONLIBSF_VERSION_MAJOR)
message(FATAL_ERROR "Unable to parse plugin version number ${commonlibsf_plugin_version}.")
endif()

set(commonlibsf_plugin_version "REL::Version{${COMMONLIBSF_VERSION_MAJOR}, ${COMMONLIBSF_VERSION_MINOR}, ${COMMONLIBSF_VERSION_PATCH}, ${COMMONLIBSF_VERSION_TWEAK}}")

# Handle minimum SFSE version constraints.
if(NOT DEFINED ADD_COMMONLIBSF_PLUGIN_MINIMUM_SFSE_VERSION)
set(ADD_COMMONLIBSF_PLUGIN_MINIMUM_SFSE_VERSION 0)
endif()

commonlibsf_parse_version("${ADD_COMMONLIBSF_PLUGIN_MINIMUM_SFSE_VERSION}")

if(NOT COMMONLIBSF_VERSION_MATCH)
message(FATAL_ERROR "Unable to parse SFSE minimum SFSE version number "
"${ADD_COMMONLIBSF_PLUGIN_MINIMUM_SFSE_VERSION}.")
endif()

set(commonlibsf_min_sfse_version "REL::Version{${COMMONLIBSF_VERSION_MAJOR}, ${COMMONLIBSF_VERSION_MINOR}, ${COMMONLIBSF_VERSION_PATCH}, ${COMMONLIBSF_VERSION_TWEAK}}")

# Setup compatibility configuration.
if(NOT ADD_COMMONLIBSF_PLUGIN_STRUCT_DEPENDENT)
set(commonlibsf_plugin_struct_compatibility "false")
else()
set(commonlibsf_plugin_struct_compatibility "true")
endif()

if(NOT ADD_COMMONLIBSF_PLUGIN_USE_SIGNATURE_SCANNING)
set(ADD_COMMONLIBSF_PLUGIN_USE_ADDRESS_LIBRARY TRUE)
endif()

if(ADD_COMMONLIBSF_PLUGIN_USE_ADDRESS_LIBRARY OR ADD_COMMONLIBSF_PLUGIN_USE_SIGNATURE_SCANNING)
if(NOT ADD_COMMONLIBSF_PLUGIN_USE_ADDRESS_LIBRARY)
set(commonlibsf_uses_signature_scanning "false")
else()
set(commonlibsf_uses_signature_scanning "true")
endif()
endif()

if(NOT DEFINED ADD_COMMONLIBSF_PLUGIN_COMPATIBLE_RUNTIMES)
set(commonlibsf_plugin_compatibility "{ SFSE::RUNTIME_LATEST }")
else()
list(LENGTH ${ADD_COMMONLIBSF_PLUGIN_COMPATIBLE_RUNTIMES} commonlibsf_plugin_compatibility_count)
if(commonlibsf_plugin_compatibility_count GREATER 16)
message(FATAL_ERROR "No more than 16 version numbers can be provided for COMPATIBLE_RUNTIMES.")
endif()
foreach(STARFIELD_VERSION ${ADD_COMMONLIBSF_PLUGIN_COMPATIBLE_RUNTIMES})
if(DEFINED commonlibsf_plugin_compatibility)
set(commonlibsf_plugin_compatibility "${commonlibsf_plugin_compatibility}, ")
endif()
commonlibsf_parse_version("${STARFIELD_VERSION}")
if(NOT COMMONLIBSF_VERSION_MATCH)
message(FATAL_ERROR "Unable to parse Starfield runtime version number ${STARFIELD_VERSION}.")
endif()
set(commonlibsf_plugin_compatibility "${commonlibsf_plugin_compatibility}REL::Version{${COMMONLIBSF_VERSION_MAJOR}, ${COMMONLIBSF_VERSION_MINOR}, ${COMMONLIBSF_VERSION_PATCH}, ${COMMONLIBSF_VERSION_TWEAK}}")
endforeach()
set(commonlibsf_plugin_compatibility "{ ${commonlibsf_plugin_compatibility} }")
endif()

file(WRITE "${commonlibsf_plugin_file}"
"#pragma once\n\n"
"#define SFSEPluginVersion extern \"C\" __declspec(dllexport) constinit SFSE::PluginVersionData SFSEPlugin_Version\n\n"
"namespace Plugin\n"
"{\n"
" static constexpr auto Name{ \"${commonlibsf_plugin_name}\"sv };\n"
" static constexpr auto Author{ \"${ADD_COMMONLIBSF_PLUGIN_AUTHOR}\"sv };\n"
" static constexpr auto Version{\n"
" ${commonlibsf_plugin_version}\n"
" };\n"
"}\n\n"
"SFSEPluginVersion = []() noexcept {\n"
" SFSE::PluginVersionData data{};\n"
"\n"
" data.PluginVersion(Plugin::Version.pack());\n"
" data.PluginName(Plugin::Name);\n"
" data.AuthorName(Plugin::Author);\n"
" data.UsesSigScanning(${commonlibsf_uses_signature_scanning});\n"
" data.IsLayoutDependent(${commonlibsf_plugin_struct_compatibility});\n"
" data.CompatibleVersions(${commonlibsf_plugin_compatibility});\n"
"\n"
" return data;\n"
"}();\n")

target_compile_definitions("${TARGET}" PRIVATE __CMAKE_COMMONLIBSF_PLUGIN=1)
target_link_libraries("${TARGET}" PUBLIC CommonLibSF::CommonLibSF)
target_include_directories("${TARGET}" PUBLIC CommonLibSF_INCLUDE_DIRS)
target_include_directories("${TARGET}" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
endfunction()

function(add_commonlibsf_plugin TARGET)
set(options OPTIONAL USE_ADDRESS_LIBRARY USE_SIGNATURE_SCANNING STRUCT_DEPENDENT EXCLUDE_FROM_ALL)
set(oneValueArgs NAME AUTHOR VERSION MINIMUM_SFSE_VERSION)
set(multiValueArgs COMPATIBLE_RUNTIMES SOURCES)

cmake_parse_arguments(PARSE_ARGV 1 ADD_COMMONLIBSF_PLUGIN "${options}" "${oneValueArgs}"
"${multiValueArgs}")

add_library("${TARGET}" SHARED $<$<BOOL:${ADD_COMMONLIBSF_PLUGIN_EXCLUDE_FROM_ALL}>:EXCLUDE_FROM_ALL>
${ADD_COMMONLIBSF_PLUGIN_SOURCES})

target_commonlibsf_properties("${TARGET}" ${ARGN})
endfunction()
1 change: 1 addition & 0 deletions CommonLibSF/cmake/config.cmake.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
include(CMakeFindDependencyMacro)

find_dependency(spdlog CONFIG)
Expand Down

0 comments on commit e76b1e7

Please sign in to comment.