xref: /llvm-project/libc/cmake/modules/LLVMLibCTestRules.cmake (revision b11529bfa26fe8f2abc7d57cd96716a494198327)
1function(_get_common_test_compile_options output_var c_test flags)
2  _get_compile_options_from_flags(compile_flags ${flags})
3
4  # Remove -fno-math-errno if it was added.
5  if(LIBC_ADD_FNO_MATH_ERRNO)
6    list(REMOVE_ITEM compile_options "-fno-math-errno")
7  endif()
8
9  set(compile_options
10      ${LIBC_COMPILE_OPTIONS_DEFAULT}
11      ${LIBC_TEST_COMPILE_OPTIONS_DEFAULT}
12      ${compile_flags})
13
14  if(LLVM_LIBC_COMPILER_IS_GCC_COMPATIBLE)
15    list(APPEND compile_options "-fpie")
16
17    if(LLVM_LIBC_FULL_BUILD)
18      list(APPEND compile_options "-DLIBC_FULL_BUILD")
19      # Only add -ffreestanding flag in full build mode.
20      list(APPEND compile_options "-ffreestanding")
21      list(APPEND compile_options "-fno-exceptions")
22      list(APPEND compile_options "-fno-unwind-tables")
23      list(APPEND compile_options "-fno-asynchronous-unwind-tables")
24      if(NOT c_test)
25        list(APPEND compile_options "-fno-rtti")
26      endif()
27    endif()
28
29    if(LIBC_COMPILER_HAS_FIXED_POINT)
30      list(APPEND compile_options "-ffixed-point")
31    endif()
32
33    # list(APPEND compile_options "-Wall")
34    # list(APPEND compile_options "-Wextra")
35    # -DLIBC_WNO_ERROR=ON if you can't build cleanly with -Werror.
36    if(NOT LIBC_WNO_ERROR)
37      # list(APPEND compile_options "-Werror")
38    endif()
39    # list(APPEND compile_options "-Wconversion")
40    # list(APPEND compile_options "-Wno-sign-conversion")
41    # list(APPEND compile_options "-Wimplicit-fallthrough")
42    # list(APPEND compile_options "-Wwrite-strings")
43    list(APPEND compile_options "-Wextra-semi")
44    # Silence this warning because _Complex is a part of C99.
45    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
46      if(NOT c_test)
47        list(APPEND compile_options "-fext-numeric-literals")
48      endif()
49    else()
50      list(APPEND compile_options "-Wno-c99-extensions")
51      list(APPEND compile_options "-Wno-gnu-imaginary-constant")
52    endif()
53    list(APPEND compile_options "-Wno-pedantic")
54    # if(NOT CMAKE_COMPILER_IS_GNUCXX)
55    #   list(APPEND compile_options "-Wnewline-eof")
56    #   list(APPEND compile_options "-Wnonportable-system-include-path")
57    #   list(APPEND compile_options "-Wstrict-prototypes")
58    #   list(APPEND compile_options "-Wthread-safety")
59    #   list(APPEND compile_options "-Wglobal-constructors")
60    # endif()
61  endif()
62  set(${output_var} ${compile_options} PARENT_SCOPE)
63endfunction()
64
65function(_get_hermetic_test_compile_options output_var)
66  _get_common_test_compile_options(compile_options "" "")
67
68  # The GPU build requires overriding the default CMake triple and architecture.
69  if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU)
70    list(APPEND compile_options
71         -Wno-multi-gpu -nogpulib -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto
72         -mcode-object-version=${LIBC_GPU_CODE_OBJECT_VERSION})
73  elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX)
74    list(APPEND compile_options
75         "SHELL:-mllvm -nvptx-emit-init-fini-kernel=false"
76         -Wno-multi-gpu --cuda-path=${LIBC_CUDA_ROOT}
77         -nogpulib -march=${LIBC_GPU_TARGET_ARCHITECTURE} -fno-use-cxa-atexit)
78  endif()
79
80  set(${output_var} ${compile_options} PARENT_SCOPE)
81endfunction()
82
83# This is a helper function and not a build rule. It is to be used by the
84# various test rules to generate the full list of object files
85# recursively produced by "add_entrypoint_object" and "add_object_library"
86# targets.
87# Usage:
88#   get_object_files_for_test(<result var>
89#                             <skipped_entrypoints_var>
90#                             <target0> [<target1> ...])
91#
92#   The list of object files is collected in <result_var>.
93#   If skipped entrypoints were found, then <skipped_entrypoints_var> is
94#   set to a true value.
95#   targetN is either an "add_entrypoint_target" target or an
96#   "add_object_library" target.
97function(get_object_files_for_test result skipped_entrypoints_list)
98  set(object_files "")
99  set(skipped_list "")
100  set(checked_list "")
101  set(unchecked_list "${ARGN}")
102  list(REMOVE_DUPLICATES unchecked_list)
103
104  foreach(dep IN LISTS unchecked_list)
105    if (NOT TARGET ${dep})
106      # Skip tests with undefined dependencies.
107      # Compiler-RT targets are added only if they are enabled. However, such targets may not be defined
108      # at the time of the libc build. We should skip checking such targets.
109      if (NOT ${dep} MATCHES "^RTScudo.*|^RTGwp.*")
110        list(APPEND skipped_list ${dep})
111      endif()
112      continue()
113    endif()
114    get_target_property(aliased_target ${dep} "ALIASED_TARGET")
115    if(aliased_target)
116      # If the target is just an alias, switch to the real target.
117      set(dep ${aliased_target})
118    endif()
119
120    get_target_property(dep_type ${dep} "TARGET_TYPE")
121    if(NOT dep_type)
122      # Skip tests with no object dependencies.
123      continue()
124    endif()
125
126    get_target_property(dep_checked ${dep} "CHECK_OBJ_FOR_TESTS")
127
128    if(dep_checked)
129      # Target full dependency has already been checked.  Just use the results.
130      get_target_property(dep_obj ${dep} "OBJECT_FILES_FOR_TESTS")
131      get_target_property(dep_skip ${dep} "SKIPPED_LIST_FOR_TESTS")
132    else()
133      # Target full dependency hasn't been checked.  Recursively check its DEPS.
134      set(dep_obj "${dep}")
135      set(dep_skip "")
136
137      get_target_property(indirect_deps ${dep} "DEPS")
138      get_object_files_for_test(dep_obj dep_skip ${indirect_deps})
139
140      if(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})
141        get_target_property(dep_object_files ${dep} "OBJECT_FILES")
142        if(dep_object_files)
143          list(APPEND dep_obj ${dep_object_files})
144        endif()
145      elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})
146        get_target_property(is_skipped ${dep} "SKIPPED")
147        if(is_skipped)
148          list(APPEND dep_skip ${dep})
149          list(REMOVE_ITEM dep_obj ${dep})
150        endif()
151        get_target_property(object_file_raw ${dep} "OBJECT_FILE_RAW")
152        if(object_file_raw)
153          # TODO: Remove this once we stop suffixing the target with ".__internal__"
154          if(fq_target_name STREQUAL "libc.test.include.issignaling_c_test.__unit__" OR fq_target_name STREQUAL "libc.test.include.iscanonical_c_test.__unit__")
155            string(REPLACE ".__internal__" "" object_file_raw ${object_file_raw})
156          endif()
157          list(APPEND dep_obj ${object_file_raw})
158        endif()
159      elseif(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})
160        # Skip tests for externally implemented entrypoints.
161        list(APPEND dep_skip ${dep})
162        list(REMOVE_ITEM dep_obj ${dep})
163      endif()
164
165      set_target_properties(${dep} PROPERTIES
166        OBJECT_FILES_FOR_TESTS "${dep_obj}"
167        SKIPPED_LIST_FOR_TESTS "${dep_skip}"
168        CHECK_OBJ_FOR_TESTS "YES"
169      )
170
171    endif()
172
173    list(APPEND object_files ${dep_obj})
174    list(APPEND skipped_list ${dep_skip})
175
176  endforeach(dep)
177
178  list(REMOVE_DUPLICATES object_files)
179  set(${result} ${object_files} PARENT_SCOPE)
180  list(REMOVE_DUPLICATES skipped_list)
181  set(${skipped_entrypoints_list} ${skipped_list} PARENT_SCOPE)
182
183endfunction(get_object_files_for_test)
184
185# Rule to add a libc unittest.
186# Usage
187#    add_libc_unittest(
188#      <target name>
189#      SUITE <name of the suite this test belongs to>
190#      SRCS  <list of .cpp files for the test>
191#      HDRS  <list of .h files for the test>
192#      DEPENDS <list of dependencies>
193#      COMPILE_OPTIONS <list of special compile options for this target>
194#      LINK_LIBRARIES <list of linking libraries for this target>
195#    )
196function(create_libc_unittest fq_target_name)
197  if(NOT LLVM_INCLUDE_TESTS)
198    return()
199  endif()
200
201  cmake_parse_arguments(
202    "LIBC_UNITTEST"
203    "NO_RUN_POSTBUILD;C_TEST" # Optional arguments
204    "SUITE;CXX_STANDARD" # Single value arguments
205    "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;LINK_LIBRARIES;FLAGS" # Multi-value arguments
206    ${ARGN}
207  )
208  if(NOT LIBC_UNITTEST_SRCS)
209    message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp "
210                        "files.")
211  endif()
212  if(NOT LIBC_UNITTEST_DEPENDS)
213    message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of "
214                        "'add_entrypoint_object' targets.")
215  endif()
216
217  get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS})
218  if(NOT LIBC_UNITTEST_C_TEST)
219    list(APPEND fq_deps_list libc.src.__support.StringUtil.error_to_string
220                             libc.test.UnitTest.ErrnoSetterMatcher)
221  endif()
222  list(REMOVE_DUPLICATES fq_deps_list)
223
224  _get_common_test_compile_options(compile_options "${LIBC_UNITTEST_C_TEST}"
225                                   "${LIBC_UNITTEST_FLAGS}")
226  list(APPEND compile_options ${LIBC_UNITTEST_COMPILE_OPTIONS})
227
228  if(SHOW_INTERMEDIATE_OBJECTS)
229    message(STATUS "Adding unit test ${fq_target_name}")
230    if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
231      foreach(dep IN LISTS LIBC_UNITTEST_DEPENDS)
232        message(STATUS "  ${fq_target_name} depends on ${dep}")
233      endforeach()
234    endif()
235  endif()
236
237  get_object_files_for_test(
238      link_object_files skipped_entrypoints_list ${fq_deps_list})
239  if(skipped_entrypoints_list)
240    # If a test is OS/target machine independent, it has to be skipped if the
241    # OS/target machine combination does not provide any dependent entrypoints.
242    # If a test is OS/target machine specific, then such a test will live is a
243    # OS/target machine specific directory and will be skipped at the directory
244    # level if required.
245    #
246    # There can potentially be a setup like this: A unittest is setup for a
247    # OS/target machine independent object library, which in turn depends on a
248    # machine specific object library. Such a test would be testing internals of
249    # the libc and it is assumed that they will be rare in practice. So, they
250    # can be skipped in the corresponding CMake files using platform specific
251    # logic. This pattern is followed in the startup tests for example.
252    #
253    # Another pattern that is present currently is to detect machine
254    # capabilities and add entrypoints and tests accordingly. That approach is
255    # much lower level approach and is independent of the kind of skipping that
256    # is happening here at the entrypoint level.
257    if(LIBC_CMAKE_VERBOSE_LOGGING)
258      set(msg "Skipping unittest ${fq_target_name} as it has missing deps: "
259              "${skipped_entrypoints_list}.")
260      message(STATUS ${msg})
261    endif()
262    return()
263  endif()
264
265  if(LIBC_UNITTEST_NO_RUN_POSTBUILD)
266    set(fq_build_target_name ${fq_target_name})
267  else()
268    set(fq_build_target_name ${fq_target_name}.__build__)
269  endif()
270
271  add_executable(
272    ${fq_build_target_name}
273    EXCLUDE_FROM_ALL
274    ${LIBC_UNITTEST_SRCS}
275    ${LIBC_UNITTEST_HDRS}
276  )
277  target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
278  target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
279  target_compile_options(${fq_build_target_name} PRIVATE ${compile_options})
280  target_link_options(${fq_build_target_name} PRIVATE ${compile_options})
281
282  if(NOT LIBC_UNITTEST_CXX_STANDARD)
283    set(LIBC_UNITTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD})
284  endif()
285  set_target_properties(
286    ${fq_build_target_name}
287    PROPERTIES
288      CXX_STANDARD ${LIBC_UNITTEST_CXX_STANDARD}
289  )
290
291  set(link_libraries ${link_object_files})
292  # Test object files will depend on LINK_LIBRARIES passed down from `add_fp_unittest`
293  foreach(lib IN LISTS LIBC_UNITTEST_LINK_LIBRARIES)
294    if(TARGET ${lib}.unit)
295      list(APPEND link_libraries ${lib}.unit)
296    else()
297      list(APPEND link_libraries ${lib})
298    endif()
299  endforeach()
300
301  set_target_properties(${fq_build_target_name}
302    PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
303
304  add_dependencies(
305    ${fq_build_target_name}
306    ${fq_deps_list}
307  )
308
309  # LibcUnitTest should not depend on anything in LINK_LIBRARIES.
310  if(NOT LIBC_UNITTEST_C_TEST)
311    list(APPEND link_libraries LibcDeathTestExecutors.unit LibcTest.unit)
312  endif()
313
314  target_link_libraries(${fq_build_target_name} PRIVATE ${link_libraries})
315
316  if(NOT LIBC_UNITTEST_NO_RUN_POSTBUILD)
317    add_custom_target(
318      ${fq_target_name}
319      COMMAND ${fq_build_target_name}
320      COMMENT "Running unit test ${fq_target_name}"
321    )
322  endif()
323
324  if(LIBC_UNITTEST_SUITE)
325    add_dependencies(
326      ${LIBC_UNITTEST_SUITE}
327      ${fq_target_name}
328    )
329  endif()
330  add_dependencies(libc-unit-tests ${fq_target_name})
331endfunction(create_libc_unittest)
332
333function(add_libc_unittest target_name)
334  add_target_with_flags(
335    ${target_name}
336    CREATE_TARGET create_libc_unittest
337    ${ARGN}
338  )
339endfunction(add_libc_unittest)
340
341function(add_libc_exhaustive_testsuite suite_name)
342  add_custom_target(${suite_name})
343  add_dependencies(exhaustive-check-libc ${suite_name})
344endfunction(add_libc_exhaustive_testsuite)
345
346function(add_libc_long_running_testsuite suite_name)
347  add_custom_target(${suite_name})
348  add_dependencies(libc-long-running-tests ${suite_name})
349endfunction(add_libc_long_running_testsuite)
350
351# Rule to add a fuzzer test.
352# Usage
353#    add_libc_fuzzer(
354#      <target name>
355#      SRCS  <list of .cpp files for the test>
356#      HDRS  <list of .h files for the test>
357#      DEPENDS <list of dependencies>
358#    )
359function(add_libc_fuzzer target_name)
360  cmake_parse_arguments(
361    "LIBC_FUZZER"
362    "NEED_MPFR" # Optional arguments
363    "" # Single value arguments
364    "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
365    ${ARGN}
366  )
367  if(NOT LIBC_FUZZER_SRCS)
368    message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp "
369                        "files.")
370  endif()
371  if(NOT LIBC_FUZZER_DEPENDS)
372    message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of "
373                        "'add_entrypoint_object' targets.")
374  endif()
375
376  list(APPEND LIBC_FUZZER_LINK_LIBRARIES "")
377  if(LIBC_FUZZER_NEED_MPFR)
378    if(NOT LIBC_TESTS_CAN_USE_MPFR)
379      message(VERBOSE "Fuzz test ${name} will be skipped as MPFR library is not available.")
380      return()
381    endif()
382    list(APPEND LIBC_FUZZER_LINK_LIBRARIES mpfr gmp)
383  endif()
384
385
386  get_fq_target_name(${target_name} fq_target_name)
387  get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS})
388  get_object_files_for_test(
389      link_object_files skipped_entrypoints_list ${fq_deps_list})
390  if(skipped_entrypoints_list)
391    if(LIBC_CMAKE_VERBOSE_LOGGING)
392      set(msg "Skipping fuzzer target ${fq_target_name} as it has missing deps: "
393              "${skipped_entrypoints_list}.")
394      message(STATUS ${msg})
395    endif()
396    add_custom_target(${fq_target_name})
397
398    # A post build custom command is used to avoid running the command always.
399    add_custom_command(
400      TARGET ${fq_target_name}
401      POST_BUILD
402      COMMAND ${CMAKE_COMMAND} -E echo ${msg}
403    )
404    return()
405  endif()
406
407  add_executable(
408    ${fq_target_name}
409    EXCLUDE_FROM_ALL
410    ${LIBC_FUZZER_SRCS}
411    ${LIBC_FUZZER_HDRS}
412  )
413  target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
414  target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
415
416  target_link_libraries(${fq_target_name} PRIVATE
417    ${link_object_files}
418    ${LIBC_FUZZER_LINK_LIBRARIES}
419  )
420
421  set_target_properties(${fq_target_name}
422      PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
423
424  add_dependencies(
425    ${fq_target_name}
426    ${fq_deps_list}
427  )
428  add_dependencies(libc-fuzzer ${fq_target_name})
429
430  target_compile_options(${fq_target_name}
431    PRIVATE
432    ${LIBC_FUZZER_COMPILE_OPTIONS})
433
434endfunction(add_libc_fuzzer)
435
436# Get libgcc_s to be used in hermetic and integration tests.
437if(NOT MSVC AND NOT LIBC_CC_SUPPORTS_NOSTDLIBPP)
438  execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libgcc_s.so.1
439                  OUTPUT_VARIABLE LIBGCC_S_LOCATION)
440  string(STRIP ${LIBGCC_S_LOCATION} LIBGCC_S_LOCATION)
441endif()
442
443# DEPRECATED: Use add_hermetic_test instead.
444#
445# Rule to add an integration test. An integration test is like a unit test
446# but does not use the system libc. Not even the startup objects from the
447# system libc are linked in to the final executable. The final exe is fully
448# statically linked. The libc that the final exe links to consists of only
449# the object files of the DEPENDS targets.
450#
451# Usage:
452#   add_integration_test(
453#     <target name>
454#     SUITE <the suite to which the test should belong>
455#     SRCS <src1.cpp> [src2.cpp ...]
456#     HDRS [hdr1.cpp ...]
457#     DEPENDS <list of entrypoint or other object targets>
458#     ARGS <list of command line arguments to be passed to the test>
459#     ENV <list of environment variables to set before running the test>
460#     COMPILE_OPTIONS <list of special compile options for this target>
461#   )
462#
463# The DEPENDS list can be empty. If not empty, it should be a list of
464# targets added with add_entrypoint_object or add_object_library.
465function(add_integration_test test_name)
466  get_fq_target_name(${test_name} fq_target_name)
467  set(supported_targets gpu linux)
468  if(NOT (${LIBC_TARGET_OS} IN_LIST supported_targets))
469    message(STATUS "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.")
470    return()
471  endif()
472  cmake_parse_arguments(
473    "INTEGRATION_TEST"
474    "" # No optional arguments
475    "SUITE" # Single value arguments
476    "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LOADER_ARGS" # Multi-value arguments
477    ${ARGN}
478  )
479
480  if(NOT INTEGRATION_TEST_SUITE)
481    message(FATAL_ERROR "SUITE not specified for ${fq_target_name}")
482  endif()
483  if(NOT INTEGRATION_TEST_SRCS)
484    message(FATAL_ERROR "The SRCS list for add_integration_test is missing.")
485  endif()
486  if(NOT LLVM_LIBC_FULL_BUILD AND NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1)
487    message(FATAL_ERROR "The 'crt1' target for the integration test is missing.")
488  endif()
489
490  get_fq_target_name(${test_name}.libc fq_libc_target_name)
491
492  get_fq_deps_list(fq_deps_list ${INTEGRATION_TEST_DEPENDS})
493  list(APPEND fq_deps_list
494      # All integration tests use the operating system's startup object with the
495      # integration test object and need to inherit the same dependencies.
496      libc.startup.${LIBC_TARGET_OS}.crt1
497      libc.test.IntegrationTest.test
498      # We always add the memory functions objects. This is because the
499      # compiler's codegen can emit calls to the C memory functions.
500      libc.src.string.memcmp
501      libc.src.string.memcpy
502      libc.src.string.memmove
503      libc.src.string.memset
504      libc.src.strings.bcmp
505      libc.src.strings.bzero
506  )
507
508  if(libc.src.compiler.__stack_chk_fail IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
509    # __stack_chk_fail should always be included if supported to allow building
510    # libc with the stack protector enabled.
511    list(APPEND fq_deps_list libc.src.compiler.__stack_chk_fail)
512  endif()
513
514  list(REMOVE_DUPLICATES fq_deps_list)
515
516  # TODO: Instead of gathering internal object files from entrypoints,
517  # collect the object files with public names of entrypoints.
518  get_object_files_for_test(
519      link_object_files skipped_entrypoints_list ${fq_deps_list})
520  if(skipped_entrypoints_list)
521    if(LIBC_CMAKE_VERBOSE_LOGGING)
522      set(msg "Skipping integration test ${fq_target_name} as it has missing deps: "
523              "${skipped_entrypoints_list}.")
524      message(STATUS ${msg})
525    endif()
526    return()
527  endif()
528  list(REMOVE_DUPLICATES link_object_files)
529
530  # Make a library of all deps
531  add_library(
532    ${fq_target_name}.__libc__
533    STATIC
534    EXCLUDE_FROM_ALL
535    ${link_object_files}
536  )
537  set_target_properties(${fq_target_name}.__libc__
538      PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
539  set_target_properties(${fq_target_name}.__libc__
540      PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc)
541
542  set(fq_build_target_name ${fq_target_name}.__build__)
543  add_executable(
544    ${fq_build_target_name}
545    EXCLUDE_FROM_ALL
546    ${INTEGRATION_TEST_SRCS}
547    ${INTEGRATION_TEST_HDRS}
548  )
549  set_target_properties(${fq_build_target_name}
550      PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
551  target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
552  target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
553
554  _get_hermetic_test_compile_options(compile_options "")
555  target_compile_options(${fq_build_target_name} PRIVATE
556                         ${compile_options} ${INTEGRATION_TEST_COMPILE_OPTIONS})
557
558  if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU)
559    target_link_options(${fq_build_target_name} PRIVATE
560      ${LIBC_COMPILE_OPTIONS_DEFAULT} ${INTEGRATION_TEST_COMPILE_OPTIONS}
561      -Wno-multi-gpu -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto
562      "-Wl,-mllvm,-amdgpu-lower-global-ctor-dtor=0" -nostdlib -static
563      "-Wl,-mllvm,-amdhsa-code-object-version=${LIBC_GPU_CODE_OBJECT_VERSION}")
564  elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX)
565    target_link_options(${fq_build_target_name} PRIVATE
566      ${LIBC_COMPILE_OPTIONS_DEFAULT} ${INTEGRATION_TEST_COMPILE_OPTIONS}
567      "-Wl,--suppress-stack-size-warning" -Wno-multi-gpu
568      "-Wl,-mllvm,-nvptx-lower-global-ctor-dtor=1"
569      "-Wl,-mllvm,-nvptx-emit-init-fini-kernel"
570      -march=${LIBC_GPU_TARGET_ARCHITECTURE} -nostdlib -static
571      "--cuda-path=${LIBC_CUDA_ROOT}")
572  elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP)
573    target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static)
574  else()
575    # Older version of gcc does not support `nostdlib++` flag.  We use
576    # `nostdlib` and link against libgcc_s, which cannot be linked statically.
577    target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib)
578    list(APPEND link_libraries ${LIBGCC_S_LOCATION})
579  endif()
580  target_link_libraries(
581    ${fq_build_target_name}
582    ${fq_target_name}.__libc__
583    libc.startup.${LIBC_TARGET_OS}.crt1
584    libc.test.IntegrationTest.test
585  )
586  add_dependencies(${fq_build_target_name}
587                   libc.test.IntegrationTest.test
588                   ${INTEGRATION_TEST_DEPENDS})
589
590  # Tests on the GPU require an external loader utility to launch the kernel.
591  if(TARGET libc.utils.gpu.loader)
592    add_dependencies(${fq_build_target_name} libc.utils.gpu.loader)
593    get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE")
594  endif()
595
596  # We have to use a separate var to store the command as a list because
597  # the COMMAND option of `add_custom_target` cannot handle empty vars in the
598  # command. For example, if INTEGRATION_TEST_ENV is empty, the actual
599  # command also will not run. So, we use this list and tell `add_custom_target`
600  # to expand the list (by including the option COMMAND_EXPAND_LISTS). This
601  # makes `add_custom_target` construct the correct command and execute it.
602  set(test_cmd
603      ${INTEGRATION_TEST_ENV}
604      $<$<BOOL:${LIBC_TARGET_OS_IS_GPU}>:${gpu_loader_exe}>
605      ${CMAKE_CROSSCOMPILING_EMULATOR}
606      ${INTEGRATION_TEST_LOADER_ARGS}
607      $<TARGET_FILE:${fq_build_target_name}> ${INTEGRATION_TEST_ARGS})
608  add_custom_target(
609    ${fq_target_name}
610    COMMAND ${test_cmd}
611    COMMAND_EXPAND_LISTS
612    COMMENT "Running integration test ${fq_target_name}"
613  )
614  add_dependencies(${INTEGRATION_TEST_SUITE} ${fq_target_name})
615endfunction(add_integration_test)
616
617# Rule to add a hermetic program. A hermetic program is one whose executable is fully
618# statically linked and consists of pieces drawn only from LLVM's libc. Nothing,
619# including the startup objects, come from the system libc.
620#
621# For the GPU, these can be either tests or benchmarks, depending on the value
622# of the LINK_LIBRARIES arg.
623#
624# Usage:
625#   add_libc_hermetic(
626#     <target name>
627#     SUITE <the suite to which the test should belong>
628#     SRCS <src1.cpp> [src2.cpp ...]
629#     HDRS [hdr1.cpp ...]
630#     DEPENDS <list of entrypoint or other object targets>
631#     ARGS <list of command line arguments to be passed to the test>
632#     ENV <list of environment variables to set before running the test>
633#     COMPILE_OPTIONS <list of special compile options for the test>
634#     LINK_LIBRARIES <list of linking libraries for this target>
635#     LOADER_ARGS <list of special args to loaders (like the GPU loader)>
636#   )
637function(add_libc_hermetic test_name)
638  if(NOT TARGET libc.startup.${LIBC_TARGET_OS}.crt1)
639    message(VERBOSE "Skipping ${fq_target_name} as it is not available on ${LIBC_TARGET_OS}.")
640    return()
641  endif()
642  cmake_parse_arguments(
643    "HERMETIC_TEST"
644    "IS_GPU_BENCHMARK" # Optional arguments
645    "SUITE" # Single value arguments
646    "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS;LINK_LIBRARIES;LOADER_ARGS" # Multi-value arguments
647    ${ARGN}
648  )
649
650  if(NOT HERMETIC_TEST_SUITE)
651    message(FATAL_ERROR "SUITE not specified for ${fq_target_name}")
652  endif()
653  if(NOT HERMETIC_TEST_SRCS)
654    message(FATAL_ERROR "The SRCS list for add_integration_test is missing.")
655  endif()
656
657  get_fq_target_name(${test_name} fq_target_name)
658  get_fq_target_name(${test_name}.libc fq_libc_target_name)
659
660  get_fq_deps_list(fq_deps_list ${HERMETIC_TEST_DEPENDS})
661  list(APPEND fq_deps_list
662      # Hermetic tests use the platform's startup object. So, their deps also
663      # have to be collected.
664      libc.startup.${LIBC_TARGET_OS}.crt1
665      # We always add the memory functions objects. This is because the
666      # compiler's codegen can emit calls to the C memory functions.
667      libc.src.__support.StringUtil.error_to_string
668      libc.src.string.memcmp
669      libc.src.string.memcpy
670      libc.src.string.memmove
671      libc.src.string.memset
672      libc.src.strings.bcmp
673      libc.src.strings.bzero
674  )
675
676  if(libc.src.compiler.__stack_chk_fail IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
677    # __stack_chk_fail should always be included if supported to allow building
678    # libc with the stack protector enabled.
679    list(APPEND fq_deps_list libc.src.compiler.__stack_chk_fail)
680  endif()
681
682  if(libc.src.time.clock IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
683    # We will link in the 'clock' implementation if it exists for test timing.
684    list(APPEND fq_deps_list libc.src.time.clock)
685  endif()
686
687  list(REMOVE_DUPLICATES fq_deps_list)
688
689  # TODO: Instead of gathering internal object files from entrypoints,
690  # collect the object files with public names of entrypoints.
691  get_object_files_for_test(
692      link_object_files skipped_entrypoints_list ${fq_deps_list})
693  if(skipped_entrypoints_list)
694    if(LIBC_CMAKE_VERBOSE_LOGGING)
695      set(msg "Skipping hermetic test ${fq_target_name} as it has missing deps: "
696              "${skipped_entrypoints_list}.")
697      message(STATUS ${msg})
698    endif()
699    return()
700  endif()
701  list(REMOVE_DUPLICATES link_object_files)
702
703  # Make a library of all deps
704  add_library(
705    ${fq_target_name}.__libc__
706    STATIC
707    EXCLUDE_FROM_ALL
708    ${link_object_files}
709  )
710  set_target_properties(${fq_target_name}.__libc__
711      PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
712  set_target_properties(${fq_target_name}.__libc__
713      PROPERTIES ARCHIVE_OUTPUT_NAME ${fq_target_name}.libc)
714
715  set(fq_build_target_name ${fq_target_name}.__build__)
716  add_executable(
717    ${fq_build_target_name}
718    EXCLUDE_FROM_ALL
719    ${HERMETIC_TEST_SRCS}
720    ${HERMETIC_TEST_HDRS}
721  )
722  set_target_properties(${fq_build_target_name}
723    PROPERTIES
724      RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
725      #OUTPUT_NAME ${fq_target_name}
726  )
727
728  target_include_directories(${fq_build_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
729  target_include_directories(${fq_build_target_name} PRIVATE ${LIBC_SOURCE_DIR})
730  _get_hermetic_test_compile_options(compile_options "")
731  target_compile_options(${fq_build_target_name} PRIVATE
732                         ${compile_options}
733                         ${HERMETIC_TEST_COMPILE_OPTIONS})
734
735  set(link_libraries "")
736  foreach(lib IN LISTS HERMETIC_TEST_LINK_LIBRARIES)
737    if(TARGET ${lib}.hermetic)
738      list(APPEND link_libraries ${lib}.hermetic)
739    else()
740      list(APPEND link_libraries ${lib})
741    endif()
742  endforeach()
743
744  if(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU)
745    target_link_options(${fq_build_target_name} PRIVATE
746      ${LIBC_COMPILE_OPTIONS_DEFAULT} -Wno-multi-gpu
747      -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto
748      "-Wl,-mllvm,-amdgpu-lower-global-ctor-dtor=0" -nostdlib -static
749      "-Wl,-mllvm,-amdhsa-code-object-version=${LIBC_GPU_CODE_OBJECT_VERSION}")
750  elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX)
751    target_link_options(${fq_build_target_name} PRIVATE
752      ${LIBC_COMPILE_OPTIONS_DEFAULT} -Wno-multi-gpu
753      "-Wl,--suppress-stack-size-warning"
754      "-Wl,-mllvm,-nvptx-lower-global-ctor-dtor=1"
755      "-Wl,-mllvm,-nvptx-emit-init-fini-kernel"
756      -march=${LIBC_GPU_TARGET_ARCHITECTURE} -nostdlib -static
757      "--cuda-path=${LIBC_CUDA_ROOT}")
758  elseif(LIBC_CC_SUPPORTS_NOSTDLIBPP)
759    target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib++ -static)
760  else()
761    # Older version of gcc does not support `nostdlib++` flag.  We use
762    # `nostdlib` and link against libgcc_s, which cannot be linked statically.
763    target_link_options(${fq_build_target_name} PRIVATE -nolibc -nostartfiles -nostdlib)
764    list(APPEND link_libraries ${LIBGCC_S_LOCATION})
765  endif()
766  target_link_libraries(
767    ${fq_build_target_name}
768    PRIVATE
769      libc.startup.${LIBC_TARGET_OS}.crt1
770      ${link_libraries}
771      LibcHermeticTestSupport.hermetic
772      ${fq_target_name}.__libc__)
773  add_dependencies(${fq_build_target_name}
774                   LibcTest.hermetic
775                   libc.test.UnitTest.ErrnoSetterMatcher
776                   ${fq_deps_list})
777  # TODO: currently the dependency chain is broken such that getauxval cannot properly
778  # propagate to hermetic tests. This is a temporary workaround.
779  if (LIBC_TARGET_ARCHITECTURE_IS_AARCH64)
780    target_link_libraries(
781      ${fq_build_target_name}
782      PRIVATE
783        libc.src.sys.auxv.getauxval
784    )
785  endif()
786
787  # Tests on the GPU require an external loader utility to launch the kernel.
788  if(TARGET libc.utils.gpu.loader)
789    add_dependencies(${fq_build_target_name} libc.utils.gpu.loader)
790    get_target_property(gpu_loader_exe libc.utils.gpu.loader "EXECUTABLE")
791  endif()
792
793  set(test_cmd ${HERMETIC_TEST_ENV}
794      $<$<BOOL:${LIBC_TARGET_OS_IS_GPU}>:${gpu_loader_exe}> ${CMAKE_CROSSCOMPILING_EMULATOR} ${HERMETIC_TEST_LOADER_ARGS}
795      $<TARGET_FILE:${fq_build_target_name}> ${HERMETIC_TEST_ARGS})
796  add_custom_target(
797    ${fq_target_name}
798    DEPENDS ${fq_target_name}-cmd
799  )
800
801  add_custom_command(
802    OUTPUT ${fq_target_name}-cmd
803    COMMAND ${test_cmd}
804    COMMAND_EXPAND_LISTS
805    COMMENT "Running hermetic test ${fq_target_name}"
806    ${LIBC_HERMETIC_TEST_JOB_POOL}
807  )
808
809  set_source_files_properties(${fq_target_name}-cmd
810    PROPERTIES
811      SYMBOLIC "TRUE"
812  )
813
814  add_dependencies(${HERMETIC_TEST_SUITE} ${fq_target_name})
815  if(NOT ${HERMETIC_TEST_IS_GPU_BENCHMARK})
816    # If it is a benchmark, it will already have been added to the
817    # gpu-benchmark target
818    add_dependencies(libc-hermetic-tests ${fq_target_name})
819  endif()
820endfunction(add_libc_hermetic)
821
822# A convenience function to add both a unit test as well as a hermetic test.
823function(add_libc_test test_name)
824  cmake_parse_arguments(
825    "LIBC_TEST"
826    "UNIT_TEST_ONLY;HERMETIC_TEST_ONLY" # Optional arguments
827    "" # Single value arguments
828    "" # Multi-value arguments
829    ${ARGN}
830  )
831  if(LIBC_ENABLE_UNITTESTS AND NOT LIBC_TEST_HERMETIC_TEST_ONLY)
832    add_libc_unittest(${test_name}.__unit__ ${LIBC_TEST_UNPARSED_ARGUMENTS})
833  endif()
834  if(LIBC_ENABLE_HERMETIC_TESTS AND NOT LIBC_TEST_UNIT_TEST_ONLY)
835    add_libc_hermetic(
836      ${test_name}.__hermetic__
837      LINK_LIBRARIES
838        LibcTest.hermetic
839      ${LIBC_TEST_UNPARSED_ARGUMENTS}
840    )
841    get_fq_target_name(${test_name} fq_test_name)
842    if(TARGET ${fq_test_name}.__hermetic__ AND TARGET ${fq_test_name}.__unit__)
843      # Tests like the file tests perform file operations on disk file. If we
844      # don't chain up the unit test and hermetic test, then those tests will
845      # step on each other's files.
846      add_dependencies(${fq_test_name}.__hermetic__ ${fq_test_name}.__unit__)
847    endif()
848  endif()
849endfunction(add_libc_test)
850
851# Tests all implementations that can run on the target CPU.
852function(add_libc_multi_impl_test name suite)
853  get_property(fq_implementations GLOBAL PROPERTY ${name}_implementations)
854  foreach(fq_config_name IN LISTS fq_implementations)
855    get_target_property(required_cpu_features ${fq_config_name} REQUIRE_CPU_FEATURES)
856    cpu_supports(can_run "${required_cpu_features}")
857    if(can_run)
858      string(FIND ${fq_config_name} "." last_dot_loc REVERSE)
859      math(EXPR name_loc "${last_dot_loc} + 1")
860      string(SUBSTRING ${fq_config_name} ${name_loc} -1 target_name)
861      add_libc_test(
862        ${target_name}_test
863        SUITE
864          ${suite}
865        COMPILE_OPTIONS
866          ${LIBC_COMPILE_OPTIONS_NATIVE}
867        LINK_LIBRARIES
868          LibcMemoryHelpers
869        ${ARGN}
870        DEPENDS
871          ${fq_config_name}
872          libc.src.__support.macros.sanitizer
873      )
874      get_fq_target_name(${fq_config_name}_test fq_target_name)
875    else()
876      message(STATUS "Skipping test for '${fq_config_name}' insufficient host cpu features '${required_cpu_features}'")
877    endif()
878  endforeach()
879endfunction()
880