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(GET flags 0 flag) 216 list(REMOVE_AT flags 0) 217 extract_flag_modifier(${flag} real_flag modifier) 218 219 if(NOT "${modifier}" STREQUAL "NO") 220 expand_flags_for_libc_unittest( 221 ${target_name} 222 "${flags}" 223 DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER 224 FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER 225 "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" 226 ) 227 endif() 228 229 if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") 230 return() 231 endif() 232 233 set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) 234 list(REMOVE_ITEM NEW_FLAGS ${flag}) 235 get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) 236 237 # Only target with `flag` has `.__NO_flag` target, `flag__NO` and 238 # `flag__ONLY` do not. 239 if(NOT "${modifier}") 240 set(TARGET_NAME "${target_name}.__NO_${flag}") 241 else() 242 set(TARGET_NAME "${target_name}") 243 endif() 244 245 expand_flags_for_libc_unittest( 246 ${TARGET_NAME} 247 "${flags}" 248 DEPENDS "${NEW_DEPS}" IGNORE_MARKER 249 FLAGS "${NEW_FLAGS}" IGNORE_MARKER 250 "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" 251 ) 252endfunction(expand_flags_for_libc_unittest) 253 254function(add_libc_unittest target_name) 255 cmake_parse_arguments( 256 "ADD_TO_EXPAND" 257 "" # Optional arguments 258 "" # Single value arguments 259 "DEPENDS;FLAGS" # Multi-value arguments 260 ${ARGN} 261 ) 262 263 get_fq_target_name(${target_name} fq_target_name) 264 265 if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) 266 message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") 267 endif() 268 269 get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) 270 get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) 271 272 list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) 273 remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) 274 list(SORT flags) 275 276 if(SHOW_INTERMEDIATE_OBJECTS AND flags) 277 message(STATUS "Unit test ${fq_target_name} has FLAGS: ${flags}") 278 endif() 279 280 expand_flags_for_libc_unittest( 281 ${fq_target_name} 282 "${flags}" 283 DEPENDS ${fq_deps_list} IGNORE_MARKER 284 FLAGS ${flags} IGNORE_MARKER 285 ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} 286 ) 287endfunction(add_libc_unittest) 288 289function(add_libc_testsuite suite_name) 290 add_custom_target(${suite_name}) 291 add_dependencies(check-llvmlibc ${suite_name}) 292endfunction(add_libc_testsuite) 293 294function(add_libc_exhaustive_testsuite suite_name) 295 add_custom_target(${suite_name}) 296 add_dependencies(exhaustive-check-libc ${suite_name}) 297endfunction(add_libc_exhaustive_testsuite) 298 299function(add_libc_long_running_testsuite suite_name) 300 add_custom_target(${suite_name}) 301 add_dependencies(libc-long-running-tests ${suite_name}) 302endfunction(add_libc_long_running_testsuite) 303 304# Rule to add a fuzzer test. 305# Usage 306# add_libc_fuzzer( 307# <target name> 308# SRCS <list of .cpp files for the test> 309# HDRS <list of .h files for the test> 310# DEPENDS <list of dependencies> 311# ) 312function(add_libc_fuzzer target_name) 313 cmake_parse_arguments( 314 "LIBC_FUZZER" 315 "" # No optional arguments 316 "" # Single value arguments 317 "SRCS;HDRS;DEPENDS" # Multi-value arguments 318 ${ARGN} 319 ) 320 if(NOT LIBC_FUZZER_SRCS) 321 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 322 "files.") 323 endif() 324 if(NOT LIBC_FUZZER_DEPENDS) 325 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 326 "'add_entrypoint_object' targets.") 327 endif() 328 329 get_fq_target_name(${target_name} fq_target_name) 330 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 331 get_object_files_for_test( 332 link_object_files skipped_entrypoints_list ${fq_deps_list}) 333 if(skipped_entrypoints_list) 334 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 335 "${skipped_entrypoints_list}.") 336 message(STATUS ${msg}) 337 add_custom_target(${fq_target_name}) 338 339 # A post build custom command is used to avoid running the command always. 340 add_custom_command( 341 TARGET ${fq_target_name} 342 POST_BUILD 343 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 344 ) 345 return() 346 endif() 347 348 add_executable( 349 ${fq_target_name} 350 EXCLUDE_FROM_ALL 351 ${LIBC_FUZZER_SRCS} 352 ${LIBC_FUZZER_HDRS} 353 ) 354 target_include_directories( 355 ${fq_target_name} 356 PRIVATE 357 ${LIBC_SOURCE_DIR} 358 ${LIBC_BUILD_DIR} 359 ${LIBC_BUILD_DIR}/include 360 ) 361 362 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 363 364 set_target_properties(${fq_target_name} 365 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 366 367 add_dependencies( 368 ${fq_target_name} 369 ${fq_deps_list} 370 ) 371 add_dependencies(libc-fuzzer ${fq_target_name}) 372endfunction(add_libc_fuzzer) 373 374# Rule to add an integration test. An integration test is like a unit test 375# but does not use the system libc. Not even the loader from the system libc 376# is linked to the final executable. The final exe is fully statically linked. 377# The libc that the final exe links to consists of only the object files of 378# the DEPENDS targets. 379# 380# Usage: 381# add_integration_test( 382# <target name> 383# SUITE <the suite to which the test should belong> 384# SRCS <src1.cpp> [src2.cpp ...] 385# HDRS [hdr1.cpp ...] 386# LOADER <fully qualified loader target name> 387# DEPENDS <list of entrypoint or other object targets> 388# ARGS <list of command line arguments to be passed to the test> 389# ENV <list of environment variables to set before running the test> 390# ) 391# 392# The loader target should provide a property named LOADER_OBJECT which is 393# the full path to the object file produces when the loader is built. 394# 395# The DEPENDS list can be empty. If not empty, it should be a list of 396# targets added with add_entrypoint_object or add_object_library. 397function(add_integration_test test_name) 398 get_fq_target_name(${test_name} fq_target_name) 399 if(NOT (${LIBC_TARGET_OS} STREQUAL "linux")) 400 message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.") 401 return() 402 endif() 403 cmake_parse_arguments( 404 "INTEGRATION_TEST" 405 "" # No optional arguments 406 "SUITE;LOADER" # Single value arguments 407 "SRCS;HDRS;DEPENDS;ARGS;ENV" # Multi-value arguments 408 ${ARGN} 409 ) 410 411 if(NOT INTEGRATION_TEST_SUITE) 412 message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") 413 endif() 414 if(NOT INTEGRATION_TEST_LOADER) 415 message(FATAL_ERROR "The LOADER to link to the integration test is missing.") 416 endif() 417 if(NOT INTEGRATION_TEST_SRCS) 418 message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") 419 endif() 420 421 get_fq_target_name(${test_name}.libc fq_libc_target_name) 422 423 get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) 424 # Add memory functions to which compilers can emit calls. 425 list(APPEND fq_deps_list 426 libc.src.string.bcmp 427 libc.src.string.bzero 428 libc.src.string.memcmp 429 libc.src.string.memcpy 430 libc.src.string.memset) 431 list(REMOVE_DUPLICATES fq_deps_list) 432 # TODO: Instead of gathering internal object files from entrypoints, 433 # collect the object files with public names of entrypoints. 434 get_object_files_for_test( 435 link_object_files skipped_entrypoints_list ${fq_deps_list}) 436 if(skipped_entrypoints_list) 437 message(STATUS "Skipping ${fq_target_name} as it has skipped deps.") 438 return() 439 endif() 440 441 # Create a sysroot structure 442 set(sysroot ${CMAKE_CURRENT_BINARY_DIR}/${test_name}/sysroot) 443 file(MAKE_DIRECTORY ${sysroot}) 444 file(MAKE_DIRECTORY ${sysroot}/include) 445 set(sysroot_lib ${sysroot}/lib) 446 file(MAKE_DIRECTORY ${sysroot_lib}) 447 get_target_property(loader_object_file ${INTEGRATION_TEST_LOADER} LOADER_OBJECT) 448 get_target_property(crti_object_file libc.loader.linux.crti LOADER_OBJECT) 449 get_target_property(crtn_object_file libc.loader.linux.crtn LOADER_OBJECT) 450 set(dummy_archive $<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_DIRECTORY>/lib$<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_NAME>.a) 451 if(NOT loader_object_file) 452 message(FATAL_ERROR "Missing LOADER_OBJECT property of ${INTEGRATION_TEST_LOADER}.") 453 endif() 454 set(loader_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) 455 add_custom_command( 456 OUTPUT ${loader_dst} ${sysroot}/lib/crti.o ${sysroot}/lib/crtn.o ${sysroot}/lib/libm.a ${sysroot}/lib/libc++.a 457 COMMAND cmake -E copy ${loader_object_file} ${loader_dst} 458 COMMAND cmake -E copy ${crti_object_file} ${sysroot}/lib 459 COMMAND cmake -E copy ${crtn_object_file} ${sysroot}/lib 460 # We copy the dummy archive as libm.a and libc++.a as the compiler drivers expect them. 461 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libm.a 462 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libc++.a 463 DEPENDS ${INTEGRATION_TEST_LOADER} libc.loader.linux.crti libc.loader.linux.crtn libc_integration_test_dummy 464 ) 465 add_custom_target( 466 ${fq_target_name}.__copy_loader__ 467 DEPENDS ${loader_dst} 468 ) 469 470 add_library( 471 ${fq_libc_target_name} 472 STATIC 473 ${link_object_files} 474 ) 475 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_NAME c) 476 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${sysroot_lib}) 477 478 add_executable( 479 ${fq_target_name} 480 EXCLUDE_FROM_ALL 481 ${INTEGRATION_TEST_SRCS} 482 ${INTEGRATION_TEST_HDRS} 483 ) 484 set_target_properties(${fq_target_name} 485 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 486 target_include_directories( 487 ${fq_target_name} 488 PRIVATE 489 ${LIBC_SOURCE_DIR} 490 ${LIBC_BUILD_DIR} 491 ${LIBC_BUILD_DIR}/include 492 ) 493 # We set a number of link options to prevent picking up system libc binaries. 494 # Also, we restrict the integration tests to fully static executables. The 495 # rtlib is set to compiler-rt to make the compiler drivers pick up the compiler 496 # runtime binaries using full paths. Otherwise, files like crtbegin.o are passed 497 # as is (and not as paths like /usr/lib/.../crtbegin.o). 498 target_link_options(${fq_target_name} PRIVATE --sysroot=${sysroot} -static -stdlib=libc++ --rtlib=compiler-rt) 499 add_dependencies(${fq_target_name} 500 ${fq_target_name}.__copy_loader__ 501 ${fq_libc_target_name} 502 libc.utils.IntegrationTest.test) 503 504 add_custom_command( 505 TARGET ${fq_target_name} 506 POST_BUILD 507 COMMAND ${INTEGRATION_TEST_ENV} $<TARGET_FILE:${fq_target_name}> ${INTEGRATION_TEST_ARGS} 508 ) 509 510 add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name}) 511endfunction(add_integration_test) 512