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 foreach(dep IN LISTS ARGN) 19 get_target_property(dep_type ${dep} "TARGET_TYPE") 20 if(NOT dep_type) 21 # Target for which TARGET_TYPE property is not set do not 22 # provide any object files. 23 continue() 24 endif() 25 26 if(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE}) 27 get_target_property(dep_object_files ${dep} "OBJECT_FILES") 28 if(dep_object_files) 29 list(APPEND object_files ${dep_object_files}) 30 endif() 31 elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}) 32 get_target_property(is_skipped ${dep} "SKIPPED") 33 if(is_skipped) 34 list(APPEND skipped_list ${dep}) 35 continue() 36 endif() 37 get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW") 38 if(object_file_raw) 39 list(APPEND object_files ${object_file_raw}) 40 endif() 41 endif() 42 43 get_target_property(indirect_deps ${dep} "DEPS") 44 get_object_files_for_test( 45 indirect_objfiles indirect_skipped_list ${indirect_deps}) 46 list(APPEND object_files ${indirect_objfiles}) 47 if(indirect_skipped_list) 48 list(APPEND skipped_list ${indirect_skipped_list}) 49 endif() 50 endforeach(dep) 51 list(REMOVE_DUPLICATES object_files) 52 set(${result} ${object_files} PARENT_SCOPE) 53 list(REMOVE_DUPLICATES skipped_list) 54 set(${skipped_entrypoints_list} ${skipped_list} PARENT_SCOPE) 55endfunction(get_object_files_for_test) 56 57# Rule to add a libc unittest. 58# Usage 59# add_libc_unittest( 60# <target name> 61# SUITE <name of the suite this test belongs to> 62# SRCS <list of .cpp files for the test> 63# HDRS <list of .h files for the test> 64# DEPENDS <list of dependencies> 65# COMPILE_OPTIONS <list of special compile options for this target> 66# LINK_LIBRARIES <list of linking libraries for this target> 67# ) 68function(create_libc_unittest fq_target_name) 69 if(NOT LLVM_INCLUDE_TESTS) 70 return() 71 endif() 72 73 cmake_parse_arguments( 74 "LIBC_UNITTEST" 75 "NO_RUN_POSTBUILD;NO_LIBC_UNITTEST_TEST_MAIN" # Optional arguments 76 "SUITE;CXX_STANDARD" # Single value arguments 77 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES;FLAGS" # Multi-value arguments 78 ${ARGN} 79 ) 80 if(NOT LIBC_UNITTEST_SRCS) 81 message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp " 82 "files.") 83 endif() 84 if(NOT LIBC_UNITTEST_DEPENDS) 85 message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of " 86 "'add_entrypoint_object' targets.") 87 endif() 88 89 get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS}) 90 get_object_files_for_test( 91 link_object_files skipped_entrypoints_list ${fq_deps_list}) 92 if(skipped_entrypoints_list) 93 # If a test is OS/target machine independent, it has to be skipped if the 94 # OS/target machine combination does not provide any dependent entrypoints. 95 # If a test is OS/target machine specific, then such a test will live is a 96 # OS/target machine specific directory and will be skipped at the directory 97 # level if required. 98 # 99 # There can potentially be a setup like this: A unittest is setup for a 100 # OS/target machine independent object library, which in turn depends on a 101 # machine specific object library. Such a test would be testing internals of 102 # the libc and it is assumed that they will be rare in practice. So, they 103 # can be skipped in the corresponding CMake files using platform specific 104 # logic. This pattern is followed in the loader tests for example. 105 # 106 # Another pattern that is present currently is to detect machine 107 # capabilities and add entrypoints and tests accordingly. That approach is 108 # much lower level approach and is independent of the kind of skipping that 109 # is happening here at the entrypoint level. 110 111 set(msg "Skipping unittest ${fq_target_name} as it has missing deps: " 112 "${skipped_entrypoints_list}.") 113 message(STATUS ${msg}) 114 return() 115 endif() 116 117 if(SHOW_INTERMEDIATE_OBJECTS) 118 message(STATUS "Adding unit test ${fq_target_name}") 119 if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") 120 foreach(dep IN LISTS ADD_OBJECT_DEPENDS) 121 message(STATUS " ${fq_target_name} depends on ${dep}") 122 endforeach() 123 endif() 124 endif() 125 126 add_executable( 127 ${fq_target_name} 128 EXCLUDE_FROM_ALL 129 ${LIBC_UNITTEST_SRCS} 130 ${LIBC_UNITTEST_HDRS} 131 ) 132 target_include_directories( 133 ${fq_target_name} 134 PRIVATE 135 ${LIBC_SOURCE_DIR} 136 ${LIBC_BUILD_DIR} 137 ${LIBC_BUILD_DIR}/include 138 ) 139 target_compile_options( 140 ${fq_target_name} 141 PRIVATE ${LIBC_COMPILE_OPTIONS_DEFAULT} 142 ) 143 if(LIBC_UNITTEST_COMPILE_OPTIONS) 144 target_compile_options( 145 ${fq_target_name} 146 PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS} 147 ) 148 endif() 149 if(NOT LIBC_UNITTEST_CXX_STANDARD) 150 set(LIBC_UNITTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD}) 151 endif() 152 set_target_properties( 153 ${fq_target_name} 154 PROPERTIES 155 CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD} 156 ) 157 158 # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest` 159 set(link_libraries ${link_object_files} ${LIBC_UNITTEST_LINK_LIBRARIES}) 160 161 set_target_properties(${fq_target_name} 162 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 163 164 add_dependencies( 165 ${fq_target_name} 166 ${fq_deps_list} 167 ) 168 169 # LibcUnitTest and libc_test_utils should not depend on anything in LINK_LIBRARIES. 170 if(NO_LIBC_UNITTEST_TEST_MAIN) 171 list(APPEND link_libraries LibcUnitTest libc_test_utils) 172 else() 173 list(APPEND link_libraries LibcUnitTest LibcUnitTestMain libc_test_utils) 174 endif() 175 176 target_link_libraries(${fq_target_name} PRIVATE ${link_libraries}) 177 178 if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD) 179 add_custom_command( 180 TARGET ${fq_target_name} 181 POST_BUILD 182 COMMAND $<TARGET_FILE:${fq_target_name}> 183 ) 184 endif() 185 186 if(LIBC_UNITTEST_SUITE) 187 add_dependencies( 188 ${LIBC_UNITTEST_SUITE} 189 ${fq_target_name} 190 ) 191 endif() 192endfunction(create_libc_unittest) 193 194# Internal function, used by `add_libc_unittest`. 195function(expand_flags_for_libc_unittest target_name flags) 196 cmake_parse_arguments( 197 "EXPAND_FLAGS" 198 "IGNORE_MARKER" # No Optional arguments 199 "" # No Single-value arguments 200 "DEPENDS;FLAGS" # Multi-value arguments 201 ${ARGN} 202 ) 203 204 list(LENGTH flags nflags) 205 if(NOT ${nflags}) 206 create_libc_unittest( 207 ${target_name} 208 DEPENDS "${EXPAND_FLAGS_DEPENDS}" 209 FLAGS "${EXPAND_FLAGS_FLAGS}" 210 "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" 211 ) 212 return() 213 endif() 214 215 list(POP_FRONT flags flag) 216 extract_flag_modifier(${flag} real_flag modifier) 217 218 if(NOT "${modifier}" STREQUAL "NO") 219 expand_flags_for_libc_unittest( 220 ${target_name} 221 "${flags}" 222 DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER 223 FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER 224 "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" 225 ) 226 endif() 227 228 if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") 229 return() 230 endif() 231 232 set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) 233 list(REMOVE_ITEM NEW_FLAGS ${flag}) 234 get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) 235 236 # Only target with `flag` has `.__NO_flag` target, `flag__NO` and 237 # `flag__ONLY` do not. 238 if(NOT "${modifier}") 239 set(TARGET_NAME "${target_name}.__NO_${flag}") 240 else() 241 set(TARGET_NAME "${target_name}") 242 endif() 243 244 expand_flags_for_libc_unittest( 245 ${TARGET_NAME} 246 "${flags}" 247 DEPENDS "${NEW_DEPS}" IGNORE_MARKER 248 FLAGS "${NEW_FLAGS}" IGNORE_MARKER 249 "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" 250 ) 251endfunction(expand_flags_for_libc_unittest) 252 253function(add_libc_unittest target_name) 254 cmake_parse_arguments( 255 "ADD_TO_EXPAND" 256 "" # Optional arguments 257 "" # Single value arguments 258 "DEPENDS;FLAGS" # Multi-value arguments 259 ${ARGN} 260 ) 261 262 get_fq_target_name(${target_name} fq_target_name) 263 264 if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) 265 message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") 266 endif() 267 268 get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) 269 get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) 270 271 list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) 272 remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) 273 list(SORT flags) 274 275 if(SHOW_INTERMEDIATE_OBJECTS AND flags) 276 message(STATUS "Unit test ${fq_target_name} has FLAGS: ${flags}") 277 endif() 278 279 expand_flags_for_libc_unittest( 280 ${fq_target_name} 281 "${flags}" 282 DEPENDS ${fq_deps_list} IGNORE_MARKER 283 FLAGS ${flags} IGNORE_MARKER 284 ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} 285 ) 286endfunction(add_libc_unittest) 287 288function(add_libc_testsuite suite_name) 289 add_custom_target(${suite_name}) 290 add_dependencies(check-llvmlibc ${suite_name}) 291endfunction(add_libc_testsuite) 292 293function(add_libc_exhaustive_testsuite suite_name) 294 add_custom_target(${suite_name}) 295 add_dependencies(exhaustive-check-libc ${suite_name}) 296endfunction(add_libc_exhaustive_testsuite) 297 298function(add_libc_long_running_testsuite suite_name) 299 add_custom_target(${suite_name}) 300 add_dependencies(libc-long-running-tests ${suite_name}) 301endfunction(add_libc_long_running_testsuite) 302 303# Rule to add a fuzzer test. 304# Usage 305# add_libc_fuzzer( 306# <target name> 307# SRCS <list of .cpp files for the test> 308# HDRS <list of .h files for the test> 309# DEPENDS <list of dependencies> 310# ) 311function(add_libc_fuzzer target_name) 312 cmake_parse_arguments( 313 "LIBC_FUZZER" 314 "" # No optional arguments 315 "" # Single value arguments 316 "SRCS;HDRS;DEPENDS" # Multi-value arguments 317 ${ARGN} 318 ) 319 if(NOT LIBC_FUZZER_SRCS) 320 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 321 "files.") 322 endif() 323 if(NOT LIBC_FUZZER_DEPENDS) 324 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 325 "'add_entrypoint_object' targets.") 326 endif() 327 328 get_fq_target_name(${target_name} fq_target_name) 329 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 330 get_object_files_for_test( 331 link_object_files skipped_entrypoints_list ${fq_deps_list}) 332 if(skipped_entrypoints_list) 333 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 334 "${skipped_entrypoints_list}.") 335 message(STATUS ${msg}) 336 add_custom_target(${fq_target_name}) 337 338 # A post build custom command is used to avoid running the command always. 339 add_custom_command( 340 TARGET ${fq_target_name} 341 POST_BUILD 342 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 343 ) 344 return() 345 endif() 346 347 add_executable( 348 ${fq_target_name} 349 EXCLUDE_FROM_ALL 350 ${LIBC_FUZZER_SRCS} 351 ${LIBC_FUZZER_HDRS} 352 ) 353 target_include_directories( 354 ${fq_target_name} 355 PRIVATE 356 ${LIBC_SOURCE_DIR} 357 ${LIBC_BUILD_DIR} 358 ${LIBC_BUILD_DIR}/include 359 ) 360 361 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 362 363 set_target_properties(${fq_target_name} 364 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 365 366 add_dependencies( 367 ${fq_target_name} 368 ${fq_deps_list} 369 ) 370 add_dependencies(libc-fuzzer ${fq_target_name}) 371endfunction(add_libc_fuzzer) 372 373# Rule to add an integration test. An integration test is like a unit test 374# but does not use the system libc. Not even the loader from the system libc 375# is linked to the final executable. The final exe is fully statically linked. 376# The libc that the final exe links to consists of only the object files of 377# the DEPENDS targets. 378# 379# Usage: 380# add_integration_test( 381# <target name> 382# SUITE <the suite to which the test should belong> 383# SRCS <src1.cpp> [src2.cpp ...] 384# HDRS [hdr1.cpp ...] 385# LOADER <fully qualified loader target name> 386# DEPENDS <list of entrypoint or other object targets> 387# ARGS <list of command line arguments to be passed to the test> 388# ENV <list of environment variables to set before running the test> 389# ) 390# 391# The loader target should provide a property named LOADER_OBJECT which is 392# the full path to the object file produces when the loader is built. 393# 394# The DEPENDS list can be empty. If not empty, it should be a list of 395# targets added with add_entrypoint_object or add_object_library. 396function(add_integration_test test_name) 397 get_fq_target_name(${test_name} fq_target_name) 398 if(NOT (${LIBC_TARGET_OS} STREQUAL "linux")) 399 message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.") 400 return() 401 endif() 402 cmake_parse_arguments( 403 "INTEGRATION_TEST" 404 "" # No optional arguments 405 "SUITE;LOADER" # Single value arguments 406 "SRCS;HDRS;DEPENDS;ARGS;ENV" # Multi-value arguments 407 ${ARGN} 408 ) 409 410 if(NOT INTEGRATION_TEST_SUITE) 411 message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") 412 endif() 413 if(NOT INTEGRATION_TEST_LOADER) 414 message(FATAL_ERROR "The LOADER to link to the integration test is missing.") 415 endif() 416 if(NOT INTEGRATION_TEST_SRCS) 417 message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") 418 endif() 419 420 get_fq_target_name(${test_name}.libc fq_libc_target_name) 421 422 get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) 423 # Add memory functions to which compilers can emit calls. 424 list(APPEND fq_deps_list 425 libc.src.string.bcmp 426 libc.src.string.bzero 427 libc.src.string.memcmp 428 libc.src.string.memcpy 429 libc.src.string.memset) 430 list(REMOVE_DUPLICATES fq_deps_list) 431 # TODO: Instead of gathering internal object files from entrypoints, 432 # collect the object files with public names of entrypoints. 433 get_object_files_for_test( 434 link_object_files skipped_entrypoints_list ${fq_deps_list}) 435 if(skipped_entrypoints_list) 436 message(STATUS "Skipping ${fq_target_name} as it has skipped deps.") 437 return() 438 endif() 439 440 # Create a sysroot structure 441 set(sysroot ${CMAKE_CURRENT_BINARY_DIR}/${test_name}/sysroot) 442 file(MAKE_DIRECTORY ${sysroot}) 443 file(MAKE_DIRECTORY ${sysroot}/include) 444 set(sysroot_lib ${sysroot}/lib) 445 file(MAKE_DIRECTORY ${sysroot_lib}) 446 get_target_property(loader_object_file ${INTEGRATION_TEST_LOADER} LOADER_OBJECT) 447 get_target_property(crti_object_file libc.loader.linux.crti LOADER_OBJECT) 448 get_target_property(crtn_object_file libc.loader.linux.crtn LOADER_OBJECT) 449 set(dummy_archive $<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_DIRECTORY>/lib$<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_NAME>.a) 450 if(NOT loader_object_file) 451 message(FATAL_ERROR "Missing LOADER_OBJECT property of ${INTEGRATION_TEST_LOADER}.") 452 endif() 453 set(loader_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) 454 add_custom_command( 455 OUTPUT ${loader_dst} ${sysroot}/lib/crti.o ${sysroot}/lib/crtn.o ${sysroot}/lib/libm.a ${sysroot}/lib/libc++.a 456 COMMAND cmake -E copy ${loader_object_file} ${loader_dst} 457 COMMAND cmake -E copy ${crti_object_file} ${sysroot}/lib 458 COMMAND cmake -E copy ${crtn_object_file} ${sysroot}/lib 459 # We copy the dummy archive as libm.a and libc++.a as the compiler drivers expect them. 460 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libm.a 461 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libc++.a 462 DEPENDS ${INTEGRATION_TEST_LOADER} libc.loader.linux.crti libc.loader.linux.crtn libc_integration_test_dummy 463 ) 464 add_custom_target( 465 ${fq_target_name}.__copy_loader__ 466 DEPENDS ${loader_dst} 467 ) 468 469 add_library( 470 ${fq_libc_target_name} 471 STATIC 472 ${link_object_files} 473 ) 474 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_NAME c) 475 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${sysroot_lib}) 476 477 add_executable( 478 ${fq_target_name} 479 EXCLUDE_FROM_ALL 480 ${INTEGRATION_TEST_SRCS} 481 ${INTEGRATION_TEST_HDRS} 482 ) 483 set_target_properties(${fq_target_name} 484 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 485 target_include_directories( 486 ${fq_target_name} 487 PRIVATE 488 ${LIBC_SOURCE_DIR} 489 ${LIBC_BUILD_DIR} 490 ${LIBC_BUILD_DIR}/include 491 ) 492 # We set a number of link options to prevent picking up system libc binaries. 493 # Also, we restrict the integration tests to fully static executables. The 494 # rtlib is set to compiler-rt to make the compiler drivers pick up the compiler 495 # runtime binaries using full paths. Otherwise, files like crtbegin.o are passed 496 # as is (and not as paths like /usr/lib/.../crtbegin.o). 497 target_link_options(${fq_target_name} PRIVATE --sysroot=${sysroot} -static -stdlib=libc++ --rtlib=compiler-rt) 498 add_dependencies(${fq_target_name} 499 ${fq_target_name}.__copy_loader__ 500 ${fq_libc_target_name} 501 libc.utils.IntegrationTest.test) 502 503 add_custom_command( 504 TARGET ${fq_target_name} 505 POST_BUILD 506 COMMAND ${INTEGRATION_TEST_ENV} $<TARGET_FILE:${fq_target_name}> ${INTEGRATION_TEST_ARGS} 507 ) 508 509 add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name}) 510endfunction(add_integration_test) 511