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