diff --git a/cmake/myx/lib/AddInterfaceLibrary.cmake b/cmake/myx/lib/AddInterfaceLibrary.cmake new file mode 100644 index 0000000..cf8851e --- /dev/null +++ b/cmake/myx/lib/AddInterfaceLibrary.cmake @@ -0,0 +1,150 @@ +#[=======================================================================[.rst: +myx_add_interface_library +------------------------- + +Вспомогательная функция для создания интерфейсной библиотеки:: + + myx_add_interface_library(TARGET_NAME + [ PACKAGES packages ] | + [ LINK_LIBRARIES link_libraries ] | + [ OUTPUT_NAME output_name ] | + [ EXPORT_FILE_NAME file_name ] | + [ EXPORT_BASE_NAME base_name ] | + [ HEADERS headers ]) + +Обязательные параметры: `TARGET_NAME` - имя библиотеки. +Параметр `OUTPUT_NAME` определяет базовое имя выходных файлов. +Параметр `EXPORT_FILE_NAME` задаёт имя заголовочного файла экспортируемых +переменных, а `EXPORT_BASE_NAME` - базовый суффикс для формирования имён переменных. +Все остальные параметры передаются в стандартную функцию `add_library()` + +#]=======================================================================] + +include_guard(GLOBAL) + +include(CMakePackageConfigHelpers) +include(GenerateExportHeader) + +if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + set(MYX_CMAKE_LIB_DIR_BACKPORT "${CMAKE_CURRENT_LIST_DIR}") +endif() + +function(myx_add_interface_library TARGET_NAME) + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + set(CMAKE_CURRENT_FUNCTION_LIST_DIR ${MYX_CMAKE_LIB_DIR_BACKPORT}) + endif() + + if(TARGET ${TARGET_NAME}) + myx_message_fatal_error("Target ${TARGET_NAME} already exists") + endif() + + set(options) + set(oneValueArgs OUTPUT_NAME) + set(multiValueArgs HEADERS LINK_LIBRARIES PACKAGES) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_OUTPUT_NAME) + set(ARG_OUTPUT_NAME ${TARGET_NAME}) + endif() + + # Вызов стандартной функции `add_library()` + if(${CMAKE_VERSION} VERSION_LESS "3.19.0") + add_library(${TARGET_NAME} INTERFACE) + # TODO + target_sources(${TARGET_NAME} INTERFACE ${ARG_HEADERS}) + else() + add_library(${TARGET_NAME} INTERFACE ${ARG_HEADERS}) + endif() + + foreach(__iter ${ARG_PACKAGES}) + target_include_directories(${TARGET_NAME} INTERFACE ${${__iter}_INCLUDE_DIRS}) + target_compile_definitions(${TARGET_NAME} INTERFACE ${${__iter}_COMPILE_DEFINITIONS}) + endforeach() + + if(ARG_LINK_LIBRARIES) + target_link_libraries(${TARGET_NAME} INTERFACE ${ARG_LINK_LIBRARIES}) + foreach(__lib ${ARG_LINK_LIBRARIES}) + if(TARGET ${__lib}) + install( + TARGETS ${__lib} + EXPORT ${TARGET_NAME}Targets + COMPONENT DEV) + endif() + endforeach() + endif() + + # Библиотека, состоящая только из заголовочных файлов не требует сборки. + # Стандартные пути к заголовочным файлам + target_include_directories(${TARGET_NAME} INTERFACE $) + + # Если вызов был выполнен не из проекта верхнего уровня, + # то созданная цель исключается из цели `all`. + # При этом сама цель `${TARGET_NAME}` может участвовать в сборке, + # если окажется в перечне зависимостей. + if(NOT PROJECT_IS_TOP_LEVEL) + set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL True) + return() + endif() + + # Правила для установки библиотек и относящихся к ним файлов. + # Если вызов был выполнен не из проекта верхнего уровня, + # то эти правила не обрабатываются. + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) + + configure_package_config_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/library-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}Config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + + install(EXPORT ${TARGET_NAME}Targets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + COMPONENT DEV) + + install( + FILES + ${PROJECT_BINARY_DIR}/${TARGET_NAME}ConfigVersion.cmake + ${PROJECT_BINARY_DIR}/${TARGET_NAME}Config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + COMPONENT DEV) + + # Формирование файла для pkg-config + file(WRITE "${PROJECT_BINARY_DIR}/${TARGET_NAME}.pc" + "prefix=${CMAKE_INSTALL_PREFIX}\n" + "exec_prefix=${CMAKE_INSTALL_PREFIX}\n" + "includedir=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\n" + "\n" + "Name: ${TARGET_NAME}\n" + "Description: ${TARGET_NAME} library\n" + "Version: ${PROJECT_VERSION}\n" + "\n" + "Requires:\n" + "Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\n") + + # Установка файла для pkg-config + install( + FILES "${PROJECT_BINARY_DIR}/${TARGET_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + COMPONENT DEV) + + # Установка библиотеки из заголовочных файлов + target_include_directories(${TARGET_NAME} SYSTEM INTERFACE $) + install( + TARGETS ${TARGET_NAME} + EXPORT ${TARGET_NAME}Targets + COMPONENT DEV) + + # Установка публичных заголовочных файлов + if(PROJECT_IS_TOP_LEVEL AND ARG_HEADERS) + myx_install_relative(${PROJECT_SOURCE_DIR} + FILES ${ARG_HEADERS} + DESTINATION ${CMAKE_INSTALL_PREFIX} + COMPONENT DEV) + endif() +endfunction() diff --git a/cmake/myx/lib/AddObjectLibrary.cmake b/cmake/myx/lib/AddObjectLibrary.cmake new file mode 100644 index 0000000..2fc2443 --- /dev/null +++ b/cmake/myx/lib/AddObjectLibrary.cmake @@ -0,0 +1,195 @@ +#[=======================================================================[.rst: +myx_add_object_library +---------------------- + +Вспомогательная функция для создания объектной библиотеки:: + + myx_add_object_library(TARGET_NAME + [ OUTPUT_NAME output_name ] | + [ EXPORT_FILE_NAME file_name ] | + [ EXPORT_BASE_NAME base_name ]) + +Обязательные параметры: `TARGET_NAME` - имя библиотеки. +Параметр `OUTPUT_NAME` определяет базовое имя выходных файлов. +Параметр `EXPORT_FILE_NAME` задаёт имя заголовочного файла экспортируемых +переменных, а `EXPORT_BASE_NAME` - базовый суффикс для формирования имён переменных. +Все остальные параметры передаются в стандартную функцию `add_library()` + +#]=======================================================================] + +include_guard(GLOBAL) + +include(CMakePackageConfigHelpers) +include(GenerateExportHeader) + +if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + set(MYX_CMAKE_LIB_DIR_BACKPORT "${CMAKE_CURRENT_LIST_DIR}") +endif() + +function(myx_add_object_library TARGET_NAME) + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + set(CMAKE_CURRENT_FUNCTION_LIST_DIR ${MYX_CMAKE_LIB_DIR_BACKPORT}) + endif() + + set(options) + set(oneValueArgs OUTPUT_NAME EXPORT_FILE_NAME EXPORT_BASE_NAME) + set(multiValueArgs) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT ARG_OUTPUT_NAME) + set(ARG_OUTPUT_NAME ${TARGET_NAME}) + endif() + + # Вызов стандартной функции `add_library()` + add_library(${TARGET_NAME} OBJECT ${ARG_UNPARSED_ARGUMENTS}) + + string(TOUPPER ${TARGET_NAME} __project_name_upper) + # Опция для разрешения сборки динамической библиотеки + cmake_dependent_option(${__project_name_upper}_BUILD_SHARED + "Build shared library for ${TARGET_NAME}" ON "BUILD_SHARED_LIBS" OFF) + # Опция для разрешения сборки статической библиотеки + cmake_dependent_option(${__project_name_upper}_BUILD_STATIC + "Build static library for ${TARGET_NAME}" ON "NOT BUILD_SHARED_LIBS" OFF) + + # Стандартные пути к заголовочным файлам + target_include_directories(${TARGET_NAME} + PUBLIC + $ + PRIVATE + $ + $ + ) + + # Сборка позиционно-независимых объектных файлов нужна + # для создания динамической библиотеки + set_target_properties(${TARGET_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) + + if(NOT EXPORT_BASE_NAME) + set(ARG_EXPORT_BASE_NAME ${__project_name_upper}) + endif() + + if(NOT ARG_EXPORT_FILE_NAME) + set(ARG_EXPORT_FILE_NAME "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}/${TARGET_NAME}_export.hpp") + endif() + + generate_export_header(${TARGET_NAME} + BASE_NAME ${ARG_EXPORT_BASE_NAME} + EXPORT_MACRO_NAME "EXPORT_${ARG_EXPORT_BASE_NAME}" + DEPRECATED_MACRO_NAME "DEPRECATED_${ARG_EXPORT_BASE_NAME}" + NO_DEPRECATED_MACRO_NAME "NO_DEPRECATED_${ARG_EXPORT_BASE_NAME}" + NO_EXPORT_MACRO_NAME "NO_EXPORT_${ARG_EXPORT_BASE_NAME}" + STATIC_DEFINE "STATIC_DEFINE_${ARG_EXPORT_BASE_NAME}" + EXPORT_FILE_NAME ${ARG_EXPORT_FILE_NAME} + DEFINE_NO_DEPRECATED + ) + set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INTERFACE_HEADER_FILES "${ARG_EXPORT_FILE_NAME}") + + # Цель для создания динамической библиотеки из объектных файлов + if(${__project_name_upper}_BUILD_SHARED) + # Для создания динамической библиотеки используются объектные файлы цели ${TARGET_NAME} + add_library(${TARGET_NAME}_shared SHARED $) + # Установка дополнительных свойств для цели ${TARGET_NAME}_shared + # VERSION: версия библиотеки + # SOVERSION: мажорная версия библиотеки + # LIBRARY_OUTPUT_DIRECTORY: каталог для записи результатов сборки + # OUTPUT_NAME: базовое имя выходного файла (без учёта расширения) + set_target_properties(${TARGET_NAME}_shared PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} + OUTPUT_NAME ${ARG_OUTPUT_NAME}) + endif() + + # Цель для создания статической библиотеки из объектных файлов + if(${__project_name_upper}_BUILD_STATIC) + # Для создания статической библиотеки используются + # объектные файлы цели ${TARGET_NAME} + add_library(${TARGET_NAME}_static STATIC $) + # Установка дополнительных свойств для цели ${TARGET_NAME}_static + # ARCHIVE_OUTPUT_DIRECTORY: каталог для записи результатов сборки + # OUTPUT_NAME: базовое имя выходного файла (без учёта расширения) + set_target_properties(${TARGET_NAME}_static PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR} + OUTPUT_NAME ${ARG_OUTPUT_NAME}) + endif() + + # Если вызов был выполнен не из проекта верхнего уровня, + # то созданная цель исключается из цели `all`. + # При этом сама цель `${TARGET_NAME}` может участвовать в сборке, + # если окажется в перечне зависимостей. + if(NOT PROJECT_IS_TOP_LEVEL) + set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL True) + return() + endif() + + # Правила для установки библиотек и относящихся к ним файлов. + # Если вызов был выполнен не из проекта верхнего уровня, + # то эти правила не обрабатываются. + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}ConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) + + configure_package_config_file( + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/library-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}Config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + + install(EXPORT ${TARGET_NAME}Targets + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + COMPONENT DEV + ) + + install( + FILES + ${PROJECT_BINARY_DIR}/${TARGET_NAME}ConfigVersion.cmake + ${PROJECT_BINARY_DIR}/${TARGET_NAME}Config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME} + COMPONENT DEV + ) + + # Формирование файла для pkg-config + file(WRITE "${PROJECT_BINARY_DIR}/${TARGET_NAME}.pc" + "prefix=${CMAKE_INSTALL_PREFIX}\n" + "exec_prefix=${CMAKE_INSTALL_PREFIX}\n" + "includedir=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\n" + "libdir=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\n" + "\n" + "Name: ${TARGET_NAME}\n" + "Description: ${TARGET_NAME} library\n" + "Version: ${PROJECT_VERSION}\n" + "\n" + "Requires:\n" + "Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\n" + "Libs: -L${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} -l${TARGET_NAME}\n") + + # Установка файла для pkg-config + install( + FILES "${PROJECT_BINARY_DIR}/${TARGET_NAME}.pc" + COMPONENT DEV + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + # Установка динамической библиотеки + if(TARGET ${TARGET_NAME}_shared) + install( + TARGETS ${TARGET_NAME}_shared + EXPORT ${TARGET_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT LIB + ) + endif() + + # Установка статической библиотеки + if(TARGET ${TARGET_NAME}_static) + install( + TARGETS ${TARGET_NAME}_static + EXPORT ${TARGET_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT STATIC + ) + endif() +endfunction()