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# ) 67function(add_libc_unittest target_name) 68 if(NOT LLVM_INCLUDE_TESTS) 69 return() 70 endif() 71 72 cmake_parse_arguments( 73 "LIBC_UNITTEST" 74 "" # No optional arguments 75 "SUITE" # Single value arguments 76 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments 77 "NO_LIBC_UNITTEST_TEST_MAIN" 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 142 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 143 144 set_target_properties(${fq_target_name} 145 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 146 147 add_dependencies( 148 ${fq_target_name} 149 ${fq_deps_list} 150 ) 151 152 if(NO_LIBC_UNITTEST_TEST_MAIN) 153 target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest libc_test_utils) 154 else() 155 target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest LibcUnitTestMain libc_test_utils) 156 endif() 157 158 add_custom_command( 159 TARGET ${fq_target_name} 160 POST_BUILD 161 COMMAND $<TARGET_FILE:${fq_target_name}> 162 ) 163 if(LIBC_UNITTEST_SUITE) 164 add_dependencies( 165 ${LIBC_UNITTEST_SUITE} 166 ${fq_target_name} 167 ) 168 endif() 169endfunction(add_libc_unittest) 170 171function(add_libc_testsuite suite_name) 172 add_custom_target(${suite_name}) 173 add_dependencies(check-llvmlibc ${suite_name}) 174endfunction(add_libc_testsuite) 175 176function(add_libc_exhaustive_testsuite suite_name) 177 add_custom_target(${suite_name}) 178 add_dependencies(exhaustive-check-libc ${suite_name}) 179endfunction(add_libc_exhaustive_testsuite) 180 181# Rule to add a fuzzer test. 182# Usage 183# add_libc_fuzzer( 184# <target name> 185# SRCS <list of .cpp files for the test> 186# HDRS <list of .h files for the test> 187# DEPENDS <list of dependencies> 188# ) 189function(add_libc_fuzzer target_name) 190 cmake_parse_arguments( 191 "LIBC_FUZZER" 192 "" # No optional arguments 193 "" # Single value arguments 194 "SRCS;HDRS;DEPENDS" # Multi-value arguments 195 ${ARGN} 196 ) 197 if(NOT LIBC_FUZZER_SRCS) 198 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 199 "files.") 200 endif() 201 if(NOT LIBC_FUZZER_DEPENDS) 202 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 203 "'add_entrypoint_object' targets.") 204 endif() 205 206 get_fq_target_name(${target_name} fq_target_name) 207 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 208 get_object_files_for_test( 209 link_object_files skipped_entrypoints_list ${fq_deps_list}) 210 if(skipped_entrypoints_list) 211 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 212 "${skipped_entrypoints_list}.") 213 message(STATUS ${msg}) 214 add_custom_target(${fq_target_name}) 215 216 # A post build custom command is used to avoid running the command always. 217 add_custom_command( 218 TARGET ${fq_target_name} 219 POST_BUILD 220 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 221 ) 222 return() 223 endif() 224 225 add_executable( 226 ${fq_target_name} 227 EXCLUDE_FROM_ALL 228 ${LIBC_FUZZER_SRCS} 229 ${LIBC_FUZZER_HDRS} 230 ) 231 target_include_directories( 232 ${fq_target_name} 233 PRIVATE 234 ${LIBC_SOURCE_DIR} 235 ${LIBC_BUILD_DIR} 236 ${LIBC_BUILD_DIR}/include 237 ) 238 239 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 240 241 set_target_properties(${fq_target_name} 242 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 243 244 add_dependencies( 245 ${fq_target_name} 246 ${fq_deps_list} 247 ) 248 add_dependencies(libc-fuzzer ${fq_target_name}) 249endfunction(add_libc_fuzzer) 250