diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a8a6409c..1208a49d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Add `sentry_scope_acquire` and `sentry_scope_release` to access the isolation scope. ([#2160](https://github.com/getsentry/sentry-native/pull/2160)) - Native: Provide exception metadata, registers, and a basic crashing-thread stack to `on_crash` for crash identification. ([#2174](https://github.com/getsentry/sentry-native/pull/2174)) - Native: reduce disk I/O on scope changes by sending incremental updates to the crash daemon over IPC. ([#2155](https://github.com/getsentry/sentry-native/pull/2155), [#2157](https://github.com/getsentry/sentry-native/pull/2157)) +- Add pkg-config support for shared builds. ([#1411](https://github.com/getsentry/sentry-native/pull/1411)) - Native: install handlers for the daemon's own fatal signals and unhandled exceptions, allowing best-effort recovery actions such as dumping pending envelopes before termination. ([#2220](https://github.com/getsentry/sentry-native/pull/2220)) - Report the host OS in the OS context under Wine and Proton, instead of the Windows version that Wine emulates. On Linux, prefer the host `os-release` inside Steam Runtime and Flatpak containers, and report SteamOS and Bazzite as the OS name. ([#2241](https://github.com/getsentry/sentry-native/pull/2241)) - Add `sentry_scope_set_trace` for per-scope traces. ([#2236](https://github.com/getsentry/sentry-native/pull/2236)) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d967bfdd..10c5f8bf7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,6 +246,7 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") endif() option(SENTRY_ENABLE_INSTALL "Enable sentry installation" "${SENTRY_MAIN_PROJECT}") +option(SENTRY_INSTALL_PKGCONFIG "Generate and install pkg-config file for shared builds" ON) if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$") message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to 'breakpad'") @@ -1033,11 +1034,11 @@ elseif(SENTRY_BACKEND_NATIVE) # Install daemon install(TARGETS sentry-crash - RUNTIME DESTINATION bin + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) if(WIN32) install(TARGETS sentry-wer - RUNTIME DESTINATION bin + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) @@ -1209,3 +1210,34 @@ if(SENTRY_BUILD_SHARED_LIBS) "$<$:-Wl,-z,max-page-size=16384>" ) endif() + +# Generate and install pkg-config file +if(SENTRY_INSTALL_PKGCONFIG AND SENTRY_BUILD_SHARED_LIBS) + set(PREFIX "${CMAKE_INSTALL_PREFIX}") + set(PKGCONFIG_VERSION "${SENTRY_VERSION_FULL}") + # Use absolute paths for libdir, includedir, and bindir to support non-standard layouts (e.g., lib64) + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") + else() + set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") + endif() + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") + else() + set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() + if(IS_ABSOLUTE "${CMAKE_INSTALL_BINDIR}") + set(PKGCONFIG_BINDIR "${CMAKE_INSTALL_BINDIR}") + else() + set(PKGCONFIG_BINDIR "\${prefix}/${CMAKE_INSTALL_BINDIR}") + endif() + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/sentry.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/sentry.pc" + @ONLY + ) + sentry_install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/sentry.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + ) +endif() diff --git a/sentry.pc.in b/sentry.pc.in new file mode 100644 index 000000000..0de461e28 --- /dev/null +++ b/sentry.pc.in @@ -0,0 +1,12 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=@PKGCONFIG_LIBDIR@ +includedir=@PKGCONFIG_INCLUDEDIR@ +bindir=@PKGCONFIG_BINDIR@ + +Name: sentry +Description: Sentry SDK for C, C++ and native applications +Version: @PKGCONFIG_VERSION@ +URL: https://sentry.io/ +Libs: -L${libdir} -lsentry +Cflags: -I${includedir} diff --git a/tests/cmake.py b/tests/cmake.py index 0da3d4053..1eb7aef0c 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -23,7 +23,7 @@ def __init__(self, factory): self.runs = dict() self.factory = factory - def compile(self, targets, options=None, cflags=None): + def compile(self, targets, options=None, cflags=None, source_dir=None): # We build in tmp for all of our tests. Disable the warning MSVC generates to not clutter the build logs. if cflags is None: cflags = [] @@ -35,11 +35,12 @@ def compile(self, targets, options=None, cflags=None): key = ";".join(f"{k}={v}" for k, v in options.items()) if cflags: key += ";" + ";".join(cflags) + key = (source_dir, key) # cache the build configuration if key not in self.runs: cwd = self.factory.mktemp("cmake") - cmake_configure(cwd, options, cflags) + cmake_configure(cwd, options, cflags, source_dir) cmake_build(cwd, targets, options) self.runs[key] = (cwd, set(targets)) else: @@ -97,6 +98,7 @@ def destroy(self): exe_name("sentry-crash"), exe_name("sentry_early_init"), exe_name("sentry_test_platform"), + exe_name("sentry_test_pkgconfig"), ] cmd = [ os.environ.get("LLVM_COV", "llvm-cov"), @@ -129,7 +131,7 @@ def destroy(self): ) -def cmake_configure(cwd, options, cflags=None): +def cmake_configure(cwd, options, cflags=None, source_dir=None): if cflags is None: cflags = [] __tracebackhide__ = True @@ -166,7 +168,8 @@ def cmake_configure(cwd, options, cflags=None): options.update(get_android_config()) - source_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + if source_dir is None: + source_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) cmake = ["cmake"] if "scan-build" in os.environ.get("RUN_ANALYZER", ""): @@ -206,7 +209,7 @@ def cmake_configure(cwd, options, cflags=None): env = dict(os.environ) env["CFLAGS"] = env["CXXFLAGS"] = " ".join(cflags) - config_cmd.append(source_dir) + config_cmd.append(os.fspath(source_dir)) print("\n{} > {}".format(cwd, " ".join(config_cmd)), flush=True) try: diff --git a/tests/fixtures/pkgconfig/CMakeLists.txt b/tests/fixtures/pkgconfig/CMakeLists.txt new file mode 100644 index 000000000..d7b2a340a --- /dev/null +++ b/tests/fixtures/pkgconfig/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.10) +project(sentry_pkgconfig C) + +# Options that match the main sentry-native build +option(SENTRY_BUILD_FORCE32 "Force a 32bit compile on a 64bit host" OFF) +option(WITH_ASAN_OPTION "Build with address sanitizer" OFF) +option(WITH_TSAN_OPTION "Build with thread sanitizer" OFF) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(SENTRY REQUIRED IMPORTED_TARGET sentry) + +pkg_get_variable(SENTRY_BINDIR sentry bindir) +find_program(SENTRY_HANDLER_PATH sentry-crash + PATHS "${SENTRY_BINDIR}" + NO_DEFAULT_PATH ONLY_CMAKE_FIND_ROOT_PATH) + +add_executable(sentry_test_pkgconfig main.c) +target_link_libraries(sentry_test_pkgconfig PRIVATE PkgConfig::SENTRY) +target_compile_definitions(sentry_test_pkgconfig PRIVATE + SENTRY_HANDLER_PATH=\"${SENTRY_HANDLER_PATH}\") + +if(NOT MSVC) + if(SENTRY_BUILD_FORCE32) + target_compile_options(sentry_test_pkgconfig PRIVATE -m32) + target_link_libraries(sentry_test_pkgconfig PRIVATE -m32) + endif() + + if(WITH_ASAN_OPTION) + target_compile_options(sentry_test_pkgconfig PRIVATE -fsanitize=address) + target_link_libraries(sentry_test_pkgconfig PRIVATE -fsanitize=address) + endif() + + if(WITH_TSAN_OPTION) + target_compile_options(sentry_test_pkgconfig PRIVATE -fsanitize=thread) + target_link_libraries(sentry_test_pkgconfig PRIVATE -fsanitize=thread) + endif() +endif() diff --git a/tests/fixtures/pkgconfig/main.c b/tests/fixtures/pkgconfig/main.c new file mode 100644 index 000000000..933d08551 --- /dev/null +++ b/tests/fixtures/pkgconfig/main.c @@ -0,0 +1,20 @@ +#include +#include + +int +main(void) +{ + const char *database = getenv("SENTRY_DATABASE_PATH"); + if (!database) { + return 1; + } + + sentry_options_t *options = sentry_options_new(); + sentry_options_set_handler_path(options, SENTRY_HANDLER_PATH); + sentry_options_set_database_path(options, database); + if (sentry_init(options) != 0) { + return 1; + } + sentry_close(); + return 0; +} diff --git a/tests/test_build.py b/tests/test_build.py index 956a90789..15fa22a2d 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -2,10 +2,14 @@ import sys import os import struct +import shutil +import shlex +from pathlib import Path import pytest -from . import SENTRY_VERSION, lib_name +from . import SENTRY_VERSION, lib_name, run +from .build_config import get_test_executable_env from .conditions import ( has_breakpad, has_crashpad, @@ -193,3 +197,45 @@ def test_static_mpack(cmake): assert any( symbol.startswith("sentry__mpack_") for symbol in symbols ), f"{library.name} contains no Sentry-prefixed MPack symbols" + + +@pytest.mark.skipif( + sys.platform not in ("linux", "darwin") or not has_native or is_qemu or is_wine, + reason="test requires a native Unix build", +) +def test_pkgconfig(cmake, tmp_path, monkeypatch): + if shutil.which("pkg-config") is None: + pytest.skip("pkg-config is not installed") + + destdir = tmp_path / "install" + directory = cmake(["sentry"], {"SENTRY_TRANSPORT": "none"}) + subprocess.run( + ["cmake", "--install", directory], + env=dict(os.environ, DESTDIR=str(destdir)), + check=True, + ) + + (pc,) = destdir.rglob("sentry.pc") + (library,) = destdir.rglob(lib_name("sentry")) + _assert_shared_version(library.parent) + + monkeypatch.setenv("PKG_CONFIG_PATH", str(pc.parent)) + monkeypatch.setenv("PKG_CONFIG_LIBDIR", str(pc.parent)) + monkeypatch.setenv("PKG_CONFIG_SYSROOT_DIR", str(destdir)) + env = get_test_executable_env() + version = subprocess.check_output( + ["pkg-config", "--modversion", "sentry"], env=env, text=True + ) + assert version.strip() == SENTRY_VERSION + flags = subprocess.check_output( + ["pkg-config", "--libs-only-L", "sentry"], env=env, text=True + ) + assert shlex.split(flags) == [f"-L{library.parent}"] + + consumer = cmake( + ["sentry_test_pkgconfig"], + {"CMAKE_FIND_ROOT_PATH": str(destdir)}, + source_dir=Path(__file__).parent / "fixtures" / "pkgconfig", + ) + env["SENTRY_DATABASE_PATH"] = str(tmp_path / "database") + run(consumer, "sentry_test_pkgconfig", [], env=env)