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 ${ARGN} 78 ) 79 if(NOT LIBC_UNITTEST_SRCS) 80 message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp " 81 "files.") 82 endif() 83 if(NOT LIBC_UNITTEST_DEPENDS) 84 message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of " 85 "'add_entrypoint_object' targets.") 86 endif() 87 88 get_fq_target_name(${target_name} fq_target_name) 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 add_executable( 118 ${fq_target_name} 119 EXCLUDE_FROM_ALL 120 ${LIBC_UNITTEST_SRCS} 121 ${LIBC_UNITTEST_HDRS} 122 ) 123 target_include_directories( 124 ${fq_target_name} 125 PRIVATE 126 ${LIBC_SOURCE_DIR} 127 ${LIBC_BUILD_DIR} 128 ${LIBC_BUILD_DIR}/include 129 ) 130 if(LIBC_UNITTEST_COMPILE_OPTIONS) 131 target_compile_options( 132 ${fq_target_name} 133 PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS} 134 ) 135 endif() 136 137 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 138 139 set_target_properties(${fq_target_name} 140 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 141 142 add_dependencies( 143 ${fq_target_name} 144 ${fq_deps_list} 145 ) 146 147 target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest libc_test_utils) 148 149 add_custom_command( 150 TARGET ${fq_target_name} 151 POST_BUILD 152 COMMAND $<TARGET_FILE:${fq_target_name}> 153 ) 154 if(LIBC_UNITTEST_SUITE) 155 add_dependencies( 156 ${LIBC_UNITTEST_SUITE} 157 ${fq_target_name} 158 ) 159 endif() 160endfunction(add_libc_unittest) 161 162function(add_libc_testsuite suite_name) 163 add_custom_target(${suite_name}) 164 add_dependencies(check-llvmlibc ${suite_name}) 165endfunction(add_libc_testsuite) 166 167function(add_libc_exhaustive_testsuite suite_name) 168 add_custom_target(${suite_name}) 169 add_dependencies(exhaustive-check-libc ${suite_name}) 170endfunction(add_libc_exhaustive_testsuite) 171 172# Rule to add a fuzzer test. 173# Usage 174# add_libc_fuzzer( 175# <target name> 176# SRCS <list of .cpp files for the test> 177# HDRS <list of .h files for the test> 178# DEPENDS <list of dependencies> 179# ) 180function(add_libc_fuzzer target_name) 181 cmake_parse_arguments( 182 "LIBC_FUZZER" 183 "" # No optional arguments 184 "" # Single value arguments 185 "SRCS;HDRS;DEPENDS" # Multi-value arguments 186 ${ARGN} 187 ) 188 if(NOT LIBC_FUZZER_SRCS) 189 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp " 190 "files.") 191 endif() 192 if(NOT LIBC_FUZZER_DEPENDS) 193 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of " 194 "'add_entrypoint_object' targets.") 195 endif() 196 197 get_fq_target_name(${target_name} fq_target_name) 198 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS}) 199 get_object_files_for_test( 200 link_object_files skipped_entrypoints_list ${fq_deps_list}) 201 if(skipped_entrypoints_list) 202 set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: " 203 "${skipped_entrypoints_list}.") 204 message(STATUS ${msg}) 205 add_custom_target(${fq_target_name}) 206 207 # A post build custom command is used to avoid running the command always. 208 add_custom_command( 209 TARGET ${fq_target_name} 210 POST_BUILD 211 COMMAND ${CMAKE_COMMAND} -E echo ${msg} 212 ) 213 return() 214 endif() 215 216 add_executable( 217 ${fq_target_name} 218 EXCLUDE_FROM_ALL 219 ${LIBC_FUZZER_SRCS} 220 ${LIBC_FUZZER_HDRS} 221 ) 222 target_include_directories( 223 ${fq_target_name} 224 PRIVATE 225 ${LIBC_SOURCE_DIR} 226 ${LIBC_BUILD_DIR} 227 ${LIBC_BUILD_DIR}/include 228 ) 229 230 target_link_libraries(${fq_target_name} PRIVATE ${link_object_files}) 231 232 set_target_properties(${fq_target_name} 233 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 234 235 add_dependencies( 236 ${fq_target_name} 237 ${fq_deps_list} 238 ) 239 add_dependencies(libc-fuzzer ${fq_target_name}) 240endfunction(add_libc_fuzzer) 241