CMakeLists.txt raw

   1  # Copyright (c) 2023-present The Limenka developers
   2  # Distributed under the MIT software license, see the accompanying
   3  # file COPYING or https://opensource.org/license/mit/.
   4  
   5  # Ubuntu 22.04 LTS Jammy Jellyfish, https://wiki.ubuntu.com/Releases, EOSS in June 2027:
   6  #  - CMake 3.22.1, https://packages.ubuntu.com/jammy/cmake
   7  #
   8  # Centos Stream 9, https://www.centos.org/cl-vs-cs/#end-of-life, EOL in May 2027:
   9  #  - CMake 3.26.5, https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/
  10  cmake_minimum_required(VERSION 3.22)
  11  
  12  if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
  13    message(FATAL_ERROR "In-source builds are not allowed.")
  14  endif()
  15  
  16  if(POLICY CMP0171)
  17    # `codegen` is a reserved target name.
  18    # See: https://cmake.org/cmake/help/latest/policy/CMP0171.html
  19    cmake_policy(SET CMP0171 NEW)
  20  endif()
  21  
  22  # When adjusting CMake flag variables, we must not override those explicitly
  23  # set by the user. These are a subset of the CACHE_VARIABLES property.
  24  get_directory_property(precious_variables CACHE_VARIABLES)
  25  
  26  #=============================
  27  # Project / Package metadata
  28  #=============================
  29  set(CLIENT_NAME "Limenka Knots")
  30  set(CLIENT_VERSION_MAJOR 29)
  31  set(CLIENT_VERSION_MINOR 3)
  32  set(CLIENT_VERSION_BUILD 0)
  33  set(CLIENT_VERSION_RC 0)
  34  set(CLIENT_VERSION_IS_RELEASE "true")
  35  set(COPYRIGHT_YEAR "2026")
  36  
  37  set(CLIENT_VERSION_SUFFIX ".knots20260508")
  38  
  39  # During the enabling of the CXX and CXXOBJ languages, we modify
  40  # CMake's compiler/linker invocation strings by appending the content
  41  # of the user-defined `APPEND_*` variables, which allows overriding
  42  # any flag. We also ensure that the APPEND_* flags are considered
  43  # during CMake's tests, which use the `try_compile()` command.
  44  #
  45  # CMake's docs state that the `CMAKE_TRY_COMPILE_PLATFORM_VARIABLES`
  46  # variable "is meant to be set by CMake's platform information modules
  47  # for the current toolchain, or by a toolchain file." We do our best
  48  # to set it before the `project()` command.
  49  set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
  50    CMAKE_CXX_COMPILE_OBJECT
  51    CMAKE_OBJCXX_COMPILE_OBJECT
  52    CMAKE_CXX_LINK_EXECUTABLE
  53  )
  54  
  55  project(LimenkaCore
  56    VERSION ${CLIENT_VERSION_MAJOR}.${CLIENT_VERSION_MINOR}.${CLIENT_VERSION_BUILD}
  57    DESCRIPTION "Limenka client software"
  58    HOMEPAGE_URL "https://limenkaknots.org/"
  59    LANGUAGES NONE
  60  )
  61  
  62  set(CLIENT_VERSION_STRING ${PROJECT_VERSION}${CLIENT_VERSION_SUFFIX})
  63  if(CLIENT_VERSION_RC GREATER 0)
  64    string(APPEND CLIENT_VERSION_STRING "rc${CLIENT_VERSION_RC}")
  65  endif()
  66  
  67  set(COPYRIGHT_HOLDERS "The %s developers")
  68  set(COPYRIGHT_HOLDERS_FINAL "The ${CLIENT_NAME} developers")
  69  set(CLIENT_BUGREPORT "https://github.com/limenkaknots/limenka/issues")
  70  
  71  #=============================
  72  # Language setup
  73  #=============================
  74  if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_HOST_APPLE)
  75    # We do not use the install_name_tool when cross-compiling for macOS.
  76    # So disable this tool check in further enable_language() commands.
  77    set(CMAKE_PLATFORM_HAS_INSTALLNAME FALSE)
  78  endif()
  79  enable_language(CXX)
  80  set(CMAKE_CXX_STANDARD 20)
  81  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  82  set(CMAKE_CXX_EXTENSIONS OFF)
  83  
  84  list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/module)
  85  include(ProcessConfigurations)
  86  
  87  # Flatten static lib dependencies.
  88  # Without this, if libfoo.a depends on libbar.a, libfoo's objects can't begin
  89  # to be compiled until libbar.a has been created.
  90  if (NOT DEFINED CMAKE_OPTIMIZE_DEPENDENCIES)
  91    set(CMAKE_OPTIMIZE_DEPENDENCIES TRUE)
  92  endif()
  93  
  94  #=============================
  95  # Configurable options
  96  #=============================
  97  include(CMakeDependentOption)
  98  # When adding a new option, end the <help_text> with a full stop for consistency.
  99  option(BUILD_DAEMON "Build limenkad executable." ON)
 100  option(BUILD_GUI "Build limenka-qt executable." OFF)
 101  set(WITH_QT_VERSION "5" CACHE STRING "Choose 5 or 6.")
 102  set_property(CACHE WITH_QT_VERSION PROPERTY STRINGS "5" "6")
 103  option(BUILD_CLI "Build limenka-cli executable." ON)
 104  option(BUILD_LIMENKACONSENSUS_LIB "Build liblimenkaconsensus library." OFF)
 105  
 106  option(BUILD_TESTS "Build test_limenka executable." ON)
 107  option(BUILD_TX "Build limenka-tx executable." ${BUILD_TESTS})
 108  option(BUILD_UTIL "Build limenka-util executable." ${BUILD_TESTS})
 109  
 110  option(BUILD_UTIL_CHAINSTATE "Build experimental limenka-chainstate executable." OFF)
 111  option(BUILD_KERNEL_LIB "Build experimental limenkakernel library." ${BUILD_UTIL_CHAINSTATE})
 112  
 113  option(WITH_SYSTEM_LEVELDB "Build with system LevelDB instead of embedded fork." OFF)
 114  mark_as_advanced(WITH_SYSTEM_LEVELDB)
 115  if(WITH_SYSTEM_LEVELDB)
 116    find_package(LevelDB MODULE REQUIRED)
 117  else()
 118    set(EMBEDDED_LEVELDB TRUE)
 119  endif()  # WITH_SYSTEM_LEVELDB
 120  
 121  option(WITH_SYSTEM_LIBSECP256K1 "Build with system libsecp256k1 instead of embedded copy." OFF)
 122  mark_as_advanced(WITH_SYSTEM_LIBSECP256K1)
 123  if(WITH_SYSTEM_LIBSECP256K1)
 124    find_package(PkgConfig REQUIRED)
 125    pkg_check_modules(libsecp256k1 REQUIRED IMPORTED_TARGET libsecp256k1)
 126    add_library(secp256k1 ALIAS PkgConfig::libsecp256k1)
 127  
 128    if(NOT (libsecp256k1_HAS_SECP256K1_ELLSWIFT_CREATE AND libsecp256k1_HAS_SECP256K1_XONLY_PUBKEY_PARSE AND libsecp256k1_HAS_SECP256K1_ECDSA_RECOVER AND libsecp256k1_HAS_SECP256K1_SCHNORRSIG_VERIFY))
 129      include(CheckSymbolExists)
 130      include(CMakePushCheckState)
 131      cmake_push_check_state(RESET)
 132      string(APPEND CMAKE_REQUIRED_FLAGS " ${libsecp256k1_CFLAGS_OTHER}")
 133      list(APPEND CMAKE_REQUIRED_INCLUDES ${libsecp256k1_INCLUDE_DIRS})
 134      list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${libsecp256k1_LDFLAGS})
 135      list(APPEND CMAKE_REQUIRED_LIBRARIES ${libsecp256k1_LIBRARIES})
 136      unset(missing_modules)
 137      macro(check_secp256k1_module module symbol)
 138        string(TOUPPER "${symbol}" VAR)
 139        set(VAR "libsecp256k1_HAS_${VAR}")
 140        unset(${VAR})
 141        check_symbol_exists("${symbol}" "secp256k1_${module}.h" ${VAR})
 142        if(NOT ${VAR})
 143          list(APPEND missing_modules "${module}")
 144        endif()
 145      endmacro()
 146      message(CHECK_START "Checking for required libsecp256k1 modules")
 147      list(APPEND CMAKE_MESSAGE_INDENT "  ")
 148      check_secp256k1_module(ellswift secp256k1_ellswift_create)
 149      check_secp256k1_module(extrakeys secp256k1_xonly_pubkey_parse)
 150      check_secp256k1_module(recovery secp256k1_ecdsa_recover)
 151      check_secp256k1_module(schnorrsig secp256k1_schnorrsig_verify)
 152      list(POP_BACK CMAKE_MESSAGE_INDENT)
 153      if(missing_modules)
 154        list(JOIN missing_modules ", " missing_modules)
 155        message(FATAL_ERROR "System-installed libsecp256k1 lacks these required modules: ${missing_modules}.")
 156      endif()
 157      message(CHECK_PASS "all were found")
 158      cmake_pop_check_state()
 159    endif()
 160  endif()  # WITH_SYSTEM_LIBSECP256K1
 161  
 162  option(ENABLE_WALLET "Enable wallet." ON)
 163  option(WITH_SQLITE "Enable SQLite wallet support." ${ENABLE_WALLET})
 164  if(WITH_SQLITE)
 165    if(VCPKG_TARGET_TRIPLET)
 166      # Use of the `unofficial::` namespace is a vcpkg package manager convention.
 167      find_package(unofficial-sqlite3 CONFIG REQUIRED)
 168    else()
 169      find_package(SQLite3 3.7.17 REQUIRED)
 170    endif()
 171    set(USE_SQLITE ON)
 172  endif()
 173  option(WITH_BDB "Enable Berkeley DB (BDB) wallet support." OFF)
 174  cmake_dependent_option(WARN_INCOMPATIBLE_BDB "Warn when using a Berkeley DB (BDB) version other than 4.8." ON "WITH_BDB" OFF)
 175  if(WITH_BDB)
 176    find_package(BerkeleyDB 4.8 MODULE REQUIRED)
 177    set(USE_BDB ON)
 178    if(NOT BerkeleyDB_VERSION VERSION_EQUAL 4.8)
 179      message(WARNING "Found Berkeley DB (BDB) other than 4.8.\n"
 180                      "BDB (legacy) wallets opened by this build will not be portable!"
 181      )
 182      if(WARN_INCOMPATIBLE_BDB)
 183        message(FATAL_ERROR "If this is intended, pass \"-DWARN_INCOMPATIBLE_BDB=OFF\".\n"
 184                        "Passing \"-DWITH_BDB=OFF\" will suppress this warning."
 185        )
 186      endif()
 187    endif()
 188  endif()
 189  cmake_dependent_option(BUILD_WALLET_TOOL "Build limenka-wallet tool." ${BUILD_TESTS} "ENABLE_WALLET" OFF)
 190  
 191  option(ENABLE_HARDENING "Attempt to harden the resulting executables." ON)
 192  option(REDUCE_EXPORTS "Attempt to reduce exported symbols in the resulting executables." OFF)
 193  option(WERROR "Treat compiler warnings as errors." OFF)
 194  option(WITH_CCACHE "Attempt to use ccache for compiling." ON)
 195  
 196  option(WITH_MINIUPNPC "Enable UPnP." OFF)
 197  if(WITH_MINIUPNPC)
 198    find_package(MiniUPnPc MODULE REQUIRED)
 199  endif()
 200  
 201  option(WITH_ZMQ "Enable ZMQ notifications." OFF)
 202  if(WITH_ZMQ)
 203    find_package(ZeroMQ 4.0.0 MODULE REQUIRED)
 204  endif()
 205  
 206  option(WITH_USDT "Enable tracepoints for Userspace, Statically Defined Tracing." OFF)
 207  if(WITH_USDT)
 208    find_package(USDT MODULE REQUIRED)
 209  endif()
 210  
 211  option(ENABLE_EXTERNAL_SIGNER "Enable external signer support." ON)
 212  
 213  option(ENABLE_TOR_SUBPROCESS "Enable support for executing a dedicated Tor node." ON)
 214  
 215  cmake_dependent_option(WITH_QRENCODE "Enable QR code support." ON "BUILD_GUI" OFF)
 216  if(WITH_QRENCODE)
 217    find_package(QRencode MODULE REQUIRED)
 218    set(USE_QRCODE TRUE)
 219  endif()
 220  
 221  cmake_dependent_option(WITH_DBUS "Enable DBus support." ON "NOT CMAKE_SYSTEM_NAME MATCHES \"(Windows|Darwin)\" AND BUILD_GUI" OFF)
 222  
 223  cmake_dependent_option(WITH_TASKBAR_PROGRESS "Enable GUI taskbar progress." ON "CMAKE_SYSTEM_NAME STREQUAL \"Windows\" AND BUILD_GUI" OFF)
 224  
 225  set(WITH_MULTIPROCESS OFF)
 226  if(WITH_MULTIPROCESS)
 227    find_package(Libmultiprocess REQUIRED COMPONENTS Lib)
 228    find_package(LibmultiprocessNative REQUIRED COMPONENTS Bin
 229      NAMES Libmultiprocess
 230    )
 231  endif()
 232  
 233  cmake_dependent_option(BUILD_GUI_TESTS "Build test_limenka-qt executable." ON "BUILD_GUI;BUILD_TESTS" OFF)
 234  if(BUILD_GUI)
 235    set(qt_components Core Gui Widgets LinguistTools)
 236    if(ENABLE_WALLET)
 237      list(APPEND qt_components Network)
 238    endif()
 239    if(WITH_DBUS)
 240      list(APPEND qt_components DBus)
 241      set(USE_DBUS TRUE)
 242    endif()
 243    if(WITH_TASKBAR_PROGRESS)
 244      set(LIMENKA_QT_WIN_TASKBAR TRUE)
 245    endif()
 246    if(BUILD_GUI_TESTS)
 247      list(APPEND qt_components Test)
 248    endif()
 249    if("${WITH_QT_VERSION}" STREQUAL "6")
 250      find_package(Qt 6.2 MODULE REQUIRED
 251        COMPONENTS ${qt_components}
 252      )
 253    else()  # ! Qt 6
 254    find_package(Qt 5.11.3 MODULE REQUIRED
 255      COMPONENTS ${qt_components}
 256    )
 257    endif()  # Qt 6
 258    unset(qt_components)
 259  endif()
 260  
 261  option(BUILD_BENCH "Build bench_limenka executable." OFF)
 262  option(BUILD_FUZZ_BINARY "Build fuzz binary." OFF)
 263  option(BUILD_FOR_FUZZING "Build for fuzzing. Enabling this will disable all other targets and override BUILD_FUZZ_BINARY." OFF)
 264  
 265  option(INSTALL_MAN "Install man pages." ON)
 266  
 267  set(APPEND_CPPFLAGS "" CACHE STRING "Preprocessor flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
 268  set(APPEND_CFLAGS "" CACHE STRING "C compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
 269  set(APPEND_CXXFLAGS "" CACHE STRING "(Objective) C++ compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
 270  set(APPEND_LDFLAGS "" CACHE STRING "Linker flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.")
 271  # Appending to this low-level rule variables is the only way to
 272  # guarantee that the flags appear at the end of the command line.
 273  string(APPEND CMAKE_CXX_COMPILE_OBJECT " ${APPEND_CPPFLAGS} ${APPEND_CXXFLAGS}")
 274  string(APPEND CMAKE_CXX_CREATE_SHARED_LIBRARY " ${APPEND_LDFLAGS}")
 275  string(APPEND CMAKE_CXX_LINK_EXECUTABLE " ${APPEND_LDFLAGS}")
 276  
 277  set(configure_warnings)
 278  
 279  include(CheckLinkerSupportsPIE)
 280  check_linker_supports_pie(configure_warnings)
 281  
 282  # The core_interface library aims to encapsulate common build flags.
 283  # It is a usage requirement for all targets except for secp256k1, which
 284  # gets its flags by other means.
 285  add_library(core_interface INTERFACE)
 286  add_library(core_interface_relwithdebinfo INTERFACE)
 287  add_library(core_interface_debug INTERFACE)
 288  target_link_libraries(core_interface INTERFACE
 289    $<$<CONFIG:RelWithDebInfo>:core_interface_relwithdebinfo>
 290    $<$<CONFIG:Debug>:core_interface_debug>
 291  )
 292  
 293  if(BUILD_FOR_FUZZING)
 294    message(WARNING "BUILD_FOR_FUZZING=ON will disable all other targets and force BUILD_FUZZ_BINARY=ON.")
 295    set(BUILD_DAEMON OFF)
 296    set(BUILD_CLI OFF)
 297    set(BUILD_LIMENKACONSENSUS_LIB OFF)
 298    set(BUILD_TX OFF)
 299    set(BUILD_UTIL OFF)
 300    set(BUILD_UTIL_CHAINSTATE OFF)
 301    set(BUILD_KERNEL_LIB OFF)
 302    set(BUILD_WALLET_TOOL OFF)
 303    set(BUILD_GUI OFF)
 304    set(ENABLE_EXTERNAL_SIGNER OFF)
 305    set(WITH_MINIUPNPC OFF)
 306    set(WITH_ZMQ OFF)
 307    set(BUILD_TESTS OFF)
 308    set(BUILD_GUI_TESTS OFF)
 309    set(BUILD_BENCH OFF)
 310    set(BUILD_FUZZ_BINARY ON)
 311  
 312    target_compile_definitions(core_interface INTERFACE
 313      FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
 314    )
 315  endif()
 316  
 317  set(RDTS_CONSENT "UNSET" CACHE STRING "RDTS (BIP110) consent behaviour.")
 318  set(RDTS_CONSENT_OPTIONS "RUNTIME_CHECK" "RUNTIME_WARN" "IMPLICIT")
 319  set_property(CACHE RDTS_CONSENT PROPERTY STRINGS "UNSET" ${RDTS_CONSENT_OPTIONS})
 320  list(APPEND RDTS_CONSENT_OPTIONS "UNSUPPORTED_UNSAFE_NO_ENFORCEMENT")
 321  if(RDTS_CONSENT MATCHES "^(1|ON|YES|TRUE|Y)$")
 322    set(RDTS_CONSENT "IMPLICIT")
 323  endif()
 324  if(BUILD_DAEMON OR BUILD_GUI OR BUILD_KERNEL_LIB)
 325    if(NOT RDTS_CONSENT IN_LIST RDTS_CONSENT_OPTIONS)
 326      message(FATAL_ERROR
 327        "RDTS_CONSENT is not set.\n"
 328        "This version of Limenka Knots applies the BIP110 (RDTS) network upgrade, which fixes critical vulnerabilities in long-standing network design. To avoid applying this upgrade by accident, please choose whether to accept it now, or defer explicit confirmation until runtime.\n"
 329        "Important: Because this upgrade already has broad community support, reverting to an older software version does not reject it. Running outdated software after any network upgrade only leaves your node vulnerable to displaying fake or fraudulent transactions. To effectively reject this upgrade, you need to run alternative software designed to split away from the upgraded network.\n"
 330        "To accept this upgrade now, add to your CMake options:\n"
 331        "  -D RDTS_CONSENT=IMPLICIT\n"
 332        "Or to defer explicit confirmation until runtime (via GUI prompt or consensusrules=rdts config setting):\n"
 333        "  -D RDTS_CONSENT=RUNTIME_CHECK    (causes limenkad to exit if absent)\n"
 334        "  -D RDTS_CONSENT=RUNTIME_WARN     (causes limenkad to warn hourly if absent)\n"
 335        "For more information, see: https://limenkaknots.org/learn/2026-rdts"
 336      )
 337    endif()
 338  else()
 339    # Nothing else enforces consensus rules, but RDTSConsentFlags::UNSET doesn't actually exist so would break the build
 340    set(RDTS_CONSENT "IMPLICIT")
 341  endif()
 342  if(RDTS_CONSENT STREQUAL "UNSUPPORTED_UNSAFE_NO_ENFORCEMENT")
 343    list(APPEND configure_warnings
 344      "RDTS_CONSENT=UNSUPPORTED_UNSAFE_NO_ENFORCEMENT is NOT a supported option. Use at your own risk!
 345  Note that this will NOT make your node reject RDTS\; it will merely be insecure after RDTS activates."
 346    )
 347  endif()
 348  
 349  # The external_lib_interface interface library aims to encapsulate build flags, which
 350  # are specific to non-internal libraries.
 351  # It is intended to be a usage requirement for non-internal library
 352  # targets.
 353  add_library(external_lib_interface INTERFACE)
 354  
 355  include(TryAppendCXXFlags)
 356  include(TryAppendLinkerFlag)
 357  
 358  # Sanitizers require leaving undefined symbols in the library
 359  if(NOT SANITIZERS)
 360  try_append_linker_flag("-Wl,--no-undefined" TARGET external_lib_interface)
 361  endif()
 362  
 363  # Redefine/adjust per-configuration flags.
 364  target_compile_definitions(core_interface_debug INTERFACE
 365    DEBUG
 366    DEBUG_LOCKORDER
 367    DEBUG_LOCKCONTENTION
 368    RPC_DOC_CHECK
 369    ABORT_ON_FAILED_ASSUME
 370  )
 371  
 372  if(WIN32)
 373    #[=[
 374    This build system supports two ways to build binaries for Windows.
 375  
 376    1. Building on Windows using MSVC.
 377    Implementation notes:
 378    - /DWIN32 and /D_WINDOWS definitions are included into the CMAKE_CXX_FLAGS_INIT
 379      and CMAKE_CXX_FLAGS_INIT variables by default.
 380    - A run-time library is selected using the CMAKE_MSVC_RUNTIME_LIBRARY variable.
 381    - MSVC-specific options, for example, /Zc:__cplusplus, are additionally required.
 382  
 383    2. Cross-compiling using MinGW.
 384    Implementation notes:
 385    - WIN32 and _WINDOWS definitions must be provided explicitly.
 386    - A run-time library must be specified explicitly using _MT definition.
 387    ]=]
 388  
 389    target_compile_definitions(core_interface INTERFACE
 390      _WIN32_WINNT=0x0A00
 391      _WIN32_IE=0x0A00
 392      WIN32_LEAN_AND_MEAN
 393      NOMINMAX
 394    )
 395  
 396    if(MSVC)
 397      if(VCPKG_TARGET_TRIPLET MATCHES "-static")
 398        set(msvc_library_linkage "")
 399      else()
 400        set(msvc_library_linkage "DLL")
 401      endif()
 402      set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>${msvc_library_linkage}")
 403      unset(msvc_library_linkage)
 404  
 405      target_compile_definitions(core_interface INTERFACE
 406        _UNICODE;UNICODE
 407      )
 408      target_compile_options(core_interface INTERFACE
 409        /utf-8
 410        /Zc:preprocessor
 411        /Zc:__cplusplus
 412        /sdl
 413      )
 414      # Improve parallelism in MSBuild.
 415      # See: https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/.
 416      list(APPEND CMAKE_VS_GLOBALS "UseMultiToolTask=true")
 417    endif()
 418  
 419    if(MINGW)
 420      target_compile_definitions(core_interface INTERFACE
 421        WIN32
 422        _WINDOWS
 423        _MT
 424      )
 425      # Avoid the use of aligned vector instructions when building for Windows.
 426      # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412.
 427      try_append_cxx_flags("-Wa,-muse-unaligned-vector-move" TARGET core_interface SKIP_LINK)
 428      try_append_linker_flag("-static" TARGET core_interface)
 429      # We support Windows 10+, however it's not possible to set these values accordingly,
 430      # due to a bug in mingw-w64. See https://sourceforge.net/p/mingw-w64/bugs/968/.
 431      # As a best effort, target Windows 8.
 432      try_append_linker_flag("-Wl,--major-subsystem-version,6" TARGET core_interface)
 433      try_append_linker_flag("-Wl,--minor-subsystem-version,2" TARGET core_interface)
 434  
 435      find_library(MINGWTHRD_LIBRARY mingwthrd)
 436      if (MINGWTHRD_LIBRARY)
 437        target_link_libraries(core_interface INTERFACE
 438          ${MINGWTHRD_LIBRARY}
 439        )
 440      endif()
 441    endif()
 442  
 443    # Workaround producing large object files, which cannot be handled by the assembler.
 444    # More likely to happen with no, or lower levels of optimisation.
 445    # See discussion in https://github.com/limenka/limenka/issues/28109.
 446    if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
 447      try_append_cxx_flags("/bigobj" TARGET core_interface_debug SKIP_LINK)
 448    else()
 449      try_append_cxx_flags("-Wa,-mbig-obj" TARGET core_interface_debug SKIP_LINK)
 450    endif()
 451  endif()
 452  
 453  # Use 64-bit off_t on 32-bit Linux.
 454  if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SIZEOF_VOID_P EQUAL 4)
 455    # Ensure 64-bit offsets are used for filesystem accesses for 32-bit compilation.
 456    target_compile_definitions(core_interface INTERFACE
 457      _FILE_OFFSET_BITS=64
 458    )
 459  endif()
 460  
 461  if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 462    target_compile_definitions(core_interface INTERFACE OBJC_OLD_DISPATCH_PROTOTYPES=0)
 463    # These flags are specific to ld64, and may cause issues with other linkers.
 464    # For example: GNU ld will interpret -dead_strip as -de and then try and use
 465    # "ad_strip" as the symbol for the entry point.
 466    try_append_linker_flag("-Wl,-dead_strip" TARGET core_interface)
 467    try_append_linker_flag("-Wl,-dead_strip_dylibs" TARGET core_interface)
 468    if(CMAKE_HOST_APPLE)
 469      try_append_linker_flag("-Wl,-headerpad_max_install_names" TARGET core_interface)
 470    endif()
 471  endif()
 472  
 473  set(THREADS_PREFER_PTHREAD_FLAG ON)
 474  find_package(Threads REQUIRED)
 475  target_link_libraries(core_interface INTERFACE
 476    Threads::Threads
 477  )
 478  
 479  add_library(sanitize_interface INTERFACE)
 480  target_link_libraries(core_interface INTERFACE sanitize_interface)
 481  if(SANITIZERS)
 482    # First check if the compiler accepts flags. If an incompatible pair like
 483    # -fsanitize=address,thread is used here, this check will fail. This will also
 484    # fail if a bad argument is passed, e.g. -fsanitize=undfeined
 485    try_append_cxx_flags("-fsanitize=${SANITIZERS}" TARGET sanitize_interface
 486      RESULT_VAR cxx_supports_sanitizers
 487      SKIP_LINK
 488    )
 489    if(NOT cxx_supports_sanitizers)
 490      message(FATAL_ERROR "Compiler did not accept requested flags.")
 491    endif()
 492  
 493    # Some compilers (e.g. GCC) require additional libraries like libasan,
 494    # libtsan, libubsan, etc. Make sure linking still works with the sanitize
 495    # flag. This is a separate check so we can give a better error message when
 496    # the sanitize flags are supported by the compiler but the actual sanitizer
 497    # libs are missing.
 498    try_append_linker_flag("-fsanitize=${SANITIZERS}" VAR SANITIZER_LDFLAGS
 499      SOURCE "
 500        #include <cstdint>
 501        #include <cstddef>
 502        extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; }
 503        __attribute__((weak)) // allow for libFuzzer linking
 504        int main() { return 0; }
 505      "
 506      RESULT_VAR linker_supports_sanitizers
 507      NO_CACHE_IF_FAILED
 508    )
 509    if(NOT linker_supports_sanitizers)
 510      message(FATAL_ERROR "Linker did not accept requested flags, you are missing required libraries.")
 511    endif()
 512  endif()
 513  target_link_options(sanitize_interface INTERFACE ${SANITIZER_LDFLAGS})
 514  
 515  if(BUILD_FUZZ_BINARY)
 516    target_link_libraries(core_interface INTERFACE ${FUZZ_LIBS})
 517    include(CheckSourceCompilesWithFlags)
 518    check_cxx_source_compiles_with_flags("
 519        #include <cstdint>
 520        #include <cstddef>
 521        extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; }
 522        // No main() function.
 523      " FUZZ_BINARY_LINKS_WITHOUT_MAIN_FUNCTION
 524      LDFLAGS ${SANITIZER_LDFLAGS}
 525      LINK_LIBRARIES ${FUZZ_LIBS}
 526    )
 527  endif()
 528  
 529  include(AddBoostIfNeeded)
 530  add_boost_if_needed()
 531  
 532  if(BUILD_DAEMON OR BUILD_GUI OR BUILD_CLI OR BUILD_TESTS OR BUILD_BENCH OR BUILD_FUZZ_BINARY)
 533    find_package(Libevent 2.1.8 MODULE REQUIRED)
 534  endif()
 535  
 536  include(cmake/iopriority.cmake)
 537  
 538  include(cmake/introspection.cmake)
 539  
 540  include(cmake/mempressure.cmake)
 541  
 542  include(cmake/ccache.cmake)
 543  
 544  add_library(warn_interface INTERFACE)
 545  target_link_libraries(core_interface INTERFACE warn_interface)
 546  if(MSVC)
 547    try_append_cxx_flags("/W3" TARGET warn_interface SKIP_LINK)
 548    try_append_cxx_flags("/wd4018" TARGET warn_interface SKIP_LINK)
 549    try_append_cxx_flags("/wd4146" TARGET warn_interface SKIP_LINK)
 550    try_append_cxx_flags("/wd4244" TARGET warn_interface SKIP_LINK)
 551    try_append_cxx_flags("/wd4267" TARGET warn_interface SKIP_LINK)
 552    try_append_cxx_flags("/wd4715" TARGET warn_interface SKIP_LINK)
 553    try_append_cxx_flags("/wd4805" TARGET warn_interface SKIP_LINK)
 554    target_compile_definitions(warn_interface INTERFACE
 555      _CRT_SECURE_NO_WARNINGS
 556      _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
 557    )
 558  else()
 559    try_append_cxx_flags("-Wall" TARGET warn_interface SKIP_LINK)
 560    try_append_cxx_flags("-Wextra" TARGET warn_interface SKIP_LINK)
 561    try_append_cxx_flags("-Wgnu" TARGET warn_interface SKIP_LINK)
 562    # Some compilers will ignore -Wformat-security without -Wformat, so just combine the two here.
 563    try_append_cxx_flags("-Wformat -Wformat-security" TARGET warn_interface SKIP_LINK)
 564    try_append_cxx_flags("-Wvla" TARGET warn_interface SKIP_LINK)
 565    try_append_cxx_flags("-Wshadow-field" TARGET warn_interface SKIP_LINK)
 566    try_append_cxx_flags("-Wthread-safety" TARGET warn_interface SKIP_LINK)
 567    try_append_cxx_flags("-Wloop-analysis" TARGET warn_interface SKIP_LINK)
 568    try_append_cxx_flags("-Wredundant-decls" TARGET warn_interface SKIP_LINK)
 569    try_append_cxx_flags("-Wunused-member-function" TARGET warn_interface SKIP_LINK)
 570    try_append_cxx_flags("-Wdate-time" TARGET warn_interface SKIP_LINK)
 571    try_append_cxx_flags("-Wconditional-uninitialized" TARGET warn_interface SKIP_LINK)
 572    try_append_cxx_flags("-Wduplicated-branches" TARGET warn_interface SKIP_LINK)
 573    try_append_cxx_flags("-Wduplicated-cond" TARGET warn_interface SKIP_LINK)
 574    try_append_cxx_flags("-Wlogical-op" TARGET warn_interface SKIP_LINK)
 575    try_append_cxx_flags("-Woverloaded-virtual" TARGET warn_interface SKIP_LINK)
 576    try_append_cxx_flags("-Wsuggest-override" TARGET warn_interface SKIP_LINK)
 577    try_append_cxx_flags("-Wimplicit-fallthrough" TARGET warn_interface SKIP_LINK)
 578    try_append_cxx_flags("-Wunreachable-code-loop-increment" TARGET warn_interface SKIP_LINK)
 579    try_append_cxx_flags("-Wdocumentation" TARGET warn_interface SKIP_LINK)
 580    try_append_cxx_flags("-Wself-assign" TARGET warn_interface SKIP_LINK)
 581    try_append_cxx_flags("-Wbidi-chars=any" TARGET warn_interface SKIP_LINK)
 582    try_append_cxx_flags("-Wundef" TARGET warn_interface SKIP_LINK)
 583  
 584    # Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
 585    # unknown options if any other warning is produced. Test the -Wfoo case, and
 586    # set the -Wno-foo case if it works.
 587    try_append_cxx_flags("-Wunused-parameter" TARGET warn_interface SKIP_LINK
 588      IF_CHECK_PASSED "-Wno-unused-parameter"
 589    )
 590  endif()
 591  
 592  configure_file(cmake/script/Coverage.cmake Coverage.cmake USE_SOURCE_PERMISSIONS COPYONLY)
 593  configure_file(cmake/script/CoverageFuzz.cmake CoverageFuzz.cmake USE_SOURCE_PERMISSIONS COPYONLY)
 594  configure_file(cmake/script/CoverageInclude.cmake.in CoverageInclude.cmake USE_SOURCE_PERMISSIONS @ONLY)
 595  configure_file(cmake/script/cov_tool_wrapper.sh.in cov_tool_wrapper.sh.in USE_SOURCE_PERMISSIONS COPYONLY)
 596  configure_file(contrib/filter-lcov.py filter-lcov.py USE_SOURCE_PERMISSIONS COPYONLY)
 597  
 598  # Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
 599  try_append_cxx_flags("-fno-extended-identifiers" TARGET core_interface SKIP_LINK)
 600  
 601  # Avoiding the `-ffile-prefix-map` compiler option because it implies
 602  # `-fcoverage-prefix-map` on Clang or `-fprofile-prefix-map` on GCC,
 603  # which can cause issues with coverage builds, particularly when using
 604  # Clang in the OSS-Fuzz environment due to its use of other options
 605  # and a third party script, or with GCC.
 606  try_append_cxx_flags("-fdebug-prefix-map=A=B" TARGET core_interface SKIP_LINK
 607    IF_CHECK_PASSED "-fdebug-prefix-map=${PROJECT_SOURCE_DIR}/src=."
 608  )
 609  try_append_cxx_flags("-fmacro-prefix-map=A=B" TARGET core_interface SKIP_LINK
 610    IF_CHECK_PASSED "-fmacro-prefix-map=${PROJECT_SOURCE_DIR}/src=."
 611  )
 612  
 613  # Currently all versions of gcc are subject to a class of bugs, see the
 614  # gccbug_90348 test case (only reproduces on GCC 11 and earlier) and
 615  # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111843. To work around that, set
 616  # -fstack-reuse=none for all gcc builds. (Only gcc understands this flag).
 617  try_append_cxx_flags("-fstack-reuse=none" TARGET core_interface)
 618  
 619  if(ENABLE_HARDENING)
 620    add_library(hardening_interface INTERFACE)
 621    target_link_libraries(core_interface INTERFACE hardening_interface)
 622    if(MSVC)
 623      try_append_linker_flag("/DYNAMICBASE" TARGET hardening_interface)
 624      try_append_linker_flag("/HIGHENTROPYVA" TARGET hardening_interface)
 625      try_append_linker_flag("/NXCOMPAT" TARGET hardening_interface)
 626    else()
 627  
 628      # _FORTIFY_SOURCE requires that there is some level of optimization,
 629      # otherwise it does nothing and just creates a compiler warning.
 630      try_append_cxx_flags("-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"
 631        RESULT_VAR cxx_supports_fortify_source
 632        SOURCE "int main() {
 633                # if !defined __OPTIMIZE__ || __OPTIMIZE__ <= 0
 634                  #error
 635                #endif
 636                }"
 637      )
 638      if(cxx_supports_fortify_source)
 639        target_compile_options(hardening_interface INTERFACE
 640          -U_FORTIFY_SOURCE
 641          -D_FORTIFY_SOURCE=3
 642        )
 643      endif()
 644      unset(cxx_supports_fortify_source)
 645  
 646      try_append_cxx_flags("-D_GLIBCXX_ASSERTIONS" TARGET hardening_interface)
 647  
 648      try_append_cxx_flags("-Wstack-protector" TARGET hardening_interface SKIP_LINK)
 649      try_append_cxx_flags("-fstack-protector-all" TARGET hardening_interface)
 650      try_append_cxx_flags("-fcf-protection=full" TARGET hardening_interface)
 651  
 652      if(MINGW)
 653        # stack-clash-protection is a no-op for Windows.
 654        # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details.
 655      else()
 656        try_append_cxx_flags("-fstack-clash-protection" TARGET hardening_interface)
 657      endif()
 658  
 659      if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
 660        if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 661          try_append_cxx_flags("-mbranch-protection=bti" TARGET hardening_interface SKIP_LINK)
 662        else()
 663          try_append_cxx_flags("-mbranch-protection=standard" TARGET hardening_interface SKIP_LINK)
 664        endif()
 665      endif()
 666  
 667      try_append_linker_flag("-Wl,--enable-reloc-section" TARGET hardening_interface)
 668      try_append_linker_flag("-Wl,--dynamicbase" TARGET hardening_interface)
 669      try_append_linker_flag("-Wl,--nxcompat" TARGET hardening_interface)
 670      try_append_linker_flag("-Wl,--high-entropy-va" TARGET hardening_interface)
 671      try_append_linker_flag("-Wl,-z,relro" TARGET hardening_interface)
 672      try_append_linker_flag("-Wl,-z,now" TARGET hardening_interface)
 673      try_append_linker_flag("-Wl,-z,separate-code" TARGET hardening_interface)
 674      if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 675        try_append_linker_flag("-Wl,-fixup_chains" TARGET hardening_interface)
 676      endif()
 677    endif()
 678  endif()
 679  
 680  if(REDUCE_EXPORTS)
 681    set(CMAKE_CXX_VISIBILITY_PRESET hidden)
 682    try_append_linker_flag("-Wl,--exclude-libs,ALL" TARGET core_interface)
 683    try_append_linker_flag("-Wl,-no_exported_symbols" VAR CMAKE_EXE_LINKER_FLAGS)
 684  endif()
 685  
 686  if(WERROR)
 687    if(MSVC)
 688      set(werror_flag "/WX")
 689    else()
 690      set(werror_flag "-Werror")
 691    endif()
 692    try_append_cxx_flags(${werror_flag} TARGET core_interface SKIP_LINK RESULT_VAR compiler_supports_werror)
 693    if(NOT compiler_supports_werror)
 694      message(FATAL_ERROR "WERROR set but ${werror_flag} is not usable.")
 695    endif()
 696    unset(werror_flag)
 697  endif()
 698  
 699  if(BUILD_LIMENKACONSENSUS_LIB)
 700    set(HAVE_CONSENSUS_LIB TRUE)
 701  endif()
 702  
 703  # Prefer Unix-style package components over frameworks on macOS.
 704  # This improves compatibility with Python version managers.
 705  set(Python3_FIND_FRAMEWORK LAST CACHE STRING "")
 706  # Search for generic names before more specialized ones. This
 707  # improves compatibility with Python version managers that use shims.
 708  set(Python3_FIND_UNVERSIONED_NAMES FIRST CACHE STRING "")
 709  mark_as_advanced(Python3_FIND_FRAMEWORK Python3_FIND_UNVERSIONED_NAMES)
 710  if(BUILD_GUI)
 711    find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter)
 712  else()
 713  find_package(Python3 3.10 COMPONENTS Interpreter)
 714  endif()
 715  if(Python3_EXECUTABLE)
 716    set(PYTHON_COMMAND ${Python3_EXECUTABLE})
 717  else()
 718    list(APPEND configure_warnings
 719      "Minimum required Python not found. Utils and rpcauth tests are disabled."
 720    )
 721  endif()
 722  
 723  if(BUILD_GUI)
 724    find_program(RSVG_CONVERT rsvg-convert REQUIRED)
 725    if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 726      find_program(IMAGEMAGICK_CONVERT NAMES magick convert)
 727      find_program(PNG2ICNS png2icns REQUIRED)
 728    else()
 729      find_program(IMAGEMAGICK_CONVERT NAMES magick convert REQUIRED)
 730      find_program(PNG2ICNS png2icns)
 731    endif()
 732  endif()
 733  
 734  target_compile_definitions(core_interface INTERFACE ${DEPENDS_COMPILE_DEFINITIONS})
 735  target_compile_definitions(core_interface_relwithdebinfo INTERFACE ${DEPENDS_COMPILE_DEFINITIONS_RELWITHDEBINFO})
 736  target_compile_definitions(core_interface_debug INTERFACE ${DEPENDS_COMPILE_DEFINITIONS_DEBUG})
 737  
 738  # If the {CXX,LD}FLAGS environment variables are defined during building depends
 739  # and configuring this build system, their content might be duplicated.
 740  if(DEFINED ENV{CXXFLAGS})
 741    deduplicate_flags(CMAKE_CXX_FLAGS)
 742  endif()
 743  if(DEFINED ENV{LDFLAGS})
 744    deduplicate_flags(CMAKE_EXE_LINKER_FLAGS)
 745  endif()
 746  
 747  if(BUILD_TESTS)
 748    enable_testing()
 749  endif()
 750  
 751  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29)
 752    # have "make test" depend on "make all"
 753    set(CMAKE_SKIP_TEST_ALL_DEPENDENCY FALSE)
 754  endif()
 755  
 756  # TODO: The `CMAKE_SKIP_BUILD_RPATH` variable setting can be deleted
 757  #       in the future after reordering Guix script commands to
 758  #       perform binary checks after the installation step.
 759  # Relevant discussions:
 760  # - https://github.com/hebasto/limenka/pull/236#issuecomment-2183120953
 761  # - https://github.com/limenka/limenka/pull/30312#issuecomment-2191235833
 762  # NetBSD always requires runtime paths to be set for executables.
 763  if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
 764    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
 765  else()
 766    set(CMAKE_SKIP_INSTALL_RPATH TRUE)
 767  endif()
 768  add_subdirectory(test)
 769  add_subdirectory(doc)
 770  
 771  add_subdirectory(src)
 772  
 773  include(cmake/tests.cmake)
 774  
 775  include(Maintenance)
 776  setup_split_debug_script()
 777  add_maintenance_targets()
 778  add_windows_deploy_target()
 779  add_macos_deploy_target()
 780  
 781  message("\n")
 782  message("Configure summary")
 783  message("=================")
 784  message("Executables:")
 785  message("  limenkad ............................ ${BUILD_DAEMON}")
 786  if(BUILD_DAEMON AND WITH_MULTIPROCESS)
 787    set(limenka_daemon_status ON)
 788  else()
 789    set(limenka_daemon_status OFF)
 790  endif()
 791  if(BUILD_GUI)
 792    if("${WITH_QT_VERSION}" STREQUAL "6")
 793      set(limenka_qt_status "ON (Qt 6)")
 794    else()
 795      set(limenka_qt_status "ON (Qt 5)")
 796    endif()
 797  else()
 798    set(limenka_qt_status "OFF")
 799  endif()
 800  message("  limenka-qt (GUI) .................... ${limenka_qt_status}")
 801  if(BUILD_GUI AND WITH_MULTIPROCESS)
 802    set(limenka_gui_status ON)
 803  else()
 804    set(limenka_gui_status OFF)
 805  endif()
 806  message("  limenka-cli ......................... ${BUILD_CLI}")
 807  message("  liblimenkaconsensus ................. ${BUILD_LIMENKACONSENSUS_LIB}")
 808  message("  limenka-tx .......................... ${BUILD_TX}")
 809  message("  limenka-util ........................ ${BUILD_UTIL}")
 810  message("  limenka-wallet ...................... ${BUILD_WALLET_TOOL}")
 811  message("  limenka-chainstate (experimental) ... ${BUILD_UTIL_CHAINSTATE}")
 812  message("  liblimenkakernel (experimental) ..... ${BUILD_KERNEL_LIB}")
 813  message("Optional features:")
 814  message("  wallet support ...................... ${ENABLE_WALLET}")
 815  if(ENABLE_WALLET)
 816    message("   - descriptor wallets (SQLite) ...... ${WITH_SQLITE}")
 817    message("   - legacy wallets (Berkeley DB) ..... ${WITH_BDB}")
 818  endif()
 819  message("  external signer ..................... ${ENABLE_EXTERNAL_SIGNER}")
 820  message("  tor subprocess ...................... ${ENABLE_TOR_SUBPROCESS}")
 821  message("  port mapping using UPnP ............. ${WITH_MINIUPNPC}")
 822  message("  ZeroMQ .............................. ${WITH_ZMQ}")
 823  message("  USDT tracing ........................ ${WITH_USDT}")
 824  message("  QR code (GUI) ....................... ${WITH_QRENCODE}")
 825  message("  DBus (GUI) .......................... ${WITH_DBUS}")
 826  message("  RDTS consent ........................ ${RDTS_CONSENT}")
 827  message("Tests:")
 828  message("  test_limenka ........................ ${BUILD_TESTS}")
 829  message("  test_limenka-qt ..................... ${BUILD_GUI_TESTS}")
 830  message("  bench_limenka ....................... ${BUILD_BENCH}")
 831  message("  fuzz binary ......................... ${BUILD_FUZZ_BINARY}")
 832  message("")
 833  if(CMAKE_CROSSCOMPILING)
 834    set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}")
 835  else()
 836    set(cross_status "FALSE")
 837  endif()
 838  message("Cross compiling ....................... ${cross_status}")
 839  message("C++ compiler .......................... ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}, ${CMAKE_CXX_COMPILER}")
 840  include(FlagsSummary)
 841  flags_summary()
 842  message("Attempt to harden executables ......... ${ENABLE_HARDENING}")
 843  message("Treat compiler warnings as errors ..... ${WERROR}")
 844  message("Use ccache for compiling .............. ${WITH_CCACHE}")
 845  message("\n")
 846  if(configure_warnings)
 847      message("  ******\n")
 848      foreach(warning IN LISTS configure_warnings)
 849        message(WARNING "${warning}")
 850      endforeach()
 851      message("  ******\n")
 852  endif()
 853  
 854  # We want all build properties to be encapsulated properly.
 855  include(WarnAboutGlobalProperties)
 856