CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
# at least cmake 3.7 is required for:
# * to request source directory path from target:
# `get_target_property(FOO_SOURCE_DIR Foo SOURCE_DIR)`
# * to request BUILDSYSTEM_TARGETS property from a directory.
#
# at least cmake 3.3 is required for:
# * to use IN_LIST in if command:
# (https://cmake.org/cmake/help/v3.3/command/if.html )
# `if(<variable|string> IN_LIST <variable>)`
#
###############################################################################
## cmake policy change ########################################################
###############################################################################
#if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "X.Y.Z")
# # Policy CMP00NN is not set: ...
# #
# # ...
# cmake_policy(SET CMP00NN NEW) # cmake >= X.Y
#endif()
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.1.0")
# Policy CMP0054 is not set: Only interpret if () arguments as variables or
# keywords when unquoted. Run "cmake --help-policy CMP0054" for policy
# details. Use the cmake_policy command to set the policy and suppress this
# warning.
#
# Quoted variables like "MSVC" will no longer be dereferenced when the
# policy is set to NEW. Since the policy is not set the OLD behavior will
# be used.
cmake_policy(SET CMP0054 NEW) # cmake >= 3.1
endif()
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.3.0")
# Policy CMP0057 is not set: Support new IN_LIST if() operator. Run "cmake
# --help-policy CMP0057" for policy details. Use the cmake_policy command to
# set the policy and suppress this warning.
#
# IN_LIST will be interpreted as an operator when the policy is set to NEW.
# Since the policy is not set the OLD behavior will be used.
cmake_policy(SET CMP0057 NEW)
endif()
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
# Policy CMP0074 is not set: find_package uses PackageName_ROOT variables.
# Run "cmake --help-policy CMP0074" for policy details. Use the
# cmake_policy command to set the policy and suppress this warning.
cmake_policy(SET CMP0074 NEW) # cmake >= 3.12
endif()
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
# Policy CMP0075 is not set: Include file check macros honor
# CMAKE_REQUIRED_LIBRARIES. Run "cmake --help-policy CMP0075" for policy
# details. Use the cmake_policy command to set the policy and suppress this
# warning.
#
# CMAKE_REQUIRED_LIBRARIES is set to:
#
# .../_3dparty/arc/xz/lib/Release/Win32/liblzma/liblzma.lib
#
# For compatibility with CMake 3.11 and below this check is ignoring it.
# use global override to avoid policy drop in case where a 3dparty cmake list
# declared cmake version requirement less than 3.12.0
set(CMAKE_POLICY_DEFAULT_CMP0075 NEW)
#cmake_policy(SET CMP0075 NEW) # cmake >= 3.12
endif()
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0")
# Policy CMP0087 is not set: Install CODE|SCRIPT allow the use of generator
# expressions. Run "cmake --help-policy CMP0087" for policy details. Use
# the cmake_policy command to set the policy and suppress this warning.
cmake_policy(SET CMP0087 NEW) # cmake >= 3.14
endif()
# enable project folders
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
###############################################################################
## cmake builtin search paths and includes ####################################
###############################################################################
# low level initialization
include(${CMAKE_CURRENT_LIST_DIR}/__init__/__init__.cmake)
include(tacklelib/Project)
include(tacklelib/EnableTargetsExtension)
include(tacklelib/_3dparty/Qt)
###############################################################################
## builtin detection ##########################################################
###############################################################################
## CAUTION:
## Must be called before the `project(...)` to workaround the error:
## `CMake Error: CMake was unable to find a build program corresponding to "MinGW Makefiles". CMAKE_MAKE_PROGRAM is not set. You probably need to select a different build tool.`
## by declare respective variable BEFORE the project main initialization.
##
#tkl_preload_variables(-S -s "CMAKE_MAKE_PROGRAM" "PATH;CMAKE_MAKE_PROGRAM")
# builtin detection runs by the `project` function
project("tacklelib")
set(PROJECT_LIB_NAME tacklelib)
set(LIB_TARGET_NAME ${PROJECT_LIB_NAME})
set(TESTLIB_TARGET_NAME ${PROJECT_NAME}.testlib)
tkl_configure_environment(TACKLELIB_RUNTIME_LINK_TYPE "MSVC;GCC")
###############################################################################
## check environment variables ################################################
###############################################################################
tkl_check_var(REQUIRED . TACKLELIB_ADDRESS_MODEL)
tkl_check_var(REQUIRED . TACKLELIB_RUNTIME_LINK_TYPE)
tkl_check_var(REQUIRED . TACKLELIB_DEP_LIBS)
# utility
tkl_check_var(REQUIRED PATH BOOST_ROOT)
tkl_check_var(REQUIRED . Boost_ARCHITECTURE) # required for boost 1.66 and higher: https://gitlab.kitware.com/cmake/cmake/merge_requests/2568
tkl_check_var(REQUIRED STRING BOOST_COMPONENTS)
tkl_check_var(REQUIRED PATH FMT_ROOT)
# WORKAROUND:
# The issue:
# `CMakeLists.txt tries to override globally visible CMAKE_BUILD_TYPE through the cache` :
# https://github.com/libarchive/libarchive/issues/1163
#
tkl_register_package_var_set(LIBARCHIVE_ROOT CMAKE_BUILD_TYPE Release 0)
###############################################################################
## projects description #######################################################
###############################################################################
set(UNIT_TESTS_MAXOPT_TARGET_NAME tacklelib.unit_tests) # maximal compiler optimization, custom optimization
set(UNIT_TESTS_DEFOPT_TARGET_NAME tacklelib.unit_tests_defopt) # default compiler optimization, no custom optimization
# maximal compiler optimization, custom optimization, referenced project statically linked as library instead as sources
# (even more faster runtime because without unit test asserts from library)
set(UNIT_TESTS_LIBLINKED_TARGET_NAME tacklelib.unit_tests_liblinked)
set(BENCH_TESTS_MAXOPT_TARGET_NAME tacklelib.bench_tests) # maximal compiler optimization, custom optimization
set(BENCH_TESTS_DEFOPT_TARGET_NAME tacklelib.bench_tests_defopt) # default compiler optimization, no custom optimization
set(TESTS_TARGET_NAMES
${UNIT_TESTS_LIBLINKED_TARGET_NAME};
${UNIT_TESTS_MAXOPT_TARGET_NAME};${BENCH_TESTS_MAXOPT_TARGET_NAME};
${UNIT_TESTS_DEFOPT_TARGET_NAME};${BENCH_TESTS_DEFOPT_TARGET_NAME})
# common optimization targets
set(COMMON_OPT_APP_TARGET_NAMES
${LIB_TARGET_NAME};${TESTLIB_TARGET_NAME};${TACKLELIB_DEP_LIBS})
set(TESTS_TARGETS_SRC_DIR
unit;
unit;bench;
unit;bench)
#set(TESTS_TARGETS_PCH_SRC_DIR
# unit/liblinked;
# unit/maxopt;bench/maxopt;
# unit/defopt;bench/defopt)
set(TESTS_TARGETS_DEFINITIONS
"UNIT_TESTS\;MAXOPT\;LIBLINKED"
"UNIT_TESTS\;MAXOPT" "BENCH_TESTS\;MAXOPT\;LIBLINKED" # all bench tests always liblinked
"UNIT_TESTS\;DEFOPT" "BENCH_TESTS\;DEFOPT\;LIBLINKED")
# targets with maximal optimization
set(TESTS_MAXOPT_TARGETS
${UNIT_TESTS_LIBLINKED_TARGET_NAME};
${UNIT_TESTS_MAXOPT_TARGET_NAME};${BENCH_TESTS_MAXOPT_TARGET_NAME})
# targets with library project linkage
set(TESTS_TARGETS_LIBLINKED
1;
0;1
0;1)
# enable c++ standard usage for all targets, basically to avoid the default `--std=gnu++11` parameter for the GCC compiler
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
###############################################################################
## file globbing ##############################################################
###############################################################################
# public headers ONLY
file(GLOB_RECURSE public_headers
${CMAKE_CURRENT_LIST_DIR}/include/*.h*
)
# exclude .bak files
tkl_exclude_paths_from_path_list(. public_headers "${public_headers}" ".bak" 0)
# library
file(GLOB_RECURSE lib_headers
${CMAKE_CURRENT_LIST_DIR}/src/*.h*
)
file(GLOB_RECURSE lib_sources
${CMAKE_CURRENT_LIST_DIR}/src/*.c*
)
# testlib
file(GLOB_RECURSE testlib_headers
${CMAKE_CURRENT_LIST_DIR}/src/testlib/*.h*
)
file(GLOB_RECURSE testlib_sources
${CMAKE_CURRENT_LIST_DIR}/src/testlib/*.c*
)
# exclude .bak files
tkl_exclude_paths_from_path_list(. testlib_headers "${testlib_headers}" ".bak" 0)
tkl_exclude_paths_from_path_list(. testlib_sources "${testlib_sources}" ".bak" 0)
# tests
file(GLOB tests_headers_common
${CMAKE_CURRENT_LIST_DIR}/src/tests/*.h*
)
file(GLOB tests_sources_common
${CMAKE_CURRENT_LIST_DIR}/src/tests/*.c*
)
# exclude .bak files
tkl_exclude_paths_from_path_list(. tests_headers_common "${tests_headers_common}" ".bak" 0)
tkl_exclude_paths_from_path_list(. tests_sources_common "${tests_sources_common}" ".bak" 0)
# exclude tests
tkl_exclude_paths_from_path_list(. lib_sources "${lib_sources}" "/tests" 0)
tkl_exclude_paths_from_path_list(. lib_headers "${lib_headers}" "/tests" 0)
# exclude testlib
tkl_exclude_paths_from_path_list(. lib_sources "${lib_sources}" "/testlib" 0)
tkl_exclude_paths_from_path_list(. lib_headers "${lib_headers}" "/testlib" 0)
set(target_index 0)
foreach(target IN LISTS TESTS_TARGET_NAMES)
list(GET TESTS_TARGETS_SRC_DIR ${target_index} target_src_dir)
file(GLOB_RECURSE tests_sources_${target}
${CMAKE_CURRENT_LIST_DIR}/src/tests/${target_src_dir}/*.c*)
file(GLOB_RECURSE tests_headers_${target}
${CMAKE_CURRENT_LIST_DIR}/src/tests/${target_src_dir}/*.h*)
# exclude marked as "out of project" (/~) files/directories by pattern
tkl_exclude_paths_from_path_list(. tests_sources_${target} "${tests_sources_${target}}" "/~" 0)
tkl_exclude_paths_from_path_list(. tests_headers_${target} "${tests_headers_${target}}" "/~" 0)
# exclude .bak files
tkl_exclude_paths_from_path_list(. tests_sources_${target} "${tests_sources_${target}}" ".bak" 0)
tkl_exclude_paths_from_path_list(. tests_headers_${target} "${tests_headers_${target}}" ".bak" 0)
MATH(EXPR target_index "${target_index}+1")
endforeach()
###############################################################################
## find_package dependencies ##################################################
###############################################################################
# boost
find_package(BOOST_ROOT Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
if (Boost_FOUND)
message(STATUS "(*) Found `Boost`: `${Boost_VERSION}` Location: \"${BOOST_ROOT}\" Libs: [${Boost_LIBRARIES}]")
else()
message(WARNING "(*) Boost is not found")
endif()
# liblzma
if (LIBLZMA_LIBRARY)
find_package(LIBLZMA_LIBRARY LibLZMA)
if (LIBLZMA_FOUND)
message(STATUS "(*) Found `LibLZMA`: `${LIBLZMA_VERSION_STRING}` IncludeDirs: [${LIBLZMA_INCLUDE_DIRS}] Libs: [${LIBLZMA_LIBRARIES}]")
else()
message(WARNING "(*) LibLZMA is not found")
endif()
endif()
###############################################################################
## add_subdirectory dependencies ##############################################
###############################################################################
# test
set(TESTLIB_ENABLED 0)
set(TESTS_ENABLED 0)
if (GTEST_ROOT AND NOT (TACKLELIB_SKIP_TESTLIB AND TACKLELIB_SKIP_TESTS))
if (MSVC)
if (TACKLELIB_LINK_TYPE STREQUAL "dynamic")
set(GTEST_MSVC_SEARCH "MD") # if by `find_package`
set(BUILD_SHARED_LIBS ON) # if by `add_subdirectory`
elseif (TACKLELIB_LINK_TYPE STREQUAL "static")
set(GTEST_MSVC_SEARCH "MT") # if by `find_package`
set(BUILD_SHARED_LIBS OFF) # if by `add_subdirectory`
endif()
elseif (GCC)
# nothing is required
endif()
#find_package(GTest)
#if (GTEST_FOUND AND NOT TACKLELIB_SKIP_TESTLIB)
if (NOT TACKLELIB_SKIP_TESTLIB)
set(TESTLIB_ENABLED 1)
endif()
# automatic tests enable by specific source file
#if (GTEST_FOUND AND NOT TACKLELIB_SKIP_TESTS)
if (NOT TACKLELIB_SKIP_TESTS)
set(tests_sources_common_filtered ${tests_sources_common})
if (tests_sources_common_filtered)
list(FILTER tests_sources_common_filtered EXCLUDE REGEX "(.*)/test_main.cpp")
if (NOT "${tests_sources_common_filtered}" EQUAL "${tests_sources_common}")
tkl_add_target_subdirectory(GTEST_ROOT gtest _3dparty/test/googletest)
set(TESTS_ENABLED 1)
endif()
endif()
endif()
endif()
# fmt
tkl_add_target_subdirectory(FMT_ROOT fmt _3dparty/utility/fmt)
# fmt has it's own cmake list, so we have to call it from here
tkl_initialize_library_target_defaults(fmt "${TACKLELIB_ADDRESS_MODEL}bit")
# pystring
tkl_add_target_subdirectory(PYSTRING_PROXY_ROOT pystring _3dparty/utility/pystring)
# p7 logger
tkl_add_target_subdirectory(P7CLIENT_PROXY_ROOT p7client _3dparty/log/p7client)
if (TARGET p7client)
get_target_property(INCLUDE_DIRS_p7client p7client INCLUDE_DIRECTORIES)
get_target_property(LIBRARIES_p7client p7client LINK_LIBRARIES)
endif()
# 7zip
tkl_add_target_subdirectory(_7ZIP_ROOT 7zip _3dparty/arc/7zip)
if (TARGET 7zip)
get_target_property(INCLUDE_DIRS_7zip 7zip INCLUDE_DIRECTORIES)
get_target_property(LIBRARIES_7zip 7zip LINK_LIBRARIES)
endif()
# libarchive
tkl_add_target_subdirectory(LIBARCHIVE_ROOT libarchive _3dparty/arc/libarchive)
tkl_set_target_property(LIBARCHIVE_ROOT * * "^archive$$|^archive_static$$" * . EXCLUDE_FROM_DEFAULT_BUILD ON) # all except output libraries
tkl_set_target_property(LIBARCHIVE_ROOT * * "^archive$$|^archive_static$$" * . EXCLUDE_FROM_ALL ON) # all except output libraries
if (TARGET archive)
tkl_set_target_property(LIBARCHIVE_ROOT * "^archive$$" . * . EXCLUDE_FROM_DEFAULT_BUILD ON) # all except output libraries
tkl_set_target_property(LIBARCHIVE_ROOT * "^archive$$" . * . EXCLUDE_FROM_ALL ON) # all except output libraries
# libarchive has it's own cmake list, so we have to call it from here
if (TACKLELIB_ADDRESS_MODEL)
tkl_initialize_library_target_defaults(archive "${TACKLELIB_ADDRESS_MODEL}bit")
else()
tkl_initialize_library_target_defaults(archive "")
endif()
endif()
if (TARGET archive_static)
# libarchive has it's own cmake list, so we have to call it from here
if (TACKLELIB_ADDRESS_MODEL)
tkl_initialize_library_target_defaults(archive_static "${TACKLELIB_ADDRESS_MODEL}bit")
else()
tkl_initialize_library_target_defaults(archive_static "")
endif()
endif()
###############################################################################
## target definitions #########################################################
###############################################################################
# library
add_library(${LIB_TARGET_NAME} STATIC
${lib_sources};${lib_headers};${public_headers}
)
tkl_initialize_library_target_defaults(${LIB_TARGET_NAME} "${TACKLELIB_ADDRESS_MODEL}bit")
tkl_source_groups_from_dir_list("Header Files (interface)" FILES ${CMAKE_CURRENT_LIST_DIR}/include *.h*)
tkl_source_groups_from_dir_list("Header Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.h*)
tkl_source_groups_from_dir_list("Source Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.c*)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
if (FMT_ROOT)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${FMT_ROOT}/include
)
endif()
if (PYSTRING_ROOT)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${PYSTRING_ROOT}
)
endif()
if (Boost_INCLUDE_DIRS)
target_include_directories(${LIB_TARGET_NAME}
PRIVATE
${Boost_INCLUDE_DIRS}
)
endif()
if (INCLUDE_DIRS_p7client)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${INCLUDE_DIRS_p7client}
)
endif()
if (INCLUDE_DIRS_7zip)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${INCLUDE_DIRS_7zip}
)
endif()
if (LIBARCHIVE_ROOT)
target_include_directories(${LIB_TARGET_NAME}
PUBLIC
${LIBARCHIVE_ROOT}
)
endif()
if (LIBLZMA_FOUND)
target_include_directories(${LIB_TARGET_NAME}
PRIVATE
${LIBLZMA_INCLUDE_DIRS}
)
endif()
# to calculate relative path to source files in log output
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
LOG_SRC_ROOT="${LOG_SRC_ROOT}"
)
# to declare export/import from source files
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
LIBRARY_API
LIBRARY_API_EXPORTS_TACKLELIB
)
if (MINGW)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
MINGW
)
endif()
# we need the same Boost definitions here to maintain the link with the same libraries
if (Boost_FOUND)
if (BOOST_ALL_NO_LIB)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
BOOST_ALL_NO_LIB
)
endif()
if (BOOST_ALL_DYN_LINK)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
BOOST_ALL_DYN_LINK
)
endif()
if (BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS # Force to use C++11 lambda functions to implement scope exits
)
endif()
endif()
if (LIBARCHIVE_ROOT)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
LIBARCHIVE_STATIC
)
endif()
if (LIBLZMA_FOUND)
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
LZMA_API_STATIC
)
endif()
if (WIN32)
if (${CMAKE_SIZEOF_VOID_P} EQUAL "8")
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
_AMD64_ # in case of build x64 is required from Windows SDK: `Windows Kits\8.1\Include\shared\stralign.h(120): warning C4090: 'argument': different '__unaligned' qualifiers`
)
else()
tkl_add_target_compile_definitions(${LIB_TARGET_NAME} *
PRIVATE
_X86_ # in case of dynamic library linkage which is required from Windows SDK: `fatal error C1189: #error: "No Target Architecture"`
)
endif()
endif()
# CAUTION:
# The `-lpthread` flag MUST BE after local linking objects and libraries
# dependent on it!
# Either move it to the end of linker command line or use flag `-pthread`
# both for compiler and linker instead the `-lpthread` flag.
# See details here:
# https://stackoverflow.com/questions/19901934/strange-linking-error-dso-missing-from-command-line/19905704#19905704
# https://stackoverflow.com/questions/24814698/how-can-i-add-a-flag-at-the-end-of-the-linking-command-line-using-cmake
#
# CAUTION:
# C++11 is required for at least these:
# * tacklelib/utility/time.hpp: `error: ‘get_time’ is not a member of ‘std’`
# https://stackoverflow.com/questions/46456606/stdget-time-has-not-been-declared/46459130#46459130
#
if (GCC)
tkl_add_target_compile_properties(${LIB_TARGET_NAME} *
#--std=c++11 # minimal required standard
-pthread # compile dependency for `tacklelib/utility/time.hpp` to use `pthread_time.h`
)
tkl_add_target_link_properties(${LIB_TARGET_NAME} NOTSTATIC *
-pthread # compile dependency for `tacklelib/utility/time.hpp` to use `pthread_time.h`
)
endif()
if (Boost_LIBRARY_DIRS)
tkl_add_target_link_directories(${LIB_TARGET_NAME} *
PRIVATE
${Boost_LIBRARY_DIRS}
)
endif()
if (FMT_ROOT)
target_link_libraries(${LIB_TARGET_NAME}
PUBLIC
fmt
)
endif()
if (PYSTRING_ROOT)
target_link_libraries(${LIB_TARGET_NAME}
PUBLIC
pystring
)
endif()
if (Boost_FOUND)
target_link_libraries(${LIB_TARGET_NAME}
PRIVATE
${Boost_LIBRARIES}
)
endif()
if (TARGET p7client)
target_link_libraries(${LIB_TARGET_NAME}
PUBLIC
p7client
)
endif()
if (TARGET 7zip)
target_link_libraries(${LIB_TARGET_NAME}
PUBLIC
7zip
)
endif()
if (TARGET archive_static)
target_link_libraries(${LIB_TARGET_NAME}
PUBLIC
archive_static
)
endif()
if (LIBLZMA_FOUND)
target_link_libraries(${LIB_TARGET_NAME}
PRIVATE
${LIBLZMA_LIBRARIES}
)
endif()
if (WIN32)
target_link_libraries(${LIB_TARGET_NAME}
PRIVATE
Mpr
Netapi32
)
elseif (UNIX AND NOT APPLE)
target_link_libraries(${LIB_TARGET_NAME}
PRIVATE
${CMAKE_DL_LIBS}
)
endif()
# testlib
if (TESTLIB_ENABLED)
add_library(${TESTLIB_TARGET_NAME} STATIC
${testlib_sources};${testlib_headers};${public_headers}
)
tkl_initialize_library_target_defaults(${TESTLIB_TARGET_NAME} "${TACKLELIB_ADDRESS_MODEL}bit")
tkl_source_groups_from_dir_list("Header Files (interface)" FILES ${CMAKE_CURRENT_LIST_DIR}/include *.h*)
tkl_source_groups_from_dir_list("Header Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.h*)
tkl_source_groups_from_dir_list("Source Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.c*)
target_include_directories(${TESTLIB_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include
)
if (FMT_ROOT)
target_include_directories(${TESTLIB_TARGET_NAME}
PUBLIC
${FMT_ROOT}/include
)
endif()
if (PYSTRING_ROOT)
target_include_directories(${TESTLIB_TARGET_NAME}
PUBLIC
${PYSTRING_ROOT}
)
endif()
if (Boost_INCLUDE_DIRS)
target_include_directories(${TESTLIB_TARGET_NAME}
PRIVATE
${Boost_INCLUDE_DIRS}
)
endif()
# if (GTEST_INCLUDE_DIRS)
# target_include_directories(${TESTLIB_TARGET_NAME}
# PUBLIC
# ${GTEST_INCLUDE_DIRS}
# )
# endif()
tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
PUBLIC
TACKLE_TESTLIB
GTEST_LINKED_AS_SHARED_LIBRARY=1 # gtest links as dynamic by default
)
if (MINGW)
tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
PRIVATE
MINGW
)
endif()
# we need the same Boost definitions here to maintain the link with the same libraries
#if (Boost_FOUND)
# if (BOOST_ALL_NO_LIB)
# tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
# PRIVATE
# BOOST_ALL_NO_LIB
# )
# endif()
#
# if (BOOST_ALL_DYN_LINK)
# tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
# PRIVATE
# BOOST_ALL_DYN_LINK
# )
# endif()
#
# if (BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS)
# tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
# PRIVATE
# BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS # Force to use C++11 lambda functions to implement scope exits
# )
# endif()
#endif()
if (WIN32)
if (${CMAKE_SIZEOF_VOID_P} EQUAL "8")
tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
PRIVATE
_AMD64_ # in case of build x64 is required from Windows SDK: `Windows Kits\8.1\Include\shared\stralign.h(120): warning C4090: 'argument': different '__unaligned' qualifiers`
)
else()
tkl_add_target_compile_definitions(${TESTLIB_TARGET_NAME} *
PRIVATE
_X86_ # in case of dynamic library linkage which is required from Windows SDK: `fatal error C1189: #error: "No Target Architecture"`
)
endif()
endif()
if (Boost_LIBRARY_DIRS)
tkl_add_target_link_directories(${TESTLIB_TARGET_NAME} *
PRIVATE
${Boost_LIBRARY_DIRS}
)
endif()
# if (GTEST_ROOT)
# tkl_add_target_link_directories(${TESTLIB_TARGET_NAME} *
# PRIVATE
# ${GTEST_ROOT}/${_gtest_libpath_suffixes}
# )
# endif()
target_link_libraries(${TESTLIB_TARGET_NAME}
PRIVATE
${LIB_TARGET_NAME}
)
# if (GTEST_LIBRARIES)
# target_link_libraries(${TESTLIB_TARGET_NAME}
# PRIVATE
# ${GTEST_LIBRARIES}
# )
# endif()
if (TARGET gtest)
target_link_libraries(${TESTLIB_TARGET_NAME}
PRIVATE
gtest
)
endif()
else()
list(REMOVE_ITEM COMMON_OPT_APP_TARGET_NAMES ${TESTLIB_TARGET_NAME})
endif()
###############################################################################
## target optimization ########################################################
###############################################################################
# local optimization per target basis
if (MSVC)
tkl_add_target_compile_properties("${COMMON_OPT_APP_TARGET_NAMES}" RELEASE
/Ox # Full Optimization
/Ob2 # Inline Function Expansion: Any Suitable
#/Oi # Enable Intrinsic Functions
/Ot # Enable Intrinsic Functions
/GL # Whole Program Optimization
)
tkl_add_target_compile_properties("${COMMON_OPT_APP_TARGET_NAMES}" *
/MP
)
tkl_add_target_link_properties("${COMMON_OPT_APP_TARGET_NAMES}" * RELEASE
/LTCG # Use Link Time Code Generation
)
elseif (GCC)
tkl_add_target_compile_properties("${COMMON_OPT_APP_TARGET_NAMES}" RELEASE
-O3 # Full Optimization
#/usr/bin/ranlib: .cpp.o: plugin needed to handle lto object
#-flto # Use Link Time Code Generation
)
tkl_add_target_compile_properties("${COMMON_OPT_APP_TARGET_NAMES}" *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
#/usr/bin/ranlib: .cpp.o: plugin needed to handle lto object
#tkl_add_target_link_properties("${COMMON_OPT_APP_TARGET_NAMES}" NOTSTATIC RELEASE
# -flto # Use Link Time Code Generation
#)
tkl_add_target_link_properties("${COMMON_OPT_APP_TARGET_NAMES}" NOTSTATIC *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
endif()
###############################################################################
## tests ######################################################################
###############################################################################
if (TESTS_ENABLED)
message(STATUS "(*) Tests build: ${TESTS_TARGET_NAMES} (test_main.cpp).")
# inherit dependency include directories and libraries
get_target_property(INTERFACE_COMPILE_DEFINITIONS_${LIB_TARGET_NAME} ${LIB_TARGET_NAME} INTERFACE_COMPILE_DEFINITIONS)
get_target_property(INCLUDE_DIRS_${LIB_TARGET_NAME} ${LIB_TARGET_NAME} INCLUDE_DIRECTORIES)
tkl_get_target_link_libraries_recursively(LIBRARIES_${LIB_TARGET_NAME} ${LIB_TARGET_NAME})
set(target_index 0)
foreach(target IN LISTS TESTS_TARGET_NAMES)
list(GET TESTS_TARGET_NAMES ${target_index} target)
list(GET TESTS_TARGETS_SRC_DIR ${target_index} target_src_dir)
list(GET TESTS_TARGETS_DEFINITIONS ${target_index} target_definitions)
list(GET TESTS_TARGETS_LIBLINKED ${target_index} target_is_liblinked)
set(target_sources_common
${tests_sources_common};${tests_headers_common};
${tests_sources_${target}};${tests_headers_${target}};
${public_headers};${testlib_headers}
)
if (target_is_liblinked)
set(target_sources ${target_sources_common})
else()
set(target_sources ${target_sources_common};${lib_sources};${testlib_sources})
endif()
#tkl_add_pch_header(
# "test_common.hpp" "src/tests/${target_src_dir}/pch.cpp" "pch/${target}/$<CONFIG>/test_common.pch" # create
# "test_common.hpp" "src/tests/test_common.hpp" # use + force include
# "${tests_sources_${target}}" 0) # input + output
add_executable(${target}
${target_sources}
)
tkl_initialize_executable_target_defaults(${target} "${TACKLELIB_ADDRESS_MODEL}bit;console")
# exclude test_common.cpp from compilation to avoid symbols duplications from pch.cpp
#set_source_files_properties(src/tests/test_common.cpp PROPERTIES
# HEADER_FILE_ONLY TRUE)
set_source_files_properties(src/tests/unit/pch.cpp PROPERTIES
HEADER_FILE_ONLY TRUE)
set_source_files_properties(src/tests/bench/pch.cpp PROPERTIES
HEADER_FILE_ONLY TRUE)
tkl_source_groups_from_dir_list("Header Files (interface)" FILES ${CMAKE_CURRENT_LIST_DIR}/include *.h*)
tkl_source_groups_from_dir_list("Header Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.h*)
tkl_source_groups_from_dir_list("Source Files" FILES ${CMAKE_CURRENT_LIST_DIR}/src *.c*)
tkl_source_groups_from_dir_list("tests" FILES ${CMAKE_CURRENT_LIST_DIR}/src/tests *)
target_include_directories(${target}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/tests/${target}
${CMAKE_CURRENT_LIST_DIR}/src/tests
)
target_include_directories(${target}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/include
)
if (FMT_ROOT)
target_include_directories(${target}
PUBLIC
${FMT_ROOT}/include
)
endif()
if (PYSTRING_ROOT)
target_include_directories(${target}
PUBLIC
${PYSTRING_ROOT}
)
endif()
if (Boost_INCLUDE_DIRS)
target_include_directories(${target}
PRIVATE
${Boost_INCLUDE_DIRS}
)
endif()
if (INCLUDE_DIRS_p7client)
target_include_directories(${target}
PUBLIC
${INCLUDE_DIRS_p7client}
)
endif()
if (INCLUDE_DIRS_7zip)
target_include_directories(${target}
PUBLIC
${INCLUDE_DIRS_7zip}
)
endif()
if (LIBARCHIVE_ROOT)
target_include_directories(${target}
PRIVATE
${LIBARCHIVE_ROOT}
)
endif()
if (LIBLZMA_FOUND)
target_include_directories(${target}
PRIVATE
${LIBLZMA_INCLUDE_DIRS}
)
endif()
# if (GTEST_INCLUDE_DIRS)
# target_include_directories(${target}
# PRIVATE
# ${GTEST_INCLUDE_DIRS}
# )
# endif()
tkl_add_target_compile_definitions(${target} *
PRIVATE
${target_definitions}
)
# to calculate relative path to source files in log output
tkl_add_target_compile_definitions(${target} *
PRIVATE
LOG_SRC_ROOT="${LOG_SRC_ROOT}"
)
if (MINGW)
tkl_add_target_compile_definitions(${target} *
PRIVATE
MINGW
)
endif()
if (NOT target_is_liblinked)
# we must include these definitions to link dependency appropriately
if (LIBARCHIVE_ROOT)
tkl_add_target_compile_definitions(${target} *
PRIVATE
LIBARCHIVE_STATIC
)
endif()
if (LIBLZMA_FOUND)
tkl_add_target_compile_definitions(${target} *
PRIVATE
LZMA_API_STATIC
)
endif()
if (INTERFACE_COMPILE_DEFINITIONS_${LIB_TARGET_NAME})
tkl_add_target_compile_definitions(${target} *
PRIVATE
${INTERFACE_COMPILE_DEFINITIONS_${LIB_TARGET_NAME}}
)
endif()
endif()
# we need the same Boost definitions here to maintain the link with the same libraries
#if (Boost_FOUND)
# if (BOOST_ALL_NO_LIB)
# tkl_add_target_compile_definitions(${target} *
# PRIVATE
# BOOST_ALL_NO_LIB
# )
# endif()
#
# if (BOOST_ALL_DYN_LINK)
# tkl_add_target_compile_definitions(${target} *
# PRIVATE
# BOOST_ALL_DYN_LINK
# )
# endif()
#
# if (BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS)
# tkl_add_target_compile_definitions(${target} *
# PRIVATE
# BOOST_SCOPE_EXIT_CONFIG_USE_LAMBDAS # Force to use C++11 lambda functions to implement scope exits
# )
# endif()
#endif()
if (WIN32)
if (${CMAKE_SIZEOF_VOID_P} EQUAL "8")
tkl_add_target_compile_definitions(${target} *
PRIVATE
_AMD64_ # in case of build x64 is required from Windows SDK: `Windows Kits\8.1\Include\shared\stralign.h(120): warning C4090: 'argument': different '__unaligned' qualifiers`
)
else()
tkl_add_target_compile_definitions(${target} *
PRIVATE
_X86_ # in case of dynamic library linkage which is required from Windows SDK: `fatal error C1189: #error: "No Target Architecture"`
)
endif()
endif()
# local optimization per target basis
if (${target} IN_LIST TESTS_MAXOPT_TARGETS)
if (MSVC)
tkl_add_target_compile_properties(${target} RELEASE
/Ox # Full Optimization
/Ob2 # Inline Function Expansion: Any Suitable
#/Oi # Enable Intrinsic Functions
/Ot # Enable Intrinsic Functions
/GL # Whole Program Optimization
)
tkl_add_target_compile_properties(${target} *
/MP
)
tkl_add_target_link_properties(${target} * RELEASE
/LTCG # Use Link Time Code Generation
)
elseif (GCC)
tkl_add_target_compile_properties(${target} RELEASE
-O3 # Full Optimization
#/usr/bin/ranlib: .cpp.o: plugin needed to handle lto object
#-flto # Use Link Time Code Generation
)
tkl_add_target_compile_properties(${target} *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
#/usr/bin/ranlib: .cpp.o: plugin needed to handle lto object
#tkl_add_target_link_properties(${target} NOTSTATIC RELEASE
# -flto # Use Link Time Code Generation
#)
tkl_add_target_link_properties(${target} NOTSTATIC *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
endif()
else()
if (MSVC)
tkl_add_target_compile_properties(${target} RELEASE
/O2 # Maximize Speed
)
tkl_add_target_compile_properties(${target} *
/MP
)
elseif (GCC)
tkl_add_target_compile_properties(${target} RELEASE
-O2 # More Stable Optimization
)
tkl_add_target_compile_properties(${target} *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
tkl_add_target_link_properties(${target} NOTSTATIC *
-pipe # Use pipes rather than temporary files for communication between the various stages of compilation.
)
endif()
endif()
tkl_get_target_compile_property(COMPILE_OPTIONS_${target} ${target} .)
tkl_get_target_link_property(LINK_FLAGS_${target} ${target} .)
tkl_get_target_link_property(LINK_FLAGS_${target}_DEBUG ${target} DEBUG)
tkl_get_target_link_property(LINK_FLAGS_${target}_RELEASE ${target} RELEASE)
tkl_get_target_link_property(LINK_FLAGS_${target}_MINSIZEREL ${target} MINSIZEREL)
tkl_get_target_link_property(LINK_FLAGS_${target}_RELWITHDEBINFO ${target} RELWITHDEBINFO)
tkl_print_flags(
COMPILE_OPTIONS_${target}
LINK_FLAGS_${target}
LINK_FLAGS_${target}_DEBUG
LINK_FLAGS_${target}_RELEASE
LINK_FLAGS_${target}_MINSIZEREL
LINK_FLAGS_${target}_RELWITHDEBINFO
)
if (Boost_LIBRARY_DIRS)
tkl_add_target_link_directories(${target} *
PRIVATE
${Boost_LIBRARY_DIRS}
)
endif()
# if (GTEST_ROOT)
# tkl_add_target_link_directories(${target} *
# PRIVATE
# ${GTEST_ROOT}/${_gtest_libpath_suffixes}
# )
# endif()
# libraries must be different in case of the library linkage or direct library sources usage
if (target_is_liblinked)
target_link_libraries(${target}
PRIVATE
${LIB_TARGET_NAME}
)
else()
# TODO:
# replace target_link_libraries by a custom implementation with arbitrary exclusion of specific libraries
#
target_link_libraries(${target}
PRIVATE
${LIBRARIES_${LIB_TARGET_NAME}}
)
endif()
target_link_libraries(${target}
PRIVATE
${TESTLIB_TARGET_NAME}
)
if (Boost_LIBRARIES)
target_link_libraries(${target}
PRIVATE
${Boost_LIBRARIES}
)
endif()
# if (GTEST_LIBRARIES)
# target_link_libraries(${target}
# PRIVATE
# ${GTEST_LIBRARIES}
# )
# endif()
if (TARGET gtest)
target_link_libraries(${target}
PRIVATE
gtest
)
endif()
MATH(EXPR target_index "${target_index}+1")
endforeach()
else()
message(STATUS "(*) Tests build skipped.")
endif()
###############################################################################
## packaging ##################################################################
###############################################################################
# All install commands get the same destination. this allows us to use paths
# relative to the executable.
install(TARGETS ${LIB_TARGET_NAME} DESTINATION $<CONFIGURATION>)
if (TARGET TESTLIB_TARGET_NAME)
install(TARGETS ${TESTLIB_TARGET_NAME} DESTINATION $<CONFIGURATION>)
endif()
## must be after all `install` commands!
##
#install(CODE "
# include(BundleUtilities)
# fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/\${CMAKE_INSTALL_CONFIG_NAME}/${EXE_TARGET_NAME}${CMAKE_EXECUTABLE_SUFFIX}\" \"\" \"${Boost_LIBRARY_DIRS}\")
# " COMPONENT ${LIB_TARGET_NAME} ${QT_LIBRARIES})
#if (${TESTS_ENABLED})
# set(target_index 0)
# foreach(target IN LISTS TESTS_TARGET_NAMES)
# list(GET TESTS_TARGET_NAMES ${target_index} target)
#
# install(TARGETS ${target} DESTINATION $<CONFIGURATION>)
#
# #install(CODE "
# # include(BundleUtilities)
# # fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/\${CMAKE_INSTALL_CONFIG_NAME}/${target}${CMAKE_EXECUTABLE_SUFFIX}\" \"\" \"${Boost_LIBRARY_DIRS}\")
# # " COMPONENT ${target})
#
# MATH(EXPR target_index "${target_index}+1")
# endforeach()
#endif()
## Now comes everything we need, to create a package
## there are a lot more variables you can set, and some
## you need to set for some package types, but we want to
## be minimal here.
#set(CPACK_PACKAGE_VERSION "1.0.0.0")
#
##set(CPACK_PACKAGE_FILE_NAME "${PROJECT_LIB_NAME}-${CPACK_PACKAGE_VERSION}-win32-$<CONFIGURATION>")
##set(CPACK_PACKAGE_NAME "${LIB_TARGET_NAME}")
#
## We don't want to split our program up into several incomplete pieces.
#set(CPACK_MONOLITHIC_INSTALL 1)
#
#set(CPACK_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/BundleConfig.cmake")
#
#include(CPack)
#
#set(CPACK_BUNDLE_TEMPLATE_CONFIG_FILE "${CMAKE_BINARY_DIR}/CPackConfig.cmake.in")
#set(CPACK_BUNDLE_OUTPUT_CONFIG_FILE "${CMAKE_BINARY_DIR}/CPackProperties.cmake")
#
## make cpack configuration template for later replacements with the expression generator support
#file(WRITE "${CPACK_BUNDLE_TEMPLATE_CONFIG_FILE}" "")
#file(APPEND "${CPACK_BUNDLE_TEMPLATE_CONFIG_FILE}" "set(CPACK_PACKAGE_FILE_NAME \"\${CPACK_PACKAGE_FILE_NAME}\")\n")
#
#add_custom_target(bundle
# COMMAND ${CMAKE_COMMAND}
# # this one must be written as is, DO NOT put the `$<CONFIGURATION>` inside a variable!
# -D "CPACK_PACKAGE_FILE_NAME=${PROJECT_LIB_NAME}-${CPACK_PACKAGE_VERSION}-win32-$<CONFIGURATION>"
# -D "CPACK_BUNDLE_TEMPLATE_CONFIG_FILE=${CPACK_BUNDLE_TEMPLATE_CONFIG_FILE}"
# -D "CPACK_BUNDLE_OUTPUT_CONFIG_FILE=${CPACK_BUNDLE_OUTPUT_CONFIG_FILE}"
# # this one must be after all `-D`s
# -P "${CMAKE_CURRENT_LIST_DIR}/cmake/tacklelib/tools/CPackMakeConfig.cmake"
# COMMAND "${CMAKE_CPACK_COMMAND}"
# -G "NSIS"
# -C "$<CONFIGURATION>"
# --config "${CPACK_OUTPUT_CONFIG_FILE}")
###############################################################################
## project folders ############################################################
###############################################################################
## projects
tkl_set_target_folder(CMAKE_CURRENT_LIST_DIR . * . UTILITY . util)
tkl_set_target_folder(CMAKE_CURRENT_LIST_DIR . * "tests" EXECUTABLE . exe)
tkl_set_target_folder(CMAKE_CURRENT_LIST_DIR . * . "SHARED_LIBRARY;STATIC_LIBRARY" . lib)
## 3dparty
# utility
tkl_set_target_folder(BOOST_ROOT * * . * . _3dparty/utility/boost)
tkl_set_target_folder(FMT_ROOT * * . * . _3dparty/utility/fmt)
tkl_set_target_folder(PYSTRING_PROXY_ROOT * * . * . _3dparty/utility/pystring)
# math
tkl_set_target_folder(QD_PROXY_ROOT * * . * . _3dparty/math/qd)
# log
tkl_set_target_folder(P7CLIENT_PROXY_ROOT * * . * . _3dparty/log/p7client)
# arc
tkl_set_target_folder(LIBARCHIVE_ROOT * * . UTILITY . _3dparty/arc/libarchive/util)
tkl_set_target_folder(LIBARCHIVE_ROOT * * . EXECUTABLE . _3dparty/arc/libarchive/exe)
tkl_set_target_folder(LIBARCHIVE_ROOT * * . "SHARED_LIBRARY;STATIC_LIBRARY" . _3dparty/arc/libarchive/lib)
tkl_set_target_folder(LIBARCHIVE_ROOT * * . * "UTILITY;EXECUTABLE;SHARED_LIBRARY;STATIC_LIBRARY" _3dparty/arc/libarchive)
tkl_set_target_folder(XZUTILS_PROXY_ROOT * * . * . _3dparty/arc/xz)
tkl_set_target_folder(_7ZIP_ROOT * * . * . _3dparty/arc/7zip)
# tests
tkl_set_target_folder(GTEST_ROOT * * . * . _3dparty/test/googletest)
tkl_set_target_folder(CMAKE_CURRENT_LIST_DIR . "tests" . * . tests)
tkl_set_target_folder(CMAKE_CURRENT_LIST_DIR . "build_check" . * . tests)
###############################################################################
## updated global flags and post process target properties ####################
###############################################################################
# to avoid cmake autogen specific warnings
if (generated_headers)
set_property(SOURCE ${generated_headers}
PROPERTY SKIP_AUTOGEN ON
PROPERTY GENERATED ON
)
# to avoid cmake errors generation on source file absence
set_source_files_properties(${generated_headers}
PROPERTIES
SKIP_AUTOGEN ON
GENERATED ON
)
endif()