cmake_minimum_required(VERSION 3.12 FATAL_ERROR) if(POLICY CMP0092) cmake_policy(SET CMP0092 NEW) endif() if(POLICY CMP0114) cmake_policy(SET CMP0114 NEW) endif() # Value should follow latest stable Xcode's RECOMMENDED_MACOSX_DEPLOYMENT_TARGET set(MACOS_SUPPORT_MINIMUM 10.14.6) # The value of this variable should be set prior to the first project() command invocation. # See: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET ${MACOS_SUPPORT_MINIMUM} CACHE STRING "Minimum macOS version to target for deployment" FORCE) endif() project(transmission) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) set(CMAKE_MACOSX_RPATH ON) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckFunctionExists) include(CheckLibraryExists) include(ExternalProject) include(GNUInstallDirs) include(TrMacros) set(CURL_MINIMUM 7.28.0) set(WOLFSSL_MINIMUM 3.4) set(DEFLATE_MINIMUM 1.7) set(EVENT2_MINIMUM 2.1.0) set(GIOMM_MINIMUM 2.26.0) set(GLIBMM_MINIMUM 2.60.0) set(GTKMM3_MINIMUM 3.24.0) set(GTKMM4_MINIMUM 4.11.1) set(OPENSSL_MINIMUM 0.9.7) set(MBEDTLS_MINIMUM 1.3) set(NPM_MINIMUM 8.1.307) # Node.js 14 set(PSL_MINIMUM 0.21.1) set(QT_MINIMUM 5.6) option(ENABLE_DAEMON "Build daemon" ON) tr_auto_option(ENABLE_GTK "Build GTK client" AUTO) tr_auto_option(ENABLE_QT "Build Qt client" AUTO) tr_auto_option(ENABLE_MAC "Build Mac client" AUTO) tr_auto_option(REBUILD_WEB "Rebuild the web client's generated assets. Requires Node.js and network access." OFF) option(INSTALL_WEB "Install the web client's generated assets." ON) option(ENABLE_UTILS "Build utils (create, edit, show)" ON) option(ENABLE_CLI "Build command-line client" OFF) option(ENABLE_TESTS "Build unit tests" ON) option(ENABLE_UTP "Build µTP support" ON) option(ENABLE_WERROR "Treat warnings as errors" OFF) option(ENABLE_NLS "Enable native language support" ON) option(INSTALL_DOC "Build/install documentation" ON) option(INSTALL_LIB "Install the library" OFF) tr_auto_option(ENABLE_DEPRECATED "Allow deprecated API use of upstream packages, e.g. GTK" AUTO) tr_auto_option(RUN_CLANG_TIDY "Run clang-tidy on the code" OFF) tr_auto_option(USE_SYSTEM_EVENT2 "Use system event2 library" AUTO) tr_auto_option(USE_SYSTEM_DEFLATE "Use system deflate library" AUTO) tr_auto_option(USE_SYSTEM_DHT "Use system dht library" AUTO) tr_auto_option(USE_SYSTEM_MINIUPNPC "Use system miniupnpc library" AUTO) tr_auto_option(USE_SYSTEM_NATPMP "Use system natpmp library" AUTO) tr_auto_option(USE_SYSTEM_UTP "Use system utp library" AUTO) tr_auto_option(USE_SYSTEM_B64 "Use system b64 library" AUTO) tr_auto_option(USE_SYSTEM_PSL "Use system psl library" AUTO) tr_list_option(USE_GTK_VERSION "Use specific GTK version" AUTO 3 4) tr_list_option(USE_QT_VERSION "Use specific Qt version" AUTO 5 6) tr_list_option(WITH_CRYPTO "Use specified crypto library" AUTO ccrypto mbedtls openssl wolfssl) tr_auto_option(WITH_INOTIFY "Enable inotify support (on systems that support it)" AUTO) tr_auto_option(WITH_KQUEUE "Enable kqueue support (on systems that support it)" AUTO) tr_auto_option(WITH_APPINDICATOR "Use appindicator for system tray icon in GTK client (GTK+ 3 only)" AUTO) tr_auto_option(WITH_SYSTEMD "Add support for systemd startup notification (on systems that support it)" AUTO) set(TR_NAME ${PROJECT_NAME}) # major.minor.patch[-[beta.N.]dev]+commit_hash # dev builds come between releases, e.g. autobuilds from CI # these should be the only five lines you need to change set(TR_VERSION_MAJOR "4") set(TR_VERSION_MINOR "1") set(TR_VERSION_PATCH "0") set(TR_VERSION_BETA_NUMBER "") # empty string for not beta set(TR_VERSION_DEV TRUE) # derived from above: release type if(TR_VERSION_DEV) set(TR_NIGHTLY_RELEASE 1) elseif(NOT "${TR_VERSION_BETA_NUMBER}" STREQUAL "") set(TR_BETA_RELEASE 1) else() set(TR_STABLE_RELEASE 1) endif() # derived from above: semver version string. https://semver.org/ # '4.0.0-beta.1' # '4.0.0-beta.1.dev' (a dev release between beta 1 and 2) # '4.0.0-beta.2' # '4.0.0' set(TR_SEMVER "${TR_VERSION_MAJOR}.${TR_VERSION_MINOR}.${TR_VERSION_PATCH}") if(TR_VERSION_DEV OR NOT "${TR_VERSION_BETA_NUMBER}" STREQUAL "") string(APPEND TR_SEMVER "-") if(NOT "${TR_VERSION_BETA_NUMBER}" STREQUAL "") string(APPEND TR_SEMVER "beta.${TR_VERSION_BETA_NUMBER}") endif() if(TR_VERSION_DEV AND NOT "${TR_VERSION_BETA_NUMBER}" STREQUAL "") string(APPEND TR_SEMVER ".") endif() if(TR_VERSION_DEV) string(APPEND TR_SEMVER "dev") endif() endif() set(TR_USER_AGENT_PREFIX "${TR_SEMVER}") # derived from above: peer-id prefix. https://www.bittorrent.org/beps/bep_0020.html # chars 4, 5, 6 are major, minor, patch in https://en.wikipedia.org/wiki/Base62 # char 7 is '0' for a stable release, 'B' for a beta release, or 'Z' for a dev build # '-TR400B-' (4.0.0 Beta) # '-TR400Z-' (4.0.0 Dev) # '-TR4000-' (4.0.0) set(TR_PEER_ID_PREFIX "-TR") set(BASE62 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") string(SUBSTRING "${BASE62}" "${TR_VERSION_MAJOR}" 1 TMPSTR) string(APPEND TR_PEER_ID_PREFIX "${TMPSTR}") string(SUBSTRING "${BASE62}" "${TR_VERSION_MINOR}" 1 TMPSTR) string(APPEND TR_PEER_ID_PREFIX "${TMPSTR}") string(SUBSTRING "${BASE62}" "${TR_VERSION_PATCH}" 1 TMPSTR) string(APPEND TR_PEER_ID_PREFIX "${TMPSTR}") if(TR_VERSION_DEV) string(APPEND TR_PEER_ID_PREFIX "Z") elseif(NOT "${TR_VERSION_BETA_NUMBER}" STREQUAL "") string(APPEND TR_PEER_ID_PREFIX "B") else() string(APPEND TR_PEER_ID_PREFIX "0") endif() string(APPEND TR_PEER_ID_PREFIX "-") set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") if(EXISTS ${CMAKE_SOURCE_DIR}/.git) find_package(Git) endif() if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{GIT_COMMIT}" STREQUAL "") set(TR_VCS_REVISION "$ENV{GIT_COMMIT}") elseif(NOT "$ENV{TEAMCITY_PROJECT_NAME}" STREQUAL "" AND NOT "$ENV{BUILD_VCS_NUMBER}" STREQUAL "") set(TR_VCS_REVISION "$ENV{BUILD_VCS_NUMBER}") elseif(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-list --max-count=1 HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE TR_VCS_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}") file(READ "${TR_VCS_REVISION_FILE}" TR_VCS_REVISION) endif() string(STRIP "${TR_VCS_REVISION}" TR_VCS_REVISION) if(NOT "${TR_VCS_REVISION}" STREQUAL "") file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}\n") else() set(TR_VCS_REVISION 0) file(REMOVE "${TR_VCS_REVISION_FILE}") endif() string(SUBSTRING "${TR_VCS_REVISION}" 0 10 TR_VCS_REVISION) set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(WIN32) foreach(L C CXX) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DWIN32") # Target version (Vista and up) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DWINVER=0x0600 -D_WIN32_WINNT=0x0600") # Use Unicode API (although we always use W or A names explicitly) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DUNICODE -D_UNICODE") # Ignore various deprecation and security warnings (at least for now) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS") if(MSVC) # Set source file encoding and execution charset to UTF-8 set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} /utf-8") # Reduce noise (at least for now) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} /wd4244 /wd4267") # Make caching-friendly (store debug info inside object files) foreach(T IN ITEMS ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE}) string(TOUPPER "${T}" T) string(REGEX REPLACE "[-/]Z[iI]" "-Z7" CMAKE_${L}_FLAGS_${T} "${CMAKE_${L}_FLAGS_${T}}") endforeach() endif() if(MINGW) set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -D__USE_MINGW_ANSI_STDIO=1") endif() endforeach() endif() set(CMAKE_FOLDER "third-party") find_package(FastFloat) find_package(Fmt) find_package(Small) find_package(UtfCpp) find_package(WideInteger) find_package(Threads) find_package(PkgConfig QUIET) find_package(CURL ${CURL_MINIMUM} REQUIRED) if(NOT TARGET CURL::libcurl) add_library(CURL::libcurl INTERFACE IMPORTED) target_link_libraries(CURL::libcurl INTERFACE ${CURL_LIBRARIES}) target_include_directories(CURL::libcurl INTERFACE ${CURL_INCLUDE_DIRS}) endif() if(ENABLE_DEPRECATED STREQUAL "AUTO") if(DEFINED ENV{CI}) set(ENABLE_DEPRECATED OFF) else() set(ENABLE_DEPRECATED ON) endif() endif() set(CRYPTO_PKG "") if(WITH_CRYPTO STREQUAL "AUTO" OR WITH_CRYPTO STREQUAL "ccrypto") tr_get_required_flag(WITH_CRYPTO CCRYPTO_IS_REQUIRED) find_path(CCRYPTO_INCLUDE_DIR NAMES CommonCrypto/CommonCrypto.h ${CCRYPTO_IS_REQUIRED}) mark_as_advanced(CCRYPTO_INCLUDE_DIR) tr_fixup_list_option(WITH_CRYPTO "ccrypto" CCRYPTO_INCLUDE_DIR "AUTO" CCRYPTO_IS_REQUIRED) if(WITH_CRYPTO STREQUAL "ccrypto") set(CRYPTO_PKG "ccrypto") set(CRYPTO_INCLUDE_DIRS) set(CRYPTO_LIBRARIES) endif() endif() if(WITH_CRYPTO STREQUAL "AUTO" OR WITH_CRYPTO STREQUAL "openssl") tr_get_required_flag(WITH_CRYPTO OPENSSL_IS_REQUIRED) find_package(OpenSSL ${OPENSSL_MINIMUM} ${OPENSSL_IS_REQUIRED}) tr_fixup_list_option(WITH_CRYPTO "openssl" OPENSSL_FOUND "AUTO" OPENSSL_IS_REQUIRED) if(WITH_CRYPTO STREQUAL "openssl") set(CRYPTO_PKG "openssl") set(CRYPTO_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) set(CRYPTO_LIBRARIES ${OPENSSL_LIBRARIES}) endif() endif() if(WITH_CRYPTO STREQUAL "AUTO" OR WITH_CRYPTO STREQUAL "wolfssl") tr_get_required_flag(WITH_CRYPTO WOLFSSL_IS_REQUIRED) find_package(WolfSSL ${WOLFSSL_MINIMUM} ${WOLFSSL_IS_REQUIRED}) tr_fixup_list_option(WITH_CRYPTO "wolfssl" WOLFSSL_FOUND "AUTO" WOLFSSL_IS_REQUIRED) if(WITH_CRYPTO STREQUAL "wolfssl") set(CRYPTO_PKG "wolfssl") set(CRYPTO_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIRS}) set(CRYPTO_LIBRARIES ${WOLFSSL_LIBRARIES}) endif() endif() if(WITH_CRYPTO STREQUAL "AUTO" OR WITH_CRYPTO STREQUAL "mbedtls") tr_get_required_flag(WITH_CRYPTO MBEDTLS_IS_REQUIRED) find_package(MbedTLS ${MBEDTLS_MINIMUM} ${MBEDTLS_IS_REQUIRED}) tr_fixup_list_option(WITH_CRYPTO "mbedtls" MBEDTLS_FOUND "AUTO" MBEDTLS_IS_REQUIRED) if(WITH_CRYPTO STREQUAL "mbedtls") set(CRYPTO_PKG "mbedtls") set(CRYPTO_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIRS}) set(CRYPTO_LIBRARIES ${MBEDTLS_LIBRARIES}) endif() endif() # We should have found the library by now if(CRYPTO_PKG STREQUAL "") if(WITH_CRYPTO STREQUAL "AUTO") message(FATAL_ERROR "Unable to find any supported crypto library.") else() message(FATAL_ERROR "Requested crypto library '${WITH_CRYPTO}' is not supported.") endif() else() add_library(transmission::crypto_impl INTERFACE IMPORTED) target_include_directories(transmission::crypto_impl INTERFACE ${CRYPTO_INCLUDE_DIRS}) target_link_libraries(transmission::crypto_impl INTERFACE ${CRYPTO_LIBRARIES}) endif() if(ENABLE_GTK) tr_get_required_flag(ENABLE_GTK GTK_IS_REQUIRED) if(USE_GTK_VERSION STREQUAL "AUTO" OR USE_GTK_VERSION EQUAL 4) pkg_check_modules(GTK4 gtkmm-4.0>=${GTKMM4_MINIMUM} glibmm-2.68>=${GLIBMM_MINIMUM} giomm-2.68>=${GIOMM_MINIMUM}) set(GTK_VERSION 4) set(GTK_FOUND ${GTK4_FOUND}) endif() if(NOT GTK_FOUND AND (USE_GTK_VERSION STREQUAL "AUTO" OR USE_GTK_VERSION EQUAL 3)) pkg_check_modules(GTK3 gtkmm-3.0>=${GTKMM3_MINIMUM} glibmm-2.4>=${GLIBMM_MINIMUM} giomm-2.4>=${GIOMM_MINIMUM}) set(GTK_VERSION 3) set(GTK_FOUND ${GTK3_FOUND}) endif() if(GTK_IS_REQUIRED AND NOT GTK_FOUND) message(FATAL_ERROR "GTK is required but wasn't found") endif() tr_fixup_auto_option(ENABLE_GTK GTK_FOUND GTK_IS_REQUIRED) if(ENABLE_GTK AND WITH_APPINDICATOR AND GTK_VERSION EQUAL 3) tr_get_required_flag(WITH_APPINDICATOR APPINDICATOR_IS_REQUIRED) find_package(APPINDICATOR ${APPINDICATOR_IS_REQUIRED}) tr_fixup_auto_option(WITH_APPINDICATOR APPINDICATOR_FOUND APPINDICATOR_IS_REQUIRED) else() set(WITH_APPINDICATOR OFF) endif() else() set(WITH_APPINDICATOR OFF) endif() if(GTK_FOUND) add_library(transmission::gtk_impl INTERFACE IMPORTED) target_compile_options(transmission::gtk_impl INTERFACE ${GTK${GTK_VERSION}_CFLAGS_OTHER}) target_include_directories(transmission::gtk_impl INTERFACE ${GTK${GTK_VERSION}_INCLUDE_DIRS}) target_link_directories(transmission::gtk_impl INTERFACE ${GTK${GTK_VERSION}_LIBRARY_DIRS}) target_link_libraries(transmission::gtk_impl INTERFACE ${GTK${GTK_VERSION}_LIBRARIES}) endif() if(ENABLE_QT) tr_get_required_flag(ENABLE_QT QT_IS_REQUIRED) if(POLICY CMP0020) cmake_policy(SET CMP0020 NEW) endif() set(QT_TARGETS) set(ENABLE_QT_COM_INTEROP OFF) set(ENABLE_QT_DBUS_INTEROP OFF) set(QT_REQUIRED_MODULES Core Gui Widgets Network Svg LinguistTools) set(QT_OPTIONAL_MODULES DBus AxContainer AxServer) set(MISSING_QT_MODULE) set(Qt_NAMES Qt6 Qt5) if(NOT USE_QT_VERSION STREQUAL "AUTO") set(Qt_NAMES Qt${USE_QT_VERSION}) endif() find_package(Qt NAMES ${Qt_NAMES} ${QT_MINIMUM} QUIET) if(Qt_FOUND) if(WIN32 AND Qt_VERSION_MAJOR EQUAL 5) list(APPEND QT_REQUIRED_MODULES WinExtras) endif() foreach(M ${QT_REQUIRED_MODULES}) find_package(Qt${Qt_VERSION_MAJOR}${M} ${QT_MINIMUM} QUIET) if(Qt${Qt_VERSION_MAJOR}${M}_FOUND) if(NOT M STREQUAL "LinguistTools") list(APPEND QT_TARGETS Qt${Qt_VERSION_MAJOR}::${M}) endif() else() set(QT_TARGETS) set(MISSING_QT_MODULE "${M}") break() endif() endforeach() endif() if(QT_TARGETS) foreach(M ${QT_OPTIONAL_MODULES}) find_package(Qt${Qt_VERSION_MAJOR}${M} ${QT_MINIMUM} QUIET) if(Qt${Qt_VERSION_MAJOR}${M}_FOUND) list(APPEND QT_TARGETS Qt${Qt_VERSION_MAJOR}::${M}) endif() endforeach() if(Qt${Qt_VERSION_MAJOR}AxContainer_FOUND AND Qt${Qt_VERSION_MAJOR}AxServer_FOUND) set(ENABLE_QT_COM_INTEROP ON) find_program(MIDL_EXECUTABLE midl) if(NOT MIDL_EXECUTABLE) set(ENABLE_QT_COM_INTEROP OFF) endif() endif() if(Qt${Qt_VERSION_MAJOR}DBus_FOUND) set(ENABLE_QT_DBUS_INTEROP ON) endif() endif() set(QT_FOUND ON) if(NOT QT_TARGETS OR NOT (ENABLE_QT_COM_INTEROP OR ENABLE_QT_DBUS_INTEROP)) if(QT_IS_REQUIRED) message(FATAL_ERROR "Unable to find required Qt libraries (Qt${Qt_VERSION_MAJOR}${MISSING_QT_MODULE})") endif() set(QT_FOUND OFF) endif() tr_fixup_auto_option(ENABLE_QT QT_FOUND QT_IS_REQUIRED) endif() if(QT_FOUND) add_library(transmission::qt_impl INTERFACE IMPORTED) target_link_libraries(transmission::qt_impl INTERFACE ${QT_TARGETS}) endif() if(ENABLE_MAC) tr_get_required_flag(ENABLE_MAC MAC_IS_REQUIRED) if(APPLE) set(MAC_FOUND ON) else() set(MAC_FOUND OFF) if(MAC_IS_REQUIRED) message(SEND_ERROR "Mac build is impossible on non-Mac system.") endif() endif() tr_fixup_auto_option(ENABLE_MAC MAC_FOUND MAC_IS_REQUIRED) endif() set(THIRD_PARTY_DIR ${CMAKE_SOURCE_DIR}/third-party) if(WIN32 AND NOT MINGW) set(DEFLATE_LIB_NAME deflatestatic) else() set(DEFLATE_LIB_NAME deflate) endif() tr_add_external_auto_library(DEFLATE libdeflate ${DEFLATE_LIB_NAME} TARGET deflate::deflate CMAKE_ARGS -DLIBDEFLATE_BUILD_SHARED_LIB=OFF -DLIBDEFLATE_BUILD_GZIP=OFF) tr_add_external_auto_library(EVENT2 libevent event TARGET libevent::event CMAKE_ARGS -DEVENT__DISABLE_OPENSSL:BOOL=ON -DEVENT__DISABLE_BENCHMARK:BOOL=ON -DEVENT__DISABLE_TESTS:BOOL=ON -DEVENT__DISABLE_REGRESS:BOOL=ON -DEVENT__DISABLE_SAMPLES:BOOL=ON -DEVENT__LIBRARY_TYPE:STRING=STATIC) tr_add_external_auto_library(NATPMP libnatpmp natpmp TARGET natpmp::natpmp) if(NOT USE_SYSTEM_NATPMP) target_compile_definitions(natpmp::natpmp INTERFACE NATPMP_STATICLIB) endif() tr_add_external_auto_library(MINIUPNPC miniupnpc miniupnpc TARGET miniupnpc::libminiupnpc CMAKE_ARGS -DUPNPC_BUILD_STATIC=ON -DUPNPC_BUILD_SHARED=OFF -DUPNPC_BUILD_TESTS=OFF) if(NOT USE_SYSTEM_MINIUPNPC) target_compile_definitions(miniupnpc::libminiupnpc INTERFACE MINIUPNP_STATICLIB) set(MINIUPNPC_VERSION 1.9) set(MINIUPNPC_API_VERSION 12) endif() target_compile_definitions(miniupnpc::libminiupnpc INTERFACE SYSTEM_MINIUPNP $<$:MINIUPNPC_API_VERSION=${MINIUPNPC_API_VERSION}>) # API version macro was only added in 1.7 add_subdirectory(third-party/jsonsl) add_subdirectory(third-party/wildmat) tr_add_external_auto_library(DHT dht dht TARGET dht::dht) tr_add_external_auto_library(PSL libpsl psl TARGET psl::psl) if(ENABLE_UTP) tr_add_external_auto_library(UTP libutp utp SUBPROJECT TARGET libutp::libutp CMAKE_ARGS -DLIBUTP_SHARED:BOOL=OFF) endif() tr_add_external_auto_library(B64 libb64 b64 SUBPROJECT TARGET libb64::libb64 CMAKE_ARGS -DLIBB64_SHARED:BOOL=OFF) set(TR_WEB_ASSETS ${CMAKE_SOURCE_DIR}/web/public_html) if(NOT ${REBUILD_WEB} STREQUAL "OFF") find_program(NPM npm) if ("${NPM}" STREQUAL "NPM-NOTFOUND") if ("${REBUILD_WEB}" STREQUAL "ON") message(FATAL_ERROR "Could NOT find NPM, minimum required is \"${NPM_MINIMUM}\"") else() # AUTO set(REBUILD_WEB OFF) endif() else() execute_process(COMMAND "${NPM}" --version OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NPM_VERSION_STRING) if ("${NPM_VERSION_STRING}" VERSION_GREATER_EQUAL "${NPM_MINIMUM}") message(STATUS "Found NPM: ${NPM} (found suitable version \"${NPM_VERSION_STRING}\", minimum required is \"${NPM_MINIMUM}\")") set(REBUILD_WEB ON) elseif ("${REBUILD_WEB}" STREQUAL "ON") message(FATAL_ERROR "Found NPM: ${NPM} Found unsuitable version \"${NPM_VERSION_STRING}\", but required is at least \"${NPM_MINIMUM}\"") else() # AUTO message(STATUS "Found NPM: ${NPM} Found unsuitable version \"${NPM_VERSION_STRING}\", but required is at least \"${NPM_MINIMUM}\"") set(REBUILD_WEB OFF) endif() endif() if(REBUILD_WEB) add_subdirectory(web) endif() endif() if(WITH_INOTIFY) tr_get_required_flag(WITH_INOTIFY INOTIFY_IS_REQUIRED) set(INOTIFY_FOUND OFF) check_include_file(sys/inotify.h HAVE_SYS_INOTIFY_H) check_function_exists(inotify_init HAVE_INOTIFY_INIT) if(HAVE_SYS_INOTIFY_H AND HAVE_INOTIFY_INIT) set(INOTIFY_FOUND ON) endif() tr_fixup_auto_option(WITH_INOTIFY INOTIFY_FOUND INOTIFY_IS_REQUIRED) endif() if(WITH_KQUEUE) tr_get_required_flag(WITH_KQUEUE KQUEUE_IS_REQUIRED) set(KQUEUE_FOUND OFF) check_include_files("sys/types.h;sys/event.h" HAVE_SYS_EVENT_H) check_function_exists(kqueue HAVE_KQUEUE) if(HAVE_SYS_EVENT_H AND HAVE_KQUEUE) set(KQUEUE_FOUND ON) endif() tr_fixup_auto_option(WITH_KQUEUE KQUEUE_FOUND KQUEUE_IS_REQUIRED) endif() if(WITH_SYSTEMD) tr_get_required_flag(WITH_SYSTEMD SYSTEMD_IS_REQUIRED) find_package(SYSTEMD) tr_fixup_auto_option(WITH_SYSTEMD SYSTEMD_FOUND SYSTEMD_IS_REQUIRED) endif() if(WIN32) foreach(L C CXX) # Filter out needless definitions set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DWIN32_LEAN_AND_MEAN -DNOMINMAX") endforeach() endif() unset(CMAKE_FOLDER) ## Compiler standard version set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) ### Compiler Warnings set(C_WARNING_FLAGS) set(CXX_WARNING_FLAGS) include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) if(MSVC) set(WARNING_CANDIDATES /W4) foreach(FLAG ${WARNING_CANDIDATES}) list(APPEND C_WARNING_FLAGS ${FLAG}) list(APPEND CXX_WARNING_FLAGS ${FLAG}) endforeach() else() set(WARNING_CANDIDATES -W -Wall -Wextra -Wcast-align -Wduplicated-cond -Wexit-time-destructors -Wextra-semi -Wextra-semi-stmt -Wextra-tokens -Wfloat-equal -Wgnu -Winit-self -Wint-in-bool-context -Wlogical-op -Wmissing-format-attribute -Wnull-dereference -Wpointer-arith -Wredundant-decls -Wredundant-move -Wreorder-ctor -Wrestrict -Wreturn-std-move -Wself-assign -Wself-move -Wsemicolon-before-method-body -Wsentinel -Wshadow -Wsign-compare -Wsometimes-uninitialized -Wstring-conversion -Wsuggest-destructor-override -Wsuggest-override -Wuninitialized -Wunreachable-code -Wunused -Wunused-const-variable -Wunused-parameter -Wunused-result -Wwrite-strings) if(MINGW) # Disable excessive warnings since we're using __USE_MINGW_ANSI_STDIO # Hopefully, any potential issues will be spotted on other platforms list(APPEND WARNING_CANDIDATES -Wno-format) else() list(APPEND WARNING_CANDIDATES -Wformat-security) endif() set(CMAKE_REQUIRED_FLAGS) foreach(FLAG -Werror /WX) tr_make_id("${FLAG}" FLAG_ID) set(CACHE_ID "${CMAKE_C_COMPILER_ID}_C_HAS${FLAG_ID}") string(TOLOWER "${CACHE_ID}" CACHE_ID) check_c_compiler_flag(${FLAG} ${CACHE_ID}) if(${CACHE_ID}) # Make sure the next loop only adds flags that are relevant for a particular language set(CMAKE_REQUIRED_FLAGS ${FLAG}) break() endif() endforeach() foreach(FLAG ${WARNING_CANDIDATES}) tr_make_id("${FLAG}" FLAG_ID) # if available, add to C warnings set(CACHE_ID "${CMAKE_C_COMPILER_ID}_C_HAS${FLAG_ID}") string(TOLOWER "${CACHE_ID}" CACHE_ID) check_c_compiler_flag(${FLAG} ${CACHE_ID}) if(${CACHE_ID}) list(APPEND C_WARNING_FLAGS ${FLAG}) endif() # if available, add to CXX warnings set(CACHE_ID "${CMAKE_CXX_COMPILER_ID}_CXX_HAS${FLAG_ID}") string(TOLOWER "${CACHE_ID}" CACHE_ID) check_cxx_compiler_flag(${FLAG} ${CACHE_ID}) if(${CACHE_ID}) list(APPEND CXX_WARNING_FLAGS ${FLAG}) endif() unset(CACHE_ID) unset(FLAG_ID) endforeach() unset(CMAKE_REQUIRED_FLAGS) endif() string(REPLACE ";" "$" C_WARNING_FLAGS_GENEX "${C_WARNING_FLAGS}") string(REPLACE ";" "$" CXX_WARNING_FLAGS_GENEX "${CXX_WARNING_FLAGS}") add_compile_options( $<$:${C_WARNING_FLAGS_GENEX}> $<$:${CXX_WARNING_FLAGS_GENEX}>) ### include(LargeFileSupport) check_library_exists(m sqrt "" HAVE_LIBM) if(HAVE_LIBM) set(LIBM_LIBRARY m) endif() check_library_exists(quota quotacursor_skipidtype "" HAVE_LIBQUOTA) if(HAVE_LIBQUOTA) set(LIBQUOTA_LIBRARY quota) endif() set(TR_NETWORK_LIBRARIES) if(WIN32) list(APPEND TR_NETWORK_LIBRARIES iphlpapi ws2_32) else() tr_select_library("c;socket;net" socket "" LIB) if(NOT LIB MATCHES "^(|c)$") list(APPEND TR_NETWORK_LIBRARIES ${LIB}) endif() tr_select_library("c;nsl;bind" gethostbyname "" LIB) if(NOT LIB MATCHES "^(|c)$") list(APPEND TR_NETWORK_LIBRARIES ${LIB}) endif() endif() if(RUN_CLANG_TIDY STREQUAL "AUTO") if(DEFINED ENV{LGTM_SRC} OR DEFINED ENV{APPVEYOR}) # skip clang-tidy on LGTM/appveyor set(RUN_CLANG_TIDY OFF) else() set(RUN_CLANG_TIDY ON) endif() endif() if(RUN_CLANG_TIDY) message(STATUS "Looking for clang-tidy") find_program(CLANG_TIDY clang-tidy) if(CLANG_TIDY STREQUAL "CLANG_TIDY-NOTFOUND") message(STATUS "Looking for clang-tidy - not found") else() message(STATUS "Looking for clang-tidy - found") set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY}") endif() endif() if(ENABLE_TESTS) enable_testing() add_subdirectory(tests) endif() function(tr_install_web DST_DIR) if(INSTALL_WEB) install( DIRECTORY ${TR_WEB_ASSETS} DESTINATION ${DST_DIR}) endif() endfunction() add_subdirectory(libtransmission) set(MAC_PROJECT_DIR macosx) if(ENABLE_GTK AND ENABLE_NLS) find_package(Gettext 0.19.7 REQUIRED) add_subdirectory(po) endif() foreach(P cli daemon gtk mac qt utils) string(TOUPPER "${P}" P_ID) if(ENABLE_${P_ID}) if(DEFINED ${P_ID}_PROJECT_DIR) set(P ${${P_ID}_PROJECT_DIR}) endif() add_subdirectory(${P}) endif() endforeach() if(ENABLE_DAEMON OR ENABLE_GTK OR ENABLE_QT) tr_install_web(${CMAKE_INSTALL_DATAROOTDIR}/${TR_NAME}) endif() if(INSTALL_DOC) install( FILES AUTHORS COPYING README.md docs/rpc-spec.md extras/send-email-when-torrent-done.sh DESTINATION ${CMAKE_INSTALL_DOCDIR}) install( DIRECTORY news DESTINATION ${CMAKE_INSTALL_DOCDIR}) endif() if(MSVC AND ENABLE_DAEMON AND ENABLE_QT AND ENABLE_UTILS AND WITH_CRYPTO STREQUAL "openssl" AND INSTALL_WEB) add_subdirectory(dist/msi) endif() set(CPACK_SOURCE_GENERATOR TXZ) set(CPACK_SOURCE_PACKAGE_FILE_NAME "${TR_NAME}-${TR_SEMVER}") if(NOT TR_STABLE_RELEASE AND NOT "${TR_VCS_REVISION}" STREQUAL "") # https://semver.org/#spec-item-11 # Build metadata MAY be denoted by appending a plus sign and a series of dot # separated identifiers immediately following the patch or pre-release version. # Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-]. # Identifiers MUST NOT be empty. string(APPEND CPACK_SOURCE_PACKAGE_FILE_NAME "+r${TR_VCS_REVISION}") endif() list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}" "[.]git" "node_modules") ## Code Formatting if(GIT_FOUND) execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-toplevel WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE TR_GIT_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) if(TR_GIT_ROOT AND IS_DIRECTORY "${TR_GIT_ROOT}/.git") configure_file("${CMAKE_SOURCE_DIR}/extras/pre-commit" "${TR_GIT_ROOT}/.git/hooks/pre-commit" COPYONLY) add_custom_target(check-format COMMAND "${CMAKE_SOURCE_DIR}/code_style.sh" --check WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") add_custom_target(format COMMAND "${CMAKE_SOURCE_DIR}/code_style.sh" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") set_property( TARGET check-format format PROPERTY FOLDER "utility") endif() unset(TR_GIT_ROOT) endif() include(CPack)