diff --git a/CHANGELOG.md b/CHANGELOG.md index 528a0e1733..e97212c6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [CMAKE] Add pkg-config install consumer tests + [#4503](https://github.com/open-telemetry/opentelemetry-cpp/issues/4503) + * [DOC] Fix and clarify the `StartSpanOptions` documentation [#4526](https://github.com/open-telemetry/opentelemetry-cpp/pull/4526) diff --git a/cmake/otel-install-functions.cmake b/cmake/otel-install-functions.cmake index 468d6db653..3e02e3992c 100644 --- a/cmake/otel-install-functions.cmake +++ b/cmake/otel-install-functions.cmake @@ -1,6 +1,7 @@ # Copyright The OpenTelemetry Authors # SPDX-License-Identifier: Apache-2.0 include("${PROJECT_SOURCE_DIR}/cmake/thirdparty-dependency-config.cmake") +include("${PROJECT_SOURCE_DIR}/cmake/pkgconfig.cmake") ######################################################################## # INTERNAL FUNCTIONS - do not call directly. Use the otel_* "Main" functions @@ -50,10 +51,11 @@ endfunction() # OTEL_COMPONENT_FILES_MATCHING_: Matching pattern for the files to be installed # OTEL_COMPONENT_DEPENDS_: List of components that this component depends on # OTEL_COMPONENT_THIRDPARTY_DEPENDS_: List of thirdparty dependencies that this component depends on +# OTEL_COMPONENT_DESCRIPTION_: Optional human-readable component description #----------------------------------------------------------------------- function(_otel_set_component_properties) set(optionArgs ) - set(oneValueArgs COMPONENT FILES_DIRECTORY FILES_DESTINATION) + set(oneValueArgs COMPONENT DESCRIPTION FILES_DIRECTORY FILES_DESTINATION) set(multiValueArgs TARGETS TARGETS_ALIAS FILES_MATCHING COMPONENT_DEPENDS THIRDPARTY_DEPENDS) cmake_parse_arguments(_PROPERTIES "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") @@ -95,6 +97,10 @@ function(_otel_set_component_properties) if(_PROPERTIES_THIRDPARTY_DEPENDS) set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_THIRDPARTY_DEPENDS_${_PROPERTIES_COMPONENT} "${_PROPERTIES_THIRDPARTY_DEPENDS}") endif() + + if(_PROPERTIES_DESCRIPTION) + set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_DESCRIPTION_${_PROPERTIES_COMPONENT} "${_PROPERTIES_DESCRIPTION}") + endif() endfunction() #----------------------------------------------------------------------- @@ -283,6 +289,10 @@ function(_otel_install_component _COMPONENT) COMPONENT ${_COMPONENT} FILES_MATCHING ${_COMPONENT_FILES_MATCHING}) endif() + + foreach(_TARGET IN LISTS _COMPONENT_TARGETS) + _otel_install_target_pkgconfig("${_TARGET}" "${_COMPONENT}") + endforeach() endfunction() #----------------------------------------------------------------------- @@ -357,6 +367,7 @@ endfunction() # Usage: # otel_add_component( # COMPONENT +# [DESCRIPTION ""] # [DEPRECATED_NAMES ...] # TARGETS ... # [FILES_DIRECTORY @@ -365,7 +376,7 @@ endfunction() #----------------------------------------------------------------------- function(otel_add_component) set(optionArgs DEPRECATED) - set(oneValueArgs COMPONENT FILES_DIRECTORY FILES_DESTINATION) + set(oneValueArgs COMPONENT DESCRIPTION FILES_DIRECTORY FILES_DESTINATION) set(multiValueArgs TARGETS FILES_MATCHING DEPRECATED_NAMES) cmake_parse_arguments(_OTEL_ADD_COMP "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") @@ -404,9 +415,11 @@ function(otel_add_component) message(DEBUG " FILES_DIRECTORY: ${_OTEL_ADD_COMP_FILES_DIRECTORY}") message(DEBUG " FILES_DESTINATION: ${_OTEL_ADD_COMP_FILES_DESTINATION}") message(DEBUG " FILES_MATCHING: ${_OTEL_ADD_COMP_FILES_MATCHING}") + message(DEBUG " DESCRIPTION: ${_OTEL_ADD_COMP_DESCRIPTION}") _otel_set_component_properties( COMPONENT ${_OTEL_ADD_COMP_COMPONENT} + DESCRIPTION ${_OTEL_ADD_COMP_DESCRIPTION} TARGETS ${_OTEL_ADD_COMP_TARGETS} TARGETS_ALIAS ${_ALIAS_TARGETS} FILES_DIRECTORY ${_OTEL_ADD_COMP_FILES_DIRECTORY} @@ -450,8 +463,12 @@ function(otel_install_components) get_property(_COMPONENT_FILES_DIRECTORY DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_FILES_DIRECTORY_${_COMPONENT}) get_property(_COMPONENT_FILES_DESTINATION DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_FILES_DESTINATION_${_COMPONENT}) get_property(_COMPONENT_FILES_MATCHING DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_FILES_MATCHING_${_COMPONENT}) + get_property(_COMPONENT_DESCRIPTION DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY OTEL_COMPONENT_DESCRIPTION_${_COMPONENT}) message(STATUS "Install COMPONENT ${_COMPONENT}") + if(_COMPONENT_DESCRIPTION) + message(STATUS " DESCRIPTION: ${_COMPONENT_DESCRIPTION}") + endif() message(STATUS " TARGETS: ${_COMPONENT_TARGETS}") message(STATUS " TARGETS_ALIAS: ${_COMPONENT_TARGETS_ALIAS}") message(STATUS " COMPONENT_DEPENDS: ${_COMPONENT_DEPENDS}") diff --git a/cmake/pkgconfig.cmake b/cmake/pkgconfig.cmake index 63c1a2affd..7685b739e4 100644 --- a/cmake/pkgconfig.cmake +++ b/cmake/pkgconfig.cmake @@ -5,53 +5,88 @@ include_guard(GLOBAL) # Unlike functions, macros do not introduce a scope. This is an advantage when # trying to set global variables, as we do here. -macro (opentelemetry_set_pkgconfig_paths) - if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") - set(OPENTELEMETRY_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") - else () - set(OPENTELEMETRY_PC_LIBDIR - "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") - endif () - - if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") - set(OPENTELEMETRY_PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") - else () - set(OPENTELEMETRY_PC_INCLUDEDIR - "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") - endif () -endmacro () - -# Create the pkgconfig configuration file (aka *.pc files) and the rules to install it. +macro(opentelemetry_set_pkgconfig_paths) + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(OPENTELEMETRY_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") + else() + set(OPENTELEMETRY_PC_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") + endif() + + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(OPENTELEMETRY_PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") + else() + set(OPENTELEMETRY_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() +endmacro() + +# Register pkg-config metadata on a target. Installation is performed later by +# _otel_install_target_pkgconfig() from _otel_install_component(). # # * library: the short name of the target, such as `api` or `resources`. # * name: the displayed name of the library, such as "OpenTelemetry API". # * description: the description of the library. # * ARGN: the names of any pkgconfig modules the generated module depends on. # -function (opentelemetry_add_pkgconfig library name description) +function(opentelemetry_add_pkgconfig library name description) + set(_target "opentelemetry_${library}") + + if(NOT TARGET "${_target}") + message(FATAL_ERROR + "opentelemetry_add_pkgconfig: target ${_target} not found") + endif() + + set_target_properties( + "${_target}" + PROPERTIES INTERFACE_OTEL_PKGCONFIG_SHORT_NAME "${library}" + INTERFACE_OTEL_PKGCONFIG_NAME "${name}" + INTERFACE_OTEL_PKGCONFIG_DESCRIPTION "${description}" + INTERFACE_OTEL_PKGCONFIG_REQUIRES "${ARGN}") +endfunction() + +# Generate and install a pkg-config file for a target that registered metadata +# via opentelemetry_add_pkgconfig(). +function(_otel_install_target_pkgconfig _TARGET _COMPONENT) + get_target_property(_short_name "${_TARGET}" INTERFACE_OTEL_PKGCONFIG_SHORT_NAME) + if(NOT _short_name OR _short_name STREQUAL "_short_name-NOTFOUND") + return() + endif() + + get_target_property(_pc_name "${_TARGET}" INTERFACE_OTEL_PKGCONFIG_NAME) + get_target_property(_pc_desc "${_TARGET}" INTERFACE_OTEL_PKGCONFIG_DESCRIPTION) + get_target_property(_pc_requires "${_TARGET}" INTERFACE_OTEL_PKGCONFIG_REQUIRES) + opentelemetry_set_pkgconfig_paths() - set(target "opentelemetry_${library}") - set(OPENTELEMETRY_PC_NAME "${name}") - set(OPENTELEMETRY_PC_DESCRIPTION ${description}) - string(JOIN " " OPENTELEMETRY_PC_REQUIRES ${ARGN}) - get_target_property(target_type ${target} TYPE) - if ("${target_type}" STREQUAL "INTERFACE_LIBRARY") + + set(OPENTELEMETRY_PC_NAME "${_pc_name}") + set(OPENTELEMETRY_PC_DESCRIPTION "${_pc_desc}") + string(JOIN " " OPENTELEMETRY_PC_REQUIRES ${_pc_requires}) + + get_target_property(_target_type "${_TARGET}" TYPE) + if("${_target_type}" STREQUAL "INTERFACE_LIBRARY") # Interface libraries only contain headers. They do not generate lib files # to link against with `-l`. set(OPENTELEMETRY_PC_LIBS "") - else () - set(OPENTELEMETRY_PC_LIBS "-l${target}") - endif () - get_target_property(target_defs ${target} INTERFACE_COMPILE_DEFINITIONS) - if (target_defs) - foreach (def ${target_defs}) - string(APPEND OPENTELEMETRY_PC_CFLAGS " -D${def}") - endforeach () - endif () - - # Create and install the pkg-config files. - configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/config.pc.in" "${target}.pc" @ONLY) + else() + set(OPENTELEMETRY_PC_LIBS "-lopentelemetry_${_short_name}") + endif() + + set(OPENTELEMETRY_PC_CFLAGS "") + get_target_property(_target_defs "${_TARGET}" INTERFACE_COMPILE_DEFINITIONS) + if(_target_defs AND NOT _target_defs STREQUAL "_target_defs-NOTFOUND") + foreach(_def IN LISTS _target_defs) + string(APPEND OPENTELEMETRY_PC_CFLAGS " -D${_def}") + endforeach() + endif() + + set(_target "opentelemetry_${_short_name}") + set(_pc_file "${PROJECT_BINARY_DIR}/pkgconfig/${_target}.pc") + file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/pkgconfig") + + configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/config.pc.in" "${_pc_file}" + @ONLY) + install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + FILES "${_pc_file}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + COMPONENT "${_COMPONENT}") endfunction() diff --git a/install/test/cmake/CMakeLists.txt b/install/test/cmake/CMakeLists.txt index 9e8db3f229..379dc23306 100644 --- a/install/test/cmake/CMakeLists.txt +++ b/install/test/cmake/CMakeLists.txt @@ -90,6 +90,33 @@ add_test(NAME full-package-run-test COMMAND ${CMAKE_BINARY_DIR}/build-full-package-test/full_test) # ----------------------------------------------------------- +# ----------------------------------------------------------- +# Pkg-config consumer test (compile/link/run against installed .pc files) +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + add_test( + NAME pkgconfig-test-config-test + COMMAND + ${CMAKE_COMMAND} --log-level=DEBUG -S ${CMAKE_SOURCE_DIR}/pkgconfig_test + -B build-pkgconfig-test "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" + ${INSTALL_TEST_CMAKE_OPTIONS} + "-DINSTALL_TEST_COMPONENTS=${INSTALL_TEST_COMPONENTS}" + "-DINSTALL_TEST_SRC_DIR=${INSTALL_TEST_SRC_DIR}") + + set_tests_properties( + pkgconfig-test-config-test + PROPERTIES ENVIRONMENT "PKG_CONFIG_PATH=${CMAKE_PREFIX_PATH}/lib/pkgconfig") + + add_test(NAME pkgconfig-test-build-test + COMMAND ${CMAKE_COMMAND} --build + ${CMAKE_BINARY_DIR}/build-pkgconfig-test --parallel) + + add_test(NAME pkgconfig-test-run-test + COMMAND ${CMAKE_BINARY_DIR}/build-pkgconfig-test/full_pkgconfig_test) +else() + message(STATUS "PkgConfig not found; skipping pkg-config install tests") +endif() + # ----------------------------------------------------------- # Loop over all the components to test foreach(component ${INSTALL_TEST_COMPONENTS}) diff --git a/install/test/cmake/pkgconfig_test/CMakeLists.txt b/install/test/cmake/pkgconfig_test/CMakeLists.txt new file mode 100644 index 0000000000..520a4ac773 --- /dev/null +++ b/install/test/cmake/pkgconfig_test/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.16) + +project(opentelemetry-cpp-pkgconfig-install-test LANGUAGES CXX) + +if(NOT DEFINED INSTALL_TEST_SRC_DIR) + message( + FATAL_ERROR + "INSTALL_TEST_SRC_DIR must be defined when running cmake on this test project" + ) +endif() + +if(NOT INSTALL_TEST_COMPONENTS) + message(FATAL_ERROR "INSTALL_TEST_COMPONENTS is empty") +endif() + +find_package(PkgConfig REQUIRED) +find_package(GTest CONFIG REQUIRED) + +file(GLOB _pkgconfig_pc_files + "${CMAKE_PREFIX_PATH}/lib/pkgconfig/opentelemetry_*.pc") +if(NOT _pkgconfig_pc_files) + message( + FATAL_ERROR + "No installed opentelemetry pkg-config files found under ${CMAKE_PREFIX_PATH}/lib/pkgconfig" + ) +endif() + +set(_pkgconfig_modules "") +foreach(_pc_file IN LISTS _pkgconfig_pc_files) + get_filename_component(_module "${_pc_file}" NAME_WE) + list(APPEND _pkgconfig_modules "${_module}") +endforeach() +list(SORT _pkgconfig_modules) + +message(STATUS "Testing pkg-config install on modules = ${_pkgconfig_modules}") +message( + STATUS "Testing pkg-config install on components = ${INSTALL_TEST_COMPONENTS}" +) + +pkg_check_modules(OTEL_PKGCONFIG REQUIRED ${_pkgconfig_modules}) + +add_executable(full_pkgconfig_test) + +foreach(component IN LISTS INSTALL_TEST_COMPONENTS) + message(STATUS "Adding test source for component ${component}") + target_sources(full_pkgconfig_test + PRIVATE "${INSTALL_TEST_SRC_DIR}/test_${component}.cc") +endforeach() + +target_include_directories(full_pkgconfig_test + PRIVATE ${OTEL_PKGCONFIG_INCLUDE_DIRS}) +target_compile_options(full_pkgconfig_test + PRIVATE ${OTEL_PKGCONFIG_CFLAGS_OTHER}) +target_link_libraries( + full_pkgconfig_test + PRIVATE ${OTEL_PKGCONFIG_LIBRARIES} ${OTEL_PKGCONFIG_LDFLAGS} GTest::gtest + GTest::gtest_main)