diff --git a/CMakeLists.txt b/CMakeLists.txt index 0492697..59a912b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,24 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.14) -if(${CMAKE_VERSION} VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() +include(cmake/PreventInSourceBuilds.cmake) + +# ---- Initialize Project ---- + +# Used to support find_package +set(package_name "cpptrace") project( cpptrace VERSION 0.2.1 + DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer " + HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace" LANGUAGES C CXX ) +# Don't change include order, OptionVariables checks if project is top level +include(cmake/ProjectIsTopLevel.cmake) +include(cmake/OptionVariables.cmake) + include(GNUInstallDirs) include(CheckCXXSourceCompiles) include(CheckCXXCompilerFlag) @@ -22,47 +31,6 @@ if(CMAKE_GENERATOR STREQUAL "Ninja") endif() endif() -option(CPPTRACE_STATIC "" OFF) - -option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF) -option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF) -option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF) -option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF) -option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF) -option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF) - -option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF) -option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF) -option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF) -option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF) -option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF) - -option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) -option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF) -option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) - -set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.") -set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.") -set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.") -option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF) - -option(CPPTRACE_BUILD_TEST "" OFF) -option(CPPTRACE_BUILD_DEMO "" OFF) -option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF) - -option(CPPTRACE_USE_SYSTEM_LIBDWARF "" OFF) -option(CPPTRACE_SANITIZER_BUILD "" OFF) - -mark_as_advanced( - CPPTRACE_BACKTRACE_PATH - CPPTRACE_ADDR2LINE_PATH - CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH - CPPTRACE_BUILD_TEST - CPPTRACE_BUILD_DEMO - CPPTRACE_BUILD_TEST_RDYNAMIC - CPPTRACE_SANITIZER_BUILD -) - if(CPPTRACE_SANITIZER_BUILD) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) @@ -82,6 +50,7 @@ else() set(CPPTRACE_BACKTRACE_PATH_DEFINITION "") endif() +# =============================================== Platform Support =============================================== function(check_support var source includes libraries definitions) set(CMAKE_REQUIRED_INCLUDES "${includes}") list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -195,93 +164,126 @@ endif() # =============================================== Now define the library =============================================== -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +# Target that we can modify (can't modify ALIAS targets) +# Target name should not be the same as ${PROJECT_NAME}, causes add_subdirectory issues +set(target_name "cpptrace-lib") +add_library(${target_name} ${build_type}) -set( - sources - src/cpptrace.cpp - src/demangle/demangle_with_cxxabi.cpp - src/demangle/demangle_with_winapi.cpp - src/demangle/demangle_with_nothing.cpp - src/symbols/symbols_with_addr2line.cpp - src/symbols/symbols_with_dbghelp.cpp - src/symbols/symbols_with_dl.cpp - src/symbols/symbols_with_libbacktrace.cpp - src/symbols/symbols_with_libdwarf.cpp - src/symbols/symbols_with_nothing.cpp - src/symbols/symbols_core.cpp - src/unwind/unwind_with_execinfo.cpp - src/unwind/unwind_with_nothing.cpp - src/unwind/unwind_with_unwind.cpp - src/unwind/unwind_with_winapi.cpp - src/unwind/unwind_with_dbghelp.cpp +# Alias to cause error at configuration time instead of link time if target is missing +add_library(cpptrace::cpptrace ALIAS ${target_name}) + +# Add /include files to target +# This is solely for IDE benefit, doesn't affect building +target_sources( + ${target_name} PRIVATE + include/cpptrace/cpptrace.hpp ) -if(CPPTRACE_STATIC) - add_library(cpptrace STATIC ${sources} include/cpptrace/cpptrace.hpp) -else() - add_library(cpptrace SHARED ${sources} include/cpptrace/cpptrace.hpp) - set_property(TARGET cpptrace PROPERTY POSITION_INDEPENDENT_CODE ON) -endif() +# add /src files to target +target_sources( + ${target_name} PRIVATE + # src + src/cpptrace.cpp + src/demangle/demangle_with_cxxabi.cpp + src/demangle/demangle_with_winapi.cpp + src/demangle/demangle_with_nothing.cpp + src/symbols/symbols_with_addr2line.cpp + src/symbols/symbols_with_dbghelp.cpp + src/symbols/symbols_with_dl.cpp + src/symbols/symbols_with_libbacktrace.cpp + src/symbols/symbols_with_libdwarf.cpp + src/symbols/symbols_with_nothing.cpp + src/symbols/symbols_core.cpp + src/unwind/unwind_with_execinfo.cpp + src/unwind/unwind_with_nothing.cpp + src/unwind/unwind_with_unwind.cpp + src/unwind/unwind_with_winapi.cpp + src/unwind/unwind_with_dbghelp.cpp +) target_include_directories( - cpptrace + ${target_name} PUBLIC $ $ ) -# TODO -target_compile_features(cpptrace PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert) +# ---- Generate Build Info Headers ---- -set_target_properties( - cpptrace - PROPERTIES - CXX_STANDARD_REQUIRED TRUE - CXX_EXTENSIONS OFF +# used in export header generated below +if(NOT CPPTRACE_BUILD_SHARED) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_STATIC_DEFINE) +endif() + +# generate header file with export macros prefixed with BASE_NAME +include(GenerateExportHeader) +generate_export_header( + ${target_name} + BASE_NAME cpptrace + EXPORT_FILE_NAME include/cpptrace/cpptrace_export.hpp ) -target_compile_options( - cpptrace - PRIVATE - $<$>:-Wall -Wextra -Werror=return-type -Wundef> - $<$:-Wuseless-cast> - $<$:/W4 /WX /permissive-> +# ---- Library Properties ---- + +# Hide all symbols by default +# Use SameMajorVersion versioning for shared library runtime linker lookup +set_target_properties( + ${target_name} PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + VERSION "${PROJECT_VERSION}" + SOVERSION "${PROJECT_VERSION_MAJOR}" + EXPORT_NAME "cpptrace" + OUTPUT_NAME "cpptrace" +) + +# Header files generated by CMake +target_include_directories( + ${target_name} SYSTEM PUBLIC + "$" +) + +# Header files from /include +target_include_directories( + ${target_name} ${warning_guard} PUBLIC + "$" +) + +# Require C++11 support +target_compile_features( + ${target_name} + PRIVATE cxx_std_11 ) # =============================================== Apply options to build =============================================== if(HAS_CXX_EXCEPTION_TYPE) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE) endif() -function(check_backtrace_error) - if(NOT HAS_BACKTRACE) - if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") - message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly.") - else() - message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.") - endif() - endif() -endfunction() - # Symbols if(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) - check_backtrace_error() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) - target_link_libraries(cpptrace PRIVATE backtrace) + if(NOT HAS_BACKTRACE) + if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") + message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly.") + else() + message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.") + endif() + endif() + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) + target_link_libraries(${target_name} PRIVATE backtrace dl) endif() if(CPPTRACE_GET_SYMBOLS_WITH_LIBDL) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL) - target_link_libraries(cpptrace PRIVATE dl) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL) + target_link_libraries(${target_name} PRIVATE dl) endif() if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) # set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.") # option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF) if(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH) else() if("${CPPTRACE_ADDR2LINE_PATH}" STREQUAL "") if(APPLE) @@ -293,35 +295,46 @@ if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) set(CPPTRACE_ADDR2LINE_PATH_FINAL "${CPPTRACE_ADDR2LINE_PATH}") endif() message(STATUS "Cpptrace: Using ${CPPTRACE_ADDR2LINE_PATH_FINAL} for addr2line path") - target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}") + target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}") endif() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) if(UNIX) - target_link_libraries(cpptrace PRIVATE dl) + target_link_libraries(${target_name} PRIVATE dl) endif() endif() if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) - if(CPPTRACE_USE_SYSTEM_LIBDWARF) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) + if(CPPTRACE_USE_EXTERNAL_LIBDWARF) find_package(libdwarf REQUIRED) - target_link_libraries(cpptrace PRIVATE libdwarf) + target_link_libraries(${target_name} PRIVATE libdwarf::dwarf-static) else() add_subdirectory(bundled/libdwarf) - target_link_libraries(cpptrace PRIVATE dwarf) + target_link_libraries(${target_name} PRIVATE dwarf) + # set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + # set(PIC_ALWAYS TRUE) + # set(BUILD_DWARFDUMP FALSE) + # include(FetchContent) + # FetchContent_Declare( + # libdwarf + # GIT_REPOSITORY https://github.com/jeremy-rifkin/libdwarf-code.git + # GIT_TAG 4ca6a7bc86b27d56741923f03c15b5aa274efc28 # v0.8.0 + # ) + # FetchContent_MakeAvailable(libdwarf) + # target_link_libraries(${target_name} PRIVATE dwarf-static) endif() if(UNIX) - target_link_libraries(cpptrace PRIVATE dl) + target_link_libraries(${target_name} PRIVATE dl) endif() endif() if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) - target_link_libraries(cpptrace PRIVATE dbghelp) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) + target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING) endif() # Unwinding @@ -329,30 +342,30 @@ if(CPPTRACE_UNWIND_WITH_UNWIND) if(NOT HAS_UNWIND) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.") endif() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_UNWIND) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_UNWIND) endif() if(CPPTRACE_UNWIND_WITH_EXECINFO) if(NOT HAS_EXECINFO) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.") endif() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO) endif() if(CPPTRACE_UNWIND_WITH_WINAPI) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_WINAPI) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_WINAPI) endif() if(CPPTRACE_UNWIND_WITH_DBGHELP) if(NOT HAS_STACKWALK) message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_DBGHELP specified but dbghelp stackwalk64 doesn't seem to be available.") endif() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_DBGHELP) - target_link_libraries(cpptrace PRIVATE dbghelp) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_DBGHELP) + target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_UNWIND_WITH_NOTHING) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_NOTHING) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_NOTHING) endif() # Demangling @@ -360,100 +373,38 @@ if(CPPTRACE_DEMANGLE_WITH_CXXABI) if(NOT HAS_CXXABI) message(WARNING "Cpptrace: CPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.") endif() - target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI) endif() if(CPPTRACE_DEMANGLE_WITH_WINAPI) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_WINAPI) - target_link_libraries(cpptrace PRIVATE dbghelp) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_WINAPI) + target_link_libraries(${target_name} PRIVATE dbghelp) endif() if(CPPTRACE_DEMANGLE_WITH_NOTHING) - target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING) endif() if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") - target_compile_definitions(cpptrace PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH}) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH}) endif() if(NOT "${CPPTRACE_HARD_MAX_FRAMES}" STREQUAL "") - target_compile_definitions(cpptrace PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES}) + target_compile_definitions(${target_name} PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES}) endif() -# ====================================================================================================================== +# =============================================== Install =============================================== if(NOT CMAKE_SKIP_INSTALL_RULES) - include(CMakePackageConfigHelpers) - - if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) - set(CPPTRACE_STATIC_EXPORT_TARGETS cpptrace dwarf) - else() - set(CPPTRACE_STATIC_EXPORT_TARGETS cpptrace) - endif() - - if(CPPTRACE_STATIC) - install( - TARGETS ${CPPTRACE_STATIC_EXPORT_TARGETS} - EXPORT cpptrace_targets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) - else() - install( - TARGETS cpptrace - EXPORT cpptrace_targets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) - endif() - - install( - FILES - include/cpptrace/cpptrace.hpp - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpptrace - ) - - export( - EXPORT cpptrace_targets - FILE ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace_targets.cmake - NAMESPACE cpptrace:: - ) - - configure_package_config_file( - cmake/cpptrace-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace - ) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake - VERSION ${PACKAGE_VERSION} - COMPATIBILITY SameMajorVersion - ) - - install( - EXPORT cpptrace_targets - FILE cpptrace_targets.cmake - NAMESPACE cpptrace:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace - ) - - install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace - ) + include(cmake/InstallRules.cmake) endif() +# =============================================== Demo/test =============================================== + if(CPPTRACE_BUILD_TEST) add_executable(test test/test.cpp) target_compile_features(test PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert) - target_link_libraries(test PRIVATE cpptrace) + target_link_libraries(test PRIVATE ${target_name}) # Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4) if(HAS_DWARF4) @@ -474,7 +425,7 @@ endif() if(CPPTRACE_BUILD_DEMO) add_executable(demo test/demo.cpp) target_compile_features(demo PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert) - target_link_libraries(demo PRIVATE cpptrace) + target_link_libraries(demo PRIVATE ${target_name}) # Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4) if(HAS_DWARF4) diff --git a/README.md b/README.md index 1a36b31..790dd79 100644 --- a/README.md +++ b/README.md @@ -585,7 +585,7 @@ configurable with `CPPTRACE_HARD_MAX_FRAMES`. | Library | CMake config | Platforms | Info | | ------------ | ---------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| libdwarf | `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` | linux, macos, mingw | Libdwarf is the preferred method for symbol resolution for cpptrace, and it's bundled in this repository for ease of use. | +| libdwarf | `CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF` | linux, macos, mingw | Libdwarf is the preferred method for symbol resolution for cpptrace. Cpptrace will get it via FetchContent or find_package depending on `CPPTRACE_USE_EXTERNAL_LIBDWARF`. | | dbghelp | `CPPTRACE_GET_SYMBOLS_WITH_DBGHELP` | windows | Dbghelp.h is the preferred method for symbol resolution on windows under msvc/clang and is supported on all windows machines. | | libbacktrace | `CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE` | linux, macos*, mingw* | Libbacktrace is already installed on most systems or available through the compiler directly. For clang you must specify the absolute path to `backtrace.h` using `CPPTRACE_BACKTRACE_PATH`. | | addr2line | `CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE` | linux, macos, mingw | Symbols are resolved by invoking `addr2line` (or `atos` on mac) via `fork()` (on linux/unix, and `popen` under mingw). | @@ -634,7 +634,6 @@ Back-ends: - `CPPTRACE_DEMANGLE_WITH_NOTHING=On/Off` Back-end configuration: -- `CPPTRACE_STATIC=On/Off`: Create cpptrace as a static library. - `CPPTRACE_BACKTRACE_PATH=`: Path to libbacktrace backtrace.h, needed when compiling with clang/ - `CPPTRACE_HARD_MAX_FRAMES=`: Some back-ends write to a fixed-size buffer. This is the size of that buffer. Default is `100`. @@ -643,7 +642,13 @@ Back-end configuration: injection). - `CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH=On/Off`: Specifies whether cpptrace should let the system search the PATH environment variable directories for the binary. -- `CPPTRACE_USE_SYSTEM_LIBDWARF=On/Off`: Use libdwarf resolved via `find_package` rather than the bundled libdwarf. + +Other userufl configurations: +- `CPPTRACE_BUILD_SHARED=On/Off`: Override for `BUILD_SHARED_LIBS`. +- `CPPTRACE_INCLUDES_WITH_SYSTEM=On/Off`: Marks cpptrace headers as `SYSTEM` which will hide any warnings that aren't + the fault of your project. Defaults to On. +- `CPPTRACE_INSTALL_CMAKEDIR`: Override for the installation path for the cmake configs. +- `CPPTRACE_USE_EXTERNAL_LIBDWARF=On/Off`: Get libdwarf from `find_package` rather than `FetchContent`. Testing: - `CPPTRACE_BUILD_TEST` Build a small test program diff --git a/bundled/libdwarf/CMakeLists.txt b/bundled/libdwarf/CMakeLists.txt index 148e5df..d04803d 100644 --- a/bundled/libdwarf/CMakeLists.txt +++ b/bundled/libdwarf/CMakeLists.txt @@ -430,11 +430,17 @@ set( libdwarf_private.h ) +set( + libdwarf_export_headers + dwarf.h + libdwarf.h +) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) include(GNUInstallDirs) -add_library(dwarf OBJECT ${libdwarf_sources} ${libdwarf_headers}) +add_library(dwarf STATIC ${libdwarf_sources} ${libdwarf_headers}) set_property(TARGET dwarf PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_definitions(dwarf PUBLIC LIBDWARF_BUILD) @@ -452,53 +458,53 @@ else() endif() # TODO: Something will have to be figured out for dynamic linking, but for now there's nothing to install -# if(NOT CMAKE_SKIP_INSTALL_RULES) -# include(CMakePackageConfigHelpers) +if(NOT CMAKE_SKIP_INSTALL_RULES) + include(CMakePackageConfigHelpers) -# install( -# TARGETS dwarf -# EXPORT libdwarf_targets -# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} -# INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -# RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} -# ) + install( + TARGETS dwarf + EXPORT libdwarf_targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) -# install( -# FILES -# ${libdwarf_headers} -# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libdwarf -# ) + install( + FILES + ${libdwarf_export_headers} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libdwarf + ) -# export( -# EXPORT libdwarf_targets -# FILE ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf_targets.cmake -# NAMESPACE libdwarf:: -# ) + export( + EXPORT libdwarf_targets + FILE ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf_targets.cmake + NAMESPACE libdwarf:: + ) -# configure_package_config_file( -# cmake/libdwarf-config.cmake.in -# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake -# INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf -# ) + configure_package_config_file( + cmake/libdwarf-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf + ) -# write_basic_package_version_file( -# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake -# VERSION "${VERSION}" -# COMPATIBILITY SameMajorVersion -# ) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake + VERSION "${VERSION}" + COMPATIBILITY SameMajorVersion + ) -# install( -# EXPORT libdwarf_targets -# FILE libdwarf_targets.cmake -# NAMESPACE libdwarf:: -# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf -# ) + install( + EXPORT libdwarf_targets + FILE libdwarf_targets.cmake + NAMESPACE libdwarf:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf + ) -# install( -# FILES -# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake -# ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake -# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf -# ) -# endif() + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libdwarf/libdwarf-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libdwarf + ) +endif() diff --git a/ci/test-all-configs.py b/ci/test-all-configs.py index 2e02209..46c0a95 100644 --- a/ci/test-all-configs.py +++ b/ci/test-all-configs.py @@ -15,6 +15,9 @@ failed = False expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test/expected/") +def get_c_compiler_counterpart(compiler: str) -> str: + return compiler.replace("clang++", "clang").replace("g++", "gcc") + MAX_LINE_DIFF = 2 def similarity(name: str, target: List[str]) -> int: @@ -149,6 +152,7 @@ def build(matrix): "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", + f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", f"-D{matrix['unwind']}=On", f"-D{matrix['symbols']}=On", @@ -167,6 +171,7 @@ def build(matrix): "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", + f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", f"-D{matrix['unwind']}=On", f"-D{matrix['symbols']}=On", @@ -190,6 +195,7 @@ def build_full_or_auto(matrix): "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", + f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h", "-DCPPTRACE_BUILD_TEST=On" @@ -205,6 +211,7 @@ def build_full_or_auto(matrix): "..", f"-DCMAKE_BUILD_TYPE={matrix['target']}", f"-DCMAKE_CXX_COMPILER={matrix['compiler']}", + f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}", f"-DCMAKE_CXX_STANDARD={matrix['std']}", "-DCPPTRACE_BUILD_TEST=On" ] @@ -309,7 +316,8 @@ def main(): #"CPPTRACE_UNWIND_WITH_NOTHING", ], "symbols": [ - "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE", + # Disabled due to libbacktrace bug + # "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDL", "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE", "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF", diff --git a/cmake/InstallRules.cmake b/cmake/InstallRules.cmake new file mode 100644 index 0000000..fa23e85 --- /dev/null +++ b/cmake/InstallRules.cmake @@ -0,0 +1,69 @@ +include(CMakePackageConfigHelpers) + +# copy header files to CMAKE_INSTALL_INCLUDEDIR +# don't include third party header files +install( + DIRECTORY + "${PROJECT_SOURCE_DIR}/include/" # our header files + "${PROJECT_BINARY_DIR}/include/" # generated header files + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT ${package_name}-development + # PATTERN "**/third_party" EXCLUDE # skip third party directory + # PATTERN "**/third_party/**" EXCLUDE # skip third party files +) + +# copy target build output artifacts to OS dependent locations +# (Except includes, that just sets a compiler flag with the path) +install( + TARGETS ${target_name} + EXPORT ${package_name}-targets + RUNTIME # + COMPONENT ${package_name}-runtime + LIBRARY # + COMPONENT ${package_name}-runtime + NAMELINK_COMPONENT ${package_name}-development + ARCHIVE # + COMPONENT ${package_name}-development + INCLUDES # + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# create config file that points to targets file +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/in/cpptrace-config-cmake.in" + "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" + @ONLY +) + +# copy config file for find_package to find +install( + FILES "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" + DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# create version file for consumer to check version in CMake +write_basic_package_version_file( + "${package_name}-config-version.cmake" + COMPATIBILITY SameMajorVersion # a.k.a SemVer +) + +# copy version file for find_package to find for version check +install( + FILES "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake" + DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# create targets file included by config file with targets for consumers +install( + EXPORT ${package_name}-targets + NAMESPACE cpptrace:: + DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# support packaging library +if(PROJECT_IS_TOP_LEVEL) + include(CPack) +endif() diff --git a/cmake/OptionVariables.cmake b/cmake/OptionVariables.cmake new file mode 100644 index 0000000..239aa0e --- /dev/null +++ b/cmake/OptionVariables.cmake @@ -0,0 +1,170 @@ +# Included further down to avoid interfering with our cache variables +# include(GNUInstallDirs) + +# ---- Options Summary ---- + +# --------------------------------------------------------------------------------------------------- +# | Option | Availability | Default | +# |=================================|===============|===============================================| +# | BUILD_SHARED_LIBS | Top-Level | OFF | +# | BUILD_TESTING | Top-Level | OFF | +# | CMAKE_INSTALL_INCLUDEDIR | Top-Level | include/${package_name}-${PROJECT_VERSION} | +# |---------------------------------|---------------|-----------------------------------------------| +# | ASSERT_BUILD_SHARED | Always | ${BUILD_SHARED_LIBS} | +# | ASSERT_BUILD_TESTING | Always | ${BUILD_TESTING} AND ${PROJECT_IS_TOP_LEVEL} | +# | ASSERT_INCLUDES_WITH_SYSTEM | Not Top-Level | ON | +# | ASSERT_INSTALL_CMAKEDIR | Always | ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name} | +# | CPPTRACE_USE_EXTERNAL_LIBDWARF | Always | OFF | +# | ... | | | +# --------------------------------------------------------------------------------------------------- + +# ---- Build Shared ---- + +# Sometimes it's useful to be able to single out a dependency to be built as +# static or shared, even if obtained from source +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_SHARED_LIBS "Build shared libs" OFF) +endif() +option( + CPPTRACE_BUILD_SHARED + "Override BUILD_SHARED_LIBS for ${package_name} library" + ${BUILD_SHARED_LIBS} +) +mark_as_advanced(CPPTRACE_BUILD_SHARED) +set(build_type STATIC) +if(CPPTRACE_BUILD_SHARED) + set(build_type SHARED) +endif() + +# ---- Warning Guard ---- + +# target_include_directories with SYSTEM modifier will request the compiler to +# omit warnings from the provided paths, if the compiler supports that. +# This is to provide a user experience similar to find_package when +# add_subdirectory or FetchContent is used to consume this project. +set(warning_guard ) +if(NOT PROJECT_IS_TOP_LEVEL) + option( + CPPTRACE_INCLUDES_WITH_SYSTEM + "Use SYSTEM modifier for ${package_name}'s includes, disabling warnings" + ON + ) + mark_as_advanced(CPPTRACE_INCLUDES_WITH_SYSTEM) + if(CPPTRACE_INCLUDES_WITH_SYSTEM) + set(warning_guard SYSTEM) + endif() +endif() + +# ---- Enable Testing ---- + +# By default tests aren't enabled even with BUILD_TESTING=ON unless the library +# is built as a top level project. +# This is in order to cut down on unnecessary compile times, since it's unlikely +# for users to want to run the tests of their dependencies. +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_TESTING "Build tests" OFF) +endif() +if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING) + set(build_testing ON) +endif() +option( + CPPTRACE_BUILD_TESTING + "Override BUILD_TESTING for ${package_name} library" + ${build_testing} +) +set(build_testing ) +mark_as_advanced(CPPTRACE_BUILD_TESTING) + +# ---- Install Include Directory ---- + +# Adds an extra directory to the include path by default, so that when you link +# against the target, you get `/include/` added to your +# include paths rather than `/include`. +# This doesn't affect include paths used by consumers of this project, but helps +# prevent consumers having access to other projects in the same include +# directory (e.g. usr/include). +# The variable type is STRING rather than PATH, because otherwise passing +# -DCMAKE_INSTALL_INCLUDEDIR=include on the command line would expand to an +# absolute path with the base being the current CMake directory, leading to +# unexpected errors. +if(PROJECT_IS_TOP_LEVEL) + set( + CMAKE_INSTALL_INCLUDEDIR "include/${package_name}-${PROJECT_VERSION}" + CACHE STRING "" + ) + # marked as advanced in GNUInstallDirs version, so we follow their lead + mark_as_advanced(CMAKE_INSTALL_INCLUDEDIR) +endif() + +# do not include earlier or we can't set CMAKE_INSTALL_INCLUDEDIR above +# include required for CMAKE_INSTALL_LIBDIR below +include(GNUInstallDirs) + +# ---- Install CMake Directory ---- + +# This allows package maintainers to freely override the installation path for +# the CMake configs. +# This doesn't affects include paths used by consumers of this project. +# The variable type is STRING rather than PATH, because otherwise passing +# -DCPPTRACE_INSTALL_CMAKEDIR=lib/cmake on the command line would expand to an +# absolute path with the base being the current CMake directory, leading to +# unexpected errors. +set( + CPPTRACE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}" + CACHE STRING "CMake package config location relative to the install prefix" +) +# depends on CMAKE_INSTALL_LIBDIR which is marked as advanced in GNUInstallDirs +mark_as_advanced(CPPTRACE_INSTALL_CMAKEDIR) + +# ---- Symbol Options ---- + +option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF) +option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF) +option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF) +option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF) +option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF) +option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF) + +# ---- Unwinding Options ---- + +option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF) +option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF) +option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF) +option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF) +option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF) + +# ---- Demangling Options ---- + +option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) +option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF) +option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) + +# ---- Back-end configurations ---- + +set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.") +set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.") +set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.") +option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF) + +# ---- Other configurations ---- + +if(PROJECT_IS_TOP_LEVEL) + option(CPPTRACE_BUILD_TEST "" OFF) + option(CPPTRACE_BUILD_DEMO "" OFF) + option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF) + mark_as_advanced( + CPPTRACE_BUILD_TEST + CPPTRACE_BUILD_DEMO + CPPTRACE_BUILD_TEST_RDYNAMIC + ) +endif() + +option(CPPTRACE_USE_EXTERNAL_LIBDWARF "" OFF) +option(CPPTRACE_SANITIZER_BUILD "" OFF) + +mark_as_advanced( + CPPTRACE_BACKTRACE_PATH + CPPTRACE_ADDR2LINE_PATH + CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH + CPPTRACE_SANITIZER_BUILD +) diff --git a/cmake/PreventInSourceBuilds.cmake b/cmake/PreventInSourceBuilds.cmake new file mode 100644 index 0000000..43113fe --- /dev/null +++ b/cmake/PreventInSourceBuilds.cmake @@ -0,0 +1,8 @@ +# In-source build guard +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds are not supported. " + "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' before rebuilding this project." + ) +endif() diff --git a/cmake/ProjectIsTopLevel.cmake b/cmake/ProjectIsTopLevel.cmake new file mode 100644 index 0000000..3435fc0 --- /dev/null +++ b/cmake/ProjectIsTopLevel.cmake @@ -0,0 +1,6 @@ +# This variable is set by project() in CMake 3.21+ +string( + COMPARE EQUAL + "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" + PROJECT_IS_TOP_LEVEL +) diff --git a/cmake/cpptrace-config.cmake.in b/cmake/cpptrace-config.cmake.in deleted file mode 100644 index c84bed9..0000000 --- a/cmake/cpptrace-config.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -@PACKAGE_INIT@ - -include(${CMAKE_CURRENT_LIST_DIR}/cpptrace_targets.cmake) diff --git a/cmake/has_backtrace.cpp b/cmake/has_backtrace.cpp new file mode 100644 index 0000000..784994a --- /dev/null +++ b/cmake/has_backtrace.cpp @@ -0,0 +1,9 @@ +#ifdef CPPTRACE_BACKTRACE_PATH +#include CPPTRACE_BACKTRACE_PATH +#else +#include +#endif + +int main() { + backtrace_state* state = backtrace_create_state(nullptr, true, nullptr, nullptr); +} diff --git a/cmake/in/cpptrace-config-cmake.in b/cmake/in/cpptrace-config-cmake.in new file mode 100644 index 0000000..3361ff5 --- /dev/null +++ b/cmake/in/cpptrace-config-cmake.in @@ -0,0 +1,14 @@ +# Init @ variables before doing anything else +@PACKAGE_INIT@ + +# Dependencies +include(CMakeFindDependencyMacro) +find_dependency(libdwarf REQUIRED) + +# We cannot modify an existing IMPORT target +if(NOT TARGET assert::assert) + + # import targets + include("${CMAKE_CURRENT_LIST_DIR}/@package_name@-targets.cmake") + +endif() diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index e00e99a..f509d11 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -7,6 +7,8 @@ #include #include +#include "cpptrace/cpptrace_export.hpp" + #if __cplusplus >= 202002L #ifdef __has_include #if __has_include() @@ -16,24 +18,18 @@ #endif #endif -#if defined(_WIN32) || defined(__CYGWIN__) - #define CPPTRACE_API __declspec(dllexport) -#else - #define CPPTRACE_API -#endif - namespace cpptrace { struct object_trace; struct stacktrace; - struct raw_trace { + struct CPPTRACE_EXPORT raw_trace { std::vector frames; - CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0); - CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); - CPPTRACE_API object_trace resolve_object_trace() const; - CPPTRACE_API stacktrace resolve() const; - CPPTRACE_API void clear(); - CPPTRACE_API bool empty() const noexcept; + static raw_trace current(std::uint_least32_t skip = 0); + static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); + object_trace resolve_object_trace() const; + stacktrace resolve() const; + void clear(); + bool empty() const noexcept; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; @@ -45,20 +41,20 @@ namespace cpptrace { inline const_iterator cend() const noexcept { return frames.cend(); } }; - struct object_frame { + struct CPPTRACE_EXPORT object_frame { std::string obj_path; std::string symbol; std::uintptr_t raw_address = 0; std::uintptr_t obj_address = 0; }; - struct object_trace { + struct CPPTRACE_EXPORT object_trace { std::vector frames; - CPPTRACE_API static object_trace current(std::uint_least32_t skip = 0); - CPPTRACE_API static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); - CPPTRACE_API stacktrace resolve() const; - CPPTRACE_API void clear(); - CPPTRACE_API bool empty() const noexcept; + static object_trace current(std::uint_least32_t skip = 0); + static object_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); + stacktrace resolve() const; + void clear(); + bool empty() const noexcept; using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; @@ -70,7 +66,7 @@ namespace cpptrace { inline const_iterator cend() const noexcept { return frames.cend(); } }; - struct stacktrace_frame { + struct CPPTRACE_EXPORT stacktrace_frame { std::uintptr_t address; std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel std::uint_least32_t column; // UINT_LEAST32_MAX if not present @@ -89,21 +85,21 @@ namespace cpptrace { return !operator==(other); } - CPPTRACE_API std::string to_string() const; - CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); + std::string to_string() const; + friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame); }; - struct stacktrace { + struct CPPTRACE_EXPORT stacktrace { std::vector frames; - CPPTRACE_API static stacktrace current(std::uint_least32_t skip = 0); - CPPTRACE_API static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth); - CPPTRACE_API void print() const; - CPPTRACE_API void print(std::ostream& stream) const; - CPPTRACE_API void print(std::ostream& stream, bool color) const; - CPPTRACE_API void clear(); - CPPTRACE_API bool empty() const noexcept; - CPPTRACE_API std::string to_string(bool color = false) const; - CPPTRACE_API friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace); + static stacktrace current(std::uint_least32_t skip = 0); + static stacktrace current(std::uint_least32_t skip, std::uint_least32_t max_depth); + void print() const; + void print(std::ostream& stream) const; + void print(std::ostream& stream, bool color) const; + void clear(); + bool empty() const noexcept; + std::string to_string(bool color = false) const; + friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace); using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; @@ -114,27 +110,27 @@ namespace cpptrace { inline const_iterator cbegin() const noexcept { return frames.cbegin(); } inline const_iterator cend() const noexcept { return frames.cend(); } private: - CPPTRACE_API void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const; + void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const; friend void print_terminate_trace(); }; - CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip = 0); - CPPTRACE_API raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); - CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip = 0); - CPPTRACE_API object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); - CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip = 0); - CPPTRACE_API stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); + CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip = 0); + CPPTRACE_EXPORT raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); + CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip = 0); + CPPTRACE_EXPORT object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); + CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip = 0); + CPPTRACE_EXPORT stacktrace generate_trace(std::uint_least32_t skip, std::uint_least32_t max_depth); // utilities: - CPPTRACE_API std::string demangle(const std::string& name); - CPPTRACE_API void absorb_trace_exceptions(bool absorb); - CPPTRACE_API bool isatty(int fd); + CPPTRACE_EXPORT std::string demangle(const std::string& name); + CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb); + CPPTRACE_EXPORT bool isatty(int fd); - CPPTRACE_API extern const int stdin_fileno; - CPPTRACE_API extern const int stderr_fileno; - CPPTRACE_API extern const int stdout_fileno; + CPPTRACE_EXPORT extern const int stdin_fileno; + CPPTRACE_EXPORT extern const int stderr_fileno; + CPPTRACE_EXPORT extern const int stdout_fileno; - CPPTRACE_API void register_terminate_handler(); + CPPTRACE_EXPORT void register_terminate_handler(); enum class cache_mode { // Only minimal lookup tables @@ -146,15 +142,15 @@ namespace cpptrace { }; namespace experimental { - CPPTRACE_API void set_cache_mode(cache_mode mode); + CPPTRACE_EXPORT void set_cache_mode(cache_mode mode); } namespace detail { - CPPTRACE_API bool should_absorb_trace_exceptions(); - CPPTRACE_API enum cache_mode get_cache_mode(); + CPPTRACE_EXPORT bool should_absorb_trace_exceptions(); + CPPTRACE_EXPORT enum cache_mode get_cache_mode(); } - class exception : public std::exception { + class CPPTRACE_EXPORT exception : public std::exception { mutable raw_trace trace; mutable stacktrace resolved_trace; mutable std::string what_string; @@ -177,7 +173,7 @@ namespace cpptrace { const stacktrace& get_trace() const noexcept; }; - class exception_with_message : public exception { + class CPPTRACE_EXPORT exception_with_message : public exception { mutable std::string message; protected: diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index 4d77424..c2d81f7 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -28,17 +28,16 @@ #define CYAN ESC "36m" namespace cpptrace { - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE raw_trace raw_trace::current(std::uint_least32_t skip) { return generate_raw_trace(skip + 1); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE raw_trace raw_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { return generate_raw_trace(skip + 1, max_depth); } - CPPTRACE_API object_trace raw_trace::resolve_object_trace() const { try { return object_trace{detail::get_frames_object_info(frames)}; @@ -50,7 +49,6 @@ namespace cpptrace { } } - CPPTRACE_API stacktrace raw_trace::resolve() const { try { std::vector trace = detail::resolve_frames(frames); @@ -66,27 +64,24 @@ namespace cpptrace { } } - CPPTRACE_API void raw_trace::clear() { frames.clear(); } - CPPTRACE_API bool raw_trace::empty() const noexcept { return frames.empty(); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE object_trace object_trace::current(std::uint_least32_t skip) { return generate_object_trace(skip + 1); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE object_trace object_trace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { return generate_object_trace(skip + 1, max_depth); } - CPPTRACE_API stacktrace object_trace::resolve() const { try { return stacktrace{detail::resolve_frames(frames)}; @@ -98,23 +93,21 @@ namespace cpptrace { } } - CPPTRACE_API void object_trace::clear() { frames.clear(); } - CPPTRACE_API bool object_trace::empty() const noexcept { return frames.empty(); } - CPPTRACE_API std::string stacktrace_frame::to_string() const { + std::string stacktrace_frame::to_string() const { std::ostringstream oss; oss << *this; return std::move(oss).str(); } - CPPTRACE_API std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) { + std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) { stream << std::hex << "0x" @@ -138,32 +131,28 @@ namespace cpptrace { return stream; } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE stacktrace stacktrace::current(std::uint32_t skip) { return generate_trace(skip + 1); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE stacktrace stacktrace::current(std::uint_least32_t skip, std::uint_least32_t max_depth) { return generate_trace(skip + 1, max_depth); } - CPPTRACE_API void stacktrace::print() const { print(std::cerr, true); } - CPPTRACE_API void stacktrace::print(std::ostream& stream) const { print(stream, true); } - CPPTRACE_API void stacktrace::print(std::ostream& stream, bool color) const { print(stream, color, true, nullptr); } - CPPTRACE_API void stacktrace::print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const { if(color) { detail::enable_virtual_terminal_processing_if_needed(); @@ -224,28 +213,25 @@ namespace cpptrace { } } - CPPTRACE_API void stacktrace::clear() { frames.clear(); } - CPPTRACE_API bool stacktrace::empty() const noexcept { return frames.empty(); } - CPPTRACE_API std::string stacktrace::to_string(bool color) const { std::ostringstream oss; print(oss, color, false, nullptr); return std::move(oss).str(); } - CPPTRACE_API std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) { + std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) { return stream << trace.to_string(); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE raw_trace generate_raw_trace(std::uint_least32_t skip) { try { return raw_trace{detail::capture_frames(skip + 1, UINT_LEAST32_MAX)}; @@ -257,7 +243,7 @@ namespace cpptrace { } } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE raw_trace generate_raw_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { try { return raw_trace{detail::capture_frames(skip + 1, max_depth)}; @@ -269,7 +255,7 @@ namespace cpptrace { } } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE object_trace generate_object_trace(std::uint_least32_t skip) { try { return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, UINT_LEAST32_MAX))}; @@ -281,7 +267,7 @@ namespace cpptrace { } } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE object_trace generate_object_trace(std::uint_least32_t skip, std::uint_least32_t max_depth) { try { return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))}; @@ -293,12 +279,12 @@ namespace cpptrace { } } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE stacktrace generate_trace(std::uint_least32_t skip) { return generate_trace(skip + 1, UINT_LEAST32_MAX); } - CPPTRACE_FORCE_NO_INLINE CPPTRACE_API + CPPTRACE_FORCE_NO_INLINE stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) { try { std::vector frames = detail::capture_frames(skip + 1, max_depth); @@ -315,19 +301,17 @@ namespace cpptrace { } } - CPPTRACE_API std::string demangle(const std::string& name) { return detail::demangle(name); } - CPPTRACE_API bool isatty(int fd) { return detail::isatty(fd); } - CPPTRACE_API extern const int stdin_fileno = detail::fileno(stdin); - CPPTRACE_API extern const int stdout_fileno = detail::fileno(stdout); - CPPTRACE_API extern const int stderr_fileno = detail::fileno(stderr); + extern const int stdin_fileno = detail::fileno(stdin); + extern const int stdout_fileno = detail::fileno(stdout); + extern const int stderr_fileno = detail::fileno(stderr); CPPTRACE_FORCE_NO_INLINE void print_terminate_trace() { generate_trace(1).print( @@ -338,7 +322,7 @@ namespace cpptrace { ); } - [[noreturn]] CPPTRACE_API void terminate_handler() { + [[noreturn]] void terminate_handler() { try { auto ptr = std::current_exception(); if(ptr == nullptr) { @@ -371,7 +355,7 @@ namespace cpptrace { abort(); } - CPPTRACE_API void register_terminate_handler() { + void register_terminate_handler() { std::set_terminate(terminate_handler); } @@ -380,22 +364,22 @@ namespace cpptrace { std::atomic cache_mode(cache_mode::prioritize_speed); // NOSONAR } - CPPTRACE_API void absorb_trace_exceptions(bool absorb) { + void absorb_trace_exceptions(bool absorb) { detail::absorb_trace_exceptions = absorb; } namespace experimental { - CPPTRACE_API void set_cache_mode(cache_mode mode) { + void set_cache_mode(cache_mode mode) { detail::cache_mode = mode; } } namespace detail { - CPPTRACE_API bool should_absorb_trace_exceptions() { + bool should_absorb_trace_exceptions() { return absorb_trace_exceptions; } - CPPTRACE_API enum cache_mode get_cache_mode() { + enum cache_mode get_cache_mode() { return cache_mode; } diff --git a/test/add_subdirectory-integration/CMakeLists.txt b/test/add_subdirectory-integration/CMakeLists.txt index 94bd9df..46b7a24 100644 --- a/test/add_subdirectory-integration/CMakeLists.txt +++ b/test/add_subdirectory-integration/CMakeLists.txt @@ -5,14 +5,14 @@ project(demo_project VERSION 0.0.1 LANGUAGES CXX) add_executable(main main.cpp) add_subdirectory(cpptrace) -target_link_libraries(main cpptrace) +target_link_libraries(main cpptrace::cpptrace) target_compile_features(main PRIVATE cxx_std_11) if(WIN32) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ + $ $ ) endif() diff --git a/test/fetchcontent-integration/CMakeLists.txt b/test/fetchcontent-integration/CMakeLists.txt index 31d3453..e638086 100644 --- a/test/fetchcontent-integration/CMakeLists.txt +++ b/test/fetchcontent-integration/CMakeLists.txt @@ -20,7 +20,7 @@ if(WIN32) add_custom_command( TARGET main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ + $ $ ) endif() diff --git a/test/speedtest/CMakeLists.txt b/test/speedtest/CMakeLists.txt index 57ed6fa..6cfd3ee 100644 --- a/test/speedtest/CMakeLists.txt +++ b/test/speedtest/CMakeLists.txt @@ -35,6 +35,7 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) set(cpptrace_DIR "../../build/foo/lib/cmake/cpptrace") +set(libdwarf_DIR "../../build/foo/lib/cmake/libdwarf") find_package(cpptrace REQUIRED) add_executable(speedtest speedtest.cpp)