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(add_libc_unittest 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" # 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_target_name(${target_name} fq_target_name) 90 get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS}) 91 get_object_files_for_test( 92 link_object_files skipped_entrypoints_list ${fq_deps_list}) 93 if(skipped_entrypoints_list) 94 # If a test is OS/target machine independent, it has to be skipped if the 95 # OS/target machine combination does not provide any dependent entrypoints. 96 # If a test is OS/target machine specific, then such a test will live is a 97 # OS/target machine specific directory and will be skipped at the directory 98 # level if required. 99 # 100 # There can potentially be a setup like this: A unittest is setup for a 101 # OS/target machine independent object library, which in turn depends on a 102 # machine specific object library. Such a test would be testing internals of 103 # the libc and it is assumed that they will be rare in practice. So, they 104 # can be skipped in the corresponding CMake files using platform specific 105 # logic. This pattern is followed in the loader tests for example. 106 # 107 # Another pattern that is present currently is to detect machine 108 # capabilities and add entrypoints and tests accordingly. That approach is 109 # much lower level approach and is independent of the kind of skipping that 110 # is happening here at the entrypoint level. 111 112 set(msg "Skipping unittest ${fq_target_name} as it has missing deps: " 113 "${skipped_entrypoints_list}.") 114 message(STATUS ${msg}) 115 return() 116 endif() 117 118 add_executable( 119 ${fq_target_name} 120 EXCLUDE_FROM_ALL 121 ${LIBC_UNITTEST_SRCS} 122 ${LIBC_UNITTEST_HDRS} 123 ) 124 target_include_directories( 125 ${fq_target_name} 126 PRIVATE 127 ${LIBC_SOURCE_DIR} 128 ${LIBC_BUILD_DIR} 129 ${LIBC_BUILD_DIR}/include 130 ) 131 target_compile_options( 132 ${fq_target_name} 133 PRIVATE ${LIBC_COMPILE_OPTIONS_DEFAULT} 134 ) 135 if(LIBC_UNITTEST_COMPILE_OPTIONS) 136 target_compile_options( 137 ${fq_target_name} 138 PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS} 139 ) 140 endif() 141 if(LIBC_UNITTEST_CXX_STANDARD) 142 set_target_properties( 143 ${fq_target_name} 144 PROPERTIES 145 CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD} 146 ) 147 endif() 148 149 # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest` 150 set(link_libraries ${link_object_files} ${LIBC_UNITTEST_LINK_LIBRARIES}) 151 152 set_target_properties(${fq_target_name} 153 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 154 155 add_dependencies( 156 ${fq_target_name} 157 ${fq_deps_list} 158 ) 159 160 # LibcUnitTest and libc_test_utils should not depend on anything in LINK_LIBRARIES. 161 if(NO_LIBC_UNITTEST_TEST_MAIN) 162 list(APPEND link_libraries LibcUnitTest libc_test_utils) 163 else() 164 list(APPEND link_libraries LibcUnitTest LibcUnitTestMain libc_test_utils) 165 endif() 166 167 target_link_libraries(${fq_target_name} PRIVATE ${link_libraries}) 168 169 if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD) 170 add_custom_command( 171 TARGET ${fq_target_name} 172 POST_BUILD 173 COMMAND $<TARGET_FILE:${fq_target_name}> 174 ) 175 endif() 176 177 if(LIBC_UNITTEST_SUITE) 178 add_dependencies( 179 ${LIBC_UNITTEST_SUITE} 180 ${fq_target_name} 181 ) 182 endif() 183endfunction(add_libc_unittest) 184 185function(add_libc_testsuite suite_name) 186 add_custom_target(${suite_name}) 187 add_dependencies(check-llvmlibc ${suite_name}) 188endfunction(add_libc_testsuite) 189 190function(add_libc_exhaustive_testsuite suite_name) 191 add_custom_target(${suite_name}) 192 add_dependencies(exhaustive-check-libc ${suite_name}) 193endfunction(add_libc_exhaustive_testsuite) 194 195function(add_libc_long_running_testsuite suite_name) 196 add_custom_target(${suite_name}) 197 add_dependencies(libc-long-running-tests ${suite_name}) 198endfunction(add_libc_long_running_testsuite) 199 200# Rule to add a fuzzer test. 201# Usage 202# add_libc_fuzzer( 203# <target name> 204# SRCS <list of .cpp files for the test> 205# HDRS <list of .h files for the test> 206# DEPENDS <list of dependencies> 207# ) 208function(add_libc_fuzzer target_name) 209 cmake_parse_arguments( 210 "LIBC_FUZZER" 211 "" # No optional arguments 212 "" # Single value arguments 213 "SRCS;HDRS;DEPENDS" # Multi-value arguments 214 ${ARGN} 215 ) 216 if(NOT LIBC_FUZZER_SRCS) 217 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 218 "files.") 219 endif() 220 if(NOT LIBC_FUZZER_DEPENDS) 221 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 222 "'add_entrypoint_object' targets.") 223 endif() 224 225 get_fq_target_name(${target_name} fq_target_name) 226 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 227 get_object_files_for_test( 228 link_object_files skipped_entrypoints_list ${fq_deps_list}) 229 if(skipped_entrypoints_list) 230 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 231 "${skipped_entrypoints_list}.") 232 message(STATUS ${msg}) 233 add_custom_target(${fq_target_name}) 234 235 # A post build custom command is used to avoid running the command always. 236 add_custom_command( 237 TARGET ${fq_target_name} 238 POST_BUILD 239 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 240 ) 241 return() 242 endif() 243 244 add_executable( 245 ${fq_target_name} 246 EXCLUDE_FROM_ALL 247 ${LIBC_FUZZER_SRCS} 248 ${LIBC_FUZZER_HDRS} 249 ) 250 target_include_directories( 251 ${fq_target_name} 252 PRIVATE 253 ${LIBC_SOURCE_DIR} 254 ${LIBC_BUILD_DIR} 255 ${LIBC_BUILD_DIR}/include 256 ) 257 258 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 259 260 set_target_properties(${fq_target_name} 261 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 262 263 add_dependencies( 264 ${fq_target_name} 265 ${fq_deps_list} 266 ) 267 add_dependencies(libc-fuzzer ${fq_target_name}) 268endfunction(add_libc_fuzzer) 269 270# Rule to add an integration test. An integration test is like a unit test 271# but does not use the system libc. Not even the loader from the system libc 272# is linked to the final executable. The final exe is fully statically linked. 273# The libc that the final exe links to consists of only the object files of 274# the DEPENDS targets. 275# 276# Usage: 277# add_integration_test( 278# <target name> 279# SUITE <the suite to which the test should belong> 280# SRCS <src1.cpp> [src2.cpp ...] 281# HDRS [hdr1.cpp ...] 282# LOADER <fully qualified loader target name> 283# DEPENDS <list of entrypoint or other object targets> 284# ARGS <list of command line arguments to be passed to the test> 285# ENV <list of environment variables to set before running the test> 286# ) 287# 288# The loader target should provide a property named LOADER_OBJECT which is 289# the full path to the object file produces when the loader is built. 290# 291# The DEPENDS list can be empty. If not empty, it should be a list of 292# targets added with add_entrypoint_object or add_object_library. 293function(add_integration_test test_name) 294 get_fq_target_name(${test_name} fq_target_name) 295 if(NOT (${LIBC_TARGET_OS} STREQUAL "linux")) 296 message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.") 297 return() 298 endif() 299 cmake_parse_arguments( 300 "INTEGRATION_TEST" 301 "" # No optional arguments 302 "SUITE;LOADER" # Single value arguments 303 "SRCS;HDRS;DEPENDS;ARGS;ENV" # Multi-value arguments 304 ${ARGN} 305 ) 306 307 if(NOT INTEGRATION_TEST_SUITE) 308 message(FATAL_ERROR "SUITE not specified for ${fq_target_name}") 309 endif() 310 if(NOT INTEGRATION_TEST_LOADER) 311 message(FATAL_ERROR "The LOADER to link to the integration test is missing.") 312 endif() 313 if(NOT INTEGRATION_TEST_SRCS) 314 message(FATAL_ERROR "The SRCS list for add_integration_test is missing.") 315 endif() 316 317 get_fq_target_name(${test_name}.libc fq_libc_target_name) 318 319 get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS}) 320 # Add memory functions to which compilers can emit calls. 321 list(APPEND fq_deps_list 322 libc.src.string.bcmp 323 libc.src.string.bzero 324 libc.src.string.memcmp 325 libc.src.string.memcpy 326 libc.src.string.memset) 327 list(REMOVE_DUPLICATES fq_deps_list) 328 # TODO: Instead of gathering internal object files from entrypoints, 329 # collect the object files with public names of entrypoints. 330 get_object_files_for_test( 331 link_object_files skipped_entrypoints_list ${fq_deps_list}) 332 if(skipped_entrypoints_list) 333 message(STATUS "Skipping ${fq_target_name} as it has skipped deps.") 334 return() 335 endif() 336 337 # Create a sysroot structure 338 set(sysroot ${CMAKE_CURRENT_BINARY_DIR}/${test_name}/sysroot) 339 file(MAKE_DIRECTORY ${sysroot}) 340 file(MAKE_DIRECTORY ${sysroot}/include) 341 set(sysroot_lib ${sysroot}/lib) 342 file(MAKE_DIRECTORY ${sysroot_lib}) 343 get_target_property(loader_object_file ${INTEGRATION_TEST_LOADER} LOADER_OBJECT) 344 get_target_property(crti_object_file libc.loader.linux.crti LOADER_OBJECT) 345 get_target_property(crtn_object_file libc.loader.linux.crtn LOADER_OBJECT) 346 set(dummy_archive $<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_DIRECTORY>/lib$<TARGET_PROPERTY:libc_integration_test_dummy,ARCHIVE_OUTPUT_NAME>.a) 347 if(NOT loader_object_file) 348 message(FATAL_ERROR "Missing LOADER_OBJECT property of ${INTEGRATION_TEST_LOADER}.") 349 endif() 350 set(loader_dst ${sysroot_lib}/${LIBC_TARGET_ARCHITECTURE}-linux-gnu/crt1.o) 351 add_custom_command( 352 OUTPUT ${loader_dst} ${sysroot}/lib/crti.o ${sysroot}/lib/crtn.o ${sysroot}/lib/libm.a ${sysroot}/lib/libc++.a 353 COMMAND cmake -E copy ${loader_object_file} ${loader_dst} 354 COMMAND cmake -E copy ${crti_object_file} ${sysroot}/lib 355 COMMAND cmake -E copy ${crtn_object_file} ${sysroot}/lib 356 # We copy the dummy archive as libm.a and libc++.a as the compiler drivers expect them. 357 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libm.a 358 COMMAND cmake -E copy ${dummy_archive} ${sysroot}/lib/libc++.a 359 DEPENDS ${INTEGRATION_TEST_LOADER} libc.loader.linux.crti libc.loader.linux.crtn libc_integration_test_dummy 360 ) 361 add_custom_target( 362 ${fq_target_name}.__copy_loader__ 363 DEPENDS ${loader_dst} 364 ) 365 366 add_library( 367 ${fq_libc_target_name} 368 STATIC 369 ${link_object_files} 370 ) 371 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_NAME c) 372 set_target_properties(${fq_libc_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${sysroot_lib}) 373 374 add_executable( 375 ${fq_target_name} 376 EXCLUDE_FROM_ALL 377 ${INTEGRATION_TEST_SRCS} 378 ${INTEGRATION_TEST_HDRS} 379 ) 380 set_target_properties(${fq_target_name} 381 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 382 target_include_directories( 383 ${fq_target_name} 384 PRIVATE 385 ${LIBC_SOURCE_DIR} 386 ${LIBC_BUILD_DIR} 387 ${LIBC_BUILD_DIR}/include 388 ) 389 # We set a number of link options to prevent picking up system libc binaries. 390 # Also, we restrict the integration tests to fully static executables. The 391 # rtlib is set to compiler-rt to make the compiler drivers pick up the compiler 392 # runtime binaries using full paths. Otherwise, files like crtbegin.o are passed 393 # as is (and not as paths like /usr/lib/.../crtbegin.o). 394 target_link_options(${fq_target_name} PRIVATE --sysroot=${sysroot} -static -stdlib=libc++ --rtlib=compiler-rt) 395 add_dependencies(${fq_target_name} 396 ${fq_target_name}.__copy_loader__ 397 ${fq_libc_target_name} 398 libc.utils.IntegrationTest.test) 399 400 add_custom_command( 401 TARGET ${fq_target_name} 402 POST_BUILD 403 COMMAND ${INTEGRATION_TEST_ENV} $<TARGET_FILE:${fq_target_name}> ${INTEGRATION_TEST_ARGS} 404 ) 405 406 add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name}) 407endfunction(add_integration_test) 408