From e76b1e75c69662c71ab724676523f36f6f0c30f3 Mon Sep 17 00:00:00 2001 From: Angad <66992519+ThirdEyeSqueegee@users.noreply.github.com> Date: Thu, 14 Sep 2023 21:19:58 -0700 Subject: [PATCH] feat: add add_commonlibsf_plugin CMake command (#49) `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 --- .gitignore | 1 - CommonLibSF/cmake/CommonLibSF.cmake | 157 ++++++++++++++++++++++++++++ CommonLibSF/cmake/config.cmake.in | 1 + 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 CommonLibSF/cmake/CommonLibSF.cmake diff --git a/.gitignore b/.gitignore index 9a4abfb2..19da71fa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ CMakeUserPresets.json CMakeFiles CMakeCache.txt -*.cmake /.vs* /.vscode* /.idea diff --git a/CommonLibSF/cmake/CommonLibSF.cmake b/CommonLibSF/cmake/CommonLibSF.cmake new file mode 100644 index 00000000..3e951f8e --- /dev/null +++ b/CommonLibSF/cmake/CommonLibSF.cmake @@ -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 $<$:EXCLUDE_FROM_ALL> + ${ADD_COMMONLIBSF_PLUGIN_SOURCES}) + + target_commonlibsf_properties("${TARGET}" ${ARGN}) +endfunction() diff --git a/CommonLibSF/cmake/config.cmake.in b/CommonLibSF/cmake/config.cmake.in index cdaf907e..0595a7f1 100644 --- a/CommonLibSF/cmake/config.cmake.in +++ b/CommonLibSF/cmake/config.cmake.in @@ -1,4 +1,5 @@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@.cmake") include(CMakeFindDependencyMacro) find_dependency(spdlog CONFIG)