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