cmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # prevent multiple inclusion if (DEFINED THIRD_PARTY_MODULE_LOADED) return() endif () set(THIRD_PARTY_MODULE_LOADED YES) cmake_policy(SET CMP0067 NEW) # make check_cxx_source_compiles() pick up the variables for the C++ version include(CheckCXXSourceCompiles) macro (save_default_library_suffixes) set(DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) endmacro () macro (restore_default_library_suffixes) set(CMAKE_FIND_LIBRARY_SUFFIXES ${DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES}) unset(DEFAULT_CMAKE_FIND_LIBRARY_SUFFIXES) endmacro () macro (configure_static_library_suffixes) # allows to look for static libraries in particular NOTE: code duplicated in Config.cmake.in if (WIN32) set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib) else () set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif () endmacro () macro (configure_dynamic_library_suffixes) # allows to look for dynamic libraries in particular if (WIN32) set(CMAKE_FIND_LIBRARY_SUFFIXES .dll .dll.a) elseif (APPLE) set(CMAKE_FIND_LIBRARY_SUFFIXES .dylib .so) else () set(CMAKE_FIND_LIBRARY_SUFFIXES .so) endif () endmacro () function (validate_visibility VISIBILITY) if (NOT (VISIBILITY STREQUAL PUBLIC OR VISIBILITY STREQUAL PRIVATE)) message(FATAL_ERROR "Specified visibility ${VISIBILITY} is invalid (must be either PUBLIC or PRIVATE).") endif () endfunction () function (parse_arguments_for_use_functions) # parse arguments set(OPTIONAL_ARGS OPTIONAL ONLY_HEADERS) set(ONE_VALUE_ARGS VISIBILITY LIBRARIES_VARIABLE PACKAGES_VARIABLE PKG_CONFIG_MODULES_VARIABLE TARGET_NAME PACKAGE_NAME) set(MULTI_VALUE_ARGS PKG_CONFIG_MODULES PACKAGE_ARGS) cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) # validate values if (ARGS_VISIBILITY) validate_visibility(${ARGS_VISIBILITY}) else () set(ARGS_VISIBILITY PRIVATE) endif () if (NOT ARGS_LIBRARIES_VARIABLE) set(ARGS_LIBRARIES_VARIABLE "${ARGS_VISIBILITY}_LIBRARIES") endif () 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_PACKAGE_NAME "${ARGS_PACKAGE_NAME}" PARENT_SCOPE) set(ARGS_PACKAGE_ARGS "${ARGS_PACKAGE_ARGS}" PARENT_SCOPE) set(ARGS_PKG_CONFIG_MODULES "${ARGS_PKG_CONFIG_MODULES}" PARENT_SCOPE) set(ARGS_OPTIONAL "${ARGS_OPTIONAL}" PARENT_SCOPE) set(ARGS_ONLY_HEADERS "${ARGS_ONLY_HEADERS}" PARENT_SCOPE) if (NOT ARGS_OPTIONAL) set(ARGS_FIND_PACKAGE "REQUIRED" PARENT_SCOPE) set(ARGS_PKG_CHECK_MODULES "REQUIRED" PARENT_SCOPE) endif () endfunction () function (use_iconv) parse_arguments_for_use_functions(${ARGN}) # check whether iconv from the standard library can be used set(FORCE_EXTERNAL_ICONV OFF CACHE PATH "whether to force usage of external iconv (rather than the using the one bundled with glibc)") if (NOT FORCE_EXTERNAL_ICONV) # check whether iconv exists in standard lib include(CheckFunctionExists) check_function_exists(iconv HAS_ICONV) endif () if (NOT FORCE_EXTERNAL_ICONV AND HAS_ICONV) message(STATUS "Using iconv from the standard library for target ${META_PROJECT_NAME}.") return() endif () # find external iconv library if (NOT TARGET Iconv::Iconv) set(Iconv_IS_BUILT_IN FALSE) find_package(Iconv ${ARGS_FIND_PACKAGE}) if (NOT Iconv_FOUND) return() endif () endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};Iconv::Iconv" PARENT_SCOPE) set("${ARGS_PACKAGES_VARIABLE}" "${${ARGS_PACKAGES_VARIABLE}};Iconv" PARENT_SCOPE) endfunction () macro (_cpp_utilities_use_openssl OPENSSL_TARGETS) find_package(OpenSSL ${ARGS_FIND_PACKAGE}) if (NOT OpenSSL_FOUND) message(STATUS "Unable to find OpenSSL") return() endif () foreach (OPENSSL_TARGET ${OPENSSL_TARGETS}) if (TARGET "${OPENSSL_TARGET}") continue() endif () set(MESSAGE_MODE WARNING) if (REQUIRED IN_LIST ARGS_FIND_PACKAGE) set(MESSAGE_MODE FATAL_ERROR) endif () message( "${MESSAGE_MODE}" "Found OpenSSL but imported target \"${OPENSSL_TARGET}\" is missing. Possibly the devel package for OpenSSL is not installed." ) return() endforeach () message(STATUS "Found required OpenSSL targets (${OPENSSL_TARGETS})") set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${OPENSSL_TARGETS}") if (WIN32 AND OPENSSL_USE_STATIC_LIBS) # FIXME: preferably use pkg-config to cover this case without hardcoding OpenSSL's dependencies under Windows set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};-lws2_32;-lgdi32;-lcrypt32") endif () set("${ARGS_PACKAGES_VARIABLE}" "${${ARGS_PACKAGES_VARIABLE}};OpenSSL" PARENT_SCOPE) set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}}" PARENT_SCOPE) set("PKG_CONFIG_OpenSSL_Crypto" "libcrypto" PARENT_SCOPE) endmacro () function (use_openssl) parse_arguments_for_use_functions(${ARGN}) _cpp_utilities_use_openssl("OpenSSL::SSL;OpenSSL::Crypto") set("PKG_CONFIG_OpenSSL_SSL" "libssl" PARENT_SCOPE) endfunction () function (use_crypto) parse_arguments_for_use_functions(${ARGN}) _cpp_utilities_use_openssl("OpenSSL::Crypto") endfunction () function (use_zlib) parse_arguments_for_use_functions(${ARGN}) find_package(ZLIB ${ARGS_FIND_PACKAGE}) if (NOT ZLIB_FOUND) return() endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};ZLIB::ZLIB" PARENT_SCOPE) set("${ARGS_PACKAGES_VARIABLE}" "${${ARGS_PACKAGES_VARIABLE}};ZLIB" PARENT_SCOPE) set("PKG_CONFIG_ZLIB_ZLIB" "zlib" PARENT_SCOPE) endfunction () function (use_target) parse_arguments_for_use_functions(${ARGN}) if (NOT TARGET "${ARGS_TARGET_NAME}") if (ARGS_OPTIONAL) return() endif () message(FATAL_ERROR "Target \"${ARGS_TARGET_NAME}\" does not exist.") endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${ARGS_TARGET_NAME}" PARENT_SCOPE) if (ARGS_PACKAGE_NAME) set("${ARGS_PACKAGES_VARIABLE}" "${${ARGS_PACKAGES_VARIABLE}};${ARGS_PACKAGE_NAME}" PARENT_SCOPE) if (ARGS_PACKAGE_ARGS) set("PACKAGE_ARGS_${ARGS_PACKAGE_NAME}" "${ARGS_PACKAGE_ARGS}" PARENT_SCOPE) endif () endif () endfunction () function (use_package) parse_arguments_for_use_functions(${ARGN}) if (NOT ARGS_PACKAGE_NAME) message(FATAL_ERROR "No PACKAGE_NAME specified.") endif () if (NOT ARGS_TARGET_NAME) message(FATAL_ERROR "No TARGET_NAME specified.") endif () if (NOT ARGS_PACKAGE_ARGS) set(ARGS_PACKAGE_ARGS ${ARGS_FIND_PACKAGE}) endif () find_package("${ARGS_PACKAGE_NAME}" ${ARGS_PACKAGE_ARGS}) if (NOT TARGET "${ARGS_TARGET_NAME}") if (ARGS_OPTIONAL) return() endif () message(FATAL_ERROR "Found package \"${ARGS_PACKAGE_NAME}\" but target \"${ARGS_TARGET_NAME}\" does not exist.") endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${ARGS_TARGET_NAME}" PARENT_SCOPE) set("${ARGS_PACKAGES_VARIABLE}" "${${ARGS_PACKAGES_VARIABLE}};${ARGS_PACKAGE_NAME}" PARENT_SCOPE) set("PACKAGE_ARGS_${ARGS_PACKAGE_NAME}" "${ARGS_PACKAGE_ARGS}" 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 () # add target only if it has not already been added; otherwise just add the existing target to the library variable if (NOT TARGET "${ARGS_TARGET_NAME}") 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}") endif () 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 () function (use_standard_filesystem) # parse and validate arguments parse_arguments_for_use_functions(${ARGN}) # set c++ standard for `check_cxx_source_compiles()` set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED 17) # check whether an additional library for std::filesystem support is required set(TEST_PROGRAM [[ #include #include #include int main() { auto ec = std::error_code(); const auto cwd = std::filesystem::current_path(); const auto t = std::filesystem::last_write_time(cwd, ec); std::filesystem::last_write_time(cwd, t, ec); return static_cast(cwd.string().size()); } ]]) set(DEFAULT_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(REQUIRED_LIBRARY FAILED) set(INDEX 0) foreach (LIBRARY "" "stdc++fs" "c++fs") if (NOT LIBRARY STREQUAL "") set(CMAKE_REQUIRED_LIBRARIES ${DEFAULT_REQUIRED_LIBRARIES} -l${LIBRARY}) endif () check_cxx_source_compiles("${TEST_PROGRAM}" STD_FILESYSTEM_TEST_${INDEX}) if (STD_FILESYSTEM_TEST_${INDEX}) set(REQUIRED_LIBRARY "${LIBRARY}") break() endif () math(EXPR INDEX "${INDEX}+1") endforeach () # handle error if (REQUIRED_LIBRARY STREQUAL "FAILED") message( FATAL_ERROR "Unable to compile a simple std::filesystem example. A compiler supporting C++17 is required to build this project." ) return() endif () # handle case when no library is required if (REQUIRED_LIBRARY STREQUAL "") message(STATUS "Linking ${META_PROJECT_NAME} against special library for std::filesystem support is not required.") return() endif () # prefer the static version of the library because the ABI might not be stable (note: stdc++fs seems to be only available # as static lib anyways) configure_static_library_suffixes() set(USED_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) find_library(STANDARD_FILE_SYSTEM_LIBRARY "${REQUIRED_LIBRARY}") configure_dynamic_library_suffixes() if (NOT STANDARD_FILE_SYSTEM_LIBRARY) # fallback to using -l if the library is hidden in some sub directory set(STANDARD_FILE_SYSTEM_LIBRARY "-l${REQUIRED_LIBRARY}") endif () message( STATUS "Linking ${META_PROJECT_NAME} against library \"${STANDARD_FILE_SYSTEM_LIBRARY}\" for std::filesystem support.") set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${STANDARD_FILE_SYSTEM_LIBRARY}" PARENT_SCOPE) endfunction () # skip subsequent configuration if only the function includes are wanted if (META_NO_3RDPARTY_CONFIG) return() endif () # add options for deciding whether to build/use static or shared libraries if (("${META_PROJECT_TYPE}" STREQUAL "library") OR ("${META_PROJECT_TYPE}" STREQUAL "plugin") OR ("${META_PROJECT_TYPE}" STREQUAL "qtplugin") OR ("${META_PROJECT_TYPE}" STREQUAL "")) set(META_PROJECT_IS_LIBRARY YES) elseif ("${META_PROJECT_TYPE}" STREQUAL "application") set(META_PROJECT_IS_APPLICATION YES) endif () if (META_PROJECT_IS_LIBRARY) # when development defaults are enabled, build shared libs by default when targeting GNU/Linux set(BUILD_SHARED_LIBS_BY_DEFAULT "${BUILD_SHARED_LIBS}") if (ENABLE_DEVEL_DEFAULTS AND CMAKE_SYSTEM_NAME STREQUAL "Linux") set(BUILD_SHARED_LIBS_BY_DEFAULT ON) endif () if (DEFINED CACHE{${META_PROJECT_VARNAME}_BUILD_SHARED_LIBS}) # allow overriding BUILD_SHARED_LIBS via a project-specific cache variable set(BUILD_SHARED_LIBS "${${META_PROJECT_VARNAME}_BUILD_SHARED_LIBS}") else () # make BUILD_SHARED_LIBS an overridable cache variable option(BUILD_SHARED_LIBS "whether to build shared or static libraries" "${BUILD_SHARED_LIBS_BY_DEFAULT}") endif () option( STATIC_LIBRARY_LINKAGE "prefer linking against dependencies statically; adds additional flags for static linkage; only applies when building shared libraries" OFF) elseif (META_PROJECT_IS_APPLICATION) option( STATIC_LINKAGE "prefer linking against dependencies statically; adds additional flags for static linkage; only applies when building applications" OFF) endif () # configure "static linkage" if ((STATIC_LINKAGE AND META_PROJECT_IS_APPLICATION) OR (STATIC_LIBRARY_LINKAGE AND META_PROJECT_IS_LIBRARY)) set(STATIC_LINKAGE_CONFIGURED ON) # add options to opt out from linking statically against the C and C++ standard library as it might not work under all # platforms (see https://github.com/Martchus/syncthingtray/issues/64) option(NO_STATIC_LIBGCC "prevent linking statically against libgcc despite aiming otherwise for a statically linked build" OFF) option(NO_STATIC_LIBSTDCXX "prevent linking statically against libstdc++ despite aiming otherwise for a statically linked build" OFF) # add additional linker flags to achieve a fully statically linked build set(STATIC_LINKAGE_LINKER_FLAGS) if (NOT APPLE) list(APPEND STATIC_LINKAGE_LINKER_FLAGS -static) # note: The -static flag is considered completely unsupported under Apple platforms (see # https://stackoverflow.com/questions/844819/how-to-static-link-on-os-x). endif () if (NOT NO_STATIC_LIBGCC) list(APPEND STATIC_LINKAGE_LINKER_FLAGS -static-libgcc) endif () if (NOT NO_STATIC_LIBSTDCXX) list(APPEND STATIC_LINKAGE_LINKER_FLAGS -static-libstdc++) endif () if (META_PROJECT_IS_APPLICATION) list(APPEND META_ADDITIONAL_LINK_FLAGS ${STATIC_LINKAGE_LINKER_FLAGS}) endif () list(APPEND META_ADDITIONAL_LINK_FLAGS_TEST_TARGET ${STATIC_LINKAGE_LINKER_FLAGS}) # 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 () set(STATIC_LINKAGE_CONFIGURED OFF) endif ()