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