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