From fe4e4b2ef53c348d4a0208efbdc07624633cbb36 Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 19 Aug 2016 16:13:41 +0200 Subject: [PATCH] Fix issues concerning building/using static libraries - Ensure static versions of 3rd party libs are found correctly - Link tests against statically if only static libraries have been built - Ensure standard lib is linked statically when static linkage is enabled --- CMakeLists.txt | 2 +- application/global.h | 18 ++++----- cmake/modules/3rdParty.cmake | 67 ++++++++++++++++++++----------- cmake/modules/AppTarget.cmake | 18 ++++++++- cmake/modules/BasicConfig.cmake | 16 +++++++- cmake/modules/Doxygen.cmake | 1 - cmake/modules/LibraryTarget.cmake | 26 ++++++++---- cmake/modules/TestTarget.cmake | 10 +++-- cmake/templates/Config.cmake.in | 13 +++++- 9 files changed, 122 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 052fa96..8dc2a03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ set(META_VERSION_PATCH 0) # find required 3rd party libraries include(3rdParty) -find_iconv(AUTO_LINKAGE REQUIRED) +use_iconv(AUTO_LINKAGE REQUIRED) # include modules to apply configuration include(BasicConfig) diff --git a/application/global.h b/application/global.h index bb46d1e..fe380d0 100644 --- a/application/global.h +++ b/application/global.h @@ -3,24 +3,24 @@ #ifdef _WIN32 # ifndef PLATFORM_WINDOWS -/*! - * \brief Defined on Windows. - */ +/// \brief Defined when compiling for Windows. # define PLATFORM_WINDOWS # endif +# if defined(__MINGW32__) || defined(__MINGW64__) +# ifndef PLATFORM_MINGW +/// \brief Defined when compiling with mingw(-w64). +# define PLATFORM_MINGW +# endif +# endif #elif __unix__ # ifndef PLATFORM_UNIX -/*! - * \brief Defined on any UNIX system. - */ +/// \brief Defined when compiling for any UNIX (like) system. # define PLATFORM_UNIX # endif #endif #ifdef __linux__ # ifndef PLATFORM_LINUX -/*! - * \brief Defined on Linux. - */ +/// \brief Defined when compiling for Linux. # define PLATFORM_LINUX # endif #endif diff --git a/cmake/modules/3rdParty.cmake b/cmake/modules/3rdParty.cmake index f690aae..e6ec9e6 100644 --- a/cmake/modules/3rdParty.cmake +++ b/cmake/modules/3rdParty.cmake @@ -35,7 +35,7 @@ if(NOT DEFINED FIND_THIRD_PARTY_LIBRARIES_EXISTS) elseif("${REQUIRED}" STREQUAL "REQUIRED") set(${NAME}_REQUIRED "REQUIRED") else() - message(FATAL_ERROR "Invalid use of use_external_library; must specify either REQUIRED or OPTIONAL.") + message(FATAL_ERROR "Invalid use of link_against_library; must specify either REQUIRED or OPTIONAL.") endif() # add library to list of libraries to link against when building dynamic libraries or applications @@ -55,9 +55,36 @@ if(NOT DEFINED FIND_THIRD_PARTY_LIBRARIES_EXISTS) # add library to list of libraries to be provided as transitive dependencies when building static libraries list(APPEND STATIC_LIBRARIES ${${NAME}_STATIC_LIB}) + message(STATUS "Adding ${${NAME}_STATIC_LIB} to static library dependencies of ${META_PROJECT_NAME}.") endmacro() - macro(use_external_library_from_package NAME VERSION INCLUDE_VAR LIBRARY_VAR LINKAGE REQUIRED) + macro(use_external_library NAME LINKAGE REQUIRED) + save_default_library_suffixes() + configure_dynamic_library_suffixes() + find_library(${NAME}_DYNAMIC_LIB ${NAME}) + configure_static_library_suffixes() + find_library(${NAME}_STATIC_LIB ${NAME}) + link_against_library(${NAME} ${LINKAGE} ${REQUIRED}) + restore_default_library_suffixes() + endmacro() + + function(use_external_library_from_package_dynamic NAME PKGNAME INCLUDE_VAR LIBRARY_VAR COMPAT_VERSION) + # internally used by use_external_library_from_package to find dynamic libraries + configure_dynamic_library_suffixes() + find_package(${PKGNAME} ${COMPAT_VERSION}) + include_directories(${${INCLUDE_VAR}}) + set(${NAME}_DYNAMIC_LIB ${${LIBRARY_VAR}} PARENT_SCOPE) + endfunction() + + function(use_external_library_from_package_static NAME PKGNAME INCLUDE_VAR LIBRARY_VAR COMPAT_VERSION) + # internally used by use_external_library_from_package to find static libraries + configure_static_library_suffixes() + find_package(${PKGNAME} ${COMPAT_VERSION}) + include_directories(${${INCLUDE_VAR}}) + set(${NAME}_STATIC_LIB ${${LIBRARY_VAR}} PARENT_SCOPE) + endfunction() + + macro(use_external_library_from_package NAME PKGNAME VERSION INCLUDE_VAR LIBRARY_VAR LINKAGE REQUIRED) save_default_library_suffixes() # handle specified VERSION @@ -67,24 +94,28 @@ if(NOT DEFINED FIND_THIRD_PARTY_LIBRARIES_EXISTS) set(${NAME}_COMPATIBLE_VERSION ${VERSION}) endif() - # find dynamic library + # use the find_library approach first because it is less buggy when trying to detect static libraries configure_dynamic_library_suffixes() - find_package(${NAME} ${${NAME}_COMPATIBLE_VERSION}) - include_directories(${${INCLUDE_VAR}}) - set(${NAME}_DYNAMIC_LIB ${${LIBRARY_VAR}}) - unset(${${LIBRARY_VAR}}) - - # find static library + find_library(${NAME}_DYNAMIC_LIB ${NAME}) configure_static_library_suffixes() - find_package(${NAME} ${${NAME}_COMPATIBLE_VERSION}) - set(${NAME}_STATIC_LIB ${${LIBRARY_VAR}}) + find_library(${NAME}_STATIC_LIB ${NAME}) + + # fall back to actual use of find_package + # use separate functions to get a new scope + if(NOT ${NAME}_DYNAMIC_LIB) + use_external_library_from_package_dynamic(${NAME} ${PKGNAME} ${INCLUDE_VAR} "${LIBRARY_VAR}" "${${NAME}_COMPATIBLE_VERSION}") + endif() + if(NOT ${NAME}_STATIC_LIB) + use_external_library_from_package_static(${NAME} ${PKGNAME} ${INCLUDE_VAR} "${LIBRARY_VAR}" "${${NAME}_COMPATIBLE_VERSION}") + endif() + link_against_library(${NAME} ${LINKAGE} ${REQUIRED}) restore_default_library_suffixes() endmacro() - macro(find_iconv LINKAGE REQUIRED) + macro(use_iconv LINKAGE REQUIRED) # check whether iconv exists in the standard library include(CheckFunctionExists) check_function_exists(iconv HAS_ICONV) @@ -92,17 +123,7 @@ if(NOT DEFINED FIND_THIRD_PARTY_LIBRARIES_EXISTS) message(STATUS "Using iconv from the standard library for ${META_PROJECT_NAME}.") else() # find external iconv library - save_default_library_suffixes() - - configure_dynamic_library_suffixes() - find_library(ICONV_DYNAMIC_LIB iconv) - - configure_static_library_suffixes() - find_library(ICONV_STATIC_LIB iconv) - - link_against_library(ICONV ${LINKAGE} ${REQUIRED}) - - restore_default_library_suffixes() + use_external_library(iconv ${LINKAGE} ${REQUIRED}) endif() endmacro() endif() diff --git a/cmake/modules/AppTarget.cmake b/cmake/modules/AppTarget.cmake index 730dccf..656ba45 100644 --- a/cmake/modules/AppTarget.cmake +++ b/cmake/modules/AppTarget.cmake @@ -1,15 +1,31 @@ # before including this module, BasicConfig must be included +# check whether project type is set correctly +if(NOT "${META_PROJECT_TYPE}" STREQUAL "application") + message(FATAL_ERROR "The AppTarget CMake module is intended to be used for building application projects only (and not for libraries).") +endif() + # set the windows extension to "exe", this is required by the Windows specific WindowsResources module if(WIN32) set(WINDOWS_EXT "exe") endif(WIN32) +# use correct linker flags and compile definitions (depend on linkage) +if(STATIC_LINKAGE) + set(ACTUAL_ADDITIONAL_LINK_FLAGS ${ADDITIONAL_STATIC_LINK_FLAGS}) + set(ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_STATIC_COMPILE_DEFINITIONS}) +else() + set(ACTUAL_ADDITIONAL_LINK_FLAGS ${ADDITIONAL_LINK_FLAGS}) + set(ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_COMPILE_DEFINITIONS}) +endif() # 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}) -target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} ${LIBRARIES}) +target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} ${ACTUAL_ADDITIONAL_LINK_FLAGS} ${LIBRARIES}) set_target_properties(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} PROPERTIES CXX_STANDARD 11 + COMPILE_DEFINITIONS "${ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS}" + LINK_SEARCH_START_STATIC ${STATIC_LINKAGE} + LINK_SEARCH_END_STATIC ${STATIC_LINKAGE} ) # add install target for binary diff --git a/cmake/modules/BasicConfig.cmake b/cmake/modules/BasicConfig.cmake index 5120eef..46242b2 100644 --- a/cmake/modules/BasicConfig.cmake +++ b/cmake/modules/BasicConfig.cmake @@ -58,9 +58,21 @@ if(LOGGING_ENABLED) message(STATUS "Logging is enabled.") endif() +# options for deciding whether to build static and/or shared libraries +if((NOT ${META_PROJECT_TYPE} STREQUAL "library") AND (NOT ${META_PROJECT_TYPE} STREQUAL "")) + option(BUILD_STATIC_LIBS "whether to build static libraries (disabled by default)" OFF) + option(BUILD_SHARED_LIBS "whether to build dynamic libraries (enabled by default)" ON) +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) +if(("${META_PROJECT_TYPE}" STREQUAL "library") OR ("${META_PROJECT_TYPE}" STREQUAL "")) + option(STATIC_LIBRARY_LINKAGE "forces static linkage when building dynamic libraries" OFF) +elseif("${META_PROJECT_TYPE}" STREQUAL "application") + option(STATIC_LINKAGE "forces static linkage when building applications" OFF) +endif() + +# additional linker flags used when static linkage is enables +set(ADDITIONAL_STATIC_LINK_FLAGS -static -static-libstdc++ -static-libgcc) # options for enabling/disabling Qt GUI (if available) if(WIDGETS_HEADER_FILES OR WIDGETS_SRC_FILES OR WIDGETS_UI_FILES) diff --git a/cmake/modules/Doxygen.cmake b/cmake/modules/Doxygen.cmake index f7e1747..0428c6a 100644 --- a/cmake/modules/Doxygen.cmake +++ b/cmake/modules/Doxygen.cmake @@ -20,7 +20,6 @@ endif() if(NOT DOXYGEN_BIN) message(WARNING "Doxygen not found, unable to add target for generating API documentation.") - else() # load cached configuration and other variables set(DOXY_LANGUAGE "English" CACHE STRING "specifies the language of the API documentation generated with Doxygen") diff --git a/cmake/modules/LibraryTarget.cmake b/cmake/modules/LibraryTarget.cmake index 2271c67..f8b25fa 100644 --- a/cmake/modules/LibraryTarget.cmake +++ b/cmake/modules/LibraryTarget.cmake @@ -1,5 +1,10 @@ # before including this module, BasicConfig must be included +# check whether project type is set correctly +if((NOT "${META_PROJECT_TYPE}" STREQUAL "library") AND (NOT "${META_PROJECT_TYPE}" STREQUAL "")) + message(FATAL_ERROR "The LibraryTarget CMake module is intended to be used for building library projects only (and not for applications).") +endif() + # include for configure_package_config_file and write_basic_package_version_file include(CMakePackageConfigHelpers) @@ -36,23 +41,29 @@ if(MINGW) endif(MINGW) # add target for building the library -option(BUILD_SHARED_LIBS "whether to build dynamic libraries (enabled by default)" ON) if(BUILD_SHARED_LIBS) + # use correct linker flags and compile definitions (depend on linkage) + if(STATIC_LIBRARY_LINKAGE) + set(ACTUAL_ADDITIONAL_LINK_FLAGS ${ADDITIONAL_STATIC_LINK_FLAGS}) + set(ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_STATIC_COMPILE_DEFINITIONS}) + else() + set(ACTUAL_ADDITIONAL_LINK_FLAGS ${ADDITIONAL_LINK_FLAGS}) + set(ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS ${ADDITIONAL_COMPILE_DEFINITIONS}) + endif() + # add library to be created, set libs to link against, set version and C++ standard add_library(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} SHARED ${HEADER_FILES} ${SRC_FILES} ${WIDGETS_FILES} ${QML_FILES} ${RES_FILES} ${QM_FILES} ${WINDOWS_ICON_PATH}) - target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} ${LIBRARIES}) + target_link_libraries(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} ${ACTUAL_ADDITIONAL_LINK_FLAGS} ${LIBRARIES}) set_target_properties(${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} PROPERTIES VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH} SOVERSION ${META_VERSION_MAJOR} CXX_STANDARD 11 + COMPILE_DEFINITIONS "${ACTUAL_ADDITIONAL_COMPILE_DEFINITIONS}" + LINK_SEARCH_START_STATIC ${STATIC_LINKAGE} + LINK_SEARCH_END_STATIC ${STATIC_LINKAGE} ) endif() # add target for building a static version of the library -if(MINGW) - option(BUILD_STATIC_LIBS "whether to build static libraries (enabled by default on mingw-w64 platform)" ON) -else() - option(BUILD_STATIC_LIBS "whether to build static libraries (disabled by default on none-mingw-w64- platform)" OFF) -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 @@ -62,6 +73,7 @@ if(BUILD_STATIC_LIBS) SOVERSION ${META_VERSION_MAJOR} OUTPUT_NAME ${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX} CXX_STANDARD 11 + COMPILE_DEFINITIONS "${ADDITIONAL_STATIC_COMPILE_DEFINITIONS}" ) set(META_STATIC_LIB_DEPENDS ${${TARGET_PREFIX}${META_PROJECT_NAME}${TARGET_SUFFIX}_static_LIB_DEPENDS}) # used in config file endif() diff --git a/cmake/modules/TestTarget.cmake b/cmake/modules/TestTarget.cmake index a81cf49..dbf6105 100644 --- a/cmake/modules/TestTarget.cmake +++ b/cmake/modules/TestTarget.cmake @@ -13,12 +13,16 @@ add_executable(${META_PROJECT_NAME}_tests EXCLUDE_FROM_ALL ${TEST_HEADER_FILES} # always link test applications against c++utilities, cppunit and pthreads find_library(CPP_UNIT_LIB cppunit) find_library(PTHREAD_LIB pthread) -list(APPEND TEST_LIBRARIES ${CPP_UTILITIES_SHARED_LIB} ${CPP_UNIT_LIB} ${PTHREAD_LIB}) +list(APPEND TEST_LIBRARIES ${CPP_UTILITIES_LIB} ${CPP_UNIT_LIB} ${PTHREAD_LIB}) # test applications of my projects always use c++utilities and cppunit if(NOT META_PROJECT_TYPE OR "${META_PROJECT_TYPE}" STREQUAL "library") # default project type is library # when testing a library, the test application always needs to link against it - list(APPEND TEST_LIBRARIES ${META_PROJECT_NAME}) + if(BUILD_SHARED_LIBS) + list(APPEND TEST_LIBRARIES ${META_PROJECT_NAME}) + else() + list(APPEND TEST_LIBRARIES ${META_PROJECT_NAME}_static) + endif() else() # otherwise, the tests application needs the path of the application to be tested set(APPLICATION_PATH "-a ${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}") @@ -39,6 +43,6 @@ if(MINGW AND CMAKE_CROSSCOMPILING AND CPP_UTILITIES_SOURCE_DIR) list(APPEND RUNTIME_LIBRARY_PATH "${CMAKE_FIND_ROOT_PATH}/bin") endif() add_custom_target(${META_PROJECT_NAME}_run_tests COMMAND "${CPP_UTILITIES_SOURCE_DIR}/scripts/wine.sh" "${CMAKE_CURRENT_BINARY_DIR}/${META_PROJECT_NAME}_tests.${WINDOWS_EXT}" ${RUNTIME_LIBRARY_PATH}) - add_dependencies(${META_PROJECT_NAME}_run_tests ${META_PROJECT_NAME}) + add_dependencies(${META_PROJECT_NAME}_run_tests ${META_PROJECT_NAME}_tests) endif() endif() diff --git a/cmake/templates/Config.cmake.in b/cmake/templates/Config.cmake.in index 82f8bfd..5d04f1d 100644 --- a/cmake/templates/Config.cmake.in +++ b/cmake/templates/Config.cmake.in @@ -12,21 +12,30 @@ set(@META_PROJECT_VARNAME@_MODULE_DIRS "@PACKAGE_CMAKE_MODULE_INSTALL_DESTINATIO set(@META_PROJECT_VARNAME@_CONFIG_DIRS "@PACKAGE_CMAKE_CONFIG_INSTALL_DESTINATION@") set(@META_PROJECT_VARNAME@_REQUIRED_CFLAGS "@META_REQUIRED_CFLAGS@") +if(@META_PROJECT_VARNAME@_HAS_SHARED_LIB) + set(@META_PROJECT_VARNAME@_LIB "${@META_PROJECT_VARNAME@_SHARED_LIB}") +else() + set(@META_PROJECT_VARNAME@_LIB "${@META_PROJECT_VARNAME@_STATIC_LIB}") +endif() + macro(use_@META_PROJECT_VARNAME@) include_directories(BEFORE SYSTEM ${@META_PROJECT_VARNAME@_INCLUDE_DIRS}) link_directories(${@META_PROJECT_VARNAME@_LIB_DIR}) # add library to list of libraries to link against when building dynamic libraries or applications - if(@META_PROJECT_VARNAME@_HAS_STATIC_LIB 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"))) + if(@META_PROJECT_VARNAME@_HAS_STATIC_LIB 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") OR (NOT ARGV0 AND NOT @META_PROJECT_VARNAME@_HAS_SHARED_LIB))) list(APPEND LIBRARIES ${@META_PROJECT_VARNAME@_STATIC_LIB} ${@META_PROJECT_VARNAME@_STATIC_LIB_DEPENDS}) message(STATUS "Linking ${META_PROJECT_NAME} statically against @META_PROJECT_NAME@ (${@META_PROJECT_VARNAME@_STATIC_LIB} ${@META_PROJECT_VARNAME@_STATIC_LIB_DEPENDS}).") elseif(@META_PROJECT_VARNAME@_HAS_SHARED_LIB AND (NOT ARGV0 OR ("${ARGV0}" STREQUAL "SHARED"))) list(APPEND LIBRARIES ${@META_PROJECT_VARNAME@_SHARED_LIB}) message(STATUS "Linking ${META_PROJECT_NAME} dynamically against @META_PROJECT_NAME@ (${@META_PROJECT_VARNAME@_SHARED_LIB}).") + elseif(ARGV0) + message(FATAL_ERROR "Can not link ${META_PROJECT_NAME} against @META_PROJECT_NAME@ with the specified linkage ${ARGV0}.") else() - message(ERROR "Specified linkage ${ARGV0} is not available.") + message(FATAL_ERROR "Can not link ${META_PROJECT_NAME} against @META_PROJECT_NAME@.") endif() # add library (including dependencies) to list of libraries to be provided as transitive dependencies when building static libraries list(APPEND STATIC_LIBRARIES ${@META_PROJECT_VARNAME@_STATIC_LIB} ${@META_PROJECT_VARNAME@_STATIC_LIB_DEPENDS}) + message(STATUS "Adding ${@META_PROJECT_VARNAME@_STATIC_LIB} ${@META_PROJECT_VARNAME@_STATIC_LIB_DEPENDS} to static library dependencies of ${META_PROJECT_NAME}.") # make CMake modules of the project available list(APPEND CMAKE_MODULE_PATH ${@META_PROJECT_VARNAME@_MODULE_DIRS}) endmacro()