1function(_get_common_test_compile_options output_var c_test flags) 2 _get_compile_options_from_flags(compile_flags ${flags}) 3 4 # Remove -fno-math-errno if it was added. 5 if(LIBC_ADD_FNO_MATH_ERRNO) 6 list(REMOVE_ITEM compile_options "-fno-math-errno") 7 endif() 8 9 set(compile_options 10 ${LIBC_COMPILE_OPTIONS_DEFAULT} 11 ${LIBC_TEST_COMPILE_OPTIONS_DEFAULT} 12 ${compile_flags}) 13 14 if(LLVM_LIBC_COMPILER_IS_GCC_COMPATIBLE) 15 list(APPEND compile_options "-fpie") 16 17 if(LLVM_LIBC_FULL_BUILD) 18 list(APPEND compile_options "-DLIBC_FULL_BUILD") 19 # Only add -ffreestanding flag in full build mode. 20 list(APPEND compile_options "-ffreestanding") 21 list(APPEND compile_options "-fno-exceptions") 22 list(APPEND compile_options "-fno-unwind-tables") 23 list(APPEND compile_options "-fno-asynchronous-unwind-tables") 24 if(NOT c_test) 25 list(APPEND compile_options "-fno-rtti") 26 endif() 27 endif() 28 29 if(LIBC_COMPILER_HAS_FIXED_POINT) 30 list(APPEND compile_options "-ffixed-point") 31 endif() 32 33 # list(APPEND compile_options "-Wall") 34 # list(APPEND compile_options "-Wextra") 35 # -DLIBC_WNO_ERROR=ON if you can't build cleanly with -Werror. 36 if(NOT LIBC_WNO_ERROR) 37 # list(APPEND compile_options "-Werror") 38 endif() 39 # list(APPEND compile_options "-Wconversion") 40 # list(APPEND compile_options "-Wno-sign-conversion") 41 # list(APPEND compile_options "-Wimplicit-fallthrough") 42 # list(APPEND compile_options "-Wwrite-strings") 43 list(APPEND compile_options "-Wextra-semi") 44 # Silence this warning because _Complex is a part of C99. 45 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 46 if(NOT c_test) 47 list(APPEND compile_options "-fext-numeric-literals") 48 endif() 49 else() 50 list(APPEND compile_options "-Wno-c99-extensions") 51 list(APPEND compile_options "-Wno-gnu-imaginary-constant") 52 endif() 53 list(APPEND compile_options "-Wno-pedantic") 54 # if(NOT CMAKE_COMPILER_IS_GNUCXX) 55 # list(APPEND compile_options "-Wnewline-eof") 56 # list(APPEND compile_options "-Wnonportable-system-include-path") 57 # list(APPEND compile_options "-Wstrict-prototypes") 58 # list(APPEND compile_options "-Wthread-safety") 59 # list(APPEND compile_options "-Wglobal-constructors") 60 # endif() 61 endif() 62 set(${output_var} ${compile_options} PARENT_SCOPE) 63endfunction() 64 65function(_get_hermetic_test_compile_options output_var) 66 _get_common_test_compile_options(compile_options "" "") 67 68 # The GPU build requires overriding the default CMake triple and architecture. 69 if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU) 70 list(APPEND compile_options 71 -Wno-multi-gpu -nogpulib -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto 72 -mcode-object-version=${LIBC_GPU_CODE_OBJECT_VERSION}) 73 elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) 74 list(APPEND compile_options 75 "SHELL:-mllvm -nvptx-emit-init-fini-kernel=false" 76 -Wno-multi-gpu --cuda-path=${LIBC_CUDA_ROOT} 77 -nogpulib -march=${LIBC_GPU_TARGET_ARCHITECTURE} -fno-use-cxa-atexit) 78 endif() 79 80 set(${output_var} ${compile_options} PARENT_SCOPE) 81endfunction() 82 83# This is a helper function and not a build rule. It is to be used by the 84# various test rules to generate the full list of object files 85# recursively produced by "add_entrypoint_object" and "add_object_library" 86# targets. 87# Usage: 88# get_object_files_for_test(<result var> 89# <skipped_entrypoints_var> 90# <target0> [<target1> ...]) 91# 92# The list of object files is collected in <result_var>. 93# If skipped entrypoints were found, then <skipped_entrypoints_var> is 94# set to a true value. 95# targetN is either an "add_entrypoint_target" target or an 96# "add_object_library" target. 97function(get_object_files_for_test result skipped_entrypoints_list) 98 set(object_files "") 99 set(skipped_list "") 100 set(checked_list "") 101 set(unchecked_list "${ARGN}") 102 list(REMOVE_DUPLICATES unchecked_list) 103 104 foreach(dep IN LISTS unchecked_list) 105 if (NOT TARGET ${dep}) 106 # Skip tests with undefined dependencies. 107 # Compiler-RT targets are added only if they are enabled. However, such targets may not be defined 108 # at the time of the libc build. We should skip checking such targets. 109 if (NOT ${dep} MATCHES "^RTScudo.*|^RTGwp.*") 110 list(APPEND skipped_list ${dep}) 111 endif() 112 continue() 113 endif() 114 get_target_property(aliased_target ${dep} "ALIASED_TARGET") 115 if(aliased_target) 116 # If the target is just an alias, switch to the real target. 117 set(dep ${aliased_target}) 118 endif() 119 120 get_target_property(dep_type ${dep} "TARGET_TYPE") 121 if(NOT dep_type) 122 # Skip tests with no object dependencies. 123 continue() 124 endif() 125 126 get_target_property(dep_checked ${dep} "CHECK_OBJ_FOR_TESTS") 127 128 if(dep_checked) 129 # Target full dependency has already been checked. Just use the results. 130 get_target_property(dep_obj ${dep} "OBJECT_FILES_FOR_TESTS") 131 get_target_property(dep_skip ${dep} "SKIPPED_LIST_FOR_TESTS") 132 else() 133 # Target full dependency hasn't been checked. Recursively check its DEPS. 134 set(dep_obj "${dep}") 135 set(dep_skip "") 136 137 get_target_property(indirect_deps ${dep} "DEPS") 138 get_object_files_for_test(dep_obj dep_skip ${indirect_deps}) 139 140 if(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE}) 141 get_target_property(dep_object_files ${dep} "OBJECT_FILES") 142 if(dep_object_files) 143 list(APPEND dep_obj ${dep_object_files}) 144 endif() 145 elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}) 146 get_target_property(is_skipped ${dep} "SKIPPED") 147 if(is_skipped) 148 list(APPEND dep_skip ${dep}) 149 list(REMOVE_ITEM dep_obj ${dep}) 150 endif() 151 get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW") 152 if(object_file_raw) 153 # TODO: Remove this once we stop suffixing the target with ".__internal__" 154 if(fq_target_name STREQUAL "libc.test.include.issignaling_c_test.__unit__" OR fq_target_name STREQUAL "libc.test.include.iscanonical_c_test.__unit__") 155 string(REPLACE ".__internal__" "" object_file_raw ${object_file_raw}) 156 endif() 157 list(APPEND dep_obj ${object_file_raw}) 158 endif() 159 elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE}) 160 # Skip tests for externally implemented entrypoints. 161 list(APPEND dep_skip ${dep}) 162 list(REMOVE_ITEM dep_obj ${dep}) 163 endif() 164 165 set_target_properties(${dep} PROPERTIES 166 OBJECT_FILES_FOR_TESTS "${dep_obj}" 167 SKIPPED_LIST_FOR_TESTS "${dep_skip}" 168 CHECK_OBJ_FOR_TESTS "YES" 169 ) 170 171 endif() 172 173 list(APPEND object_files ${dep_obj}) 174 list(APPEND skipped_list ${dep_skip}) 175 176 endforeach(dep) 177 178 list(REMOVE_DUPLICATES object_files) 179 set(${result} ${object_files} PARENT_SCOPE) 180 list(REMOVE_DUPLICATES skipped_list) 181 set(${skipped_entrypoints_list} ${skipped_list} PARENT_SCOPE) 182 183endfunction(get_object_files_for_test) 184 185# Rule to add a libc unittest. 186# Usage 187# add_libc_unittest( 188# <target name> 189# SUITE <name of the suite this test belongs to> 190# SRCS <list of .cpp files for the test> 191# HDRS <list of .h files for the test> 192# DEPENDS <list of dependencies> 193# COMPILE_OPTIONS <list of special compile options for this target> 194# LINK_LIBRARIES <list of linking libraries for this target> 195# ) 196function(create_libc_unittest fq_target_name) 197 if(NOT LLVM_INCLUDE_TESTS) 198 return() 199 endif() 200 201 cmake_parse_arguments( 202 "LIBC_UNITTEST" 203 "NO_RUN_POSTBUILD;C_TEST" # Optional arguments 204 "SUITE;CXX_STANDARD" # Single value arguments 205 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES;FLAGS" # Multi-value arguments 206 ${ARGN} 207 ) 208 if(NOT LIBC_UNITTEST_SRCS) 209 message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp " 210 "files.") 211 endif() 212 if(NOT LIBC_UNITTEST_DEPENDS) 213 message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of " 214 "'add_entrypoint_object' targets.") 215 endif() 216 217 get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS}) 218 if(NOT LIBC_UNITTEST_C_TEST) 219 list(APPEND fq_deps_list libc.src.__support.StringUtil.error_to_string 220 libc.test.UnitTest.ErrnoSetterMatcher) 221 endif() 222 list(REMOVE_DUPLICATES fq_deps_list) 223 224 _get_common_test_compile_options(compile_options "${LIBC_UNITTEST_C_TEST}" 225 "${LIBC_UNITTEST_FLAGS}") 226 list(APPEND compile_options ${LIBC_UNITTEST_COMPILE_OPTIONS}) 227 228 if(SHOW_INTERMEDIATE_OBJECTS) 229 message(STATUS "Adding unit test ${fq_target_name}") 230 if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") 231 foreach(dep IN LISTS LIBC_UNITTEST_DEPENDS) 232 message(STATUS " ${fq_target_name} depends on ${dep}") 233 endforeach() 234 endif() 235 endif() 236 237 get_object_files_for_test( 238 link_object_files skipped_entrypoints_list ${fq_deps_list}) 239 if(skipped_entrypoints_list) 240 # If a test is OS/target machine independent, it has to be skipped if the 241 # OS/target machine combination does not provide any dependent entrypoints. 242 # If a test is OS/target machine specific, then such a test will live is a 243 # OS/target machine specific directory and will be skipped at the directory 244 # level if required. 245 # 246 # There can potentially be a setup like this: A unittest is setup for a 247 # OS/target machine independent object library, which in turn depends on a 248 # machine specific object library. Such a test would be testing internals of 249 # the libc and it is assumed that they will be rare in practice. So, they 250 # can be skipped in the corresponding CMake files using platform specific 251 # logic. This pattern is followed in the startup tests for example. 252 # 253 # Another pattern that is present currently is to detect machine 254 # capabilities and add entrypoints and tests accordingly. That approach is 255 # much lower level approach and is independent of the kind of skipping that 256 # is happening here at the entrypoint level. 257 if(LIBC_CMAKE_VERBOSE_LOGGING) 258 set(msg "Skipping unittest ${fq_target_name} as it has missing deps: " 259 "${skipped_entrypoints_list}.") 260 message(STATUS ${msg}) 261 endif() 262 return() 263 endif() 264 265 if(LIBC_UNITTEST_NO_RUN_POSTBUILD) 266 set(fq_build_target_name ${fq_target_name}) 267 else() 268 set(fq_build_target_name ${fq_target_name}.__build__) 269 endif() 270 271 add_executable( 272 ${fq_build_target_name} 273 EXCLUDE_FROM_ALL 274 ${LIBC_UNITTEST_SRCS} 275 ${LIBC_UNITTEST_HDRS} 276 ) 277 target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR}) 278 target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR}) 279 target_compile_options(${fq_build_target_name} PRIVATE ${compile_options}) 280 target_link_options(${fq_build_target_name} PRIVATE ${compile_options}) 281 282 if(NOT LIBC_UNITTEST_CXX_STANDARD) 283 set(LIBC_UNITTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD}) 284 endif() 285 set_target_properties( 286 ${fq_build_target_name} 287 PROPERTIES 288 CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD} 289 ) 290 291 set(link_libraries ${link_object_files}) 292 # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest` 293 foreach(lib IN LISTS LIBC_UNITTEST_LINK_LIBRARIES) 294 if(TARGET ${lib}.unit) 295 list(APPEND link_libraries ${lib}.unit) 296 else() 297 list(APPEND link_libraries ${lib}) 298 endif() 299 endforeach() 300 301 set_target_properties(${fq_build_target_name} 302 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 303 304 add_dependencies( 305 ${fq_build_target_name} 306 ${fq_deps_list} 307 ) 308 309 # LibcUnitTest should not depend on anything in LINK_LIBRARIES. 310 if(NOT LIBC_UNITTEST_C_TEST) 311 list(APPEND link_libraries LibcDeathTestExecutors.unit LibcTest.unit) 312 endif() 313 314 target_link_libraries(${fq_build_target_name} PRIVATE ${link_libraries}) 315 316 if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD) 317 add_custom_target( 318 ${fq_target_name} 319 COMMAND ${fq_build_target_name} 320 COMMENT "Running unit test ${fq_target_name}" 321 ) 322 endif() 323 324 if(LIBC_UNITTEST_SUITE) 325 add_dependencies( 326 ${LIBC_UNITTEST_SUITE} 327 ${fq_target_name} 328 ) 329 endif() 330 add_dependencies(libc-unit-tests ${fq_target_name}) 331endfunction(create_libc_unittest) 332 333function(add_libc_unittest target_name) 334 add_target_with_flags( 335 ${target_name} 336 CREATE_TARGET create_libc_unittest 337 ${ARGN} 338 ) 339endfunction(add_libc_unittest) 340 341function(add_libc_exhaustive_testsuite suite_name) 342 add_custom_target(${suite_name}) 343 add_dependencies(exhaustive-check-libc ${suite_name}) 344endfunction(add_libc_exhaustive_testsuite) 345 346function(add_libc_long_running_testsuite suite_name) 347 add_custom_target(${suite_name}) 348 add_dependencies(libc-long-running-tests ${suite_name}) 349endfunction(add_libc_long_running_testsuite) 350 351# Rule to add a fuzzer test. 352# Usage 353# add_libc_fuzzer( 354# <target name> 355# SRCS <list of .cpp files for the test> 356# HDRS <list of .h files for the test> 357# DEPENDS <list of dependencies> 358# ) 359function(add_libc_fuzzer target_name) 360 cmake_parse_arguments( 361 "LIBC_FUZZER" 362 "NEED_MPFR" # Optional arguments 363 "" # Single value arguments 364 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments 365 ${ARGN} 366 ) 367 if(NOT LIBC_FUZZER_SRCS) 368 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 369 "files.") 370 endif() 371 if(NOT LIBC_FUZZER_DEPENDS) 372 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 373 "'add_entrypoint_object' targets.") 374 endif() 375 376 list(APPEND LIBC_FUZZER_LINK_LIBRARIES "") 377 if(LIBC_FUZZER_NEED_MPFR) 378 if(NOT LIBC_TESTS_CAN_USE_MPFR) 379 message(VERBOSE "Fuzz test ${name} will be skipped as MPFR library is not available.") 380 return() 381 endif() 382 list(APPEND LIBC_FUZZER_LINK_LIBRARIES mpfr gmp) 383 endif() 384 385 386 get_fq_target_name(${target_name} fq_target_name) 387 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 388 get_object_files_for_test( 389 link_object_files skipped_entrypoints_list ${fq_deps_list}) 390 if(skipped_entrypoints_list) 391 if(LIBC_CMAKE_VERBOSE_LOGGING) 392 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 393 "${skipped_entrypoints_list}.") 394 message(STATUS ${msg}) 395 endif() 396 add_custom_target(${fq_target_name}) 397 398 # A post build custom command is used to avoid running the command always. 399 add_custom_command( 400 TARGET ${fq_target_name} 401 POST_BUILD 402 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 403 ) 404 return() 405 endif() 406 407 add_executable( 408 ${fq_target_name} 409 EXCLUDE_FROM_ALL 410 ${LIBC_FUZZER_SRCS} 411 ${LIBC_FUZZER_HDRS} 412 ) 413 target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR}) 414 target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR}) 415 416 target_link_libraries(${fq_target_name} PRIVATE 417 ${link_object_files} 418 ${LIBC_FUZZER_LINK_LIBRARIES} 419 ) 420 421 set_target_properties(${fq_target_name} 422 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 423 424 add_dependencies( 425 ${fq_target_name} 426 ${fq_deps_list} 427 ) 428 add_dependencies(libc-fuzzer ${fq_target_name}) 429 430 target_compile_options(${fq_target_name} 431 PRIVATE 432 ${LIBC_FUZZER_COMPILE_OPTIONS}) 433 434endfunction(add_libc_fuzzer) 435 436# Get libgcc_s to be used in hermetic and integration tests. 437if(NOT MSVC AND NOT LIBC_CC_SUPPORTS_NOSTDLIBPP) 438 execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libgcc_s.so.1 439 OUTPUT_VARIABLE LIBGCC_S_LOCATION) 440 string(STRIP ${LIBGCC_S_LOCATION} LIBGCC_S_LOCATION) 441endif() 442 443# DEPRECATED: Use add_hermetic_test instead. 444# 445# Rule to add an integration test. An integration test is like a unit test 446# but does not use the system libc. Not even the startup objects from the 447# system libc are linked in to the final executable. The final exe is fully 448# statically linked. The libc that the final exe links to consists of only 449# the object files of the DEPENDS targets. 450# 451# Usage: 452# add_integration_test( 453# <target name> 454# SUITE <the suite to which the test should belong> 455# SRCS <src1.cpp> [src2.cpp ...] 456# HDRS [hdr1.cpp ...] 457# DEPENDS <list of entrypoint or other object targets> 458# ARGS <list of command line arguments to be passed to the test> 459# ENV <list of environment variables to set before running the test> 460# COMPILE_OPTIONS <list of special compile options for this target> 461# ) 462# 463# The DEPENDS list can be empty. If not empty, it should be a list of 464# targets added with add_entrypoint_object or add_object_library. 465function(add_integration_test test_name) 466 get_fq_target_name(${test_name} fq_target_name) 467 set(supported_targets gpu linux) 468 if(NOT (${LIBC_TARGET_OS} IN_LIST supported_targets)) 469 message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.") 470 return() 471 endif() 472 cmake_parse_arguments( 473 "INTEGRATION_TEST" 474 "" # No optional arguments 475 "SUITE" # Single value arguments 476 "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LOADER_ARGS" # Multi-value arguments 477 ${ARGN} 478 ) 479 480 if(NOT INTEGRATION_TEST_SUITE) 481 message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") 482 endif() 483 if(NOT INTEGRATION_TEST_SRCS) 484 message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") 485 endif() 486 if(NOT LLVM_LIBC_FULL_BUILD AND NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1) 487 message(FATAL_ERROR "The 'crt1' target for the integration test is missing.") 488 endif() 489 490 get_fq_target_name(${test_name}.libc fq_libc_target_name) 491 492 get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) 493 list(APPEND fq_deps_list 494 # All integration tests use the operating system's startup object with the 495 # integration test object and need to inherit the same dependencies. 496 libc.startup.${LIBC_TARGET_OS}.crt1 497 libc.test.IntegrationTest.test 498 # We always add the memory functions objects. This is because the 499 # compiler's codegen can emit calls to the C memory functions. 500 libc.src.string.memcmp 501 libc.src.string.memcpy 502 libc.src.string.memmove 503 libc.src.string.memset 504 libc.src.strings.bcmp 505 libc.src.strings.bzero 506 ) 507 508 if(libc.src.compiler.__stack_chk_fail IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS) 509 # __stack_chk_fail should always be included if supported to allow building 510 # libc with the stack protector enabled. 511 list(APPEND fq_deps_list libc.src.compiler.__stack_chk_fail) 512 endif() 513 514 list(REMOVE_DUPLICATES fq_deps_list) 515 516 # TODO: Instead of gathering internal object files from entrypoints, 517 # collect the object files with public names of entrypoints. 518 get_object_files_for_test( 519 link_object_files skipped_entrypoints_list ${fq_deps_list}) 520 if(skipped_entrypoints_list) 521 if(LIBC_CMAKE_VERBOSE_LOGGING) 522 set(msg "Skipping integration test ${fq_target_name} as it has missing deps: " 523 "${skipped_entrypoints_list}.") 524 message(STATUS ${msg}) 525 endif() 526 return() 527 endif() 528 list(REMOVE_DUPLICATES link_object_files) 529 530 # Make a library of all deps 531 add_library( 532 ${fq_target_name}.__libc__ 533 STATIC 534 EXCLUDE_FROM_ALL 535 ${link_object_files} 536 ) 537 set_target_properties(${fq_target_name}.__libc__ 538 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 539 set_target_properties(${fq_target_name}.__libc__ 540 PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc) 541 542 set(fq_build_target_name ${fq_target_name}.__build__) 543 add_executable( 544 ${fq_build_target_name} 545 EXCLUDE_FROM_ALL 546 ${INTEGRATION_TEST_SRCS} 547 ${INTEGRATION_TEST_HDRS} 548 ) 549 set_target_properties(${fq_build_target_name} 550 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 551 target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR}) 552 target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR}) 553 554 _get_hermetic_test_compile_options(compile_options "") 555 target_compile_options(${fq_build_target_name} PRIVATE 556 ${compile_options} ${INTEGRATION_TEST_COMPILE_OPTIONS}) 557 558 if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU) 559 target_link_options(${fq_build_target_name} PRIVATE 560 ${LIBC_COMPILE_OPTIONS_DEFAULT} ${INTEGRATION_TEST_COMPILE_OPTIONS} 561 -Wno-multi-gpu -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto 562 "-Wl,-mllvm,-amdgpu-lower-global-ctor-dtor=0" -nostdlib -static 563 "-Wl,-mllvm,-amdhsa-code-object-version=${LIBC_GPU_CODE_OBJECT_VERSION}") 564 elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) 565 target_link_options(${fq_build_target_name} PRIVATE 566 ${LIBC_COMPILE_OPTIONS_DEFAULT} ${INTEGRATION_TEST_COMPILE_OPTIONS} 567 "-Wl,--suppress-stack-size-warning" -Wno-multi-gpu 568 "-Wl,-mllvm,-nvptx-lower-global-ctor-dtor=1" 569 "-Wl,-mllvm,-nvptx-emit-init-fini-kernel" 570 -march=${LIBC_GPU_TARGET_ARCHITECTURE} -nostdlib -static 571 "--cuda-path=${LIBC_CUDA_ROOT}") 572 elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP) 573 target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static) 574 else() 575 # Older version of gcc does not support `nostdlib++` flag. We use 576 # `nostdlib` and link against libgcc_s, which cannot be linked statically. 577 target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib) 578 list(APPEND link_libraries ${LIBGCC_S_LOCATION}) 579 endif() 580 target_link_libraries( 581 ${fq_build_target_name} 582 ${fq_target_name}.__libc__ 583 libc.startup.${LIBC_TARGET_OS}.crt1 584 libc.test.IntegrationTest.test 585 ) 586 add_dependencies(${fq_build_target_name} 587 libc.test.IntegrationTest.test 588 ${INTEGRATION_TEST_DEPENDS}) 589 590 # Tests on the GPU require an external loader utility to launch the kernel. 591 if(TARGET libc.utils.gpu.loader) 592 add_dependencies(${fq_build_target_name} libc.utils.gpu.loader) 593 get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE") 594 endif() 595 596 # We have to use a separate var to store the command as a list because 597 # the COMMAND option of `add_custom_target` cannot handle empty vars in the 598 # command. For example, if INTEGRATION_TEST_ENV is empty, the actual 599 # command also will not run. So, we use this list and tell `add_custom_target` 600 # to expand the list (by including the option COMMAND_EXPAND_LISTS). This 601 # makes `add_custom_target` construct the correct command and execute it. 602 set(test_cmd 603 ${INTEGRATION_TEST_ENV} 604 $<$<BOOL:${LIBC_TARGET_OS_IS_GPU}>:${gpu_loader_exe}> 605 ${CMAKE_CROSSCOMPILING_EMULATOR} 606 ${INTEGRATION_TEST_LOADER_ARGS} 607 $<TARGET_FILE:${fq_build_target_name}> ${INTEGRATION_TEST_ARGS}) 608 add_custom_target( 609 ${fq_target_name} 610 COMMAND ${test_cmd} 611 COMMAND_EXPAND_LISTS 612 COMMENT "Running integration test ${fq_target_name}" 613 ) 614 add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name}) 615endfunction(add_integration_test) 616 617# Rule to add a hermetic program. A hermetic program is one whose executable is fully 618# statically linked and consists of pieces drawn only from LLVM's libc. Nothing, 619# including the startup objects, come from the system libc. 620# 621# For the GPU, these can be either tests or benchmarks, depending on the value 622# of the LINK_LIBRARIES arg. 623# 624# Usage: 625# add_libc_hermetic( 626# <target name> 627# SUITE <the suite to which the test should belong> 628# SRCS <src1.cpp> [src2.cpp ...] 629# HDRS [hdr1.cpp ...] 630# DEPENDS <list of entrypoint or other object targets> 631# ARGS <list of command line arguments to be passed to the test> 632# ENV <list of environment variables to set before running the test> 633# COMPILE_OPTIONS <list of special compile options for the test> 634# LINK_LIBRARIES <list of linking libraries for this target> 635# LOADER_ARGS <list of special args to loaders (like the GPU loader)> 636# ) 637function(add_libc_hermetic test_name) 638 if(NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1) 639 message(VERBOSE "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.") 640 return() 641 endif() 642 cmake_parse_arguments( 643 "HERMETIC_TEST" 644 "IS_GPU_BENCHMARK" # Optional arguments 645 "SUITE" # Single value arguments 646 "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LINK_LIBRARIES;LOADER_ARGS" # Multi-value arguments 647 ${ARGN} 648 ) 649 650 if(NOT HERMETIC_TEST_SUITE) 651 message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") 652 endif() 653 if(NOT HERMETIC_TEST_SRCS) 654 message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") 655 endif() 656 657 get_fq_target_name(${test_name} fq_target_name) 658 get_fq_target_name(${test_name}.libc fq_libc_target_name) 659 660 get_fq_deps_list(fq_deps_list ${HERMETIC_TEST_DEPENDS}) 661 list(APPEND fq_deps_list 662 # Hermetic tests use the platform's startup object. So, their deps also 663 # have to be collected. 664 libc.startup.${LIBC_TARGET_OS}.crt1 665 # We always add the memory functions objects. This is because the 666 # compiler's codegen can emit calls to the C memory functions. 667 libc.src.__support.StringUtil.error_to_string 668 libc.src.string.memcmp 669 libc.src.string.memcpy 670 libc.src.string.memmove 671 libc.src.string.memset 672 libc.src.strings.bcmp 673 libc.src.strings.bzero 674 ) 675 676 if(libc.src.compiler.__stack_chk_fail IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS) 677 # __stack_chk_fail should always be included if supported to allow building 678 # libc with the stack protector enabled. 679 list(APPEND fq_deps_list libc.src.compiler.__stack_chk_fail) 680 endif() 681 682 if(libc.src.time.clock IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS) 683 # We will link in the 'clock' implementation if it exists for test timing. 684 list(APPEND fq_deps_list libc.src.time.clock) 685 endif() 686 687 list(REMOVE_DUPLICATES fq_deps_list) 688 689 # TODO: Instead of gathering internal object files from entrypoints, 690 # collect the object files with public names of entrypoints. 691 get_object_files_for_test( 692 link_object_files skipped_entrypoints_list ${fq_deps_list}) 693 if(skipped_entrypoints_list) 694 if(LIBC_CMAKE_VERBOSE_LOGGING) 695 set(msg "Skipping hermetic test ${fq_target_name} as it has missing deps: " 696 "${skipped_entrypoints_list}.") 697 message(STATUS ${msg}) 698 endif() 699 return() 700 endif() 701 list(REMOVE_DUPLICATES link_object_files) 702 703 # Make a library of all deps 704 add_library( 705 ${fq_target_name}.__libc__ 706 STATIC 707 EXCLUDE_FROM_ALL 708 ${link_object_files} 709 ) 710 set_target_properties(${fq_target_name}.__libc__ 711 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 712 set_target_properties(${fq_target_name}.__libc__ 713 PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc) 714 715 set(fq_build_target_name ${fq_target_name}.__build__) 716 add_executable( 717 ${fq_build_target_name} 718 EXCLUDE_FROM_ALL 719 ${HERMETIC_TEST_SRCS} 720 ${HERMETIC_TEST_HDRS} 721 ) 722 set_target_properties(${fq_build_target_name} 723 PROPERTIES 724 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 725 #OUTPUT_NAME ${fq_target_name} 726 ) 727 728 target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR}) 729 target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR}) 730 _get_hermetic_test_compile_options(compile_options "") 731 target_compile_options(${fq_build_target_name} PRIVATE 732 ${compile_options} 733 ${HERMETIC_TEST_COMPILE_OPTIONS}) 734 735 set(link_libraries "") 736 foreach(lib IN LISTS HERMETIC_TEST_LINK_LIBRARIES) 737 if(TARGET ${lib}.hermetic) 738 list(APPEND link_libraries ${lib}.hermetic) 739 else() 740 list(APPEND link_libraries ${lib}) 741 endif() 742 endforeach() 743 744 if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU) 745 target_link_options(${fq_build_target_name} PRIVATE 746 ${LIBC_COMPILE_OPTIONS_DEFAULT} -Wno-multi-gpu 747 -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto 748 "-Wl,-mllvm,-amdgpu-lower-global-ctor-dtor=0" -nostdlib -static 749 "-Wl,-mllvm,-amdhsa-code-object-version=${LIBC_GPU_CODE_OBJECT_VERSION}") 750 elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) 751 target_link_options(${fq_build_target_name} PRIVATE 752 ${LIBC_COMPILE_OPTIONS_DEFAULT} -Wno-multi-gpu 753 "-Wl,--suppress-stack-size-warning" 754 "-Wl,-mllvm,-nvptx-lower-global-ctor-dtor=1" 755 "-Wl,-mllvm,-nvptx-emit-init-fini-kernel" 756 -march=${LIBC_GPU_TARGET_ARCHITECTURE} -nostdlib -static 757 "--cuda-path=${LIBC_CUDA_ROOT}") 758 elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP) 759 target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static) 760 else() 761 # Older version of gcc does not support `nostdlib++` flag. We use 762 # `nostdlib` and link against libgcc_s, which cannot be linked statically. 763 target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib) 764 list(APPEND link_libraries ${LIBGCC_S_LOCATION}) 765 endif() 766 target_link_libraries( 767 ${fq_build_target_name} 768 PRIVATE 769 libc.startup.${LIBC_TARGET_OS}.crt1 770 ${link_libraries} 771 LibcHermeticTestSupport.hermetic 772 ${fq_target_name}.__libc__) 773 add_dependencies(${fq_build_target_name} 774 LibcTest.hermetic 775 libc.test.UnitTest.ErrnoSetterMatcher 776 ${fq_deps_list}) 777 # TODO: currently the dependency chain is broken such that getauxval cannot properly 778 # propagate to hermetic tests. This is a temporary workaround. 779 if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64) 780 target_link_libraries( 781 ${fq_build_target_name} 782 PRIVATE 783 libc.src.sys.auxv.getauxval 784 ) 785 endif() 786 787 # Tests on the GPU require an external loader utility to launch the kernel. 788 if(TARGET libc.utils.gpu.loader) 789 add_dependencies(${fq_build_target_name} libc.utils.gpu.loader) 790 get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE") 791 endif() 792 793 set(test_cmd ${HERMETIC_TEST_ENV} 794 $<$<BOOL:${LIBC_TARGET_OS_IS_GPU}>:${gpu_loader_exe}> ${CMAKE_CROSSCOMPILING_EMULATOR} ${HERMETIC_TEST_LOADER_ARGS} 795 $<TARGET_FILE:${fq_build_target_name}> ${HERMETIC_TEST_ARGS}) 796 add_custom_target( 797 ${fq_target_name} 798 DEPENDS ${fq_target_name}-cmd 799 ) 800 801 add_custom_command( 802 OUTPUT ${fq_target_name}-cmd 803 COMMAND ${test_cmd} 804 COMMAND_EXPAND_LISTS 805 COMMENT "Running hermetic test ${fq_target_name}" 806 ${LIBC_HERMETIC_TEST_JOB_POOL} 807 ) 808 809 set_source_files_properties(${fq_target_name}-cmd 810 PROPERTIES 811 SYMBOLIC "TRUE" 812 ) 813 814 add_dependencies(${HERMETIC_TEST_SUITE} ${fq_target_name}) 815 if(NOT ${HERMETIC_TEST_IS_GPU_BENCHMARK}) 816 # If it is a benchmark, it will already have been added to the 817 # gpu-benchmark target 818 add_dependencies(libc-hermetic-tests ${fq_target_name}) 819 endif() 820endfunction(add_libc_hermetic) 821 822# A convenience function to add both a unit test as well as a hermetic test. 823function(add_libc_test test_name) 824 cmake_parse_arguments( 825 "LIBC_TEST" 826 "UNIT_TEST_ONLY;HERMETIC_TEST_ONLY" # Optional arguments 827 "" # Single value arguments 828 "" # Multi-value arguments 829 ${ARGN} 830 ) 831 if(LIBC_ENABLE_UNITTESTS AND NOT LIBC_TEST_HERMETIC_TEST_ONLY) 832 add_libc_unittest(${test_name}.__unit__ ${LIBC_TEST_UNPARSED_ARGUMENTS}) 833 endif() 834 if(LIBC_ENABLE_HERMETIC_TESTS AND NOT LIBC_TEST_UNIT_TEST_ONLY) 835 add_libc_hermetic( 836 ${test_name}.__hermetic__ 837 LINK_LIBRARIES 838 LibcTest.hermetic 839 ${LIBC_TEST_UNPARSED_ARGUMENTS} 840 ) 841 get_fq_target_name(${test_name} fq_test_name) 842 if(TARGET ${fq_test_name}.__hermetic__ AND TARGET ${fq_test_name}.__unit__) 843 # Tests like the file tests perform file operations on disk file. If we 844 # don't chain up the unit test and hermetic test, then those tests will 845 # step on each other's files. 846 add_dependencies(${fq_test_name}.__hermetic__ ${fq_test_name}.__unit__) 847 endif() 848 endif() 849endfunction(add_libc_test) 850 851# Tests all implementations that can run on the target CPU. 852function(add_libc_multi_impl_test name suite) 853 get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations) 854 foreach(fq_config_name IN LISTS fq_implementations) 855 get_target_property(required_cpu_features ${fq_config_name} REQUIRE_CPU_FEATURES) 856 cpu_supports(can_run "${required_cpu_features}") 857 if(can_run) 858 string(FIND ${fq_config_name} "." last_dot_loc REVERSE) 859 math(EXPR name_loc "${last_dot_loc} + 1") 860 string(SUBSTRING ${fq_config_name} ${name_loc} -1 target_name) 861 add_libc_test( 862 ${target_name}_test 863 SUITE 864 ${suite} 865 COMPILE_OPTIONS 866 ${LIBC_COMPILE_OPTIONS_NATIVE} 867 LINK_LIBRARIES 868 LibcMemoryHelpers 869 ${ARGN} 870 DEPENDS 871 ${fq_config_name} 872 libc.src.__support.macros.sanitizer 873 ) 874 get_fq_target_name(${fq_config_name}_test fq_target_name) 875 else() 876 message(STATUS "Skipping test for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'") 877 endif() 878 endforeach() 879endfunction() 880