Repository: bitcoin/bitcoin (language: cpp)

Issue:
build: `cmake --install` fails if only select targets are built
Noticed this when going to update the [lighting docs](https://github.com/ElementsProject/lightning/blob/master/doc/getting-started/getting-started/installation.md), porting what they do using Autotools. i.e:
```bash
./autogen.sh
./configure
make src/bitcoind src/bitcoin-cli
make install
```

If you attempt the same with master:
```bash
cmake -B build
cmake --build build --target bitcoind bitcoin-cli
cmake --install build
-- Install configuration: "RelWithDebInfo"
CMake Error at build/src/test/cmake_install.cmake:57 (file):
  file INSTALL cannot find "/root/ci_scratch/build/src/test/test_bitcoin": No
  such file or directory.
Call Stack (most recent call first):
  build/src/cmake_install.cmake:77 (include)
  build/cmake_install.cmake:57 (include)
```

The output of `cmake --build build --target list_install_components` just prints (if configured with the kernel):
```bash
cmake --build build --target list_install_components 
> Available install components are: "Kernel" "Unspecified"
```
so it seems like component based install either isn't yet possible, or the names are not easily discoverable.

Otherwise, I don't think we should fail to install because we can't find a binary the user didn't want.


Discussion / hints:
This is frustrating, but it seems to be how CMake works. `cmake --install build` doesn't force any builds, but `make -C build install` does.

I think what you're really looking for is a way to only build/install certain binaries. For that, the workaround would be to name each one as a separate component. That would allow for:
```cmake
cmake -B build
cmake --build build --target bitcoind bitcoin-cli
cmake --install build --component bitcoind bitcoin-cli
```
> I think what you're really looking for is a way to only build/install certain binaries. For that, the workaround would be to name each one as a separate component. That would allow for:
> 
> cmake -B build
> cmake --build build --target bitcoind bitcoin-cli
> cmake --install build --component bitcoind bitcoin-cli

I agree. We have already [done](https://github.com/bitcoin/bitcoin/pull/30835) it for the `bitcoinkernel` target.

On the other hand, the [`CMAKE_SKIP_INSTALL_ALL_DEPENDENCY`](https://cmake.org/cmake/help/latest/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.html) variable seems not a proper solution because it affects the `all` target.

Fix patch:
diff --git a/cmake/module/InstallBinaryComponent.cmake b/cmake/module/InstallBinaryComponent.cmake
new file mode 100644
index 0000000000000..c7b2ed9ae6a48
--- /dev/null
+++ b/cmake/module/InstallBinaryComponent.cmake
@@ -0,0 +1,26 @@
+# Copyright (c) 2025-present The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or https://opensource.org/license/mit/.
+
+include_guard(GLOBAL)
+include(GNUInstallDirs)
+
+function(install_binary_component component)
+  cmake_parse_arguments(PARSE_ARGV 1
+    IC                # prefix
+    "HAS_MANPAGE"     # options
+    ""                # one_value_keywords
+    ""                # multi_value_keywords
+  )
+  set(target_name ${component})
+  install(TARGETS ${target_name}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    COMPONENT ${component}
+  )
+  if(INSTALL_MAN AND IC_HAS_MANPAGE)
+    install(FILES ${PROJECT_SOURCE_DIR}/doc/man/${target_name}.1
+      DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
+      COMPONENT ${component}
+    )
+  endif()
+endfunction()
diff --git a/cmake/module/Maintenance.cmake b/cmake/module/Maintenance.cmake
index 61251d24397b3..bc3868184bf4c 100644
--- a/cmake/module/Maintenance.cmake
+++ b/cmake/module/Maintenance.cmake
@@ -103,7 +103,7 @@ function(add_macos_deploy_target)
 
     add_custom_command(
       OUTPUT ${PROJECT_BINARY_DIR}/${macos_app}/Contents/MacOS/Bitcoin-Qt
-      COMMAND ${CMAKE_COMMAND} --install ${PROJECT_BINARY_DIR} --config $<CONFIG> --component GUI --prefix ${macos_app}/Contents/MacOS --strip
+      COMMAND ${CMAKE_COMMAND} --install ${PROJECT_BINARY_DIR} --config $<CONFIG> --component bitcoin-qt --prefix ${macos_app}/Contents/MacOS --strip
       COMMAND ${CMAKE_COMMAND} -E rename ${macos_app}/Contents/MacOS/bin/$<TARGET_FILE_NAME:bitcoin-qt> ${macos_app}/Contents/MacOS/Bitcoin-Qt
       COMMAND ${CMAKE_COMMAND} -E rm -rf ${macos_app}/Contents/MacOS/bin
       VERBATIM
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8c42359d2d550..07544f59cfa04 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,7 +2,6 @@
 # Distributed under the MIT software license, see the accompanying
 # file COPYING or https://opensource.org/license/mit/.
 
-include(GNUInstallDirs)
 include(AddWindowsResources)
 
 configure_file(${PROJECT_SOURCE_DIR}/cmake/bitcoin-build-config.h.in bitcoin-build-config.h USE_SOURCE_PERMISSIONS @ONLY)
@@ -170,8 +169,8 @@ target_link_libraries(bitcoin_common
     $<$<PLATFORM_ID:Windows>:ws2_32>
 )
 
+include(InstallBinaryComponent)
 
-set(installable_targets)
 if(ENABLE_WALLET)
   add_subdirectory(wallet)
 
@@ -189,7 +188,7 @@ if(ENABLE_WALLET)
       bitcoin_util
       Boost::headers
     )
-    list(APPEND installable_targets bitcoin-wallet)
+    install_binary_component(bitcoin-wallet HAS_MANPAGE)
   endif()
 endif()
 
@@ -318,7 +317,7 @@ if(BUILD_DAEMON)
     bitcoin_node
     $<TARGET_NAME_IF_EXISTS:bitcoin_wallet>
   )
-  list(APPEND installable_targets bitcoind)
+  install_binary_component(bitcoind HAS_MANPAGE)
 endif()
 if(WITH_MULTIPROCESS AND BUILD_DAEMON)
   add_executable(bitcoin-node
@@ -331,7 +330,7 @@ if(WITH_MULTIPROCESS AND BUILD_DAEMON)
     bitcoin_ipc
     $<TARGET_NAME_IF_EXISTS:bitcoin_wallet>
   )
-  list(APPEND installable_targets bitcoin-node)
+  install_binary_component(bitcoin-node)
 endif()
 
 if(WITH_MULTIPROCESS AND BUILD_TESTS)
@@ -374,7 +373,7 @@ if(BUILD_CLI)
     libevent::core
     libevent::extra
   )
-  list(APPEND installable_targets bitcoin-cli)
+  install_binary_component(bitcoin-cli HAS_MANPAGE)
 endif()
 
 
@@ -387,7 +386,7 @@ if(BUILD_TX)
     bitcoin_util
     univalue
   )
-  list(APPEND installable_targets bitcoin-tx)
+  install_binary_component(bitcoin-tx HAS_MANPAGE)
 endif()
 
 
@@ -399,7 +398,7 @@ if(BUILD_UTIL)
     bitcoin_common
     bitcoin_util
   )
-  list(APPEND installable_targets bitcoin-util)
+  install_binary_component(bitcoin-util HAS_MANPAGE)
 endif()
 
 
@@ -445,17 +444,3 @@ endif()
 if(BUILD_FUZZ_BINARY)
   add_subdirectory(test/fuzz)
 endif()
-
-
-install(TARGETS ${installable_targets}
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
-unset(installable_targets)
-
-if(INSTALL_MAN)
-  # TODO: these stubs are no longer needed. man pages should be generated at install time.
-  install(DIRECTORY ../doc/man/
-    DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
-    FILES_MATCHING PATTERN *.1
-  )
-endif()
diff --git a/src/bench/CMakeLists.txt b/src/bench/CMakeLists.txt
index c55bbb1e05f8a..43b0dcdabe6c6 100644
--- a/src/bench/CMakeLists.txt
+++ b/src/bench/CMakeLists.txt
@@ -81,6 +81,4 @@ add_test(NAME bench_sanity_check_high_priority
   COMMAND bench_bitcoin -sanity-check -priority-level=high
 )
 
-install(TARGETS bench_bitcoin
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
+install_binary_component(bench_bitcoin)
diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt
index 2e07ba042a40a..9dae4b88111e6 100644
--- a/src/kernel/CMakeLists.txt
+++ b/src/kernel/CMakeLists.txt
@@ -2,6 +2,8 @@
 # Distributed under the MIT software license, see the accompanying
 # file COPYING or https://opensource.org/license/mit/.
 
+include(GNUInstallDirs)
+
 # TODO: libbitcoinkernel is a work in progress consensus engine
 #       library, as more and more modules are decoupled from the
 #       consensus engine, this list will shrink to only those
@@ -121,24 +123,23 @@ if(NOT BUILD_SHARED_LIBS)
   set(all_kernel_static_link_libs "")
   get_target_static_link_libs(bitcoinkernel all_kernel_static_link_libs)
 
-  install(TARGETS ${all_kernel_static_link_libs} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Kernel)
+  install(TARGETS ${all_kernel_static_link_libs} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT bitcoinkernel)
   list(TRANSFORM all_kernel_static_link_libs PREPEND "-l")
   # LIBS_PRIVATE is substituted in the pkg-config file.
   list(JOIN all_kernel_static_link_libs " " LIBS_PRIVATE)
 endif()
 
 configure_file(${PROJECT_SOURCE_DIR}/libbitcoinkernel.pc.in ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc @ONLY)
-install(FILES ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT Kernel)
+install(FILES ${PROJECT_BINARY_DIR}/libbitcoinkernel.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT bitcoinkernel)
 
-include(GNUInstallDirs)
 install(TARGETS bitcoinkernel
   RUNTIME
     DESTINATION ${CMAKE_INSTALL_BINDIR}
-    COMPONENT Kernel
+    COMPONENT bitcoinkernel
   LIBRARY
     DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    COMPONENT Kernel
+    COMPONENT bitcoinkernel
   ARCHIVE
     DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    COMPONENT Kernel
+    COMPONENT bitcoinkernel
 )
diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt
index a1f39037f220b..7fbbd81c415ae 100644
--- a/src/qt/CMakeLists.txt
+++ b/src/qt/CMakeLists.txt
@@ -236,7 +236,7 @@ target_link_libraries(bitcoin-qt
 )
 
 import_plugins(bitcoin-qt)
-set(installable_targets bitcoin-qt)
+install_binary_component(bitcoin-qt HAS_MANPAGE)
 if(WIN32)
   set_target_properties(bitcoin-qt PROPERTIES WIN32_EXECUTABLE TRUE)
 endif()
@@ -253,17 +253,12 @@ if(WITH_MULTIPROCESS)
     bitcoin_ipc
   )
   import_plugins(bitcoin-gui)
-  list(APPEND installable_targets bitcoin-gui)
+  install_binary_component(bitcoin-gui)
   if(WIN32)
     set_target_properties(bitcoin-gui PROPERTIES WIN32_EXECUTABLE TRUE)
   endif()
 endif()
 
-install(TARGETS ${installable_targets}
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  COMPONENT GUI
-)
-
 if(BUILD_GUI_TESTS)
   add_subdirectory(test)
 endif()


Test patch:
diff --git a/ci/test/00_setup_env_mac_native_fuzz.sh b/ci/test/00_setup_env_mac_native_fuzz.sh
index 1a453a4353f8c..cacf2423ac30a 100755
--- a/ci/test/00_setup_env_mac_native_fuzz.sh
+++ b/ci/test/00_setup_env_mac_native_fuzz.sh
@@ -14,3 +14,4 @@ export OSX_SDK=""
 export RUN_UNIT_TESTS=false
 export RUN_FUNCTIONAL_TESTS=false
 export RUN_FUZZ_TESTS=true
+export GOAL="all"
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index 1b5a27bb6cd15..84e57311dcd3a 100755
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -14,7 +14,7 @@ export NO_DEPENDS=1
 export RUN_UNIT_TESTS=false
 export RUN_FUNCTIONAL_TESTS=false
 export RUN_FUZZ_TESTS=true
-export GOAL="install"
+export GOAL="all"
 export CI_CONTAINER_CAP="--cap-add SYS_PTRACE"  # If run with (ASan + LSan), the container needs access to ptrace (https://github.com/google/sanitizers/issues/764)
 export BITCOIN_CONFIG="\
  -DBUILD_FOR_FUZZING=ON \
diff --git a/src/qt/test/CMakeLists.txt b/src/qt/test/CMakeLists.txt
index e1e617661b4d6..3acdfeade3421 100644
--- a/src/qt/test/CMakeLists.txt
+++ b/src/qt/test/CMakeLists.txt
@@ -45,6 +45,4 @@ if(WIN32 AND VCPKG_TARGET_TRIPLET)
   )
 endif()
 
-install(TARGETS test_bitcoin-qt
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
+install_binary_component(test_bitcoin-qt)
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 859b913206782..6523407e912e6 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -213,6 +213,4 @@ endfunction()
 
 add_all_test_targets()
 
-install(TARGETS test_bitcoin
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
+install_binary_component(test_bitcoin)