diff --git a/cmake/modules/NativeExecutable.cmake b/cmake/modules/NativeExecutable.cmake --- a/cmake/modules/NativeExecutable.cmake +++ b/cmake/modules/NativeExecutable.cmake @@ -10,57 +10,86 @@ message(FATAL_ERROR "A native build cannot be cross compiled") endif() -# If we are cross compiling, create a directory for native build. -set(NATIVE_BUILD_DIR "${CMAKE_BINARY_DIR}/native") -set(NATIVE_BINARY_DIR "${NATIVE_BUILD_DIR}/bin") -set(NATIVE_BUILD_TARGET "${NATIVE_BUILD_DIR}/CMakeCache.txt") +# It is imperative that NATIVE_BUILD_DIR be in the cache. +set(NATIVE_BUILD_DIR "${CMAKE_BINARY_DIR}/native" CACHE PATH "The path of the native build directory") -if(NOT __IS_NATIVE_BUILD AND NOT TARGET native-cmake-build) - file(MAKE_DIRECTORY ${NATIVE_BUILD_DIR}) - add_custom_command( - OUTPUT ${NATIVE_BUILD_TARGET} - COMMAND ${CMAKE_COMMAND} - -G "${CMAKE_GENERATOR}" - "${CMAKE_SOURCE_DIR}" - "-D__IS_NATIVE_BUILD=1" - "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}" - "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${NATIVE_BINARY_DIR}" - # Don't require native third party dependencies we don't need. - "-DBUILD_BITCOIN_WALLET=OFF" - "-DBUILD_BITCOIN_QT=OFF" - "-DBUILD_BITCOIN_ZMQ=OFF" - "-DENABLE_QRCODE=OFF" - "-DENABLE_UPNP=OFF" - WORKING_DIRECTORY ${NATIVE_BUILD_DIR} - VERBATIM USES_TERMINAL - ) - - add_custom_target(native-cmake-build DEPENDS ${NATIVE_BUILD_TARGET}) -endif() +function(add_native_executable NAME) + if(__IS_NATIVE_BUILD) + add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN}) + else() + file(RELATIVE_PATH RELATIVE_PATH "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + set(NATIVE_TARGET "${RELATIVE_PATH}/${NAME}") + set(NATIVE_BINARY "${NATIVE_BUILD_DIR}/${NATIVE_TARGET}") -macro(add_native_executable NAME) - if(NOT __IS_NATIVE_BUILD) - set(NATIVE_BINARY "${NATIVE_BINARY_DIR}/${NAME}") - add_custom_target("build-native-${NAME}" - COMMAND ${CMAKE_COMMAND} - --build "${NATIVE_BUILD_DIR}" - --target "${NAME}" - DEPENDS ${NATIVE_BUILD_TARGET} - BYPRODUCTS ${NATIVE_BINARY} - WORKING_DIRECTORY ${NATIVE_BUILD_DIR} + # We create a symlink because cmake craps itself if the imported + # executable has the same name as the executable itself. + # https://cmake.org/pipermail/cmake/2019-May/069480.html + add_custom_command( + OUTPUT "native-${NAME}" + COMMENT "Building native ${NATIVE_TARGET}" + COMMAND ${CMAKE_COMMAND} --build "${NATIVE_BUILD_DIR}" --target "${NATIVE_TARGET}" + COMMAND ${CMAKE_COMMAND} -E create_symlink "${NATIVE_BINARY}" "native-${NAME}" + DEPENDS "${NATIVE_BUILD_DIR}/CMakeCache.txt" ${ARGN} VERBATIM USES_TERMINAL ) - add_executable(${NAME} IMPORTED) + add_executable(${NAME} IMPORTED GLOBAL) + set_target_properties(${NAME} PROPERTIES IMPORTED_LOCATION "${NATIVE_BINARY}") + + # This obviously cannot depend on a file for some mysterious reasons only + # the cmake gods are aware of, so we need a phony custom target. + add_custom_target("build-native-${NAME}" DEPENDS "native-${NAME}") add_dependencies(${NAME} "build-native-${NAME}") - set_property(TARGET ${NAME} PROPERTY IMPORTED_LOCATION ${NATIVE_BINARY}) - else() - add_executable(${NAME} EXCLUDE_FROM_ALL ${ARGN}) endif() -endmacro(add_native_executable) +endfunction(add_native_executable) function(native_target_include_directories) if(__IS_NATIVE_BUILD) target_include_directories(${ARGN}) endif() endfunction(native_target_include_directories) + +function(native_add_cmake_flags) + set_property(GLOBAL APPEND PROPERTY _NATIVE_BUILD_CMAKE_FLAGS ${ARGN}) +endfunction(native_add_cmake_flags) + +# Internal machinery +function(_gen_native_cmake_target) + message(STATUS "Configuring native build in ${NATIVE_BUILD_DIR}") + + get_property(ARGSLIST GLOBAL PROPERTY _NATIVE_BUILD_CMAKE_FLAGS) + + list(SORT ARGSLIST) + list(REMOVE_DUPLICATES ARGSLIST) + list(JOIN ARGSLIST " " ARGS) + + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/config") + configure_file( + "${CMAKE_SOURCE_DIR}/cmake/templates/NativeCmakeRunner.cmake.in" + "${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" + ) +endfunction(_gen_native_cmake_target) + +function(_gen_native_cmake_hook VAR ACCESS) + # When CMAKE_CURRENT_LIST_DIR is set to empty, we execute everything. + if("${VAR}" STREQUAL "CMAKE_CURRENT_LIST_DIR" AND + "${CMAKE_CURRENT_LIST_DIR}" STREQUAL "" AND + "${ACCESS}" STREQUAL "MODIFIED_ACCESS") + _gen_native_cmake_target() + endif() +endfunction(_gen_native_cmake_hook) + +if(NOT __IS_NATIVE_BUILD AND NOT TARGET native-cmake-build) + # Set a hook to execute when everything is set. + variable_watch(CMAKE_CURRENT_LIST_DIR _gen_native_cmake_hook) + + add_custom_command( + OUTPUT "${NATIVE_BUILD_DIR}/CMakeCache.txt" + COMMAND "${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" + DEPENDS "${CMAKE_BINARY_DIR}/config/run_native_cmake.sh" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + VERBATIM USES_TERMINAL + ) + + add_custom_target(native-cmake-build DEPENDS "${NATIVE_BUILD_DIR}/CMakeCache.txt") +endif() diff --git a/cmake/templates/NativeCmakeRunner.cmake.in b/cmake/templates/NativeCmakeRunner.cmake.in new file mode 100755 --- /dev/null +++ b/cmake/templates/NativeCmakeRunner.cmake.in @@ -0,0 +1,8 @@ +#!/bin/sh + +mkdir -p "${NATIVE_BUILD_DIR}" +cd "${NATIVE_BUILD_DIR}" +${CMAKE_COMMAND} -G${CMAKE_GENERATOR} "${CMAKE_SOURCE_DIR}" \ + -D__IS_NATIVE_BUILD=1 \ + -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} \ + ${ARGS} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,16 @@ option(ENABLE_WERROR "Promote some compiler warnings to errors" OFF) option(START_WITH_UPNP "Make UPnP the default to map ports" OFF) +# Disable what we do not need for the native build. +include(NativeExecutable) +native_add_cmake_flags( + "-DBUILD_BITCOIN_WALLET=OFF" + "-DBUILD_BITCOIN_QT=OFF" + "-DBUILD_BITCOIN_ZMQ=OFF" + "-DENABLE_QRCODE=OFF" + "-DENABLE_UPNP=OFF" +) + # Allow usage of sanitizers by setting ECM_ENABLE_SANITIZERS if(ENABLE_SANITIZERS) set(ECM_ENABLE_SANITIZERS ${ENABLE_SANITIZERS})