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
This commit is contained in:
Martchus 2016-08-19 16:13:41 +02:00
parent 1a4087abbc
commit fe4e4b2ef5
9 changed files with 122 additions and 49 deletions

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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()

View File

@ -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()

View File

@ -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()