diff --git a/cmake/modules/FindMiniUPnPc.cmake b/cmake/modules/FindMiniUPnPc.cmake new file mode 100644 --- /dev/null +++ b/cmake/modules/FindMiniUPnPc.cmake @@ -0,0 +1,31 @@ +# Try to find libminiupnpc +# MINIUPNPC_FOUND - system has libminiupnpc +# MINIUPNPC_INCLUDE_DIR - the libminiupnpc include directory +# MINIUPNPC_LIBRARY - Library needed to use libminiupnpc + +if (MINIUPNPC_INCLUDE_DIR AND MINIUPNPC_LIBRARY) + # Already in cache, be silent + set(MINIUPNPC_FIND_QUIETLY TRUE) +endif() + +find_path(MINIUPNPC_INCLUDE_DIR miniupnpc/miniupnpc.h + HINTS $ENV{MINIUPNPC_INCLUDE_DIR} +) + +find_library(MINIUPNPC_LIBRARY NAMES miniupnpc libminiupnpc + HINTS $ENV{MINIUPNPC_LIBRARY} +) + +message(STATUS "MiniUPnPc lib: " ${MINIUPNPC_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + MiniUPnPc DEFAULT_MSG + MINIUPNPC_INCLUDE_DIR + MINIUPNPC_LIBRARY +) + +mark_as_advanced(MINIUPNPC_INCLUDE_DIR MINIUPNPC_LIBRARY) + +set(MiniUPnPc_LIBRARIES ${MINIUPNPC_LIBRARY}) +set(MiniUPnPc_INCLUDE_DIRS ${MINIUPNPC_INCLUDE_DIR}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,15 @@ option(BUILD_BITCOIN_QT "Build bitcoin-qt" ON) option(ENABLE_HARDENING "Harden the executables" ON) +# ENABLE_UPNP is a tristate option: +# - AUTO (default): enable if miniupnpc is installed, disable otherwise +# - ON: force enable, make miniupnpc a requirement +# - OFF: force disable +set(ENABLE_UPNP AUTO CACHE STRING "Enable UPnP") +set_property(CACHE ENABLE_UPNP PROPERTY STRINGS AUTO ON OFF) + +option(USE_UPNP_DEFAULT "Make UPnP the default to map ports" OFF) + # Cmake uses the CMAKE_BUILD_TYPE variable to select the build configuration. # By default it supports more configurations that needed for Bitcoin ABC, and # all the releases types set NDEBUG which is unwanted as it disables the assert @@ -302,6 +311,23 @@ memenv ) +if(USE_UPNP) + target_link_libraries(server ${MINIUPNPC_LIBRARY}) + + if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + find_library(IPHLPAPI_LIBRARY NAMES iphlpapi) + if(NOT IPHLPAPI_LIBRARY) + message(FATAL_ERROR "Lib iphlpapi is missing") + endif() + target_link_libraries(server ${IPHLPAPI_LIBRARY}) + + target_compile_definitions(server + PUBLIC -DSTATICLIB + PUBLIC -DMINIUPNP_STATICLIB + ) + endif() +endif() + # Test suite. add_subdirectory(test) diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -154,5 +154,65 @@ # Activate ZeroMQ set(ENABLE_ZMQ ${BUILD_BITCOIN_ZMQ}) +# Try to find miniupnpc +find_package(MiniUPnPc) + +if(MINIUPNPC_FOUND) + set(CMAKE_REQUIRED_INCLUDES ${MINIUPNPC_INCLUDE_DIR}) + check_include_files(miniupnpc/miniwget.h HAVE_MINIWIDGET_H) + check_include_files(miniupnpc/miniupnpc.h HAVE_MINIUPNPC_H) + check_include_files(miniupnpc/upnpcommands.h HAVE_UPNPCCOMMANDS_H) + check_include_files(miniupnpc/upnperrors.h HAVE_UPNPCERRORS_H) + + # Should have them all to enable + if( + HAVE_MINIWIDGET_H AND + HAVE_MINIUPNPC_H AND + HAVE_UPNPCCOMMANDS_H AND + HAVE_UPNPCERRORS_H + ) + set(HAVE_MINIUPNPC 1) + endif() +endif() + +if(NOT HAVE_MINIUPNPC) + # If the user explicitly enabled UPnP through -DENABLE_UPNP=ON, ensure the + # miniupnpc library is present. + if(ENABLE_UPNP AND NOT ENABLE_UPNP STREQUAL "AUTO") + message(FATAL_ERROR + "UPnP requested but cannot be built. Use -DENABLE_UPNP=OFF" + ) + endif() +else() + if(ENABLE_UPNP) + # UPnP is enabled, whether implicitly (not set) or explicitly by + # passing -DENABLE_UPNP=ON on the CMake command line. + + # The expected behavior is as follow: + # - If UPnP is enabled USE_UPNP must be defined + # - If UPnP should be the default port map method, USE_UPNP should be + # defined to 1, otherwise it should be defined to 0. + # + # What we need is some tristate value: + # - /* #undef USE_UPNP */ if UPnP is disabled + # - #define USE_UPNP 0 if UPnP is enabled but not the default method + # - #define USE_UPNP 1 if UPnP is enabled and is the default method + # + # CMake cannot differentiate between a 0 value and the #undef case when + # using the configure_file function. + # To workaround this limitation, the #define 0 case is set to a value + # which is not interpreted as false by CMake but will be casted to + # boolean as false in the code. + if(USE_UPNP_DEFAULT) + set(USE_UPNP_VALUE 1) + else() + # Use parenthesis to avoid CMake to interpret as false, and use a + # quoted variable to avoid CMake escaping the parenthesis. + set(USE_UPNP_VALUE "(0)") + endif() + set(USE_UPNP ${USE_UPNP_VALUE} CACHE INTERNAL "UPnP is enabled") + endif() +endif() + # Generate the config configure_file(bitcoin-config.h.cmake.in bitcoin-config.h ESCAPE_QUOTES) diff --git a/src/config/bitcoin-config.h.cmake.in b/src/config/bitcoin-config.h.cmake.in --- a/src/config/bitcoin-config.h.cmake.in +++ b/src/config/bitcoin-config.h.cmake.in @@ -61,4 +61,10 @@ #cmakedefine ENABLE_WALLET 1 #cmakedefine ENABLE_ZMQ 1 +/* + * UPnP support not compiled if undefined, otherwise value (0 or 1) determines + * default state. + */ +#cmakedefine USE_UPNP ${USE_UPNP} + #endif // BITCOIN_BITCOIN_CONFIG_H