1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
|
cmake_minimum_required (VERSION 3.16)
# Create target to discover tests
include (CMakeParseArguments)
include(CMakeDependentOption)
file(REMOVE ${CMAKE_BINARY_DIR}/discover_all_tests.sh)
option(
ENABLE_MEMCHECK_OPTION
"If set to ON, enables automatic creation of memcheck targets"
OFF
)
option(
MIR_USE_PRECOMPILED_HEADERS
"Use precompiled headers"
ON
)
if(ENABLE_MEMCHECK_OPTION)
find_program(
VALGRIND_EXECUTABLE
valgrind)
if(VALGRIND_EXECUTABLE)
set(VALGRIND_CMD "${VALGRIND_EXECUTABLE}" "--error-exitcode=1" "--trace-children=yes")
set(VALGRIND_CMD ${VALGRIND_CMD} "--leak-check=full" "--show-leak-kinds=definite" "--errors-for-leak-kinds=definite")
set(VALGRIND_CMD ${VALGRIND_CMD} "--track-fds=yes")
set(VALGRIND_CMD ${VALGRIND_CMD} "--track-origins=yes")
set(VALGRIND_CMD ${VALGRIND_CMD} "--num-callers=128")
set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_generic")
set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_glibc_2.23")
set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_libhybris")
if (TARGET_ARCH STREQUAL "arm-linux-gnueabihf")
set(VALGRIND_CMD ${VALGRIND_CMD} "--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions_armhf")
endif()
else(VALGRIND_EXECUTABLE)
message("Not enabling memcheck as valgrind is missing on your system")
endif(VALGRIND_EXECUTABLE)
endif(ENABLE_MEMCHECK_OPTION)
if(CMAKE_CROSSCOMPILING)
set(SYSTEM_SUPPORTS_O_TMPFILE 0)
else()
try_run(SYSTEM_SUPPORTS_O_TMPFILE SYSTEM_HEADERS_SUPPORT_O_TMPFILE
${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/src/mir/mir_test_tmpfile.cpp
)
endif()
function (list_to_string LIST_VAR PREFIX STR_VAR)
foreach (value ${LIST_VAR})
set(tmp_str "${tmp_str} ${PREFIX} ${value}")
endforeach()
set(${STR_VAR} "${tmp_str}" PARENT_SCOPE)
endfunction()
function (mir_discover_tests_internal EXECUTABLE TEST_ENV_OPTIONS DETECT_FD_LEAKS )
# Set vars
set(test_cmd_no_memcheck "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${EXECUTABLE}")
set(test_cmd "${test_cmd_no_memcheck}")
set(test_env ${ARGN} ${TEST_ENV_OPTIONS})
if (TEST_ENV_OPTIONS)
set(test_name ${EXECUTABLE}---${TEST_ENV_OPTIONS}---)
else()
set(test_name ${EXECUTABLE})
endif()
set(test_no_memcheck_filter)
list(APPEND test_exclusion_filter ${MIR_EXCLUDE_TESTS})
if(ENABLE_MEMCHECK_OPTION)
set(test_cmd ${VALGRIND_CMD} ${test_cmd_no_memcheck})
list(APPEND test_no_memcheck_filter "*DeathTest.*" "ClientLatency.*")
endif()
if(cmake_build_type_lower MATCHES "threadsanitizer")
if (NOT CMAKE_COMPILER_IS_GNUCXX)
find_program(LLVM_SYMBOLIZER llvm-symbolizer-3.6)
if (LLVM_SYMBOLIZER)
set(TSAN_EXTRA_OPTIONS "external_symbolizer_path=${LLVM_SYMBOLIZER}")
endif()
endif()
# Space after ${TSAN_EXTRA_OPTIONS} works around bug in TSAN env. variable parsing
list(APPEND test_env "TSAN_OPTIONS=\"suppressions=${CMAKE_SOURCE_DIR}/tools/tsan-suppressions second_deadlock_stack=1 halt_on_error=1 history_size=7 die_after_fork=0 ${TSAN_EXTRA_OPTIONS} \"")
# TSan does not support starting threads after fork
list(APPEND test_exclusion_filter "ThreadedDispatcherSignalTest.keeps_dispatching_after_signal_interruption")
# tsan "eats" SIGQUIT, so ignore two more tests that involve it
list(APPEND test_exclusion_filter "ServerSignal/AbortDeathTest.cleanup_handler_is_called_for/0")
list(APPEND test_exclusion_filter "ServerShutdown/OnSignalDeathTest.removes_endpoint/0")
endif()
if(cmake_build_type_lower MATCHES "ubsanitizer")
list(APPEND test_env "UBSAN_OPTIONS=\"suppressions=${CMAKE_SOURCE_DIR}/tools/ubsan-suppressions print_stacktrace=1 die_after_fork=0\"")
list(APPEND test_exclusion_filter "*DeathTest*")
endif()
if(SYSTEM_SUPPORTS_O_TMPFILE EQUAL 1)
list(APPEND test_exclusion_filter "AnonymousShmFile.*" "MesaBufferAllocatorTest.software_buffers_dont_bypass" "MesaBufferAllocatorTest.creates_software_rendering_buffer")
endif()
if(MIR_SIGBUS_HANDLER_ENVIRONMENT_BROKEN)
list(APPEND test_exclusion_filter "ShmBacking.*")
endif()
# liblttng-ust is unsafe if you fork() without exec(), such as we do in the test suite
# We need to load liblttng-ust-fork.so to make this work reliably.
list(APPEND test_env "LD_PRELOAD=liblttng-ust-fork.so")
# However, we *also* need to respect any existing LD_PRELOADs in the environment,
# and only the last LD_PRELOAD set will win. So we need to coalesce the LD_PRELOADs
set(env_without_preloads ${test_env})
set(env_only_preloads ${test_env})
# Split the list into the LD_PRELOAD elements, and everything else…
list(FILTER env_without_preloads EXCLUDE REGEX "^LD_PRELOAD=.+")
list(FILTER env_only_preloads INCLUDE REGEX "^LD_PRELOAD=.+")
foreach(preload ${env_only_preloads})
# Concatenate all the preloads
string(SUBSTRING "${preload}" 11 -1 preload)
list(APPEND env_preloads "${preload}")
endforeach()
if (env_preloads)
# Join the list with colons
string(REPLACE ";" ":" env_preloads "${env_preloads}")
# Add the LD_PRELOAD=… to the end of the non-LD_PRELOAD list…
list(APPEND env_without_preloads "LD_PRELOAD=${env_preloads}")
# …and now replace the original environment list.
set(test_env ${env_without_preloads})
endif()
if(DETECT_FD_LEAKS)
set(test_cmd ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${test_cmd})
endif()
# ptest
list(JOIN test_env " --env " discover_env)
string(PREPEND discover_env "--env ")
list(JOIN test_cmd " " discover_cmd)
list(JOIN test_cmd_no_memcheck " " discover_cmd_no_memcheck)
list(JOIN test_exclusion_filter ":" test_exclusion_str)
list(JOIN test_no_memcheck_filter ":" memcheck_exclusion_str)
if (test_no_memcheck_filter)
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh ${discover_env} --test-name ${test_name} --gtest-exclude ${test_exclusion_str} --gtest-exclude ${memcheck_exclusion_str} -- ${discover_cmd}\n")
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh ${discover_env} --test-name ${test_name}_no_memcheck --gtest-exclude ${test_exclusion_str} --gtest-include ${memcheck_exclusion_str} -- ${discover_cmd_no_memcheck}\n")
else()
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh ${discover_env} --test-name ${test_name} --gtest-exclude ${test_exclusion_str} -- ${discover_cmd}\n")
endif()
# Final commands
set(test_cmd "${test_cmd}" "--gtest_filter=-${memcheck_exclusion_str}:${test_exclusion_str}")
set(test_cmd_no_memcheck "${test_cmd_no_memcheck}" "--gtest_death_test_style=threadsafe" "--gtest_filter=${memcheck_exclusion_str}:-${test_exclusion_str}")
# Normal
add_test(${test_name} ${test_cmd})
set_property(TEST ${test_name} PROPERTY ENVIRONMENT ${test_env})
if (test_no_memcheck_filter)
add_test(${test_name}_no_memcheck ${test_cmd_no_memcheck})
set_property(TEST ${test_name}_no_memcheck PROPERTY ENVIRONMENT ${test_env})
endif()
endfunction ()
function (mir_discover_tests EXECUTABLE)
mir_discover_tests_internal(${EXECUTABLE} "" FALSE ${ARGN})
endfunction()
function (mir_discover_tests_with_fd_leak_detection EXECUTABLE)
mir_discover_tests_internal(${EXECUTABLE} "" TRUE ${ARGN})
endfunction()
function (mir_discover_external_gtests)
set(one_value_args NAME COMMAND WORKING_DIRECTORY TIMEOUT)
set(multi_value_args EXPECTED_FAILURES ARGS BROKEN_TESTS)
cmake_parse_arguments(TEST "" "${one_value_args}" "${multi_value_args}" ${ARGN})
# The expected failures, in a colon-delimited list for GTest
string(REPLACE ";" ":" EXPECTED_FAILURE_STRING "${TEST_EXPECTED_FAILURES}")
# The excluded tests (broken or expected failures), in a colon-delimited list for GTest
list(APPEND TEST_BROKEN_TESTS ${TEST_EXPECTED_FAILURES})
list(APPEND TEST_BROKEN_TESTS ${MIR_EXCLUDE_TESTS})
string(REPLACE ";" ":" EXCLUDED_TESTS "${TEST_BROKEN_TESTS}")
# The command line arguments, as would be passed to the shell
string(REPLACE ";" " " TEST_ARGS_STRING "${TEST_ARGS}")
add_test(NAME ${TEST_NAME} COMMAND ${TEST_COMMAND} "--gtest_filter=-${EXCLUDED_TESTS}" ${TEST_ARGS})
if (TEST_WORKING_DIRECTORY)
set_tests_properties(${TEST_NAME} PROPERTIES WORKING_DIRECTORY ${TEST_WORKING_DIRECTORY})
endif()
if (TEST_TIMEOUT)
set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT ${TEST_TIMEOUT})
set(TEST_PROPS "--props TIMEOUT --props ${TEST_TIMEOUT}")
endif()
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"sh ${CMAKE_SOURCE_DIR}/tools/discover_gtests.sh --test-name ${TEST_NAME} --gtest-executable \"${TEST_COMMAND} ${TEST_ARGS_STRING}\" --gtest-exclude ${EXCLUDED_TESTS} ${TEST_PROPS} -- ${TEST_COMMAND} ${TEST_ARGS_STRING} \n")
foreach (xfail IN LISTS TEST_EXPECTED_FAILURES)
# Add a test verifying that the expected failures really do fail
add_test(
NAME "${TEST_NAME}_${xfail}_fails"
COMMAND
${CMAKE_BINARY_DIR}/mir_gtest/xfail_if_gtest_exists.sh ${TEST_COMMAND}
${xfail}
${TEST_ARGS}
)
if (TEST_WORKING_DIRECTORY)
set_tests_properties("${TEST_NAME}_${xfail}_fails" PROPERTIES WORKING_DIRECTORY ${TEST_WORKING_DIRECTORY})
endif()
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"echo \"add_test\(${TEST_NAME}.${xfail}_fails ${CMAKE_BINARY_DIR}/mir_gtest/xfail_if_gtest_exists.sh ${TEST_COMMAND} ${xfail} ${TEST_ARGS_STRING})\"\n")
endforeach ()
endfunction()
function (mir_add_memcheck_test)
if (ENABLE_MEMCHECK_OPTION)
add_custom_target(memcheck_test ALL)
mir_add_test(NAME "memcheck-test"
COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_memory_error)
add_dependencies(memcheck_test mir_test_memory_error)
endif()
endfunction()
function (mir_add_detect_fd_leaks_test)
if (ENABLE_MEMCHECK_OPTION)
add_custom_target(detect_fd_leaks_catches_fd_leak_test ALL)
mir_add_test(NAME "detect-fd-leaks-catches-fd-leak"
COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_fd_leak)
add_dependencies(detect_fd_leaks_catches_fd_leak_test mir_test_fd_leak)
add_custom_target(detect_fd_leaks_propagates_test_failure_test ALL)
mir_add_test(NAME "detect-fd-leaks-propagates-test-failure"
COMMAND ${CMAKE_BINARY_DIR}/mir_gtest/fail_on_success.sh ${CMAKE_SOURCE_DIR}/tools/detect_fd_leaks.bash ${VALGRIND_CMD} ${CMAKE_BINARY_DIR}/mir_gtest/mir_test_memory_error)
add_dependencies(detect_fd_leaks_propagates_test_failure_test mir_test_memory_error)
endif()
endfunction()
function (mir_add_wrapped_executable TARGET)
set(REAL_EXECUTABLE ${TARGET}.bin)
list(GET ARGN 0 modifier)
if ("${modifier}" STREQUAL "NOINSTALL")
list(REMOVE_AT ARGN 0)
else()
install(PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${REAL_EXECUTABLE}
DESTINATION ${CMAKE_INSTALL_BINDIR}
RENAME ${TARGET}
)
endif()
add_executable(${TARGET} ${ARGN})
set_target_properties(${TARGET} PROPERTIES
OUTPUT_NAME ${REAL_EXECUTABLE}
SKIP_BUILD_RPATH TRUE
)
add_custom_target(${TARGET}-wrapped
ln -fs wrapper ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}
)
add_dependencies(${TARGET} ${TARGET}-wrapped)
endfunction()
function (mir_add_test)
# Add test normally
add_test(${ARGN})
# Add to to discovery for parallel test running
set(one_value_args "NAME" WORKING_DIRECTORY)
set(multi_value_args "COMMAND")
cmake_parse_arguments(MAT "" "${one_value_args}" "${multi_value_args}" ${ARGN})
foreach (cmd ${MAT_COMMAND})
set(cmdstr "${cmdstr} \\\"${cmd}\\\"")
endforeach()
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"echo \"add_test(${MAT_NAME} ${cmdstr})\"\n")
if (MAT_WORKING_DIRECTORY)
file(APPEND ${CMAKE_BINARY_DIR}/discover_all_tests.sh
"echo \"set_tests_properties(${MAT_NAME} PROPERTIES WORKING_DIRECTORY \\\"${MAT_WORKING_DIRECTORY}\\\")\"\n")
endif()
endfunction()
function (mir_check_no_unreleased_symbols TARGET DEPENDENT_TARGET)
set(TARGET_NAME "Checking-${TARGET}-for-unreleased-symbols")
add_custom_target(
${TARGET_NAME}
# Some sort of documentation for this monstrosity:
#
# Objdump format is:
#
# $ADDRESS $FLAGS $SECTION $SIZE $SYMVER $NAME
#
# Cut first five lines which don't contain any symbol information
# Cut any lines for undefined symbols
# Cut address and flags which are fixed width
# Convert tabs to spaces
# Whitespace between fields is collapsed to one character
# Extract the symbol version (3rd field)
# Check for unreleased symbols - grep will set exit code to 0 if any are found
# finally invert the exit code
COMMAND /bin/sh -c "objdump -T $<TARGET_FILE:${TARGET}> | tail -n+5 | grep -v 'UND' | cut -c 26- | sed $'s/\t/ /g' | tr -s ' ' | cut -d ' ' -f 3 | grep -q unreleased ; test $? -gt 0"
VERBATIM
)
add_dependencies(${DEPENDENT_TARGET} ${TARGET_NAME})
endfunction()
set(
MIR_WAYLAND_GENERATOR_EXECUTABLE
"${CMAKE_BINARY_DIR}/bin/mir_wayland_generator"
CACHE
STRING
"Location of an externally supplied mir_wayland_generator executable"
)
function (mir_generate_protocol_wrapper TARGET_NAME NAME_PREFIX PROTOCOL_FILE)
if (NAME_PREFIX STREQUAL "")
set(NAME_PREFIX "@") # won't match anything
endif()
get_filename_component(PROTOCOL_NAME "${PROTOCOL_FILE}" NAME_WE)
set(OUTPUT_PATH_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${PROTOCOL_NAME}_wrapper.h")
set(OUTPUT_PATH_SRC "${CMAKE_CURRENT_BINARY_DIR}/${PROTOCOL_NAME}_wrapper.cpp")
set(PROTOCOL_PATH "${PROJECT_SOURCE_DIR}/wayland-protocols/${PROTOCOL_FILE}")
add_custom_command(
OUTPUT "${OUTPUT_PATH_HEADER}" "${OUTPUT_PATH_SRC}"
VERBATIM
COMMAND "sh" "-c"
"${MIR_WAYLAND_GENERATOR_EXECUTABLE} ${NAME_PREFIX} ${PROTOCOL_PATH} header > ${OUTPUT_PATH_HEADER}"
COMMAND "sh" "-c"
"${MIR_WAYLAND_GENERATOR_EXECUTABLE} ${NAME_PREFIX} ${PROTOCOL_PATH} source > ${OUTPUT_PATH_SRC}"
DEPENDS mir_wayland_generator "${PROTOCOL_PATH}"
)
target_sources("${TARGET_NAME}" PRIVATE "${OUTPUT_PATH_HEADER}" "${OUTPUT_PATH_SRC}")
endfunction()
function (mir_make_pkgconfig_variable PKGCONFIG_VARIABLE INPUT_PATH)
get_filename_component(INPUT_ABSOLUTE "${INPUT_PATH}" ABSOLUTE BASE_DIR "${CMAKE_INSTALL_PREFIX}")
file(RELATIVE_PATH INPUT_RELATIVE "${CMAKE_INSTALL_PREFIX}" "${INPUT_ABSOLUTE}")
string(FIND "${INPUT_RELATIVE}" ".." INPUT_EXITS_PREFIX)
if ("${INPUT_EXITS_PREFIX}" EQUAL "-1" )
set ("${PKGCONFIG_VARIABLE}" "\${prefix}/${INPUT_RELATIVE}" PARENT_SCOPE)
else()
set ("${PKGCONFIG_VARIABLE}" "${INPUT_PATH}" PARENT_SCOPE)
endif()
endfunction()
|