From 57a6e2ef4abbfd2b12ee6489366bc6609bead263 Mon Sep 17 00:00:00 2001 From: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:58:40 +0100 Subject: [PATCH] cmake: Build `bitcoin-qt` executable --- CMakeLists.txt | 16 +- cmake/module/FindQt5.cmake | 66 ++++++++ src/CMakeLists.txt | 5 + src/qt/CMakeLists.txt | 301 +++++++++++++++++++++++++++++++++++++ 4 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 cmake/module/FindQt5.cmake create mode 100644 src/qt/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 852fabf0d29..c9b71180305 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/module) include(CMakeDependentOption) # When adding a new option, end the with a full stop for consistency. option(BUILD_DAEMON "Build bitcoind executable." ON) +option(BUILD_GUI "Build bitcoin-qt executable." OFF) option(BUILD_CLI "Build bitcoin-cli executable." ON) option(BUILD_TESTS "Build test_bitcoin executable." ON) @@ -143,6 +144,17 @@ endif() cmake_dependent_option(ENABLE_EXTERNAL_SIGNER "Enable external signer support." ON "NOT WIN32" OFF) +if(BUILD_GUI) + set(qt_components Core Gui Widgets LinguistTools) + if(ENABLE_WALLET) + list(APPEND qt_components Network) + endif() + find_package(Qt5 5.11.3 MODULE REQUIRED + COMPONENTS ${qt_components} + ) + unset(qt_components) +endif() + option(BUILD_BENCH "Build bench_bitcoin executable." OFF) option(BUILD_FUZZ_BINARY "Build fuzz binary." OFF) cmake_dependent_option(BUILD_FOR_FUZZING "Build for fuzzing. Enabling this will disable all other targets and override BUILD_FUZZ_BINARY." OFF "NOT MSVC" OFF) @@ -178,6 +190,7 @@ if(BUILD_FOR_FUZZING) set(BUILD_TX OFF) set(BUILD_UTIL OFF) set(BUILD_WALLET_TOOL OFF) + set(BUILD_GUI OFF) set(ENABLE_EXTERNAL_SIGNER OFF) set(WITH_NATPMP OFF) set(WITH_MINIUPNPC OFF) @@ -337,7 +350,7 @@ endif() include(AddBoostIfNeeded) add_boost_if_needed() -if(BUILD_DAEMON OR BUILD_CLI OR BUILD_TESTS OR BUILD_BENCH OR BUILD_FUZZ_BINARY) +if(BUILD_DAEMON OR BUILD_GUI OR BUILD_CLI OR BUILD_TESTS OR BUILD_BENCH OR BUILD_FUZZ_BINARY) find_package(Libevent 2.1.8 MODULE REQUIRED) endif() @@ -460,6 +473,7 @@ message("Configure summary") message("=================") message("Executables:") message(" bitcoind ............................ ${BUILD_DAEMON}") +message(" bitcoin-qt (GUI) .................... ${BUILD_GUI}") message(" bitcoin-cli ......................... ${BUILD_CLI}") message(" bitcoin-tx .......................... ${BUILD_TX}") message(" bitcoin-util ........................ ${BUILD_UTIL}") diff --git a/cmake/module/FindQt5.cmake b/cmake/module/FindQt5.cmake new file mode 100644 index 00000000000..f39ee53d5b5 --- /dev/null +++ b/cmake/module/FindQt5.cmake @@ -0,0 +1,66 @@ +# Copyright (c) 2024-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +#[=======================================================================[ +FindQt5 +------- + +Finds the Qt 5 headers and libraries. + +This is a wrapper around find_package() command that: + - facilitates searching in various build environments + - prints a standard log message + +#]=======================================================================] + +set(_qt_homebrew_prefix) +if(CMAKE_HOST_APPLE) + find_program(HOMEBREW_EXECUTABLE brew) + if(HOMEBREW_EXECUTABLE) + execute_process( + COMMAND ${HOMEBREW_EXECUTABLE} --prefix qt@5 + OUTPUT_VARIABLE _qt_homebrew_prefix + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() +endif() + +# Save CMAKE_FIND_ROOT_PATH_MODE_LIBRARY state. +unset(_qt_find_root_path_mode_library_saved) +if(DEFINED CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) + set(_qt_find_root_path_mode_library_saved ${CMAKE_FIND_ROOT_PATH_MODE_LIBRARY}) +endif() + +# The Qt config files internally use find_library() calls for all +# dependencies to ensure their availability. In turn, the find_library() +# inspects the well-known locations on the file system; therefore, it must +# be able to find platform-specific system libraries, for example: +# /usr/x86_64-w64-mingw32/lib/libm.a or /usr/arm-linux-gnueabihf/lib/libm.a. +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) + +find_package(Qt5 ${Qt5_FIND_VERSION} + COMPONENTS ${Qt5_FIND_COMPONENTS} + HINTS ${_qt_homebrew_prefix} + PATH_SUFFIXES Qt5 # Required on OpenBSD systems. +) +unset(_qt_homebrew_prefix) + +# Restore CMAKE_FIND_ROOT_PATH_MODE_LIBRARY state. +if(DEFINED _qt_find_root_path_mode_library_saved) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ${_qt_find_root_path_mode_library_saved}) + unset(_qt_find_root_path_mode_library_saved) +else() + unset(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Qt5 + REQUIRED_VARS Qt5_DIR + VERSION_VAR Qt5_VERSION +) + +foreach(component IN LISTS Qt5_FIND_COMPONENTS ITEMS "") + mark_as_advanced(Qt5${component}_DIR) +endforeach() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ea1bc4eaed..89486eb72a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -344,6 +344,11 @@ if(BUILD_UTIL) endif() +if(BUILD_GUI) + add_subdirectory(qt) +endif() + + add_subdirectory(test/util) if(BUILD_BENCH) add_subdirectory(bench) diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt new file mode 100644 index 00000000000..c4c4f980b87 --- /dev/null +++ b/src/qt/CMakeLists.txt @@ -0,0 +1,301 @@ +# Copyright (c) 2023-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + enable_language(OBJCXX) + set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + set(CMAKE_OBJCXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_OBJCXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") + string(APPEND CMAKE_OBJCXX_COMPILE_OBJECT " ${APPEND_CPPFLAGS} ${APPEND_CXXFLAGS}") +endif() + +get_target_property(qt_lib_type Qt5::Core TYPE) + +# TODO: After the transition from Autotools to CMake, +# all `Q_IMPORT_PLUGIN` macros can be deleted from the +# qt/bitcoin.cpp and qt/test/test_main.cpp source files. +function(import_plugins target) + if(qt_lib_type STREQUAL "STATIC_LIBRARY") + set(plugins Qt5::QMinimalIntegrationPlugin) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND plugins Qt5::QXcbIntegrationPlugin) + elseif(WIN32) + list(APPEND plugins Qt5::QWindowsIntegrationPlugin Qt5::QWindowsVistaStylePlugin) + elseif(APPLE) + list(APPEND plugins Qt5::QCocoaIntegrationPlugin Qt5::QMacStylePlugin) + endif() + qt5_import_plugins(${target} + INCLUDE ${plugins} + EXCLUDE_BY_TYPE imageformats iconengines + ) + endif() +endfunction() + +# For Qt-specific commands and variables, please consult: +# - https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html +# - https://doc.qt.io/qt-5/cmake-manual.html + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOUIC_SEARCH_PATHS forms) + +# TODO: The file(GLOB ...) command should be replaced with an explicit +# file list. Such a change must be synced with the corresponding change +# to https://github.com/bitcoin-core/bitcoin-maintainer-tools/blob/main/update-translations.py +file(GLOB ts_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} locale/*.ts) +set_source_files_properties(${ts_files} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/locale) +qt5_add_translation(qm_files ${ts_files}) + +configure_file(bitcoin_locale.qrc bitcoin_locale.qrc COPYONLY) + +# The bitcoinqt sources have to include headers in +# order to parse them to collect translatable strings. +add_library(bitcoinqt STATIC EXCLUDE_FROM_ALL + bantablemodel.cpp + bantablemodel.h + bitcoin.cpp + bitcoin.h + bitcoinaddressvalidator.cpp + bitcoinaddressvalidator.h + bitcoinamountfield.cpp + bitcoinamountfield.h + bitcoingui.cpp + bitcoingui.h + bitcoinunits.cpp + bitcoinunits.h + clientmodel.cpp + clientmodel.h + csvmodelwriter.cpp + csvmodelwriter.h + guiutil.cpp + guiutil.h + initexecutor.cpp + initexecutor.h + intro.cpp + intro.h + $<$:macdockiconhandler.h> + $<$:macdockiconhandler.mm> + $<$:macnotificationhandler.h> + $<$:macnotificationhandler.mm> + $<$:macos_appnap.h> + $<$:macos_appnap.mm> + modaloverlay.cpp + modaloverlay.h + networkstyle.cpp + networkstyle.h + notificator.cpp + notificator.h + optionsdialog.cpp + optionsdialog.h + optionsmodel.cpp + optionsmodel.h + peertablemodel.cpp + peertablemodel.h + peertablesortproxy.cpp + peertablesortproxy.h + platformstyle.cpp + platformstyle.h + qvalidatedlineedit.cpp + qvalidatedlineedit.h + qvaluecombobox.cpp + qvaluecombobox.h + rpcconsole.cpp + rpcconsole.h + splashscreen.cpp + splashscreen.h + trafficgraphwidget.cpp + trafficgraphwidget.h + utilitydialog.cpp + utilitydialog.h + $<$:winshutdownmonitor.cpp> + $<$:winshutdownmonitor.h> + bitcoin.qrc + ${CMAKE_CURRENT_BINARY_DIR}/bitcoin_locale.qrc +) +target_compile_definitions(bitcoinqt + PUBLIC + QT_NO_KEYWORDS + QT_USE_QSTRINGBUILDER +) +target_include_directories(bitcoinqt + PUBLIC + $ +) +set_property(SOURCE macnotificationhandler.mm + # Ignore warnings "'NSUserNotificationCenter' is deprecated: first deprecated in macOS 11.0". + APPEND PROPERTY COMPILE_OPTIONS -Wno-deprecated-declarations +) +target_link_libraries(bitcoinqt + PUBLIC + Qt5::Widgets + PRIVATE + core_interface + bitcoin_cli + leveldb + Boost::headers + $ + $ + $ + $<$:-framework\ AppKit> + $<$:shlwapi> +) + +if(ENABLE_WALLET) + target_sources(bitcoinqt + PRIVATE + addressbookpage.cpp + addressbookpage.h + addresstablemodel.cpp + addresstablemodel.h + askpassphrasedialog.cpp + askpassphrasedialog.h + coincontroldialog.cpp + coincontroldialog.h + coincontroltreewidget.cpp + coincontroltreewidget.h + createwalletdialog.cpp + createwalletdialog.h + editaddressdialog.cpp + editaddressdialog.h + openuridialog.cpp + openuridialog.h + overviewpage.cpp + overviewpage.h + paymentserver.cpp + paymentserver.h + psbtoperationsdialog.cpp + psbtoperationsdialog.h + qrimagewidget.cpp + qrimagewidget.h + receivecoinsdialog.cpp + receivecoinsdialog.h + receiverequestdialog.cpp + receiverequestdialog.h + recentrequeststablemodel.cpp + recentrequeststablemodel.h + sendcoinsdialog.cpp + sendcoinsdialog.h + sendcoinsentry.cpp + sendcoinsentry.h + signverifymessagedialog.cpp + signverifymessagedialog.h + transactiondesc.cpp + transactiondesc.h + transactiondescdialog.cpp + transactiondescdialog.h + transactionfilterproxy.cpp + transactionfilterproxy.h + transactionoverviewwidget.cpp + transactionoverviewwidget.h + transactionrecord.cpp + transactionrecord.h + transactiontablemodel.cpp + transactiontablemodel.h + transactionview.cpp + transactionview.h + walletcontroller.cpp + walletcontroller.h + walletframe.cpp + walletframe.h + walletmodel.cpp + walletmodel.h + walletmodeltransaction.cpp + walletmodeltransaction.h + walletview.cpp + walletview.h + ) + target_link_libraries(bitcoinqt + PRIVATE + bitcoin_wallet + Qt5::Network + ) +endif() + +if(qt_lib_type STREQUAL "STATIC_LIBRARY") + # We want to define static plugins to link ourselves, thus preventing + # automatic linking against a "sane" set of default static plugins. + qt5_import_plugins(bitcoinqt + EXCLUDE_BY_TYPE bearer iconengines imageformats platforms styles + ) +endif() + +add_executable(bitcoin-qt + main.cpp + ../init/bitcoin-qt.cpp +) + +target_link_libraries(bitcoin-qt + core_interface + bitcoinqt + bitcoin_node +) + +import_plugins(bitcoin-qt) +set(installable_targets bitcoin-qt) +if(WIN32) + set_target_properties(bitcoin-qt PROPERTIES WIN32_EXECUTABLE TRUE) +endif() + + +# Gets sources to be parsed to gather translatable strings. +function(get_translatable_sources var) + set(result) + set(targets) + foreach(dir IN ITEMS ${ARGN}) + get_directory_property(dir_targets DIRECTORY ${PROJECT_SOURCE_DIR}/${dir} BUILDSYSTEM_TARGETS) + list(APPEND targets ${dir_targets}) + endforeach() + foreach(target IN LISTS targets) + get_target_property(target_sources ${target} SOURCES) + if(target_sources) + foreach(source IN LISTS target_sources) + # Get an expression from the generator expression, if any. + if(source MATCHES ":([^>]+)>$") + set(source ${CMAKE_MATCH_1}) + endif() + cmake_path(GET source EXTENSION LAST_ONLY ext) + if(ext STREQUAL ".qrc") + continue() + endif() + if(NOT IS_ABSOLUTE source) + get_target_property(target_source_dir ${target} SOURCE_DIR) + cmake_path(APPEND target_source_dir ${source} OUTPUT_VARIABLE source) + endif() + list(APPEND result ${source}) + endforeach() + endif() + endforeach() + set(${var} ${result} PARENT_SCOPE) +endfunction() + +find_program(XGETTEXT_EXECUTABLE xgettext) +find_program(SED_EXECUTABLE sed) +if(NOT XGETTEXT_EXECUTABLE) + add_custom_target(translate + COMMAND ${CMAKE_COMMAND} -E echo "Error: GNU gettext-tools not found" + ) +elseif(NOT SED_EXECUTABLE) + add_custom_target(translate + COMMAND ${CMAKE_COMMAND} -E echo "Error: GNU sed not found" + ) +else() + set(translatable_sources_directories src src/qt src/util) + if(ENABLE_WALLET) + list(APPEND translatable_sources_directories src/wallet) + endif() + get_translatable_sources(translatable_sources ${translatable_sources_directories}) + get_translatable_sources(qt_translatable_sources src/qt) + file(GLOB ui_files ${CMAKE_CURRENT_SOURCE_DIR}/forms/*.ui) + add_custom_target(translate + COMMAND ${CMAKE_COMMAND} -E env XGETTEXT=${XGETTEXT_EXECUTABLE} COPYRIGHT_HOLDERS=${COPYRIGHT_HOLDERS} ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/share/qt/extract_strings_qt.py ${translatable_sources} + COMMAND Qt5::lupdate -no-obsolete -I ${PROJECT_SOURCE_DIR}/src -locations relative ${CMAKE_CURRENT_SOURCE_DIR}/bitcoinstrings.cpp ${ui_files} ${qt_translatable_sources} -ts ${CMAKE_CURRENT_SOURCE_DIR}/locale/bitcoin_en.ts + COMMAND Qt5::lconvert -drop-translations -o ${CMAKE_CURRENT_SOURCE_DIR}/locale/bitcoin_en.xlf -i ${CMAKE_CURRENT_SOURCE_DIR}/locale/bitcoin_en.ts + COMMAND ${SED_EXECUTABLE} -i.old -e "s|source-language=\"en\" target-language=\"en\"|source-language=\"en\"|" -e "/<\\/target>/d" ${CMAKE_CURRENT_SOURCE_DIR}/locale/bitcoin_en.xlf + COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_SOURCE_DIR}/locale/bitcoin_en.xlf.old + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src + VERBATIM + ) +endif()