diff --git a/cmake/modules/3rdParty.cmake b/cmake/modules/3rdParty.cmake index 4828cb3..ac51f88 100644 --- a/cmake/modules/3rdParty.cmake +++ b/cmake/modules/3rdParty.cmake @@ -44,8 +44,8 @@ endfunction () function (parse_arguments_for_use_functions) # parse arguments set(OPTIONAL_ARGS OPTIONAL) - set(ONE_VALUE_ARGS VISIBILITY LIBRARIES_VARIABLE PACKAGES_VARIABLE TARGET_NAME) - set(MULTI_VALUE_ARGS) + set(ONE_VALUE_ARGS VISIBILITY LIBRARIES_VARIABLE PACKAGES_VARIABLE PKG_CONFIG_MODULES_VARIABLE TARGET_NAME) + set(MULTI_VALUE_ARGS PKG_CONFIG_MODULES) cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) # validate values @@ -58,20 +58,32 @@ function (parse_arguments_for_use_functions) if (NOT ARGS_LIBRARIES_VARIABLE) set(ARGS_LIBRARIES_VARIABLE "${ARGS_VISIBILITY}_LIBRARIES") endif () - if (NOT ARGS_PACKAGES_VARIABLE AND (NOT BUILD_SHARED_LIBS OR VISIBILITY STREQUAL PUBLIC)) - set(ARGS_PACKAGES_VARIABLE "INTERFACE_REQUIRED_PACKAGES") - else () - set(ARGS_PACKAGES_VARIABLE "REQUIRED_PACKAGES") + if (NOT ARGS_PACKAGES_VARIABLE) + if (NOT BUILD_SHARED_LIBS OR VISIBILITY STREQUAL PUBLIC) + set(ARGS_PACKAGES_VARIABLE "INTERFACE_REQUIRED_PACKAGES") + else () + set(ARGS_PACKAGES_VARIABLE "REQUIRED_PACKAGES") + endif () + endif () + if (NOT ARGS_PKG_CONFIG_MODULES_VARIABLE) + if (NOT BUILD_SHARED_LIBS OR VISIBILITY STREQUAL PUBLIC) + set(ARGS_PKG_CONFIG_MODULES_VARIABLE "INTERFACE_REQUIRED_PKG_CONFIG_MODULES") + else () + set(ARGS_PKG_CONFIG_MODULES_VARIABLE "REQUIRED_PKG_CONFIG_MODULES") + endif () endif () # export parsed values to parent scope set(ARGS_VISIBILITY "${ARGS_VISIBILITY}" PARENT_SCOPE) set(ARGS_LIBRARIES_VARIABLE "${ARGS_LIBRARIES_VARIABLE}" PARENT_SCOPE) set(ARGS_PACKAGES_VARIABLE "${ARGS_PACKAGES_VARIABLE}" PARENT_SCOPE) + set(ARGS_PKG_CONFIG_MODULES_VARIABLE "${ARGS_PKG_CONFIG_MODULES_VARIABLE}" PARENT_SCOPE) set(ARGS_TARGET_NAME "${ARGS_TARGET_NAME}" PARENT_SCOPE) + set(ARGS_PKG_CONFIG_MODULES "${ARGS_PKG_CONFIG_MODULES}" PARENT_SCOPE) set(ARGS_OPTIONAL "${ARGS_OPTIONAL}" PARENT_SCOPE) if (NOT ARGS_OPTIONAL) set(ARGS_FIND_PACKAGE "REQUIRED" PARENT_SCOPE) + set(ARGS_PKG_CHECK_MODULES "REQUIRED" PARENT_SCOPE) endif () endfunction () @@ -160,6 +172,47 @@ function (use_target) set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${ARGS_TARGET_NAME}" PARENT_SCOPE) endfunction () +function(use_pkg_config_module) + # parse and validate arguments + parse_arguments_for_use_functions(${ARGN}) + if (NOT ARGS_PKG_CONFIG_MODULES) + message(FATAL_ERROR "No pkg-config modules specified.") + endif () + if (NOT ARGS_TARGET_NAME) + list(LENGTH ARGS_PKG_CONFIG_MODULES ARGS_PKG_CONFIG_MODULES_LENGTH) + if (ARGS_PKG_CONFIG_MODULES_LENGTH STREQUAL 1) + list(GET ARGS_PKG_CONFIG_MODULES 0 ARGS_TARGET_NAME) + else () + message(FATAL_ERROR "No target name for multi-module pkg-config specified.") + endif () + endif () + + # skip if target has already been added + if (TARGET "${ARGS_TARGET_NAME}") + return() + endif () + + find_package(PkgConfig) + pkg_check_modules(PKG_CHECK_MODULES_RESULT ${ARGS_PKG_CHECK_MODULES} ${ARGS_PKG_CONFIG_MODULES}) + + # create interface library + add_library(${ARGS_TARGET_NAME} INTERFACE IMPORTED) + if (PKG_CONFIG_USE_STATIC_LIBS) + set(PKG_CONFIG_CHECK_SUFFIX "_STATIC") + else () + set(PKG_CONFIG_CHECK_SUFFIX "") + endif () + set_property(TARGET ${ARGS_TARGET_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${PKG_CHECK_MODULES_RESULT${PKG_CONFIG_CHECK_SUFFIX}_LINK_LIBRARIES}") + set_property(TARGET ${ARGS_TARGET_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${PKG_CHECK_MODULES_RESULT${PKG_CONFIG_CHECK_SUFFIX}_INCLUDE_DIRS}") + set_property(TARGET ${ARGS_TARGET_NAME} PROPERTY INTERFACE_COMPILE_OPTIONS "${PKG_CHECK_MODULES_RESULT${PKG_CONFIG_CHECK_SUFFIX}_CFLAGS_OTHER}") + set_property(TARGET ${ARGS_TARGET_NAME} PROPERTY INTERFACE_LINK_OPTIONS "${PKG_CHECK_MODULES_RESULT${PKG_CONFIG_CHECK_SUFFIX}_LDFLAGS_OTHER}") + + set("${ARGS_PKG_CONFIG_MODULES_VARIABLE}" "${${ARGS_PKG_CONFIG_MODULES_VARIABLE}};${ARGS_TARGET_NAME}" PARENT_SCOPE) + set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${ARGS_TARGET_NAME}" PARENT_SCOPE) + string(REPLACE "::" "_" TARGET_VARNAME "${ARGS_TARGET_NAME}") + set("PKG_CONFIG_${TARGET_VARNAME}" "${ARGS_PKG_CONFIG_MODULES}" PARENT_SCOPE) +endfunction() + # skip subsequent configuration if only the function includes are wanted if (META_NO_3RDPARTY_CONFIG) return() @@ -200,6 +253,7 @@ if ((STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION) OR (STATIC_LIBRARY_LINKAGE # prefer static libraries set(OPENSSL_USE_STATIC_LIBS ON) set(BOOST_USE_STATIC_LIBS ON) + set(PKG_CONFIG_USE_STATIC_LIBS ON) configure_static_library_suffixes() else () diff --git a/cmake/modules/LibraryTarget.cmake b/cmake/modules/LibraryTarget.cmake index f4b71bc..2f4131e 100644 --- a/cmake/modules/LibraryTarget.cmake +++ b/cmake/modules/LibraryTarget.cmake @@ -199,11 +199,23 @@ if (META_HEADER_ONLY_LIB) "${AUTOGEN_DEPS}") endif () +# generate CMake code to configure CMake-target to pkg-config module mapping +set(TARGET_TO_PKG_CONFIG_MODULE_NAME_MAPPING "set(PKG_CONFIG_${META_TARGET_NAME} \"${META_PROJECT_NAME}${META_CONFIG_SUFFIX}\")") +foreach (INTERFACE_REQUIRED_PKG_CONFIG_MODULE ${INTERFACE_REQUIRED_PKG_CONFIG_MODULES}) + string(REPLACE "::" "_" INTERFACE_REQUIRED_PKG_CONFIG_MODULE_VARNAME "${INTERFACE_REQUIRED_PKG_CONFIG_MODULE}") + set(TARGET_TO_PKG_CONFIG_MODULE_NAME_MAPPING + "${TARGET_TO_PKG_CONFIG_MODULE_NAME_MAPPING}\nset(PKG_CONFIG_${INTERFACE_REQUIRED_PKG_CONFIG_MODULE_VARNAME} \"${PKG_CONFIG_${INTERFACE_REQUIRED_PKG_CONFIG_MODULE_VARNAME}}\")") +endforeach () + # create the CMake package config file from template if (INTERFACE_REQUIRED_PACKAGES) list(REMOVE_ITEM INTERFACE_REQUIRED_PACKAGES "") list(REMOVE_DUPLICATES INTERFACE_REQUIRED_PACKAGES) endif () +if (INTERFACE_REQUIRED_PKG_CONFIG_MODULES) + list(REMOVE_ITEM INTERFACE_REQUIRED_PKG_CONFIG_MODULES "") + list(REMOVE_DUPLICATES INTERFACE_REQUIRED_PKG_CONFIG_MODULES) +endif () set(CONFIG_TARGETS "${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}${META_CONFIG_SUFFIX}Config.cmake") if (META_CONFIG_SUFFIX) list(APPEND CONFIG_TARGETS "${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}Config.cmake") @@ -242,15 +254,24 @@ macro (compute_dependencies_for_package_config DEPENDS OUTPUT_VAR_PKGS OUTPUT_VA DEPENDENCY_VARNAME "${DEPENDENCY}") if (PKG_CONFIG_${DEPENDENCY_VARNAME}) - # add pkg-config name of the dependency - set(${OUTPUT_VAR_PKGS} "${${OUTPUT_VAR_PKGS}} ${PKG_CONFIG_${DEPENDENCY_VARNAME}}") + # add pkg-config modules for the dependency + foreach (PKG_CONFIG_MODULE ${PKG_CONFIG_${DEPENDENCY_VARNAME}}) + set(${OUTPUT_VAR_PKGS} "${${OUTPUT_VAR_PKGS}} ${PKG_CONFIG_MODULE}") + endforeach () elseif (TARGET "${DEPENDENCY}") - # add library location of the target + # add interface link libraries of the target get_target_property("${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES" "${DEPENDENCY}" "INTERFACE_LINK_LIBRARIES") - if (EXISTS "${${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES}") - set(${OUTPUT_VAR_LIBS} "${${OUTPUT_VAR_LIBS}} ${${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES}") + set(${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES_EXISTING FALSE) + foreach (LIBRARY ${${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES}) + if (EXISTS ${LIBRARY}) + set(${OUTPUT_VAR_LIBS} "${${OUTPUT_VAR_LIBS}} ${LIBRARY}") + set(${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES_EXISTING TRUE) + endif () + endforeach () + if (${DEPENDENCY_VARNAME}_INTERFACE_LINK_LIBRARIES_EXISTING) continue() endif () + # add library location of the target if (META_CURRENT_CONFIGURATION) get_target_property("${DEPENDENCY_VARNAME}_IMPORTED_LOCATION_${META_CURRENT_CONFIGURATION}" "${DEPENDENCY}" "IMPORTED_LOCATION_${META_CURRENT_CONFIGURATION}") diff --git a/cmake/templates/Config.cmake.in b/cmake/templates/Config.cmake.in index 2168788..8f59755 100644 --- a/cmake/templates/Config.cmake.in +++ b/cmake/templates/Config.cmake.in @@ -26,19 +26,27 @@ set(@META_PROJECT_VARNAME_UPPER@_PRIVATE_KF_MODULES "@KF_MODULES@") set(@META_PROJECT_VARNAME_UPPER@_PUBLIC_QT_MODULES "@META_PUBLIC_QT_MODULES@") set(@META_PROJECT_VARNAME_UPPER@_PUBLIC_KF_MODULES "@META_PUBLIC_KF_MODULES@") set(@META_PROJECT_VARNAME_UPPER@_REQUIRED_PACKAGES "@INTERFACE_REQUIRED_PACKAGES@") +set(@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULES "@INTERFACE_REQUIRED_PKG_CONFIG_MODULES@") set(@META_PROJECT_VARNAME_UPPER@_HAS_QT_TRANSLATION_FILES @APP_SPECIFIC_QT_TRANSLATIONS_AVAILABLE@) set(@META_PROJECT_VARNAME_UPPER@_QT_RESOURCES @QT_RESOURCES@) +# define mapping from CMake targets to pkg-config module names +@TARGET_TO_PKG_CONFIG_MODULE_NAME_MAPPING@ + # define library config, add imported target set(@META_PROJECT_VARNAME_UPPER@_PACKAGE "@META_PROJECT_NAME@@META_CONFIG_SUFFIX@") set(@META_PROJECT_VARNAME_UPPER@_LIB "@META_TARGET_NAME@") set(@META_PROJECT_VARNAME_UPPER@_LIB_IS_SHARED "@BUILD_SHARED_LIBS@") set(@META_PROJECT_VARNAME_UPPER@_STATIC_LINKAGE "@STATIC_LINKAGE_CONFIGURED@") -set(PKG_CONFIG_@META_TARGET_NAME@ "@META_PROJECT_NAME@@META_CONFIG_SUFFIX@") if(NOT TARGET "${@META_PROJECT_VARNAME_UPPER@_LIB}") + # add target for the library itself include("${CMAKE_CURRENT_LIST_DIR}/@META_PROJECT_NAME@@META_CONFIG_SUFFIX@Targets.cmake") - # find all required packages; prefer static libraries if project was configured this way during its build + # make dependencies of the library available + # caveat: This currently does *not* cover Qt and KF modules which are so far only handled + # separately when using the function use_@META_PROJECT_VARNAME@. + + # prefer static libraries if @META_PROJECT_NAME@ was configured this way during its build if (@META_PROJECT_VARNAME_UPPER@_STATIC_LINKAGE) set(@META_PROJECT_VARNAME_UPPER@_DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) if (WIN32) @@ -46,7 +54,11 @@ if(NOT TARGET "${@META_PROJECT_VARNAME_UPPER@_LIB}") else () set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif () + set(@META_PROJECT_VARNAME_UPPER@_DEFAULT_PKG_CONFIG_USE_STATIC_LIBS ${PKG_CONFIG_USE_STATIC_LIBS}) + set(PKG_CONFIG_USE_STATIC_LIBS ON) endif () + + # find all required packages foreach (_REQUIRED_PACKAGE ${@META_PROJECT_VARNAME_UPPER@_REQUIRED_PACKAGES}) string(REGEX MATCH _REQUIRED_PACKAGE_MATCH "(.*)-([^-]*)" "${_REQUIRED_PACKAGE}") if (_REQUIRED_PACKAGE_MATCH) @@ -55,9 +67,27 @@ if(NOT TARGET "${@META_PROJECT_VARNAME_UPPER@_LIB}") find_package("${_REQUIRED_PACKAGE}" REQUIRED) endif() endforeach() + + # find all required pkg-config modules + if (@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULES) + include(3rdParty) + foreach (@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULE ${@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULES}) + string(REPLACE "::" "_" @META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULE_VARNAME "${@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULE}") + use_pkg_config_module( + TARGET_NAME "${@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULE}" + PKG_CONFIG_MODULES "${PKG_CONFIG_${@META_PROJECT_VARNAME_UPPER@_REQUIRED_PKG_CONFIG_MODULE_VARNAME}}" + LIBRARIES_VARIABLE DEV_NULL + PKG_CONFIG_MODULES_VARIABLE DEV_NULL + ) + endforeach () + endif () + + # restore preference of static libraries if (@META_PROJECT_VARNAME_UPPER@_STATIC_LINKAGE) set(CMAKE_FIND_LIBRARY_SUFFIXES ${@META_PROJECT_VARNAME_UPPER@_DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES}) unset(@META_PROJECT_VARNAME_UPPER@_DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES) + set(PKG_CONFIG_USE_STATIC_LIBS ${@META_PROJECT_VARNAME_UPPER@_DEFAULT_PKG_CONFIG_USE_STATIC_LIBS}) + unset(@META_PROJECT_VARNAME_UPPER@_DEFAULT_PKG_CONFIG_USE_STATIC_LIBS) endif () endif()