Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libc / cmake / modules / LLVMLibCTestRules.cmake
blobfc66c129609456e8c6a3f17293d41eb96ce5a167
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> ...])
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.
15 function(get_object_files_for_test result skipped_entrypoints_list)
16   set(object_files "")
17   set(skipped_list "")
18   foreach(dep IN LISTS ARGN)
19     if (NOT TARGET ${dep})
20       # Skip any tests whose dependencies have not been defined.
21       list(APPEND skipped_list ${dep})
22       continue()
23     endif()
24     get_target_property(dep_type ${dep} "TARGET_TYPE")
25     if(NOT dep_type)
26       # Target for which TARGET_TYPE property is not set do not
27       # provide any object files.
28       continue()
29     endif()
31     if(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})
32       get_target_property(dep_object_files ${dep} "OBJECT_FILES")
33       if(dep_object_files)
34         list(APPEND object_files ${dep_object_files})
35       endif()
36     elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})
37       get_target_property(is_skipped ${dep} "SKIPPED")
38       if(is_skipped)
39         list(APPEND skipped_list ${dep})
40         continue()
41       endif()
42       get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW")
43       if(object_file_raw)
44         list(APPEND object_files ${object_file_raw})
45       endif()
46     elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})
47       # We skip tests for all externally implemented entrypoints.
48       list(APPEND skipped_list ${dep})
49       continue()
50     endif()
52     get_target_property(indirect_deps ${dep} "DEPS")
53     get_object_files_for_test(
54         indirect_objfiles indirect_skipped_list ${indirect_deps})
55     list(APPEND object_files ${indirect_objfiles})
56     if(indirect_skipped_list)
57       list(APPEND skipped_list ${indirect_skipped_list})
58     endif()
59   endforeach(dep)
60   list(REMOVE_DUPLICATES object_files)
61   set(${result} ${object_files} PARENT_SCOPE)
62   list(REMOVE_DUPLICATES skipped_list)
63   set(${skipped_entrypoints_list} ${skipped_list} PARENT_SCOPE)
64 endfunction(get_object_files_for_test)
66 # Rule to add a libc unittest.
67 # Usage
68 #    add_libc_unittest(
69 #      <target name>
70 #      SUITE <name of the suite this test belongs to>
71 #      SRCS  <list of .cpp files for the test>
72 #      HDRS  <list of .h files for the test>
73 #      DEPENDS <list of dependencies>
74 #      COMPILE_OPTIONS <list of special compile options for this target>
75 #      LINK_LIBRARIES <list of linking libraries for this target>
76 #    )
77 function(create_libc_unittest fq_target_name)
78   if(NOT LLVM_INCLUDE_TESTS)
79     return()
80   endif()
82   cmake_parse_arguments(
83     "LIBC_UNITTEST"
84     "NO_RUN_POSTBUILD" # Optional arguments
85     "SUITE;CXX_STANDARD" # Single value arguments
86     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES;FLAGS" # Multi-value arguments
87     ${ARGN}
88   )
89   if(NOT LIBC_UNITTEST_SRCS)
90     message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp "
91                         "files.")
92   endif()
93   if(NOT LIBC_UNITTEST_DEPENDS)
94     message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of "
95                         "'add_entrypoint_object' targets.")
96   endif()
98   get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS})
99   list(APPEND fq_deps_list libc.src.__support.StringUtil.error_to_string
100                            libc.test.UnitTest.ErrnoSetterMatcher)
101   list(REMOVE_DUPLICATES fq_deps_list)
102   get_object_files_for_test(
103       link_object_files skipped_entrypoints_list ${fq_deps_list})
104   if(skipped_entrypoints_list)
105     # If a test is OS/target machine independent, it has to be skipped if the
106     # OS/target machine combination does not provide any dependent entrypoints.
107     # If a test is OS/target machine specific, then such a test will live is a
108     # OS/target machine specific directory and will be skipped at the directory
109     # level if required.
110     #
111     # There can potentially be a setup like this: A unittest is setup for a
112     # OS/target machine independent object library, which in turn depends on a
113     # machine specific object library. Such a test would be testing internals of
114     # the libc and it is assumed that they will be rare in practice. So, they
115     # can be skipped in the corresponding CMake files using platform specific
116     # logic. This pattern is followed in the startup tests for example.
117     #
118     # Another pattern that is present currently is to detect machine
119     # capabilities and add entrypoints and tests accordingly. That approach is
120     # much lower level approach and is independent of the kind of skipping that
121     # is happening here at the entrypoint level.
122     if(LIBC_CMAKE_VERBOSE_LOGGING)
123       set(msg "Skipping unittest ${fq_target_name} as it has missing deps: "
124               "${skipped_entrypoints_list}.")
125       message(STATUS ${msg})
126     endif()
127     return()
128   endif()
130   if(SHOW_INTERMEDIATE_OBJECTS)
131     message(STATUS "Adding unit test ${fq_target_name}")
132     if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
133       foreach(dep IN LISTS ADD_OBJECT_DEPENDS)
134         message(STATUS "  ${fq_target_name} depends on ${dep}")
135       endforeach()
136     endif()
137   endif()
139   if(LIBC_UNITTEST_NO_RUN_POSTBUILD)
140     set(fq_build_target_name ${fq_target_name})
141   else()
142     set(fq_build_target_name ${fq_target_name}.__build__)
143   endif()
145   add_executable(
146     ${fq_build_target_name}
147     EXCLUDE_FROM_ALL
148     ${LIBC_UNITTEST_SRCS}
149     ${LIBC_UNITTEST_HDRS}
150   )
151   target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
152   target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
153   target_compile_options(
154     ${fq_build_target_name}
155     PRIVATE -fpie ${LIBC_COMPILE_OPTIONS_DEFAULT}
156   )
157   if(LLVM_LIBC_FULL_BUILD)
158     target_compile_options(
159       ${fq_build_target_name}
160       PRIVATE -ffreestanding
161     )
162   endif()
163   if(LIBC_UNITTEST_COMPILE_OPTIONS)
164     target_compile_options(
165       ${fq_build_target_name}
166       PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS}
167     )
168   endif()
169   if(NOT LIBC_UNITTEST_CXX_STANDARD)
170     set(LIBC_UNITTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD})
171   endif()
172   set_target_properties(
173     ${fq_build_target_name}
174     PROPERTIES
175       CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD}
176   )
178   set(link_libraries ${link_object_files})
179   # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest`
180   foreach(lib IN LISTS LIBC_UNITTEST_LINK_LIBRARIES)
181     if(TARGET ${lib}.unit)
182       list(APPEND link_libraries ${lib}.unit)
183     else()
184       list(APPEND link_libraries ${lib})
185     endif()
186   endforeach()
188   set_target_properties(${fq_build_target_name}
189     PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
191   add_dependencies(
192     ${fq_build_target_name}
193     ${fq_deps_list}
194   )
196   # LibcUnitTest should not depend on anything in LINK_LIBRARIES.
197   list(APPEND link_libraries LibcDeathTestExecutors.unit LibcTest.unit)
199   target_link_libraries(${fq_build_target_name} PRIVATE ${link_libraries})
201   if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD)
202     add_custom_target(
203       ${fq_target_name}
204       COMMAND ${fq_build_target_name}
205       COMMENT "Running unit test ${fq_target_name}"
206     )
207   endif()
209   if(LIBC_UNITTEST_SUITE)
210     add_dependencies(
211       ${LIBC_UNITTEST_SUITE}
212       ${fq_target_name}
213     )
214   endif()
215   add_dependencies(libc-unit-tests ${fq_target_name})
216 endfunction(create_libc_unittest)
218 # Internal function, used by `add_libc_unittest`.
219 function(expand_flags_for_libc_unittest target_name flags)
220   cmake_parse_arguments(
221     "EXPAND_FLAGS"
222     "IGNORE_MARKER" # No Optional arguments
223     "" # No Single-value arguments
224     "DEPENDS;FLAGS" # Multi-value arguments
225     ${ARGN}
226   )
228   list(LENGTH flags nflags)
229   if(NOT ${nflags})
230     create_libc_unittest(
231       ${target_name}
232       DEPENDS "${EXPAND_FLAGS_DEPENDS}"
233       FLAGS "${EXPAND_FLAGS_FLAGS}"
234       "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
235     )
236     return()
237   endif()
239   list(GET flags 0 flag)
240   list(REMOVE_AT flags 0)
241   extract_flag_modifier(${flag} real_flag modifier)
243   if(NOT "${modifier}" STREQUAL "NO")
244     expand_flags_for_libc_unittest(
245       ${target_name}
246       "${flags}"
247       DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER
248       FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER
249       "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
250     )
251   endif()
253   if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
254     return()
255   endif()
257   set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
258   list(REMOVE_ITEM NEW_FLAGS ${flag})
259   get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
261   # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
262   # `flag__ONLY` do not.
263   if("${modifier}" STREQUAL "")
264     set(TARGET_NAME "${target_name}.__NO_${flag}")
265   else()
266     set(TARGET_NAME "${target_name}")
267   endif()
269   expand_flags_for_libc_unittest(
270     ${TARGET_NAME}
271     "${flags}"
272     DEPENDS "${NEW_DEPS}" IGNORE_MARKER
273     FLAGS "${NEW_FLAGS}" IGNORE_MARKER
274     "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}"
275   )
276 endfunction(expand_flags_for_libc_unittest)
278 function(add_libc_unittest target_name)
279   cmake_parse_arguments(
280     "ADD_TO_EXPAND"
281     "" # Optional arguments
282     "" # Single value arguments
283     "DEPENDS;FLAGS" # Multi-value arguments
284     ${ARGN}
285   )
287   get_fq_target_name(${target_name} fq_target_name)
289   if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
290     message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
291   endif()
293   get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
294   get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
295   
296   list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
297   remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
298   list(SORT flags)
300   if(SHOW_INTERMEDIATE_OBJECTS AND flags)
301     message(STATUS "Unit test ${fq_target_name} has FLAGS: ${flags}")
302   endif()
304   expand_flags_for_libc_unittest(
305     ${fq_target_name}
306     "${flags}"
307     DEPENDS ${fq_deps_list} IGNORE_MARKER
308     FLAGS ${flags} IGNORE_MARKER
309     ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
310   )
311 endfunction(add_libc_unittest)
313 function(add_libc_exhaustive_testsuite suite_name)
314   add_custom_target(${suite_name})
315   add_dependencies(exhaustive-check-libc ${suite_name})
316 endfunction(add_libc_exhaustive_testsuite)
318 function(add_libc_long_running_testsuite suite_name)
319   add_custom_target(${suite_name})
320   add_dependencies(libc-long-running-tests ${suite_name})
321 endfunction(add_libc_long_running_testsuite)
323 # Rule to add a fuzzer test.
324 # Usage
325 #    add_libc_fuzzer(
326 #      <target name>
327 #      SRCS  <list of .cpp files for the test>
328 #      HDRS  <list of .h files for the test>
329 #      DEPENDS <list of dependencies>
330 #    )
331 function(add_libc_fuzzer target_name)
332   cmake_parse_arguments(
333     "LIBC_FUZZER"
334     "NEED_MPFR" # Optional arguments
335     "" # Single value arguments
336     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
337     ${ARGN}
338   )
339   if(NOT LIBC_FUZZER_SRCS)
340     message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp "
341                         "files.")
342   endif()
343   if(NOT LIBC_FUZZER_DEPENDS)
344     message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of "
345                         "'add_entrypoint_object' targets.")
346   endif()
348   list(APPEND LIBC_FUZZER_LINK_LIBRARIES "")
349   if(LIBC_FUZZER_NEED_MPFR)
350     if(NOT LIBC_TESTS_CAN_USE_MPFR)
351       message(VERBOSE "Fuzz test ${name} will be skipped as MPFR library is not available.")
352       return()
353     endif()
354     list(APPEND LIBC_FUZZER_LINK_LIBRARIES mpfr gmp)
355   endif()
358   get_fq_target_name(${target_name} fq_target_name)
359   get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS})
360   get_object_files_for_test(
361       link_object_files skipped_entrypoints_list ${fq_deps_list})
362   if(skipped_entrypoints_list)
363     if(LIBC_CMAKE_VERBOSE_LOGGING)
364       set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: "
365               "${skipped_entrypoints_list}.")
366       message(STATUS ${msg})
367     endif()
368     add_custom_target(${fq_target_name})
370     # A post build custom command is used to avoid running the command always.
371     add_custom_command(
372       TARGET ${fq_target_name}
373       POST_BUILD
374       COMMAND ${CMAKE_COMMAND} -E echo ${msg}
375     )
376     return()
377   endif()
379   add_executable(
380     ${fq_target_name}
381     EXCLUDE_FROM_ALL
382     ${LIBC_FUZZER_SRCS}
383     ${LIBC_FUZZER_HDRS}
384   )
385   target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
386   target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
388   target_link_libraries(${fq_target_name} PRIVATE 
389     ${link_object_files} 
390     ${LIBC_FUZZER_LINK_LIBRARIES}
391   )
393   set_target_properties(${fq_target_name}
394       PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
396   add_dependencies(
397     ${fq_target_name}
398     ${fq_deps_list}
399   )
400   add_dependencies(libc-fuzzer ${fq_target_name})
402   target_compile_options(${fq_target_name}
403     PRIVATE
404     ${LIBC_FUZZER_COMPILE_OPTIONS})
406 endfunction(add_libc_fuzzer)
408 # DEPRECATED: Use add_hermetic_test instead.
410 # Rule to add an integration test. An integration test is like a unit test
411 # but does not use the system libc. Not even the startup objects from the
412 # system libc are linked in to the final executable. The final exe is fully
413 # statically linked. The libc that the final exe links to consists of only
414 # the object files of the DEPENDS targets.
416 # Usage:
417 #   add_integration_test(
418 #     <target name>
419 #     SUITE <the suite to which the test should belong>
420 #     SRCS <src1.cpp> [src2.cpp ...]
421 #     HDRS [hdr1.cpp ...]
422 #     DEPENDS <list of entrypoint or other object targets>
423 #     ARGS <list of command line arguments to be passed to the test>
424 #     ENV <list of environment variables to set before running the test>
425 #     COMPILE_OPTIONS <list of special compile options for this target>
426 #   )
428 # The DEPENDS list can be empty. If not empty, it should be a list of
429 # targets added with add_entrypoint_object or add_object_library.
430 function(add_integration_test test_name)
431   get_fq_target_name(${test_name} fq_target_name)
432   set(supported_targets gpu linux)
433   if(NOT (${LIBC_TARGET_OS} IN_LIST supported_targets))
434     message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.")
435     return()
436   endif()
437   cmake_parse_arguments(
438     "INTEGRATION_TEST"
439     "" # No optional arguments
440     "SUITE" # Single value arguments
441     "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LOADER_ARGS" # Multi-value arguments
442     ${ARGN}
443   )
445   if(NOT INTEGRATION_TEST_SUITE)
446     message(FATAL_ERROR "SUITE not specified for ${fq_target_name}")
447   endif()
448   if(NOT INTEGRATION_TEST_SRCS)
449     message(FATAL_ERROR "The SRCS list for add_integration_test is missing.")
450   endif()
451   if(NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1)
452     message(FATAL_ERROR "The 'crt1' target for the integration test is missing.")
453   endif()
455   get_fq_target_name(${test_name}.libc fq_libc_target_name)
457   get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS})
458   list(APPEND fq_deps_list
459       # All integration tests use the operating system's startup object with the
460       # integration test object and need to inherit the same dependencies.
461       libc.startup.${LIBC_TARGET_OS}.crt1
462       libc.test.IntegrationTest.test
463       # We always add the memory functions objects. This is because the
464       # compiler's codegen can emit calls to the C memory functions.
465       libc.src.string.bcmp
466       libc.src.string.bzero
467       libc.src.string.memcmp
468       libc.src.string.memcpy
469       libc.src.string.memmove
470       libc.src.string.memset
471   )
472   list(REMOVE_DUPLICATES fq_deps_list)
474   # TODO: Instead of gathering internal object files from entrypoints,
475   # collect the object files with public names of entrypoints.
476   get_object_files_for_test(
477       link_object_files skipped_entrypoints_list ${fq_deps_list})
478   if(skipped_entrypoints_list)
479     if(LIBC_CMAKE_VERBOSE_LOGGING)
480       set(msg "Skipping unittest ${fq_target_name} as it has missing deps: "
481               "${skipped_entrypoints_list}.")
482       message(STATUS ${msg})
483     endif()
484     return()
485   endif()
486   list(REMOVE_DUPLICATES link_object_files)
488   # Make a library of all deps
489   add_library(
490     ${fq_target_name}.__libc__
491     STATIC
492     EXCLUDE_FROM_ALL
493     ${link_object_files}
494   )
495   set_target_properties(${fq_target_name}.__libc__
496       PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
497   set_target_properties(${fq_target_name}.__libc__
498       PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc)
500   set(fq_build_target_name ${fq_target_name}.__build__)
501   add_executable(
502     ${fq_build_target_name}
503     EXCLUDE_FROM_ALL
504     # The NVIDIA 'nvlink' linker does not currently support static libraries.
505     $<$<BOOL:${LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX}>:${link_object_files}>
506     ${INTEGRATION_TEST_SRCS}
507     ${INTEGRATION_TEST_HDRS}
508   )
509   set_target_properties(${fq_build_target_name}
510       PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
511   target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
512   target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
513   target_compile_options(${fq_build_target_name}
514       PRIVATE -fpie -ffreestanding -fno-exceptions -fno-rtti ${INTEGRATION_TEST_COMPILE_OPTIONS})
515   # The GPU build requires overriding the default CMake triple and architecture.
516   if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU)
517     target_compile_options(${fq_build_target_name} PRIVATE
518                            -nogpulib -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE}
519                            -flto --target=${LIBC_GPU_TARGET_TRIPLE}
520                            -mcode-object-version=${LIBC_GPU_CODE_OBJECT_VERSION})
521   elseif(LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
522     get_nvptx_compile_options(nvptx_options ${LIBC_GPU_TARGET_ARCHITECTURE})
523     target_compile_options(${fq_build_target_name} PRIVATE
524                            -nogpulib ${nvptx_options} -fno-use-cxa-atexit
525                            --target=${LIBC_GPU_TARGET_TRIPLE})
526   endif()
528   if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
529     target_link_options(${fq_build_target_name} PRIVATE -nostdlib -static)
530   else()
531     target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static)
532   endif()
533   target_link_libraries(
534     ${fq_build_target_name}
535     # The NVIDIA 'nvlink' linker does not currently support static libraries.
536     $<$<NOT:$<BOOL:${LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX}>>:${fq_target_name}.__libc__>
537     libc.startup.${LIBC_TARGET_OS}.crt1
538     libc.test.IntegrationTest.test)
539   add_dependencies(${fq_build_target_name}
540                    libc.test.IntegrationTest.test
541                    ${INTEGRATION_TEST_DEPENDS})
543   # Tests on the GPU require an external loader utility to launch the kernel.
544   if(TARGET libc.utils.gpu.loader)
545     add_dependencies(${fq_build_target_name} libc.utils.gpu.loader)
546     get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE")
547   endif()
549   # We have to use a separate var to store the command as a list because
550   # the COMMAND option of `add_custom_target` cannot handle empty vars in the
551   # command. For example, if INTEGRATION_TEST_ENV is empty, the actual
552   # command also will not run. So, we use this list and tell `add_custom_target`
553   # to expand the list (by including the option COMMAND_EXPAND_LISTS). This
554   # makes `add_custom_target` construct the correct command and execute it.
555   set(test_cmd
556       ${INTEGRATION_TEST_ENV}
557       $<$<BOOL:${LIBC_TARGET_ARCHITECTURE_IS_GPU}>:${gpu_loader_exe}>
558       ${CMAKE_CROSSCOMPILING_EMULATOR}
559       ${INTEGRATION_TEST_LOADER_ARGS}
560       $<TARGET_FILE:${fq_build_target_name}> ${INTEGRATION_TEST_ARGS})
561   add_custom_target(
562     ${fq_target_name}
563     COMMAND ${test_cmd}
564     COMMAND_EXPAND_LISTS
565     COMMENT "Running integration test ${fq_target_name}"
566   )
567   add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name})
568 endfunction(add_integration_test)
570 set(LIBC_HERMETIC_TEST_COMPILE_OPTIONS ${LIBC_COMPILE_OPTIONS_DEFAULT}
571     -fpie -ffreestanding -fno-exceptions -fno-rtti)
572 # The GPU build requires overriding the default CMake triple and architecture.
573 if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU)
574   list(APPEND LIBC_HERMETIC_TEST_COMPILE_OPTIONS
575        -nogpulib -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto
576        --target=${LIBC_GPU_TARGET_TRIPLE}
577        -mcode-object-version=${LIBC_GPU_CODE_OBJECT_VERSION})
578 elseif(LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
579   get_nvptx_compile_options(nvptx_options ${LIBC_GPU_TARGET_ARCHITECTURE})
580   list(APPEND LIBC_HERMETIC_TEST_COMPILE_OPTIONS
581        -nogpulib ${nvptx_options} -fno-use-cxa-atexit --target=${LIBC_GPU_TARGET_TRIPLE})
582 endif()
584 # Rule to add a hermetic test. A hermetic test is one whose executable is fully
585 # statically linked and consists of pieces drawn only from LLVM's libc. Nothing,
586 # including the startup objects, come from the system libc.
588 # Usage:
589 #   add_libc_hermetic_test(
590 #     <target name>
591 #     SUITE <the suite to which the test should belong>
592 #     SRCS <src1.cpp> [src2.cpp ...]
593 #     HDRS [hdr1.cpp ...]
594 #     DEPENDS <list of entrypoint or other object targets>
595 #     ARGS <list of command line arguments to be passed to the test>
596 #     ENV <list of environment variables to set before running the test>
597 #     COMPILE_OPTIONS <list of special compile options for the test>
598 #     LINK_LIBRARIES <list of linking libraries for this target>
599 #     LOADER_ARGS <list of special args to loaders (like the GPU loader)>
600 #   )
601 function(add_libc_hermetic_test test_name)
602   if(NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1)
603     message(VERBOSE "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.")
604     return()
605   endif()
606   cmake_parse_arguments(
607     "HERMETIC_TEST"
608     "" # No optional arguments
609     "SUITE" # Single value arguments
610     "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LINK_LIBRARIES;LOADER_ARGS" # Multi-value arguments
611     ${ARGN}
612   )
614   if(NOT HERMETIC_TEST_SUITE)
615     message(FATAL_ERROR "SUITE not specified for ${fq_target_name}")
616   endif()
617   if(NOT HERMETIC_TEST_SRCS)
618     message(FATAL_ERROR "The SRCS list for add_integration_test is missing.")
619   endif()
621   get_fq_target_name(${test_name} fq_target_name)
622   get_fq_target_name(${test_name}.libc fq_libc_target_name)
624   get_fq_deps_list(fq_deps_list ${HERMETIC_TEST_DEPENDS})
625   list(APPEND fq_deps_list
626       # Hermetic tests use the platform's startup object. So, their deps also
627       # have to be collected.
628       libc.startup.${LIBC_TARGET_OS}.crt1
629       # We always add the memory functions objects. This is because the
630       # compiler's codegen can emit calls to the C memory functions.
631       libc.src.string.bcmp
632       libc.src.string.bzero
633       libc.src.string.memcmp
634       libc.src.string.memcpy
635       libc.src.string.memmove
636       libc.src.string.memset
637       libc.src.__support.StringUtil.error_to_string
638   )
639   list(REMOVE_DUPLICATES fq_deps_list)
641   # TODO: Instead of gathering internal object files from entrypoints,
642   # collect the object files with public names of entrypoints.
643   get_object_files_for_test(
644       link_object_files skipped_entrypoints_list ${fq_deps_list})
645   if(skipped_entrypoints_list)
646     set(msg "Skipping unittest ${fq_target_name} as it has missing deps: "
647             "${skipped_entrypoints_list}.")
648     message(STATUS ${msg})
649     return()
650   endif()
651   list(REMOVE_DUPLICATES link_object_files)
653   # Make a library of all deps
654   add_library(
655     ${fq_target_name}.__libc__
656     STATIC
657     EXCLUDE_FROM_ALL
658     ${link_object_files}
659   )
660   set_target_properties(${fq_target_name}.__libc__
661       PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
662   set_target_properties(${fq_target_name}.__libc__
663       PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc)
665   set(fq_build_target_name ${fq_target_name}.__build__)
666   add_executable(
667     ${fq_build_target_name}
668     EXCLUDE_FROM_ALL
669     # The NVIDIA 'nvlink' linker does not currently support static libraries.
670     $<$<BOOL:${LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX}>:${link_object_files}>
671     ${HERMETIC_TEST_SRCS}
672     ${HERMETIC_TEST_HDRS}
673   )
674   set_target_properties(${fq_build_target_name}
675     PROPERTIES
676       RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
677       #OUTPUT_NAME ${fq_target_name}
678   )
679   target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
680   target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
681   target_compile_options(${fq_build_target_name}
682       PRIVATE ${LIBC_HERMETIC_TEST_COMPILE_OPTIONS} ${HERMETIC_TEST_COMPILE_OPTIONS})
684   set(link_libraries "")
685   foreach(lib IN LISTS HERMETIC_TEST_LINK_LIBRARIES)
686     if(TARGET ${lib}.hermetic)
687       list(APPEND link_libraries ${lib}.hermetic)
688     else()
689       list(APPEND link_libraries ${lib})
690     endif()
691   endforeach()
693   if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
694     target_link_options(${fq_build_target_name} PRIVATE -nostdlib -static)
695   else()
696     target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static)
697   endif()
698   target_link_libraries(
699     ${fq_build_target_name}
700     PRIVATE
701       libc.startup.${LIBC_TARGET_OS}.crt1
702       ${link_libraries}
703       LibcTest.hermetic
704       LibcHermeticTestSupport.hermetic
705       # The NVIDIA 'nvlink' linker does not currently support static libraries.
706       $<$<NOT:$<BOOL:${LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX}>>:${fq_target_name}.__libc__>)
707   add_dependencies(${fq_build_target_name}
708                    LibcTest.hermetic
709                    libc.test.UnitTest.ErrnoSetterMatcher
710                    ${fq_deps_list})
712   # Tests on the GPU require an external loader utility to launch the kernel.
713   if(TARGET libc.utils.gpu.loader)
714     add_dependencies(${fq_build_target_name} libc.utils.gpu.loader)
715     get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE")
716   endif()
718   set(test_cmd ${HERMETIC_TEST_ENV}
719       $<$<BOOL:${LIBC_TARGET_ARCHITECTURE_IS_GPU}>:${gpu_loader_exe}> ${CMAKE_CROSSCOMPILING_EMULATOR} ${HERMETIC_TEST_LOADER_ARGS}
720       $<TARGET_FILE:${fq_build_target_name}> ${HERMETIC_TEST_ARGS})
721   add_custom_target(
722     ${fq_target_name}
723     COMMAND ${test_cmd}
724     COMMAND_EXPAND_LISTS
725     COMMENT "Running hermetic test ${fq_target_name}"
726     ${LIBC_HERMETIC_TEST_JOB_POOL}
727   )
729   add_dependencies(${HERMETIC_TEST_SUITE} ${fq_target_name})
730   add_dependencies(libc-hermetic-tests ${fq_target_name})
731 endfunction(add_libc_hermetic_test)
733 # A convenience function to add both a unit test as well as a hermetic test.
734 function(add_libc_test test_name)
735   cmake_parse_arguments(
736     "LIBC_TEST"
737     "UNIT_TEST_ONLY;HERMETIC_TEST_ONLY" # Optional arguments
738     "" # Single value arguments
739     "" # Multi-value arguments
740     ${ARGN}
741   )
742   if(LIBC_ENABLE_UNITTESTS AND NOT LIBC_TEST_HERMETIC_TEST_ONLY)
743     add_libc_unittest(${test_name}.__unit__ ${LIBC_TEST_UNPARSED_ARGUMENTS})
744   endif()
745   if(LIBC_ENABLE_HERMETIC_TESTS AND NOT LIBC_TEST_UNIT_TEST_ONLY)
746     add_libc_hermetic_test(${test_name}.__hermetic__ ${LIBC_TEST_UNPARSED_ARGUMENTS})
747     get_fq_target_name(${test_name} fq_test_name)
748     if(TARGET ${fq_test_name}.__hermetic__ AND TARGET ${fq_test_name}.__unit__)
749       # Tests like the file tests perform file operations on disk file. If we
750       # don't chain up the unit test and hermetic test, then those tests will
751       # step on each other's files.
752       add_dependencies(${fq_test_name}.__hermetic__ ${fq_test_name}.__unit__)
753     endif()
754   endif()
755 endfunction(add_libc_test)