cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(kovri VERSION 0.2.0 LANGUAGES CXX) set(KOVRI_PROJECT "The Kovri I2P Router Project") # Versioning set(KOVRI_VERSION "0.2.0-rc1") set(KOVRI_CODENAME "Mazeppa") # Get git revision if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD # use short hash for now WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE "KOVRI_GIT_REVISION" ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) else() set(KOVRI_GIT_REVISION " ") endif() endif() message(STATUS "") message(STATUS "${KOVRI_PROJECT}") message(STATUS "${KOVRI_VERSION}-${KOVRI_GIT_REVISION} \"${KOVRI_CODENAME}\"") message(STATUS "") # I2NP/NetDb RI properties (used internally) set(I2P_VERSION "0.9.35") set(I2P_NETWORK_ID "2") # "1" is reserved for I2P version less than 0.6.1.10 # Produce version definitions, output to build directory configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.h.in" "${CMAKE_BINARY_DIR}/version.h" @ONLY) # The interface target for internal usage requirements add_library(kovri-internal INTERFACE) # Include build directory so we need to git-ignore version file output target_include_directories(kovri-internal INTERFACE $) # Configurable options option(KOVRI_DATA_PATH "The path to the kovri data folder") option(WITH_BINARY "Build kovri binary" ON) option(WITH_KOVRI_UTIL "Build kovri utility binary" OFF) option(WITH_COVERAGE "Build with coverage" OFF) option(WITH_CRYPTOPP "Build with Crypto++" ON) # Default ON unless we switch libraries option(BUILD_CRYPTOPP "Build Crypto++ inside cmake" OFF) option(WITH_DOXYGEN "Enable support for Doxygen" OFF) option(WITH_HARDENING "Use hardening compiler flags" OFF) option(WITH_OPTIMIZE "Optimization flags" OFF) option(WITH_PYTHON "Build wrappers which require Boost.Python" OFF) option(WITH_STATIC "Static build" OFF) option(WITH_STATIC_DEPS "Static build with static dependencies" OFF) option(WITH_SHARED_DEPS "Dynamic build with partial in-tree shared dependencies" OFF) option(WITH_TESTS "Build unit tests" OFF) option(WITH_FUZZ_TESTS "Build fuzz tests" OFF) option(WITH_INTEGRATION_TESTS "Build integration tests" OFF) option(WITH_COTIRE "Enable cotire (compile time reducer) - precompiled header and single compilation unit builds" ${MSVC}) # The miniupnp project has, historically, be unreliable in providing timely releases after security patches. # Use this option to build your own miniupnpc (using Kovri's mindful fork) to be used in kovri-core lib. # You may also need to keep this option off if your system *must* rely on using a system installation. option(BUILD_MINIUPNPC "Build in-tree miniupnpc" OFF) # Verbosity enabled by default, but can be disabled from cli by adding -DCMAKE_VERBOSE_MAKEFILE=OFF # https://github.com/monero-project/kovri/pull/867#discussion_r184359361 option(CMAKE_VERBOSE_MAKEFILE "Enable verbose build output" ON) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cotire/CMake) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-7420 set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$:NDEBUG>) # Exceptions *should* substitute assertions in tests if (WITH_TESTS) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NDEBUG) endif() # The kovri shared object used for boost.python will need build rpath if (NOT WITH_PYTHON AND NOT WITH_SHARED_DEPS) set(CMAKE_SKIP_BUILD_RPATH TRUE) endif() # Default build is Debug if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() # Check whether we're on a 32-bit or 64-bit system if(CMAKE_SIZEOF_VOID_P EQUAL "8") set(ARCH_WIDTH "64") else() set(ARCH_WIDTH "32") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(LINUX TRUE) endif() if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") set(FREEBSD TRUE) endif() # TODO(anonimal): continue with systems as needed ### TODO(unassigned): improve detection and use-case # Minimal processor detection (needed for ARM build) set(CPU "${CMAKE_SYSTEM_PROCESSOR}") string(SUBSTRING "${CPU}" 0 3 PARSE_ARM) if (CPU STREQUAL "aarch64" OR PARSE_ARM STREQUAL "arm") set(ARM TRUE) endif() # Detect minimum compiler version if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9.2) message(FATAL_ERROR "GCC 4.9.2 or higher is required") endif() set(GNU TRUE) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5) message(FATAL_ERROR "Clang 3.5 or higher is required") endif() set(CLANG TRUE) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0.0) message(FATAL_ERROR "AppleClang 8.0.0 or higher is required") endif() set(CLANG TRUE) endif() # TODO(unassigned): an absolute hack to work around a clang (CMake?) bug that does not like our Release build type. # Strangely, we don't do anything out of the ordinary with our Release build type, nor do our deps. # The issue is not reproducible with Debug build type. See #993. # Absolutely resolve this hack when are out of out Alpha, or no later than Beta. if (CLANG) # Remove aggressive optimizations from release builds. Referencing #993 string(REPLACE "-O3" "-O1" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") endif() # Require C++14 support (minimum version compilers guarantee this) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) ### TODO(unassigned): review, cleanup, improve # Compiler flags (by vendor) if(NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Winvalid-pch") if( (NOT ARM) AND (NOT ANDROID) ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") endif() # TOOD(unassigned): we actually don't need this and it's quite annoying #set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -pedantic") # TODO(unassigned): we currently are not developing for OpenWRT # TODO(unassgiend): review if the following is incompatible with static build and enabled hardening for OpenWRT # Multiple definitions of __stack_chk_fail (libssp & libc) #set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -flto -s -ffunction-sections -fdata-sections") # -flto is added from above #set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "-Wl,--gc-sections") # https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/ if(WITH_HARDENING) # Run-time buffer overflow detection set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS _FORTIFY_SOURCE=2) # Reject potentially unsafe format string arguents set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat -Wformat-security -Werror=format-security") # Stack smashing protector set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") # Full ASLR for executables set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(GNU AND NOT MINGW) # Read-only segments after relocation and Disable lazy binding set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -z relro -z now") endif() endif() if(ANDROID) #From Android 5: "only position independent executables (PIE) are supported" set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() if(WITH_OPTIMIZE AND NOT WITH_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") endif() if(WITH_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") endif() endif() if(NOT WIN32 AND NOT WITH_STATIC AND NOT WITH_FUZZ_TESTS) # TODO: Consider separate compilation for COMMON_SRC for library. # No need in -fPIC overhead for binary if not interested in library # HINT: revert c266cff CMakeLists.txt: compilation speed up set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() set(Boost_USE_MULTITHREADED ON) if(WITH_STATIC_DEPS AND MSVC) foreach(flag_var IN ITEMS CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag_var} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endif() endforeach() set(Boost_USE_STATIC_RUNTIME ON) endif() if(WITH_STATIC) set(Boost_USE_STATIC_LIBS ON) # Apple does not support statically linked binaries on Mac OS X # https://developer.apple.com/library/content/qa/qa1118/_index.html if (NOT MSVC AND NOT APPLE) set(Boost_USE_STATIC_RUNTIME ON) endif() set(BUILD_SHARED_LIBS OFF) if(${CMAKE_CXX_COMPILER} MATCHES ".*-openwrt-.*") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") set(CMAKE_THREAD_LIBS_INIT "gcc_eh -Wl,-u,pthread_create,-u,pthread_once,-u,pthread_mutex_lock,-u,pthread_mutex_unlock,-u,pthread_join,-u,pthread_equal,-u,pthread_detach,-u,pthread_cond_wait,-u,pthread_cond_signal,-u,pthread_cond_destroy,-u,pthread_cond_broadcast,-u,pthread_cancel") endif() endif() # Speed up MSVC build # https://blogs.msdn.microsoft.com/vcblog/2016/10/26/recommendations-to-speed-c-builds-in-visual-studio/ if (MSVC) set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /DEBUG:fastlink") if (NOT WITH_COTIRE AND MSVC_IDE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") endif() endif() if(WITH_COTIRE) set(COTIRE_PCH_MEMORY_SCALING_FACTOR "300") include(cotire OPTIONAL) endif() # Get uname output function(Uname output pattern) execute_process( COMMAND sh -c "uname -a 2>&1" COMMAND grep "${pattern}" OUTPUT_VARIABLE ${output} OUTPUT_STRIP_TRAILING_WHITESPACE) set(${output} "${${output}}" PARENT_SCOPE) endfunction(Uname) # TODO(unassigned): extend Boost.Python cross-platform build support set(additional_components) if (WITH_PYTHON) Uname(ARCH_LINUX "ARCH") Uname(UBUNTU "Ubuntu") if (APPLE OR ARCH_LINUX) set(additional_components python3) elseif (UBUNTU) set(additional_components python-py35) else() message(FATAL_ERROR "Boost.Python package not yet supported for your system. See TODO in root CMake recipe or open a feature request") endif() endif() if (Boost_VERSION STRGREATER_EQUAL 107100) set(Boost_NO_BOOST_CMAKE ON CACHE BOOL "Disable the new Boost-provided CMake files (they're currently unreliable and break the build)" FORCE) endif() find_package(Boost 1.66 REQUIRED COMPONENTS filesystem log program_options ${additional_components}) if (NOT Boost_FOUND) message(FATAL_ERROR "Boost not found or requirement not satisfied. See building instructions.") endif() # Fix OpenBSD build failure due to missing Boost::log_setup target if (NOT TARGET Boost::log_setup) get_property(BoostLogDependencies TARGET Boost::log PROPERTY INTERFACE_LINK_LIBRARIES) list(REMOVE_ITEM BoostLogDependencies "Boost::log_setup") set_property(TARGET Boost::log PROPERTY INTERFACE_LINK_LIBRARIES "${BoostLogDependencies}") endif() if(NOT WITH_STATIC) target_compile_definitions(kovri-internal INTERFACE BOOST_ALL_DYN_LINK) endif() # Libraries set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) if(WITH_CRYPTOPP) if (BUILD_CRYPTOPP) include(BuildCryptoPP) else() find_package(CryptoPP REQUIRED) endif() if(NOT TARGET CryptoPP::CryptoPP) message(FATAL_ERROR "Could not find Crypto++. See building instructions.") else() target_compile_definitions(kovri-internal INTERFACE WITH_CRYPTOPP) target_include_directories(kovri-internal INTERFACE $) target_link_libraries(kovri-internal INTERFACE ${CryptoPP_LIBRARIES}) endif() endif() # Despite only cpp-netlib requiring openssl, OSX and ANDROID apparently needs this as well if (APPLE OR ANDROID OR WITH_BEAST) # If we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's if (APPLE AND NOT OpenSSL_DIR) execute_process (COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if (WITH_STATIC) set(OPENSSL_USE_STATIC_LIBS TRUE) endif() endif() # Required by Beast on all platforms for clearnet downloads find_package(OpenSSL REQUIRED) if(NOT OPENSSL_FOUND) message(FATAL_ERROR "Could not find OpenSSL. Please download and install it first!") else() target_link_libraries(kovri-internal INTERFACE ${OPENSSL_LIBRARIES}) endif() add_subdirectory(deps) if(WITH_FUZZ_TESTS) if(NOT CLANG) message(FATAL_ERROR "clang is required for fuzz tests. See building instructions.") endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6) message(FATAL_ERROR "Clang version 6 or higher is required") endif() add_subdirectory(tests/fuzz_tests) endif() # Doxygen support if(WITH_DOXYGEN) include(UseDoxygen) endif() # For Boost.Python wrapping if (WITH_PYTHON) find_package(PythonLibs REQUIRED) if (NOT PythonLibs_FOUND) message(FATAL_ERROR "Python not found or requirement not satisfied. See building instructions.") else() message(STATUS "Found Python: ${PYTHON_INCLUDE_DIR}, ${PYTHON_LIBRARIES}") target_include_directories(kovri-internal INTERFACE $) endif() endif() # Solve issue with as.exe fatal error - Object file has too many sections # http://stackoverflow.com/a/28372660 # This error appear for cotired build if (MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj") # TODO(unassigned): remove once a better solution is found (we don't want to force optimization) # Referencing https://github.com/monero-project/meta/issues/238 if (WITH_STATIC AND ARCH_WIDTH EQUAL "64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") endif() endif() # Directly link IPHLPAPI.lib instead of using `#pragma comment()` if (WIN32) target_link_libraries(kovri-internal INTERFACE "IPHLPAPI.lib") endif() # Load remaining includes target_include_directories(kovri-internal INTERFACE $) # Set data directory if not set (if CMake is not called from Makefile first) if (NOT KOVRI_DATA_PATH) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows" OR ${CMAKE_SYSTEM_NAME} STREQUAL "MSYS") set(KOVRI_DATA_PATH "$ENV{APPDATA}\\Kovri") elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") set(KOVRI_DATA_PATH "$ENV{HOME}/Library/Application\ Support/Kovri") else() # Linux, *BSD set(KOVRI_DATA_PATH "$ENV{HOME}/.kovri") endif() endif() target_compile_definitions(kovri-internal INTERFACE KOVRI_CUSTOM_DATA_PATH="${KOVRI_DATA_PATH}") # Show summary message(STATUS "---------------------------------------") message(STATUS "Build type : ${CMAKE_BUILD_TYPE}") message(STATUS "Compiler vendor : ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Compiler version : ${CMAKE_CXX_COMPILER_VERSION}") message(STATUS "Compiler path : ${CMAKE_CXX_COMPILER}") message(STATUS "Install prefix: : ${CMAKE_INSTALL_PREFIX}") message(STATUS "Kovri data directory: ${KOVRI_DATA_PATH}") message(STATUS "Options:") message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " KOVRI UTIL : ${WITH_KOVRI_UTIL}") message(STATUS " COVERAGE : ${WITH_COVERAGE}") message(STATUS " CRYPTOPP : ${WITH_CRYPTOPP}") message(STATUS " BUILD CRYPTOPP : ${BUILD_CRYPTOPP}") message(STATUS " DOXYGEN : ${WITH_DOXYGEN}") message(STATUS " HARDENING : ${WITH_HARDENING}") message(STATUS " OPTIMIZATION : ${WITH_OPTIMIZE}") message(STATUS " PYTHON : ${WITH_PYTHON}") message(STATUS " STATIC BUILD : ${WITH_STATIC}") message(STATUS " STATIC DEPS BUILD: ${WITH_STATIC_DEPS}") message(STATUS " SHARED DEPS BUILD: ${WITH_SHARED_DEPS}") message(STATUS " TESTS : ${WITH_TESTS}") message(STATUS " FUZZ TESTS : ${WITH_FUZZ_TESTS}") message(STATUS " INTEGRATION TESTS: ${WITH_INTEGRATION_TESTS}") message(STATUS " Cotire : ${WITH_COTIRE}") message(STATUS " BUILD MINIUPNPC : ${BUILD_MINIUPNPC}") message(STATUS "---------------------------------------") add_subdirectory(src) if (WITH_TESTS) enable_testing() add_subdirectory(tests/unit_tests) endif() if (WITH_INTEGRATION_TESTS) target_compile_definitions(kovri-internal INTERFACE WITH_INTEGRATION_TESTS) add_subdirectory(tests/integration_tests) endif() ## Install # TODO(unassigned): CPack # TODO(unassigned): multiple components? add_library(kovri INTERFACE) target_link_libraries(kovri INTERFACE kovri-core kovri-client kovri-internal libminiupnpc ${OPENSSL_LIBRARIES} ${CryptoPP_LIBRARIES}) set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME kovri) include(GNUInstallDirs) install(TARGETS kovri kovri-internal libminiupnpc EXPORT KovriTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) if (MINIUPNPC_BUILT) install(TARGETS miniupnpc-private EXPORT KovriTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) if (WITH_STATIC) install(TARGETS libminiupnpc-static EXPORT KovriTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) else() install(TARGETS libminiupnpc-shared EXPORT KovriTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() endif() install(TARGETS kovri-core EXPORT KovriTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kovri/core) install(TARGETS kovri-client EXPORT KovriTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kovri/client) install(EXPORT KovriTargets NAMESPACE kovri:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake COMPONENT kovri) include(CMakePackageConfigHelpers) configure_package_config_file(cmake/KovriConfig.cmake.in ${PROJECT_BINARY_DIR}/KovriConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) write_basic_package_version_file( ${PROJECT_BINARY_DIR}/KovriConfigVersion.cmake COMPATIBILITY AnyNewerVersion) install(FILES ${PROJECT_BINARY_DIR}/KovriConfig.cmake ${PROJECT_BINARY_DIR}/KovriConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake COMPONENT kovri) # TODO(unassigned): refactor this into CMake. Currently assumes `make` is not installed. # Kovri BSD currently build requires `gmake`, not `make`. find_program(MAKE make gmake nmake) install(CODE "execute_process(COMMAND ${MAKE} KOVRI_ROOT_DIR=${CMAKE_CURRENT_SOURCE_DIR} KOVRI_BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} -C ${CMAKE_CURRENT_SOURCE_DIR} install)" COMPONENT kovri)