[libc] Add cmake target for linting libc.
[lldb.git] / libc / cmake / modules / LLVMLibCRules.cmake
1 include(LLVMLibCTargetNameUtils)
2
3 # A rule for self contained header file targets.
4 # This rule merely copies the header file from the current source directory to
5 # the current binary directory.
6 # Usage:
7 #     add_header(
8 #       <target name>
9 #       HDR <header file>
10 #     )
11 function(add_header target_name)
12   cmake_parse_arguments(
13     "ADD_HEADER"
14     ""    # No optional arguments
15     "HDR" # Single value arguments
16     "DEPENDS"
17     ${ARGN}
18   )
19   if(NOT ADD_HEADER_HDR)
20     message(FATAL_ERROR "'add_header' rules requires the HDR argument specifying a headef file.")
21   endif()
22
23   set(dest_file ${CMAKE_CURRENT_BINARY_DIR}/${ADD_HEADER_HDR})
24   set(src_file ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_HEADER_HDR})
25
26   add_custom_command(
27     OUTPUT ${dest_file}
28     COMMAND cp ${src_file} ${dest_file}
29     DEPENDS ${src_file}
30   )
31
32   get_fq_target_name(${target_name} fq_target_name)
33   add_custom_target(
34     ${fq_target_name}
35     DEPENDS ${dest_file}
36   )
37
38   if(ADD_HEADER_DEPENDS)
39     get_fq_deps_list(fq_deps_list ${ADD_HEADER_DEPENDS})
40     add_dependencies(
41       ${fq_target_name} ${fq_deps_list}
42     )
43   endif()
44 endfunction(add_header)
45
46 # A rule for generated header file targets.
47 # Usage:
48 #     add_gen_header(
49 #       <target name>
50 #       DEF_FILE <.h.def file>
51 #       GEN_HDR <generated header file name>
52 #       PARAMS <list of name=value pairs>
53 #       DATA_FILES <list input data files>
54 #     )
55 function(add_gen_header target_name)
56   cmake_parse_arguments(
57     "ADD_GEN_HDR"
58     "" # No optional arguments
59     "DEF_FILE;GEN_HDR" # Single value arguments
60     "PARAMS;DATA_FILES;DEPENDS"     # Multi value arguments
61     ${ARGN}
62   )
63   if(NOT ADD_GEN_HDR_DEF_FILE)
64     message(FATAL_ERROR "`add_gen_hdr` rule requires DEF_FILE to be specified.")
65   endif()
66   if(NOT ADD_GEN_HDR_GEN_HDR)
67     message(FATAL_ERROR "`add_gen_hdr` rule requires GEN_HDR to be specified.")
68   endif()
69
70   set(out_file ${CMAKE_CURRENT_BINARY_DIR}/${ADD_GEN_HDR_GEN_HDR})
71   set(in_file ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_GEN_HDR_DEF_FILE})
72
73   set(fq_data_files "")
74   if(ADD_GEN_HDR_DATA_FILES)
75     foreach(data_file IN LISTS ADD_GEN_HDR_DATA_FILES)
76       list(APPEND fq_data_files "${CMAKE_CURRENT_SOURCE_DIR}/${data_file}")
77     endforeach(data_file)
78   endif()
79
80   set(replacement_params "")
81   if(ADD_GEN_HDR_PARAMS)
82     list(APPEND replacement_params "--args" ${ADD_GEN_HDR_PARAMS})
83   endif()
84
85   set(gen_hdr_script "${LIBC_BUILD_SCRIPTS_DIR}/gen_hdr.py")
86
87   add_custom_command(
88     OUTPUT ${out_file}
89     COMMAND $<TARGET_FILE:libc-hdrgen> -o ${out_file} --header ${ADD_GEN_HDR_GEN_HDR} --def ${in_file} ${replacement_params} -I ${LIBC_SOURCE_DIR} ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td
90     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
91     DEPENDS ${in_file} ${fq_data_files} ${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/api.td libc-hdrgen
92   )
93
94   get_fq_target_name(${target_name} fq_target_name)
95   if(ADD_GEN_HDR_DEPENDS)
96     get_fq_deps_list(fq_deps_list ${ADD_GEN_HDR_DEPENDS})
97   endif()
98   add_custom_target(
99     ${fq_target_name}
100     DEPENDS ${out_file} ${fq_deps_list}
101   )
102 endfunction(add_gen_header)
103
104 set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY")
105
106 # Rule which is essentially a wrapper over add_library to compile a set of
107 # sources to object files.
108 # Usage:
109 #     add_object_library(
110 #       <target_name>
111 #       HDRS <list of header files>
112 #       SRCS <list of source files>
113 #       DEPENDS <list of dependencies>
114 #       COMPILE_OPTIONS <optional list of special compile options for this target>
115 function(add_object_library target_name)
116   cmake_parse_arguments(
117     "ADD_OBJECT"
118     "" # No option arguments
119     "" # Single value arguments
120     "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS" # Multivalue arguments
121     ${ARGN}
122   )
123
124   if(NOT ADD_OBJECT_SRCS)
125     message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")
126   endif()
127
128   get_fq_target_name(${target_name} fq_target_name)
129   add_library(
130     ${fq_target_name}
131     OBJECT
132     ${ADD_OBJECT_SRCS}
133     ${ADD_OBJECT_HDRS}
134   )
135   target_include_directories(
136     ${fq_target_name}
137     PRIVATE
138       "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
139   )
140   if(ADD_OBJECT_COMPILE_OPTIONS)
141     target_compile_options(
142       ${fq_target_name}
143       PRIVATE ${ADD_OBJECT_COMPILE_OPTIONS}
144     )
145   endif()
146
147   set(all_object_files $<TARGET_OBJECTS:${fq_target_name}>)
148   if(ADD_OBJECT_DEPENDS)
149     get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS})
150     add_dependencies(
151       ${fq_target_name}
152       ${fq_deps_list}
153     )
154     foreach(obj_target IN LISTS fq_deps_list)
155       if(NOT TARGET obj_target)
156         # Not all targets will be visible. So, we will ignore those which aren't
157         # visible yet.
158         continue()
159       endif()
160       get_target_property(obj_type ${obj_target} "TARGET_TYPE")
161       if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})))
162         continue()
163       endif()
164       # If a dependency is also a object file library, we will collect the list of
165       # object files from it.
166       get_target_property(obj_files ${obj_target} "OBJECT_FILES")
167       list(APPEND all_object_files ${obj_files})
168     endforeach(obj_target)
169   endif()
170   list(REMOVE_DUPLICATES all_object_files)
171
172   set_target_properties(
173     ${fq_target_name}
174     PROPERTIES
175       "TARGET_TYPE" ${OBJECT_LIBRARY_TARGET_TYPE}
176       "OBJECT_FILES" "${all_object_files}"
177   )
178 endfunction(add_object_library)
179
180 set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
181
182 # A rule for entrypoint object targets.
183 # Usage:
184 #     add_entrypoint_object(
185 #       <target_name>
186 #       [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias.
187 #       [NAME] <the C name of the entrypoint if different from target_name>
188 #       SRCS <list of .cpp files>
189 #       HDRS <list of .h files>
190 #       DEPENDS <list of dependencies>
191 #       COMPILE_OPTIONS <optional list of special compile options for this target>
192 #       SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>
193 #     )
194 function(add_entrypoint_object target_name)
195   cmake_parse_arguments(
196     "ADD_ENTRYPOINT_OBJ"
197     "ALIAS;REDIRECTED" # Optional argument
198     "NAME" # Single value arguments
199     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS"  # Multi value arguments
200     ${ARGN}
201   )
202
203   get_fq_target_name(${target_name} fq_target_name)
204
205   if(ADD_ENTRYPOINT_OBJ_ALIAS)
206     # Alias targets help one add aliases to other entrypoint object targets.
207     # One can use alias targets setup OS/machine independent entrypoint targets.
208     list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size)
209     if(NOT (${deps_size} EQUAL "1"))
210       message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.")
211     endif()
212     list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target)
213     get_fq_dep_name(fq_dep_name ${dep_target})
214     if(NOT TARGET ${fq_dep_name})
215       message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; "
216                       "Target ${target_name} will be ignored.")
217       return()
218     endif()
219
220     get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE")
221     if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})))
222       message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.")
223     endif()
224
225     add_custom_target(${fq_target_name})
226     add_dependencies(${fq_target_name} ${fq_dep_name})
227     get_target_property(all_objects ${fq_dep_name} "OBJECT_FILES")
228     get_target_property(all_objects_raw ${fq_dep_name} "OBJECT_FILES_RAW")
229     set_target_properties(
230       ${fq_target_name}
231       PROPERTIES
232         "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
233         "OBJECT_FILES" "${all_objects}"
234         "OBJECT_FILES_RAW" "${all_objects_raw}"
235     )
236     return()
237   endif()
238
239   if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
240     message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
241   endif()
242   if(NOT ADD_ENTRYPOINT_OBJ_HDRS)
243     message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
244   endif()
245
246   set(entrypoint_name ${target_name})
247   if(ADD_ENTRYPOINT_OBJ_NAME)
248     set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
249   endif()
250
251   set(objects_target_name "${fq_target_name}_objects")
252
253   add_library(
254     ${objects_target_name}
255     # We want an object library as the objects will eventually get packaged into
256     # an archive (like libc.a).
257     OBJECT
258     ${ADD_ENTRYPOINT_OBJ_SRCS}
259     ${ADD_ENTRYPOINT_OBJ_HDRS}
260   )
261   target_compile_options(
262     ${objects_target_name}
263     BEFORE
264     PRIVATE
265       -fpie ${LLVM_CXX_STD_default}
266   )
267   target_include_directories(
268     ${objects_target_name}
269     PRIVATE
270       "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
271   )
272   add_dependencies(
273     ${objects_target_name}
274     libc.src.__support.common
275   )
276   set(dep_objects "")
277   if(ADD_ENTRYPOINT_OBJ_DEPENDS)
278     get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})
279     add_dependencies(
280       ${objects_target_name}
281       ${fq_deps_list}
282     )
283     foreach(dep_target IN LISTS fq_deps_list)
284       if(NOT TARGET ${dep_target})
285         # Not all targets will be visible. So, we will ignore those which aren't
286         # visible yet.
287         continue()
288       endif()
289       get_target_property(obj_type ${dep_target} "TARGET_TYPE")
290       if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})))
291         # Even from among the visible targets, we will collect object files
292         # only from add_object_library targets.
293         continue()
294       endif()
295       # Calling get_target_property requires that the target be visible at this
296       # point. For object library dependencies, this is a reasonable requirement.
297       # We can revisit this in future if we need cases which break under this
298       # requirement.
299       get_target_property(obj_files ${dep_target} "OBJECT_FILES")
300       list(APPEND dep_objects ${obj_files})
301     endforeach(dep_target)
302   endif()
303   list(REMOVE_DUPLICATES dep_objects)
304
305   if(ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS)
306     target_compile_options(
307       ${objects_target_name}
308       PRIVATE ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}
309     )
310   endif()
311
312   set(object_file_raw "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_raw.o")
313   set(object_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.o")
314
315   set(input_objects $<TARGET_OBJECTS:${objects_target_name}>)
316   add_custom_command(
317     OUTPUT ${object_file_raw}
318     DEPENDS ${input_objects}
319     COMMAND ${CMAKE_LINKER} -r ${input_objects} -o ${object_file_raw}
320   )
321
322   set(alias_attributes "0,function,global")
323   if(ADD_ENTRYPOINT_OBJ_REDIRECTED)
324     set(alias_attributes "${alias_attributes},hidden")
325   endif()
326
327   add_custom_command(
328     OUTPUT ${object_file}
329     # We llvm-objcopy here as GNU-binutils objcopy does not support the 'hidden' flag.
330     DEPENDS ${object_file_raw} ${llvm-objcopy}
331     COMMAND $<TARGET_FILE:llvm-objcopy> --add-symbol "${entrypoint_name}=.llvm.libc.entrypoint.${entrypoint_name}:${alias_attributes}" ${object_file_raw} ${object_file}
332   )
333
334   add_custom_target(
335     ${fq_target_name}
336     ALL
337     DEPENDS ${object_file}
338   )
339   set(all_objects ${object_file})
340   list(APPEND all_objects ${dep_objects})
341   set(all_objects_raw ${object_file_raw})
342   list(APPEND all_objects_raw ${dep_objects})
343   set_target_properties(
344     ${fq_target_name}
345     PROPERTIES
346       "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
347       "OBJECT_FILES" "${all_objects}"
348       "OBJECT_FILES_RAW" "${all_objects_raw}"
349   )
350
351   if(LLVM_LIBC_ENABLE_LINTING)
352     set(lint_timestamp "${CMAKE_CURRENT_BINARY_DIR}/.${target_name}.__lint_timestamp__")
353
354     add_custom_command(
355       OUTPUT ${lint_timestamp}
356       # --quiet is used to surpress warning statistics from clang-tidy like:
357       #     Suppressed X warnings (X in non-user code).
358       # There seems to be a bug in clang-tidy where by even with --quiet some
359       # messages from clang's own diagnostics engine leak through:
360       #     X warnings generated.
361       # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress
362       # these.
363       COMMAND $<TARGET_FILE:clang-tidy> "--extra-arg=-fno-caret-diagnostics" --quiet
364               # Path to directory containing compile_commands.json
365               -p ${PROJECT_BINARY_DIR}
366               ${ADD_ENTRYPOINT_OBJ_SRCS}
367       # We have two options for running commands, add_custom_command and
368       # add_custom_target. We don't want to run the linter unless source files
369       # have changed. add_custom_target explicitly runs everytime therefore we
370       # use add_custom_command. This function requires an output file and since
371       # linting doesn't produce a file, we create a dummy file using a
372       # crossplatform touch.
373       COMMAND "${CMAKE_COMMAND}" -E touch ${lint_timestamp}
374       COMMENT "Linting... ${target_name}"
375       DEPENDS ${clang-tidy} ${objects_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
376       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
377     )
378
379     add_custom_target(${fq_target_name}.__lint__
380       DEPENDS ${lint_timestamp})
381     add_dependencies(lint-libc ${fq_target_name}.__lint__)
382     add_dependencies(${fq_target_name} ${fq_target_name}.__lint__)
383   endif()
384
385 endfunction(add_entrypoint_object)
386
387 # A rule to build a library from a collection of entrypoint objects.
388 # Usage:
389 #     add_entrypoint_library(
390 #       DEPENDS <list of add_entrypoint_object targets>
391 #     )
392 function(add_entrypoint_library target_name)
393   cmake_parse_arguments(
394     "ENTRYPOINT_LIBRARY"
395     "" # No optional arguments
396     "" # No single value arguments
397     "DEPENDS" # Multi-value arguments
398     ${ARGN}
399   )
400   if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
401     message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
402   endif()
403
404   set(obj_list "")
405   foreach(dep IN LISTS ENTRYPOINT_LIBRARY_DEPENDS)
406     get_target_property(dep_type ${dep} "TARGET_TYPE")
407     if(NOT (${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}))
408       message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is not an 'add_entrypoint_object' target.")
409     endif()
410     get_target_property(target_obj_files ${dep} "OBJECT_FILES")
411     list(APPEND obj_list "${target_obj_files}")
412   endforeach(dep)
413   list(REMOVE_DUPLICATES obj_list)
414
415   set(library_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${target_name}${CMAKE_STATIC_LIBRARY_SUFFIX}")
416   add_custom_command(
417     OUTPUT ${library_file}
418     COMMAND ${CMAKE_AR} -r ${library_file} ${obj_list}
419     DEPENDS ${obj_list}
420   )
421   add_custom_target(
422     ${target_name}
423     ALL
424     DEPENDS ${library_file}
425   )
426 endfunction(add_entrypoint_library)
427
428 # Rule build a redirector object file.
429 function(add_redirector_object target_name)
430   cmake_parse_arguments(
431     "REDIRECTOR_OBJECT"
432     "" # No optional arguments
433     "SRC" # The cpp file in which the redirector is defined.
434     "" # No multivalue arguments
435     ${ARGN}
436   )
437   if(NOT REDIRECTOR_OBJECT_SRC)
438     message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
439   endif()
440
441   add_library(
442     ${target_name}
443     OBJECT
444     ${REDIRECTOR_OBJECT_SRC}
445   )
446   target_compile_options(
447     ${target_name}
448     BEFORE PRIVATE -fPIC
449   )
450 endfunction(add_redirector_object)
451
452 # Rule to build a shared library of redirector objects.
453 function(add_redirector_library target_name)
454   cmake_parse_arguments(
455     "REDIRECTOR_LIBRARY"
456     ""
457     ""
458     "DEPENDS"
459     ${ARGN}
460   )
461
462   set(obj_files "")
463   foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
464     # TODO: Ensure that each dep is actually a add_redirector_object target.
465     list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
466   endforeach(dep)
467
468   # TODO: Call the linker explicitly instead of calling the compiler driver to
469   # prevent DT_NEEDED on C++ runtime.
470   add_library(
471     ${target_name}
472     SHARED
473     ${obj_files}
474   )
475   set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
476
477   target_link_libraries(
478     ${target_name}
479     -nostdlib -lc -lm
480   )
481
482   set_target_properties(
483     ${target_name}
484     PROPERTIES
485       LINKER_LANGUAGE "C"
486   )
487 endfunction(add_redirector_library)
488
489 # Rule to add a libc unittest.
490 # Usage
491 #    add_libc_unittest(
492 #      <target name>
493 #      SUITE <name of the suite this test belongs to>
494 #      SRCS  <list of .cpp files for the test>
495 #      HDRS  <list of .h files for the test>
496 #      DEPENDS <list of dependencies>
497 #      COMPILE_OPTIONS <list of special compile options for this target>
498 #    )
499 function(add_libc_unittest target_name)
500   if(NOT LLVM_INCLUDE_TESTS)
501     return()
502   endif()
503
504   cmake_parse_arguments(
505     "LIBC_UNITTEST"
506     "" # No optional arguments
507     "SUITE" # Single value arguments
508     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
509     ${ARGN}
510   )
511   if(NOT LIBC_UNITTEST_SRCS)
512     message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp files.")
513   endif()
514   if(NOT LIBC_UNITTEST_DEPENDS)
515     message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
516   endif()
517
518   set(library_deps "")
519   get_fq_deps_list(fq_deps_list ${LIBC_UNITTEST_DEPENDS})
520   foreach(dep IN LISTS fq_deps_list)
521     get_target_property(dep_type ${dep} "TARGET_TYPE")
522     if(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})
523       get_target_property(obj_files ${dep} "OBJECT_FILES_RAW")
524       list(APPEND library_deps ${obj_files})
525     elseif(${dep_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})
526       get_target_property(obj_files ${dep} "OBJECT_FILES")
527       list(APPEND library_deps ${obj_files})
528     endif()
529     # TODO: Check if the dep is a normal CMake library target. If yes, then add it
530     # to the list of library_deps.
531   endforeach(dep)
532   list(REMOVE_DUPLICATES library_deps)
533
534   get_fq_target_name(${target_name} fq_target_name)
535   add_executable(
536     ${fq_target_name}
537     EXCLUDE_FROM_ALL
538     ${LIBC_UNITTEST_SRCS}
539     ${LIBC_UNITTEST_HDRS}
540   )
541   target_include_directories(
542     ${fq_target_name}
543     PRIVATE
544       ${LIBC_SOURCE_DIR}
545       ${LIBC_BUILD_DIR}
546       ${LIBC_BUILD_DIR}/include
547   )
548   if(LIBC_UNITTEST_COMPILE_OPTIONS)
549     target_compile_options(
550       ${target_name}
551       PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS}
552     )
553   endif()
554
555   if(library_deps)
556     target_link_libraries(${fq_target_name} PRIVATE ${library_deps})
557   endif()
558
559   set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
560
561   add_dependencies(
562     ${fq_target_name}
563     ${fq_deps_list}
564   )
565
566   target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest libc_test_utils)
567
568   add_custom_command(
569     TARGET ${fq_target_name}
570     POST_BUILD
571     COMMAND $<TARGET_FILE:${fq_target_name}>
572   )
573   if(LIBC_UNITTEST_SUITE)
574     add_dependencies(
575       ${LIBC_UNITTEST_SUITE}
576       ${fq_target_name}
577     )
578   endif()
579 endfunction(add_libc_unittest)
580
581 function(add_libc_testsuite suite_name)
582   add_custom_target(${suite_name})
583   add_dependencies(check-libc ${suite_name})
584 endfunction(add_libc_testsuite)
585
586 # Rule to add a fuzzer test.
587 # Usage
588 #    add_libc_fuzzer(
589 #      <target name>
590 #      SRCS  <list of .cpp files for the test>
591 #      HDRS  <list of .h files for the test>
592 #      DEPENDS <list of dependencies>
593 #    )
594 function(add_libc_fuzzer target_name)
595   cmake_parse_arguments(
596     "LIBC_FUZZER"
597     "" # No optional arguments
598     "" # Single value arguments
599     "SRCS;HDRS;DEPENDS" # Multi-value arguments
600     ${ARGN}
601   )
602   if(NOT LIBC_FUZZER_SRCS)
603     message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp files.")
604   endif()
605   if(NOT LIBC_FUZZER_DEPENDS)
606     message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
607   endif()
608
609   get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS})
610   set(library_deps "")
611   foreach(dep IN LISTS fq_deps_list)
612     get_target_property(dep_type ${dep} "TARGET_TYPE")
613     if (dep_type)
614       string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint)
615       if(dep_is_entrypoint)
616         get_target_property(obj_file ${dep} "OBJECT_FILES_RAW")
617         list(APPEND library_deps ${obj_file})
618         continue()
619       endif()
620     endif()
621     # TODO: Check if the dep is a normal CMake library target. If yes, then add it
622     # to the list of library_deps.
623   endforeach(dep)
624
625   get_fq_target_name(${target_name} fq_target_name)
626   add_executable(
627     ${fq_target_name}
628     EXCLUDE_FROM_ALL
629     ${LIBC_FUZZER_SRCS}
630     ${LIBC_FUZZER_HDRS}
631   )
632   target_include_directories(
633     ${fq_target_name}
634     PRIVATE
635       ${LIBC_SOURCE_DIR}
636       ${LIBC_BUILD_DIR}
637       ${LIBC_BUILD_DIR}/include
638   )
639
640   if(library_deps)
641     target_link_libraries(${fq_target_name} PRIVATE ${library_deps})
642   endif()
643
644   set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
645
646   add_dependencies(
647     ${fq_target_name}
648     ${fq_deps_list}
649   )
650   add_dependencies(libc-fuzzer ${fq_target_name})
651 endfunction(add_libc_fuzzer)
652
653 # Rule to add header only libraries.
654 # Usage
655 #    add_header_library(
656 #      <target name>
657 #      HDRS  <list of .h files part of the library>
658 #      DEPENDS <list of dependencies>
659 #    )
660 function(add_header_library target_name)
661   cmake_parse_arguments(
662     "ADD_HEADER"
663     "" # No optional arguments
664     "" # No Single value arguments
665     "HDRS;DEPENDS" # Multi-value arguments
666     ${ARGN}
667   )
668
669   if(NOT ADD_HEADER_HDRS)
670     message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
671   endif()
672
673   get_fq_target_name(${target_name} fq_target_name)
674
675   set(FULL_HDR_PATHS "")
676   # TODO: Remove this foreach block when we can switch to the new
677   # version of the CMake policy CMP0076.
678   foreach(hdr IN LISTS ADD_HEADER_HDRS)
679     list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr})
680   endforeach()
681
682   set(interface_target_name "${fq_target_name}_header_library__")
683
684   add_library(${interface_target_name} INTERFACE)
685   target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS})
686   if(ADD_HEADER_DEPENDS)
687     get_fq_deps_list(fq_deps_list ${ADD_HEADER_DEPENDS})
688     add_dependencies(${interface_target_name} ${fq_deps_list})
689   endif()
690
691   add_custom_target(${fq_target_name})
692   add_dependencies(${fq_target_name} ${interface_target_name})
693   set_target_properties(
694     ${fq_target_name}
695     PROPERTIES
696       "TARGET_TYPE" "HDR_LIBRARY"
697   )
698 endfunction(add_header_library)