diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eaef44afb..40cc779973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Improve log and metric delivery when telemetry is captured faster than envelopes can be serialized by offloading serialization to an internal thread pool. ([#1946](https://github.com/getsentry/sentry-native/pull/1946)) - Wine: fix OS version detection and cross-compiling Windows builds from Linux. ([#2001](https://github.com/getsentry/sentry-native/pull/2001)) - Destroy condition variables as approriate when no longer needed. ([#2004](https://github.com/getsentry/sentry-native/pull/2004)) +- CMake: `pkg-config` is no longer a hard build requirement. `SENTRY_LIBUNWIND_SYSTEM` and `SENTRY_BREAKPAD_SYSTEM` still prefer the `pkg-config` metadata of the system package, but now fall back to `find_library()`/`find_path()` when the tool or the `.pc` file is missing. The same applies to the exported CMake config, which no longer requires consumers of a static build to have `pkg-config` installed. - Crashpad/Windows: preserve module CodeView UUIDs for minimal PDB70 records with empty PDB filenames. ([#2003](https://github.com/getsentry/sentry-native/pull/2003)) ## 0.16.3 diff --git a/CMakeLists.txt b/CMakeLists.txt index e955145be8..ad90eebe65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -640,6 +640,7 @@ if(WIN32) endif() include(cmake/utils.cmake) +include(cmake/sentry-find-system-library.cmake) if (WIN32 AND SENTRY_BUILD_SHARED_LIBS) sentry_add_version_resource(sentry "Client Library") endif() @@ -705,12 +706,11 @@ endif() if(SENTRY_WITH_LIBUNWIND) if(LINUX) if(SENTRY_LIBUNWIND_SYSTEM) - find_package(PkgConfig REQUIRED) - pkg_check_modules(LIBUNWIND REQUIRED IMPORTED_TARGET libunwind) + sentry_find_libunwind() if(SENTRY_BUILD_SHARED_LIBS) - target_link_libraries(sentry PRIVATE PkgConfig::LIBUNWIND) + target_link_libraries(sentry PRIVATE sentry::libunwind) else() - target_link_libraries(sentry PUBLIC PkgConfig::LIBUNWIND) + target_link_libraries(sentry PUBLIC sentry::libunwind) endif() else() # Use vendored libunwind @@ -802,13 +802,12 @@ elseif(SENTRY_BACKEND_BREAKPAD) option(SENTRY_BREAKPAD_SYSTEM "Use system breakpad" OFF) if(SENTRY_BREAKPAD_SYSTEM) target_compile_definitions(sentry PRIVATE SENTRY_BREAKPAD_SYSTEM) - # system breakpad is using pkg-config, see `external/breakpad/breakpad-client.pc.in` - find_package(PkgConfig REQUIRED) - pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) + # system breakpad ships pkg-config metadata, see `external/breakpad/breakpad-client.pc.in` + sentry_find_breakpad_client() if(SENTRY_BUILD_SHARED_LIBS) - target_link_libraries(sentry PRIVATE PkgConfig::BREAKPAD) + target_link_libraries(sentry PRIVATE sentry::breakpad-client) else() - target_link_libraries(sentry PUBLIC PkgConfig::BREAKPAD) + target_link_libraries(sentry PUBLIC sentry::breakpad-client) endif() else() add_subdirectory(external) @@ -934,8 +933,8 @@ elseif(SENTRY_BACKEND_NATIVE) if(SENTRY_WITH_LIBUNWIND AND LINUX) if(SENTRY_LIBUNWIND_SYSTEM) - pkg_check_modules(LIBUNWIND_PTRACE REQUIRED IMPORTED_TARGET libunwind-ptrace) - target_link_libraries(sentry-crash PRIVATE PkgConfig::LIBUNWIND PkgConfig::LIBUNWIND_PTRACE) + sentry_find_libunwind_ptrace() + target_link_libraries(sentry-crash PRIVATE sentry::libunwind sentry::libunwind-ptrace) else() # Use unwind_remote for the daemon (includes ptrace accessors # for remote DWARF unwinding of the crashed process) @@ -1018,6 +1017,8 @@ sentry_install( FILES "${PROJECT_BINARY_DIR}/sentry-config.cmake" "${PROJECT_BINARY_DIR}/sentry-config-version.cmake" + # `sentry-config.cmake` recreates the system-library targets for consumers of a static build + "${SENTRY_SOURCE_DIR}/cmake/sentry-find-system-library.cmake" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") if(WIN32 AND MSVC AND SENTRY_BUILD_SHARED_LIBS) sentry_install(FILES $ diff --git a/README.md b/README.md index d9c63cc171..c895fa592a 100644 --- a/README.md +++ b/README.md @@ -266,8 +266,8 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`. This instructs the build system to use system-installed breakpad libraries instead of the in-tree version. - `SENTRY_LIBUNWIND_SYSTEM` (Default: `OFF`, only for Linux): - This instructs the build system to use a system-installed `libunwind` (found via `pkg-config`) instead of the - vendored copy in `vendor/libunwind`. + This instructs the build system to use a system-installed `libunwind` instead of the vendored copy in + `vendor/libunwind`. In contrast to the vendored `libunwind` which is always built as a static archive and either linked into the resulting shared library or colocated with the other static artifacts, with `SENTRY_LIBUNWIND_SYSTEM=ON`, the library @@ -275,6 +275,11 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`. controls how the dependency is exposed to consuming CMake projects, not the library type of the system `libunwind` itself. Ensure matching build and target environments when using system packages. + Both `SENTRY_BREAKPAD_SYSTEM` and `SENTRY_LIBUNWIND_SYSTEM` read the `pkg-config` metadata of the system package + when `pkg-config`/`pkgconf` is installed, and fall back to CMake's own `find_library()`/`find_path()` lookup when it + is not, so `pkg-config` is a convenience rather than a build requirement for either option. The only remaining + build-time user of `pkg-config` is the vendored `crashpad`, and only on Linux with `CRASHPAD_ENABLE_STACKTRACE=ON`. + - `SENTRY_TRANSPORT_COMPRESSION` (Default: `OFF`): Adds Gzip transport compression. Requires `zlib`. diff --git a/cmake/sentry-find-system-library.cmake b/cmake/sentry-find-system-library.cmake new file mode 100644 index 0000000000..f06542b15b --- /dev/null +++ b/cmake/sentry-find-system-library.cmake @@ -0,0 +1,112 @@ +# Locates a system library, preferring `pkg-config` metadata when both the tool and the requested module are +# available, and falling back to plain `find_library()`/`find_path()` when they are not. +# +# `pkg-config` is only ever consulted for the optional `SENTRY_LIBUNWIND_SYSTEM` and `SENTRY_BREAKPAD_SYSTEM` code +# paths, and only on Linux. Requesting it with `find_package(PkgConfig REQUIRED)` nevertheless turned the tool into a +# hard build requirement for anyone packaging sentry-native, which is why package managers end up declaring it on +# every platform, including those where it is never invoked. +# +# On success the imported target `TGT` is defined regardless of which of the two lookups provided it, so neither the +# call sites nor the installed `sentry-config.cmake` have to branch on the outcome. +# +# sentry_find_system_library( +# PKG_CONFIG_MODULE +# LIBRARY_NAMES ... +# [HEADER_NAMES
...] +# [HEADER_PATH_SUFFIXES ...]) +function(sentry_find_system_library TGT) + cmake_parse_arguments(SFSL "" "PKG_CONFIG_MODULE" "LIBRARY_NAMES;HEADER_NAMES;HEADER_PATH_SUFFIXES" ${ARGN}) + + if(TARGET "${TGT}") + return() + endif() + + string(MAKE_C_IDENTIFIER "${TGT}" prefix) + string(TOUPPER "${prefix}" prefix) + + # `pkg-config` resolves transitive `Requires:` and `Libs.private:` entries for us, so prefer it when available. + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules("${prefix}" QUIET IMPORTED_TARGET "${SFSL_PKG_CONFIG_MODULE}") + endif() + + if(TARGET "PkgConfig::${prefix}") + add_library("${TGT}" INTERFACE IMPORTED) + set_target_properties("${TGT}" PROPERTIES INTERFACE_LINK_LIBRARIES "PkgConfig::${prefix}") + return() + endif() + + # No usable `pkg-config` module, so resolve the library and its headers ourselves. + set(libraries "") + set(missing "") + foreach(name IN LISTS SFSL_LIBRARY_NAMES) + string(MAKE_C_IDENTIFIER "SENTRY_${prefix}_${name}_LIBRARY" cache_var) + find_library("${cache_var}" NAMES "${name}") + mark_as_advanced("${cache_var}") + if(${cache_var}) + list(APPEND libraries "${${cache_var}}") + else() + list(APPEND missing "lib${name}") + endif() + endforeach() + + set(include_dir "") + if(SFSL_HEADER_NAMES) + string(MAKE_C_IDENTIFIER "SENTRY_${prefix}_INCLUDE_DIR" cache_var) + if(SFSL_HEADER_PATH_SUFFIXES) + find_path("${cache_var}" NAMES ${SFSL_HEADER_NAMES} PATH_SUFFIXES ${SFSL_HEADER_PATH_SUFFIXES}) + else() + find_path("${cache_var}" NAMES ${SFSL_HEADER_NAMES}) + endif() + mark_as_advanced("${cache_var}") + if(${cache_var}) + set(include_dir "${${cache_var}}") + else() + list(GET SFSL_HEADER_NAMES 0 header) + list(APPEND missing "${header}") + endif() + endif() + + if(missing) + string(REPLACE ";" ", " missing "${missing}") + message(FATAL_ERROR + "Could not find the system dependency `${SFSL_PKG_CONFIG_MODULE}` needed for `${TGT}`.\n" + "Missing: ${missing}.\n" + "Install the matching development package, point CMake at it via `CMAKE_PREFIX_PATH`, or install " + "`pkg-config`/`pkgconf` so that `${SFSL_PKG_CONFIG_MODULE}.pc` can be used instead.") + endif() + + add_library("${TGT}" INTERFACE IMPORTED) + set_target_properties("${TGT}" PROPERTIES INTERFACE_LINK_LIBRARIES "${libraries}") + if(include_dir) + set_target_properties("${TGT}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}") + endif() +endfunction() + +# The lookups below are shared between the build itself and the installed `sentry-config.cmake`, which has to recreate +# the same imported targets for consumers of a static sentry-native. Keeping them here means the two cannot drift. + +function(sentry_find_libunwind) + sentry_find_system_library(sentry::libunwind + PKG_CONFIG_MODULE libunwind + LIBRARY_NAMES unwind + HEADER_NAMES libunwind.h) +endfunction() + +function(sentry_find_libunwind_ptrace) + # `libunwind-ptrace.pc` pulls in `libunwind-generic`, so the fallback has to link it explicitly. + sentry_find_system_library(sentry::libunwind-ptrace + PKG_CONFIG_MODULE libunwind-ptrace + LIBRARY_NAMES unwind-ptrace unwind-generic + HEADER_NAMES libunwind-ptrace.h) +endfunction() + +function(sentry_find_breakpad_client) + # `breakpad-client.pc` exposes the headers below `${includedir}/breakpad`, matching the `client//...` + # includes in `src/backends/sentry_backend_breakpad.cpp`. + sentry_find_system_library(sentry::breakpad-client + PKG_CONFIG_MODULE breakpad-client + LIBRARY_NAMES breakpad_client + HEADER_NAMES google_breakpad/common/breakpad_types.h + HEADER_PATH_SUFFIXES breakpad) +endfunction() diff --git a/sentry-config.cmake.in b/sentry-config.cmake.in index 73ac15117c..a30b7c3c2d 100644 --- a/sentry-config.cmake.in +++ b/sentry-config.cmake.in @@ -1,5 +1,6 @@ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) +include("${CMAKE_CURRENT_LIST_DIR}/sentry-find-system-library.cmake") set(SENTRY_BACKEND @SENTRY_BACKEND@) set(SENTRY_TRANSPORT @SENTRY_TRANSPORT@) @@ -16,12 +17,10 @@ if(NOT SENTRY_BUILD_SHARED_LIBS) find_dependency(ZLIB) endif() if(SENTRY_BACKEND STREQUAL "breakpad" AND SENTRY_BREAKPAD_SYSTEM) - find_dependency(PkgConfig) - pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) + sentry_find_breakpad_client() endif() if(SENTRY_LIBUNWIND_SYSTEM) - find_dependency(PkgConfig) - pkg_check_modules(LIBUNWIND REQUIRED IMPORTED_TARGET libunwind) + sentry_find_libunwind() endif() if(SENTRY_TRANSPORT STREQUAL "curl" AND NOT SENTRY_LINK_CURL STREQUAL "OFF") find_dependency(CURL)