From 2234fa433e9e1b343e7b189472804bc431eabbb0 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 21 Oct 2023 11:05:03 -0700 Subject: [PATCH] Add Asan build type that enables the Address and UB sanitizers. Uses generator statements instead of CMAKE__FLAGS_ASAN to support multiconfig generators like Xcode. --- CMakeLists.txt | 17 +++++++++++ bindings/python/tests/CMakeLists.txt | 10 +++---- .../cmake_modules/GncAddSchemeTargets.cmake | 17 +++++++---- common/cmake_modules/GncAddTest.cmake | 29 +++++++++---------- libgnucash/backend/xml/test/CMakeLists.txt | 2 +- 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd9148a523..c67221a98e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -610,6 +610,23 @@ if (MINGW) set( CMAKE_CXX_FLAGS "-DWINVER=0x0500 -D_EMULATE_GLIBC=0 ${CMAKE_CXX_FLAGS}") # Workaround for bug in gtest on mingw, see https://github.com/google/googletest/issues/893 and https://github.com/google/googletest/issues/920 endif() +if (APPLE) + execute_process(COMMAND clang --print-file-name=libclang_rt.asan_osx_dynamic.dylib + OUTPUT_VARIABLE ASAN_DYNAMIC_LIB + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(ASAN_DYNAMIC_LIB_ENV "DYLD_INSERT_LIBRARIES=${ASAN_DYNAMIC_LIB}") +elseif(UNIX) + execute_process(COMMAND gcc -print-file-name=libasan.so OUTPUT_VARIABLE LIBASAN_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND gcc -print-file-name=libstdc++.so OUTPUT_VARIABLE LIBSTDCXX_PATH OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PRELOADS "${LIBASAN_PATH} ${LIBSTDCXX_PATH}") + set(ASAN_OPTIONS "detect_leaks=0:fast_unwind_on_malloc=0") + set(ASAN_DYNAMIC_LIB_ENV "LD_PRELOAD=${PRELOADS};ASAN_OPTIONS=${ASAN_OPTIONS}") +endif () +set(ASAN_BUILD_OPTIONS -fsanitize=address -fsanitize=undefined) +set(ASAN_COMPILE_OPTIONS -g ${ASAN_BUILD_OPTIONS}) +add_compile_options("$<$:${ASAN_COMPILE_OPTIONS}>") +add_link_options("$<$:${ASAN_BUILD_OPTIONS}>") + if (APPLE AND WITH_GNUCASH) set(CMAKE_MACOSX_RPATH ON) set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}") diff --git a/bindings/python/tests/CMakeLists.txt b/bindings/python/tests/CMakeLists.txt index 9ec9ca29a9..77733f8804 100644 --- a/bindings/python/tests/CMakeLists.txt +++ b/bindings/python/tests/CMakeLists.txt @@ -7,11 +7,11 @@ if (WITH_PYTHON) endif() add_custom_target(test-python-bindings ALL DEPENDS unittest_support gnucash-core-c-build gnucash-core-c-py sw-core-utils-build sw-core-utils-py sw-app-utils-build sw-app-utils-py) add_dependencies(check test-python-bindings) - add_test(python-bindings ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in) - set_property(TEST python-bindings PROPERTY ENVIRONMENT - GNC_BUILDDIR=${CMAKE_BINARY_DIR} - PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${LIBDIR_BUILD}/gnucash:${test_core_dir} - ) + add_test(NAME python-bindings COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in) + set(PYTHON_ENV "GNC_UNINSTALLED=1;GNC_BUILDDIR=${CMAKE_BINARY_DIR};PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${LIBDIR_BUILD}/gnucash:${test_core_dir}") + set(ASAN_ENV "${ASAN_DYNAMIC_LIB_ENV};ASAN_OPTIONS=fast_unwind_on_malloc=0") + set_tests_properties(python-bindings PROPERTIES ENVIRONMENT "$,${PYTHON_ENV};${ASAN_ENV},${PYTHON_ENV}>") + endif() set(test_python_bindings_DATA diff --git a/common/cmake_modules/GncAddSchemeTargets.cmake b/common/cmake_modules/GncAddSchemeTargets.cmake index 63c1b3d205..bf86629b04 100644 --- a/common/cmake_modules/GncAddSchemeTargets.cmake +++ b/common/cmake_modules/GncAddSchemeTargets.cmake @@ -266,18 +266,23 @@ function(gnc_add_scheme_targets _TARGET) message(" GNC_MODULE_PATH: ${_GNC_MODULE_PATH}") endif() #We quote the arguments to stop CMake stripping the path separators. + set (GUILE_ENV + "${LIBRARY_PATH}" + "GNC_UNINSTALLED=YES" + "GNC_BUILDDIR=${CMAKE_BINARY_DIR}" + "GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}" + "GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}" + "GNC_MODULE_PATH=${_GNC_MODULE_PATH}" + ) + add_custom_command( OUTPUT ${output_file} COMMAND ${CMAKE_COMMAND} -E env - "${LIBRARY_PATH}" - "GNC_UNINSTALLED=YES" - "GNC_BUILDDIR=${CMAKE_BINARY_DIR}" - "GUILE_LOAD_PATH=${_GUILE_LOAD_PATH}" - "GUILE_LOAD_COMPILED_PATH=${_GUILE_LOAD_COMPILED_PATH}" - "GNC_MODULE_PATH=${_GNC_MODULE_PATH}" + "${GUILE_ENV}$<$:;${ASAN_DYNAMIC_LIB_ENV}>" ${GUILE_EXECUTABLE} -e "\(@@ \(guild\) main\)" -s ${GUILD_EXECUTABLE} compile -o ${output_file} ${source_file_abs_path} DEPENDS ${guile_depends} MAIN_DEPENDENCY ${source_file_abs_path} + COMMAND_EXPAND_LISTS VERBATIM ) endforeach(source_file) diff --git a/common/cmake_modules/GncAddTest.cmake b/common/cmake_modules/GncAddTest.cmake index 6d095afc4b..bf40e3e758 100644 --- a/common/cmake_modules/GncAddTest.cmake +++ b/common/cmake_modules/GncAddTest.cmake @@ -77,36 +77,35 @@ function(gnc_add_test _TARGET _SOURCE_FILES TEST_INCLUDE_VAR_NAME TEST_LIBS_VAR_ # Extra arguments are treated as environment variables set(HAVE_ENV_VARS TRUE) endif() + set(ENVVARS "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR}") + if (HAVE_ENV_VARS) + list(APPEND ENVVARS ${ARGN}) + endif() set(TEST_INCLUDE_DIRS ${${TEST_INCLUDE_VAR_NAME}}) set(TEST_LIBS ${${TEST_LIBS_VAR_NAME}}) set_source_files_properties (${_SOURCE_FILES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H}) - add_executable(${_TARGET} EXCLUDE_FROM_ALL ${_SOURCE_FILES}) - target_link_libraries(${_TARGET} ${TEST_LIBS}) - target_include_directories(${_TARGET} PRIVATE ${TEST_INCLUDE_DIRS}) - if (${HAVE_ENV_VARS}) - add_test(${_TARGET} ${CMAKE_BINARY_DIR}/bin/${_TARGET}) - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR};${ARGN}") + if (CMAKE_GENERATOR STREQUAL Xcode) + add_test(NAME ${_TARGET} COMMAND ${_TARGET} CONFIGURATIONS Debug;Release) else() - if (CMAKE_GENERATOR STREQUAL Xcode) - add_test(NAME ${_TARGET} COMMAND ${_TARGET} CONFIGURATIONS Debug;Release) - else() - add_test(NAME ${_TARGET} COMMAND ${_TARGET}) - endif() - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "GNC_UNINSTALLED=YES;GNC_BUILDDIR=${CMAKE_BINARY_DIR}") + add_test(NAME ${_TARGET} COMMAND ${_TARGET}) endif() + add_executable(${_TARGET} EXCLUDE_FROM_ALL ${_SOURCE_FILES}) + target_link_libraries(${_TARGET} PRIVATE ${TEST_LIBS}) + target_include_directories(${_TARGET} PRIVATE ${TEST_INCLUDE_DIRS}) + set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "$,${ENVVARS};ASAN_OPTIONS=fast_unwind_on_malloc=0,${ENVVARS}>") add_dependencies(check ${_TARGET}) endfunction() function(gnc_add_test_with_guile _TARGET _SOURCE_FILES TEST_INCLUDE_VAR_NAME TEST_LIBS_VAR_NAME) get_guile_env() gnc_add_test(${_TARGET} "${_SOURCE_FILES}" "${TEST_INCLUDE_VAR_NAME}" "${TEST_LIBS_VAR_NAME}" - "${GUILE_ENV};${ARGN}" + "${GUILE_ENV}$<$:;${ASAN_DYNAMIC_LIB_ENV}>;${ARGN}" ) endfunction() function(gnc_add_scheme_test _TARGET _SOURCE_FILE) - add_test(${_TARGET} ${GUILE_EXECUTABLE} --debug -c " + add_test(NAME ${_TARGET} COMMAND ${GUILE_EXECUTABLE} --debug -c " (set! %load-hook (lambda (filename) (when (and filename @@ -119,7 +118,7 @@ function(gnc_add_scheme_test _TARGET _SOURCE_FILE) (exit (run-test))" ) get_guile_env() - set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "${GUILE_ENV};${ARGN}") + set_tests_properties(${_TARGET} PROPERTIES ENVIRONMENT "$,${GUILE_ENV};${ASAN_DYNAMIC_LIB_ENV};ASAN_OPTIONS=fast_unwind_on_malloc=0;${ARGN},${GUILE_ENV};${ARGN}>") endfunction() function(gnc_add_scheme_tests _SOURCE_FILES) diff --git a/libgnucash/backend/xml/test/CMakeLists.txt b/libgnucash/backend/xml/test/CMakeLists.txt index 58b537e738..7b11268a6b 100644 --- a/libgnucash/backend/xml/test/CMakeLists.txt +++ b/libgnucash/backend/xml/test/CMakeLists.txt @@ -13,7 +13,7 @@ set(XML_TEST_INCLUDE_DIRS ) -set(XML_TEST_LIBS gnc-engine gnc-test-engine test-core ${LIBXML2_LDFLAGS} -lz) +set(XML_TEST_LIBS gnc-backend-xml-utils gnc-engine gnc-test-engine test-core ${LIBXML2_LDFLAGS} -lz) set(XML_GTEST_LIBS ${XML_TEST_LIBS} gtest) function(add_xml_test _TARGET _SOURCE_FILES)