From 189a6fe6babd295da2c8748bc81158c7d3242cbb Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 22 Jul 2016 01:35:32 +0200 Subject: [PATCH] Approach to enable static linkage Linking statically might be useful, especially when linking Windows applications since MinGW is less buggy then. --- CMakeLists.txt | 3 +- cmake/modules/3rdParty.cmake | 63 +++++++++++++++++++++++++++++++ cmake/modules/AppTarget.cmake | 6 +-- cmake/modules/BasicConfig.cmake | 4 ++ cmake/modules/LibraryTarget.cmake | 45 +++++++++++----------- cmake/templates/Config.cmake.in | 16 +++++++- 6 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 cmake/modules/3rdParty.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b9c305f..5d7b0a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ set(CMAKE_MODULE_FILES cmake/modules/Doxygen.cmake cmake/modules/ListToString.cmake cmake/modules/ShellCompletion.cmake + cmake/modules/3rdParty.cmake ) set(CMAKE_TEMPLATE_FILES cmake/templates/Config.cmake.in @@ -100,7 +101,7 @@ set(META_PROJECT_VARNAME CPP_UTILITIES) set(META_APP_NAME "C++ Utilities") set(META_APP_AUTHOR "Martchus") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") -set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities.") +set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities") set(META_VERSION_MAJOR 4) set(META_VERSION_MINOR 0) set(META_VERSION_PATCH 0) diff --git a/cmake/modules/3rdParty.cmake b/cmake/modules/3rdParty.cmake new file mode 100644 index 0000000..f312c87 --- /dev/null +++ b/cmake/modules/3rdParty.cmake @@ -0,0 +1,63 @@ +if(NOT DEFINED EXTERNAL_LIBRARIES_EXISTS) + set(EXTERNAL_LIBRARIES_EXISTS true) + macro(use_external_library_from_package NAME VERSION INCLUDE_VAR LIBRARY_VAR LINKAGE REQUIRED) + # need to set CMAKE_FIND_LIBRARY_SUFFIXES temporarily to be able to find static libs, save the current value to be able to restore + set(DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + + # handle arguments VERSION and REQUIRED + if("${VERSION}" STREQUAL "ANY_VERSION") + set(${NAME}_COMPATIBLE_VERSION "") + else() + set(${NAME}_COMPATIBLE_VERSION ${VERSION}) + endif() + if("${REQUIRED}" STREQUAL "OPTIONAL") + set(${NAME}_REQUIRED "") + elseif("${REQUIRED}" STREQUAL "REQUIRED") + set(${NAME}_REQUIRED "REQUIRED") + else() + message(FATAL_ERROR "Invalid use of use_external_library; must specify either REQUIRED or OPTIONAL.") + endif() + + # find dynamic library + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .so) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .dll.a .dll) + endif() + find_package(${NAME} ${${NAME}_COMPATIBLE_VERSION}) + include_directories(${${INCLUDE_VAR}}) + set(${NAME}_DYNAMIC_LIB ${${LIBRARY_VAR}}) + set(${${LIBRARY_VAR}} "") + + # find static library + if(WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a) + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() + find_package(${NAME} ${${NAME}_COMPATIBLE_VERSION}) + set(${NAME}_STATIC_LIB ${${LIBRARY_VAR}}) + + # add library to list of libraries to link against when building dynamic libraries or applications + if(${NAME}_STATIC_LIB AND (("${LINKAGE}" STREQUAL "AUTO_LINKAGE" AND ((STATIC_LINKAGE AND "${META_PROJECT_TYPE}" STREQUAL "application") OR (STATIC_LIBRARY_LINKAGE AND ("${META_PROJECT_TYPE}" STREQUAL "" OR "${META_PROJECT_TYPE}" STREQUAL "library")))) OR ("${LINKAGE}" STREQUAL "STATIC"))) + list(APPEND LIBRARIES ${${NAME}_STATIC_LIB}) + message(STATUS "Linking ${META_PROJECT_NAME} statically against external library ${NAME}.") + elseif(${NAME}_DYNAMIC_LIB AND ("${LINKAGE}" STREQUAL "AUTO_LINKAGE" OR ("${LINKAGE}" STREQUAL "SHARED"))) + list(APPEND LIBRARIES ${${NAME}_DYNAMIC_LIB}) + message(STATUS "Linking ${META_PROJECT_NAME} dynamically against external library ${NAME}.") + else() + if(${REQUIRED}) + message(FATAL_ERROR "External library ${NAME} required by ${META_PROJECT_NAME} is not available for the specified linkage ${LINKAGE}.") + else() + message(WARNING "External library ${NAME} required by ${META_PROJECT_NAME} is not available for the specified linkage ${LINKAGE}.") + endif() + endif() + + # add library to list of libraries to be provided as transitive dependencies when building static libraries + list(APPEND STATIC_LIBRARIES ${${LIBRARY_VAR}}) + + # restore altered CMAKE_FIND_LIBRARY_SUFFIXES + set(CMAKE_FIND_LIBRARY_SUFFIXES ${DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES}) + set(DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES "") + endmacro() +endif() diff --git a/cmake/modules/AppTarget.cmake b/cmake/modules/AppTarget.cmake index 4fadbb5..730dccf 100644 --- a/cmake/modules/AppTarget.cmake +++ b/cmake/modules/AppTarget.cmake @@ -1,9 +1,9 @@ # before including this module, BasicConfig must be included -# set the windows extension to "exe", this is required by the mingw-w64 specific WindowsResources module -if(MINGW) +# set the windows extension to "exe", this is required by the Windows specific WindowsResources module +if(WIN32) set(WINDOWS_EXT "exe") -endif(MINGW) +endif(WIN32) # add target for building the application add_executable(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} ${GUI_TYPE} ${HEADER_FILES} ${SRC_FILES} ${WIDGETS_FILES} ${QML_FILES} ${RES_FILES} ${QM_FILES} ${WINDOWS_ICON_PATH}) diff --git a/cmake/modules/BasicConfig.cmake b/cmake/modules/BasicConfig.cmake index 49180af..49eed69 100644 --- a/cmake/modules/BasicConfig.cmake +++ b/cmake/modules/BasicConfig.cmake @@ -69,3 +69,7 @@ if(LOGGING_ENABLED) add_definitions(-DLOGGING_ENABLED) message(STATUS "Logging is enabled.") endif() + +# options for forcing static linkage when building applications or dynamic libraries +option(STATIC_LINKAGE "forces static linkage when building applications" OFF) +option(STATIC_LIBRARY_LINKAGE "forces static linkage when building dynamic libraries" OFF) diff --git a/cmake/modules/LibraryTarget.cmake b/cmake/modules/LibraryTarget.cmake index 6ec17b0..fbd723e 100644 --- a/cmake/modules/LibraryTarget.cmake +++ b/cmake/modules/LibraryTarget.cmake @@ -25,27 +25,6 @@ set(LIB_INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/lib${SELECTED_LIB_SUFFIX}") set(CMAKE_MODULE_INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/share/${META_PROJECT_NAME}/cmake/modules") set(CMAKE_CONFIG_INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}/share/${META_PROJECT_NAME}/cmake") -# create the CMake config file from the template -configure_package_config_file( - "${CONFIG_TEMPLATE_FILE}" - "${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION - "${CMAKE_CONFIG_INSTALL_DESTINATION}" - PATH_VARS - CMAKE_MODULE_INSTALL_DESTINATION - CMAKE_CONFIG_INSTALL_DESTINATION - HEADER_INSTALL_DESTINATION - BIN_INSTALL_DESTINATION - LIB_INSTALL_DESTINATION -) - -# write the CMake version config file -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}ConfigVersion.cmake - VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}" - COMPATIBILITY SameMajorVersion -) - # remove library prefix when building with mingw-w64 (just for consistency with qmake) if(MINGW) set(CMAKE_SHARED_LIBRARY_PREFIX "") @@ -77,15 +56,37 @@ endif() if(BUILD_STATIC_LIBS) add_library(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static STATIC ${HEADER_FILES} ${SRC_FILES} ${WIDGETS_FILES} ${QML_FILES} ${RES_FILES} ${QM_FILES} ${WINDOWS_ICON_PATH}) # add target link libraries for the static lib also because otherwise Qt header files can not be located - target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static ${LIBRARIES}) + target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static ${STATIC_LIBRARIES}) set_target_properties(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static PROPERTIES VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH} SOVERSION ${META_VERSION_MAJOR} OUTPUT_NAME ${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} CXX_STANDARD 11 ) + set(META_LIB_DEPENDS ${${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static_LIB_DEPENDS}) # used in config file endif() +# create the CMake config file from the template +configure_package_config_file( + "${CONFIG_TEMPLATE_FILE}" + "${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION + "${CMAKE_CONFIG_INSTALL_DESTINATION}" + PATH_VARS + CMAKE_MODULE_INSTALL_DESTINATION + CMAKE_CONFIG_INSTALL_DESTINATION + HEADER_INSTALL_DESTINATION + BIN_INSTALL_DESTINATION + LIB_INSTALL_DESTINATION +) + +# write the CMake version config file +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}ConfigVersion.cmake + VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}" + COMPATIBILITY SameMajorVersion +) + # add install target for the CMake config files install( FILES diff --git a/cmake/templates/Config.cmake.in b/cmake/templates/Config.cmake.in index 900f92d..51d53ae 100644 --- a/cmake/templates/Config.cmake.in +++ b/cmake/templates/Config.cmake.in @@ -1,6 +1,10 @@ @PACKAGE_INIT@ +set(@META_PROJECT_VARNAME@_HAS_LIBS "@BUILD_SHARED_LIBS@") set(@META_PROJECT_VARNAME@_LIBS "@META_PROJECT_NAME@") +set(@META_PROJECT_VARNAME@_HAS_STATIC_LIBS "@BUILD_STATIC_LIBS@") +set(@META_PROJECT_VARNAME@_STATIC_LIBS "@META_PROJECT_NAME@.a") +set(@META_PROJECT_VARNAME@_STATIC_LIBS_DEPENDS @META_LIB_DEPENDS@) set(@META_PROJECT_VARNAME@_INCLUDE_DIRS "@PACKAGE_HEADER_INSTALL_DESTINATION@") set(@META_PROJECT_VARNAME@_BIN_DIR "@PACKAGE_BIN_INSTALL_DESTINATION@") set(@META_PROJECT_VARNAME@_LIB_DIR "@PACKAGE_LIB_INSTALL_DESTINATION@") @@ -10,6 +14,16 @@ set(@META_PROJECT_VARNAME@_CONFIG_DIRS "@PACKAGE_CMAKE_CONFIG_INSTALL_DESTINATIO macro(use_@META_PROJECT_VARNAME@) include_directories(BEFORE SYSTEM ${@META_PROJECT_VARNAME@_INCLUDE_DIRS}) link_directories(${@META_PROJECT_VARNAME@_LIB_DIR}) - list(APPEND LIBRARIES ${@META_PROJECT_VARNAME@_LIBS}) + # add library to list of libraries to link against when building dynamic libraries or applications + if(@META_PROJECT_VARNAME@_HAS_STATIC_LIBS AND ((NOT ARGV0 AND ((STATIC_LINKAGE AND "${META_PROJECT_TYPE}" STREQUAL "application") OR (STATIC_LIBRARY_LINKAGE AND ("${META_PROJECT_TYPE}" STREQUAL "" OR "${META_PROJECT_TYPE}" STREQUAL "library")))) OR ("${ARGV0}" STREQUAL "STATIC"))) + list(APPEND LIBRARIES ${@META_PROJECT_VARNAME@_STATIC_LIBS} ${@META_PROJECT_VARNAME@_STATIC_LIBS_DEPENDS}) + elseif(@META_PROJECT_VARNAME@_HAS_LIBS AND (NOT ARGV0 OR ("${ARGV0}" STREQUAL "SHARED"))) + list(APPEND LIBRARIES ${@META_PROJECT_VARNAME@_LIBS}) + else() + message(ERROR "Specified linkage ${ARGV0} is not available.") + endif() + # add library to list of libraries to be provided as transitive dependencies when building static libraries + list(APPEND STATIC_LIBRARIES ${@META_PROJECT_VARNAME@_STATIC_LIBS} ${@META_PROJECT_VARNAME@_STATIC_LIBS_DEPENDS}) + # make CMake modules of the project available list(APPEND CMAKE_MODULE_PATH ${@META_PROJECT_VARNAME@_MODULE_DIRS}) endmacro()