# # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # cmake_minimum_required (VERSION 3.16) project (Proton C) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/tools/cmake/Modules") set (CMAKE_THREAD_PREFER_PTHREAD TRUE) include (CTest) include (CheckLanguage) include (CheckLibraryExists) include (CheckSymbolExists) include (CheckPythonModule) find_package (OpenSSL) find_package (Threads) find_package (SWIG) find_package (CyrusSASL) # This setting mirrors FindPythonInterp behavior on Windows/macOS, which did not consult registry/frameworks first.` if (NOT DEFINED Python_FIND_REGISTRY) set(Python_FIND_REGISTRY "LAST") endif () if (NOT DEFINED Python_FIND_FRAMEWORK) set(Python_FIND_FRAMEWORK "LAST") endif () find_package(Python 3.9 REQUIRED COMPONENTS Interpreter OPTIONAL_COMPONENTS Development) include(tests/PNAddTest.cmake) # Set up runtime checks (valgrind, sanitizers etc.) include(tests/RuntimeCheck.cmake) ## Set up so that find_package(Proton) & find_package(ProtonCpp) work in the build tree set (Proton_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/config) set (ProtonCpp_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake/config) ## Variables used across components set (PN_ENV_SCRIPT "${Python_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/env.py") set (PN_C_INCLUDE_DIR "${PROJECT_BINARY_DIR}/c/include") set (PN_C_LIBRARY_DIR "${PROJECT_BINARY_DIR}/c") set (PN_C_SOURCE_DIR "${PROJECT_BINARY_DIR}/c/src") ## C set(CMAKE_C_STANDARD 99) set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_STANDARD_REQUIRED ON) set (C_STANDARD_FLAGS ${C_STANDARD_${CMAKE_C_COMPILER_ID}}) set (C_EXTENDED_FLAGS ${C_EXTENDED_${CMAKE_C_COMPILER_ID}}) ## C++ set(UNSET_CMAKE_CXX_COMPILER OFF) if (NOT DEFINED CMAKE_CXX_COMPILER) set(UNSET_CMAKE_CXX_COMPILER ON) endif () check_language (CXX) if (CMAKE_CXX_COMPILER) if (UNSET_CMAKE_CXX_COMPILER) # https://gitlab.kitware.com/cmake/cmake/-/issues/25535: check_language might set the variable incorrectly unset(CMAKE_CXX_COMPILER) unset(CMAKE_CXX_COMPILER CACHE) endif () enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS OFF) endif() # Build static C and C++ libraries in addition to shared libraries. option(BUILD_STATIC_LIBS "Build static libraries as well as shared libraries" OFF) if (CMAKE_CONFIGURATION_TYPES) # There is no single "build type"... message(STATUS "Build types are ${CMAKE_CONFIGURATION_TYPES}") else (CMAKE_CONFIGURATION_TYPES) # There is a single build configuration # If the build type is not set then set the default if (NOT CMAKE_BUILD_TYPE) set (CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type: Debug, Release, RelWithDebInfo, MinSizeRel or Coverage (default RelWithDebInfo)" FORCE) endif () # Set up extra coverage analysis options for gcc (-Og is now the recommended optimization level for debugging) # See https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html set (COVERAGE_COMPILE_FLAGS_GNU "-g -Og --coverage -fprofile-update=atomic") set (COVERAGE_LINK_FLAGS_GNU "--coverage") set (CMAKE_C_FLAGS_COVERAGE "${COVERAGE_COMPILE_FLAGS_${CMAKE_C_COMPILER_ID}}" CACHE STRING "Flags used by the C compiler during COVERAGE builds." FORCE) set (CMAKE_CXX_FLAGS_COVERAGE "${COVERAGE_COMPILE_FLAGS_${CMAKE_CXX_COMPILER_ID}}" CACHE STRING "Flags used by the CXX compiler during COVERAGE builds." FORCE) set (CMAKE_EXE_LINKER_FLAGS_COVERAGE "${COVERAGE_LINK_FLAGS_${CMAKE_C_COMPILER_ID}}" CACHE STRING "Flags used by the linker during COVERAGE builds." FORCE) set (CMAKE_MODULE_LINKER_FLAGS_COVERAGE "${COVERAGE_LINK_FLAGS_${CMAKE_C_COMPILER_ID}}" CACHE STRING "Flags used by the linker during the creation of modules during COVERAGE builds." FORCE) set (CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${COVERAGE_LINK_FLAGS_${CMAKE_C_COMPILER_ID}}" CACHE STRING "Flags used by the linker during the creation of shared libraries during COVERAGE builds." FORCE) mark_as_advanced( CMAKE_C_FLAGS_COVERAGE CMAKE_CXX_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_MODULE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE) if (CMAKE_BUILD_TYPE MATCHES "Deb") set (has_debug_symbols " (has debug symbols)") endif (CMAKE_BUILD_TYPE MATCHES "Deb") message(STATUS "Build type is \"${CMAKE_BUILD_TYPE}\"${has_debug_symbols}") endif (CMAKE_CONFIGURATION_TYPES) # Add coverage target if we're building for test coverage if (CMAKE_BUILD_TYPE MATCHES "Coverage") make_directory(coverage_results) add_custom_target(coverage WORKING_DIRECTORY ./coverage_results COMMAND ${PROJECT_SOURCE_DIR}/scripts/record-coverage.sh ${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) if (BUILD_PYTHON) add_dependencies(coverage python-coverage) endif() endif() # Try to keep any platform specific overrides together here: # MacOS has a bunch of differences in build tools and process and so we have to turn some things # off if building there: if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set (NOENABLE_WARNING_ERROR ON) set (LIB_SUFFIX "") endif () # Benchmarks should be disabled by default set (NOENABLE_BENCHMARKS ON) # Add options here called they will turn into "ENABLE_ set (OPTIONS WARNING_ERROR UNDEFINED_ERROR LINKTIME_OPTIMIZATION HIDE_UNEXPORTED_SYMBOLS FUZZ_TESTING BENCHMARKS) foreach (OPTION ${OPTIONS}) if (NOT NOENABLE_${OPTION}) set ("DEFAULT_${OPTION}" ON) endif () endforeach (OPTION) # And add the option here too with help text option(ENABLE_WARNING_ERROR "Consider compiler warnings to be errors" ${DEFAULT_WARNING_ERROR}) option(ENABLE_UNDEFINED_ERROR "Check for unresolved library symbols" ${DEFAULT_UNDEFINED_ERROR}) option(ENABLE_LINKTIME_OPTIMIZATION "Perform link time optimization" ${DEFAULT_LINKTIME_OPTIMIZATION}) option(ENABLE_HIDE_UNEXPORTED_SYMBOLS "Only export library symbols that are explicitly requested" ${DEFAULT_HIDE_UNEXPORTED_SYMBOLS}) option(ENABLE_FUZZ_TESTING "Enable building fuzzers and regression testing with libFuzzer" ${DEFAULT_FUZZ_TESTING}) option(ENABLE_BENCHMARKS "Enable building and running benchmarks with Google Benchmark" ${DEFAULT_BENCHMARKS}) option(BUILD_TOOLS "Enable building commandline programs to send and receive simple messages" ON) option(BUILD_EXAMPLES "Enable building example programs" ON) option(BUILD_TLS "Enable building separate TLS library for Proton raw connections" OFF) # Set any additional compiler specific flags set (WERROR_GNU "-Werror") set (WERROR_Clang "-Werror") set (WERROR_MSVC "/WX") set (COMMON_WARNING_GNU "-Wall -pedantic-errors") set (COMMON_WARNING_Clang "-Wall -pedantic") string (JOIN " " COMMON_WARNING_MSVC "/W4" "/analyze" # disabled warnings /wd "/wd26812" # The enum type 'pcontext_type_t' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). "/wd4090" # 'function': different 'const' qualifiers "/wd4100" # 'argv': unreferenced formal parameter "/wd4127" # conditional expression is constant "/wd4133" # 'function': incompatible types - from 'pn_durability_t *' to 'uint32_t *' "/wd4180" # qualifier applied to function type has no meaning; ignored "/wd4189" # 'status': local variable is initialized but not referenced "/wd4211" # nonstandard extension used: redefined extern to static "/wd4232" # nonstandard extension used: 'incref': address of dllimport 'pn_void_incref' is not static, identity not guaranteed "/wd4244" # '+=': conversion from 'ssize_t' to 'uint32_t', possible loss of data "/wd4245" # '=': conversion from 'int' to 'pn_socket_t', signed/unsigned mismatch "/wd4267" # '=': conversion from 'size_t' to 'uint32_t', possible loss of data "/wd4305" # 'type cast': truncation from 'pn_string_t *' to 'bool' "/wd4389" # '!=': signed/unsigned mismatch "/wd4456" # declaration of 'type' hides previous local declaration "/wd4458" # declaration of 'handler' hides class member "/wd4459" # declaration of 'buf' hides global declaration "/wd4505" # 'ssl_session_free': unreferenced function with internal linkage has been removed "/wd4701" # potentially uninitialized local variable 'evalue' used "/wd4702" # unreachable code "/wd4703" # potentially uninitialized local pointer variable 'port' used "/wd4706" # assignment within conditional expression "/wd4800" # Implicit conversion from 'type' to bool. Possible information loss "/wd4996" # 'getenv': This function or variable may be unsafe. Consider using _dupenv_s instead. "/wd6001" # Using uninitialized memory '*a->addresses'. "/wd6011" # Dereferencing NULL pointer 'buf'. "/wd6031" # Return value ignored: 'InitializeCriticalSectionAndSpinCount'. "/wd6101" # Returning uninitialized memory '*Mtu'. A successful path through the function does not set the named _Out_ parameter. "/wd6217" # Implicit cast between semantically different integer types: testing HRESULT with 'not'. "/wd6221" # Implicit cast between semantically different integer types: comparing HRESULT to an integer. "/wd6230" # Implicit cast between semantically different integer types: using HRESULT in a Boolean context. "/wd6244" # Local declaration of 'buf' hides previous declaration at line '33' "/wd6246" # Local declaration of 'type' hides declaration of the same name in outer scope. "/wd6308" # 'realloc' might return null pointer: assigning null pointer to 'a->addresses', which is passed as an argument to 'realloc', will cause the original memory block to be leaked. "/wd6336" # Arithmetic operator has precedence over question operator, use parentheses to clarify intent. "/wd6385" # Reading invalid data from 'conn->rbuffers'. "/wd6386" # Buffer overrun while writing to 'nargv': the writable size is '_Param_(1)*_Param_(2)' bytes, but '16' bytes might be written. "/wd6387" # 'rbytes' could be '0': this does not adhere to the specification for the function 'memcpy'. "/wd6553" # The annotation for function 'CertOpenStore' on _Param_(3) does not apply to a value type. # warnings as error /we "/we26819" # Unannotated fallthrough between switch labels "/we28251" # Inconsistent annotation for function: this instance has an error "/we6328" # Size mismatch: 'unsigned __int64' passed as _Param_(2) when 'int' is required in call to 'iocp_log'. "/we6340" # Mismatch on sign: 'unsigned char' passed as _Param_(5) when some signed type is required in call to 'pn_logger_logf'. ) set (CC_WARNING_GNU "-Wno-unused-parameter -Wstrict-prototypes -Wvla -Wsign-compare -Wwrite-strings -Wimplicit-fallthrough=3") set (CC_WARNING_Clang "-Wno-unused-parameter -Wstrict-prototypes -Wvla -Wsign-compare -Wwrite-strings -Wimplicit-fallthrough") set (CC_WARNING_MSVC "") set (CXX_WARNING_GNU "") set (CXX_WARNING_Clang "") set (CXX_WARNING_MSVC "") set (CATCH_UNDEFINED_Linux "-Wl,--no-undefined") set (CATCH_UNDEFINED_FreeBSD "-Wl,--no-undefined") set (CATCH_UNDEFINED_Darwin "-Wl,-undefined,error") set (CATCH_UNDEFINED_Windows "") set (HIDE_SYMBOLS_GNU "-fvisibility=hidden") set (HIDE_SYMBOLS_Clang "-fvisibility=hidden") set (HIDE_SYMBOLS_SunPro "-xldscope=hidden") if (ENABLE_LINKTIME_OPTIMIZATION) set (CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) endif (ENABLE_LINKTIME_OPTIMIZATION) if (ENABLE_WARNING_ERROR) set (WERROR ${WERROR_${CMAKE_C_COMPILER_ID}}) endif (ENABLE_WARNING_ERROR) if (ENABLE_UNDEFINED_ERROR) set (CATCH_UNDEFINED ${CATCH_UNDEFINED_${CMAKE_SYSTEM_NAME}}) endif (ENABLE_UNDEFINED_ERROR) if (ENABLE_HIDE_UNEXPORTED_SYMBOLS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${HIDE_SYMBOLS_${CMAKE_C_COMPILER_ID}}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${HIDE_SYMBOLS_${CMAKE_C_COMPILER_ID}}") endif (ENABLE_HIDE_UNEXPORTED_SYMBOLS) set (COMMON_WARNING_FLAGS ${COMMON_WARNING_${CMAKE_C_COMPILER_ID}}) set (CXX_WARNING_FLAGS "${WERROR} ${COMMON_WARNING_FLAGS} ${CXX_WARNING_${CMAKE_C_COMPILER_ID}}") set (COMPILE_WARNING_FLAGS "${WERROR} ${COMMON_WARNING_FLAGS} ${CC_WARNING_${CMAKE_C_COMPILER_ID}}") if (MSVC) set(CMAKE_DEBUG_POSTFIX "d") endif (MSVC) file(READ VERSION.txt PN_VERSION_FILE) string(STRIP ${PN_VERSION_FILE} PN_VERSION_LINE) string(REPLACE "-" ";" PN_VERSION_SPLIT "${PN_VERSION_LINE}") list(GET PN_VERSION_SPLIT 0 PN_VERSION_CLEAN) list(REMOVE_AT PN_VERSION_SPLIT 0) string(REPLACE ";" "-" PN_VERSION_QUALIFIER "${PN_VERSION_SPLIT}") string(REGEX MATCHALL "[0-9]+" PN_VERSION_LIST "${PN_VERSION_CLEAN}") list(GET PN_VERSION_LIST 0 PN_VERSION_MAJOR) list(GET PN_VERSION_LIST 1 PN_VERSION_MINOR) list(LENGTH PN_VERSION_LIST PN_VERSION_LENGTH) if (${PN_VERSION_LENGTH} GREATER 2) list(GET PN_VERSION_LIST 2 PN_VERSION_POINT) set (PN_VERSION "${PN_VERSION_MAJOR}.${PN_VERSION_MINOR}.${PN_VERSION_POINT}") else() set (PN_VERSION_POINT 0) set (PN_VERSION "${PN_VERSION_MAJOR}.${PN_VERSION_MINOR}") endif() message(STATUS "PN_VERSION: ${PN_VERSION} (${PN_VERSION_QUALIFIER})") # In rpm builds the build sets some variables: # CMAKE_INSTALL_PREFIX - this is a standard cmake variable # INCLUDE_INSTALL_DIR # LIB_INSTALL_DIR # SYSCONF_INSTALL_DIR # SHARE_INSTALL_DIR # So make these cached variables and the specific variables non cached # and derived from them. if (NOT DEFINED LIB_SUFFIX) get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) if ("${LIB64}" STREQUAL "TRUE" AND ${CMAKE_SIZEOF_VOID_P} STREQUAL "8") set(LIB_SUFFIX 64) else() set(LIB_SUFFIX "") endif() endif() # Start of variables used during install set (INCLUDE_INSTALL_DIR include CACHE PATH "Include file directory") set (LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "Library object file directory") set (SYSCONF_INSTALL_DIR etc CACHE PATH "System read only configuration directory") set (SHARE_INSTALL_DIR share CACHE PATH "Shared read only data directory") set (MAN_INSTALL_DIR share/man CACHE PATH "Manpage directory") mark_as_advanced (INCLUDE_INSTALL_DIR LIB_INSTALL_DIR SYSCONF_INSTALL_DIR SHARE_INSTALL_DIR MAN_INSTALL_DIR) # Sets variable NAME to contain relative path from ROOT to VALUE # if VALUE is already relative, it does nothing macro (pn_relative_install_dir NAME ROOT VALUE) if (IS_ABSOLUTE ${VALUE}) file(RELATIVE_PATH "${NAME}" "${ROOT}" "${VALUE}") else () set (${NAME} "${VALUE}") endif () endmacro () pn_relative_install_dir (INCLUDEDIR "${CMAKE_INSTALL_PREFIX}" "${INCLUDE_INSTALL_DIR}") pn_relative_install_dir (LIBDIR "${CMAKE_INSTALL_PREFIX}" "${LIB_INSTALL_DIR}") ## LANGUAGE BINDINGS set (PROTON_SHARE ${SHARE_INSTALL_DIR}/proton) # End of variables used during install # Set result to a native search path - used by examples and binding tests. # args after result are directories or search paths. macro(set_search_path result) set(${result} ${ARGN}) if (UNIX) string(REPLACE ";" ":" ${result} "${${result}}") # native search path separators. endif() file(TO_NATIVE_PATH "${${result}}" ${result}) # native slash separators endmacro() if (CMAKE_SYSTEM_NAME STREQUAL Windows) # CMake uses semicolon as list separator. Change ';'->'\;' function(to_native_path path result) file (TO_NATIVE_PATH "${path}" path) string (REPLACE ";" "\;" path "${path}") # without this, ...;last\dir\ would later combine with list separator ; into \; set (${result} "${path}\;" PARENT_SCOPE) endfunction() else (CMAKE_SYSTEM_NAME STREQUAL Windows) # Just change ';'->':' function(to_native_path path result) file (TO_NATIVE_PATH "${path}" path) string (REPLACE ";" ":" path "${path}") set (${result} "${path}" PARENT_SCOPE) endfunction() endif (CMAKE_SYSTEM_NAME STREQUAL Windows) add_custom_target(docs) add_custom_target(doc DEPENDS docs) # Note: Process bindings after the source lists have been defined so # the bindings can reference them. add_subdirectory(c) # Add bindings that do not require swig here - the directory name must be the same as the binding name # See below for swig bindings set(BINDINGS cpp go python) if (CMAKE_CXX_COMPILER) set (DEFAULT_CPP ON) endif() # Prerequisites for Go find_program(GO_EXE go) mark_as_advanced(GO_EXE) if (GO_EXE) set (DEFAULT_GO ON) if(WIN32 OR RUNTIME_CHECK) # Go on windows requires gcc tool chain # Go does not work with C-based runtime checkers. set (DEFAULT_GO OFF) endif() endif (GO_EXE) # Prerequisites for Python wrapper: if (Python_Development_FOUND) set (DEFAULT_PYTHON ON) endif () if(SWIG_FOUND) # Add any new swig bindings here - the directory name must be the same as the binding name list(APPEND BINDINGS ruby) include(UseSWIG) # All swig modules should include ${PROTON_HEADERS} in SWIG_MODULE__EXTRA_DEPS file(GLOB PROTON_HEADERS "${PROJECT_SOURCE_DIR}/c/include/proton/*.h") # All swig modules should include ${BINDING_DEPS} or ${BINDING_DEPS_FULL} in swig_link_libraries set (BINDING_DEPS qpid-proton-core) set (BINDING_DEPS_FULL qpid-proton) # Add a block here to detect the prerequisites to build each language binding: # # If the prerequisites for the binding are present set a variable called # DEFAULT_{uppercase name of binding} to ON # Prerequisites for Ruby: find_package(Ruby) if (RUBY_FOUND) set (DEFAULT_RUBY ON) endif () endif() # To kick-start a build with just a few bindings enabled by default, e.g. ruby and go: # # cmake -DBUILD_BINDINGS="ruby;go" # # This is only used when CMakeCache.txt is first created, after that set the normal # BUILD_XXX variables to enable/disable bindings. # if (NOT DEFINED BUILD_BINDINGS) set(BUILD_BINDINGS "${BINDINGS}") endif() foreach(BINDING ${BINDINGS}) string(TOUPPER ${BINDING} UBINDING) list(FIND BUILD_BINDINGS ${BINDING} N) if(NOBUILD_${UBINDING} OR ( N EQUAL -1 ) ) # Over-ridden or not on the BUILD_BINDINGS list set(DEFAULT_${UBINDING} OFF) endif() option(BUILD_${UBINDING} "Build ${BINDING} language binding" ${DEFAULT_${UBINDING}}) if (BUILD_${UBINDING}) add_subdirectory(${BINDING}) endif () endforeach(BINDING) unset(BUILD_BINDINGS CACHE) # Remove from cache, only relevant when creating the initial cache. install (FILES LICENSE.txt README.md tests/share/CMakeLists.txt DESTINATION ${PROTON_SHARE}) install (FILES tests/share/examples-README.md RENAME README.md DESTINATION ${PROTON_SHARE}/examples) install (DIRECTORY tests DESTINATION ${PROTON_SHARE} PATTERN share EXCLUDE) # Generate test environment settings configure_file(${PROJECT_SOURCE_DIR}/misc/config.sh.in ${PROJECT_BINARY_DIR}/config.sh @ONLY) configure_file(${PROJECT_SOURCE_DIR}/misc/config.bat.in ${PROJECT_BINARY_DIR}/config.bat @ONLY) if (BUILD_EXAMPLES) add_subdirectory(tests/examples) endif (BUILD_EXAMPLES)