1 include(LLVMLibCTargetNameUtils)
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.
11 function(add_header target_name)
12 cmake_parse_arguments(
14 "" # No optional arguments
15 "HDR" # Single value arguments
19 if(NOT ADD_HEADER_HDR)
20 message(FATAL_ERROR "'add_header' rules requires the HDR argument specifying a headef file.")
23 set(dest_file ${CMAKE_CURRENT_BINARY_DIR}/${ADD_HEADER_HDR})
24 set(src_file ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_HEADER_HDR})
28 COMMAND cp ${src_file} ${dest_file}
32 get_fq_target_name(${target_name} fq_target_name)
38 if(ADD_HEADER_DEPENDS)
39 get_fq_deps_list(fq_deps_list ${ADD_HEADER_DEPENDS})
41 ${fq_target_name} ${fq_deps_list}
44 endfunction(add_header)
46 # A rule for generated header file targets.
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>
55 function(add_gen_header target_name)
56 cmake_parse_arguments(
58 "" # No optional arguments
59 "DEF_FILE;GEN_HDR" # Single value arguments
60 "PARAMS;DATA_FILES;DEPENDS" # Multi value arguments
63 if(NOT ADD_GEN_HDR_DEF_FILE)
64 message(FATAL_ERROR "`add_gen_hdr` rule requires DEF_FILE to be specified.")
66 if(NOT ADD_GEN_HDR_GEN_HDR)
67 message(FATAL_ERROR "`add_gen_hdr` rule requires GEN_HDR to be specified.")
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})
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}")
80 set(replacement_params "")
81 if(ADD_GEN_HDR_PARAMS)
82 list(APPEND replacement_params "--args" ${ADD_GEN_HDR_PARAMS})
85 set(gen_hdr_script "${LIBC_BUILD_SCRIPTS_DIR}/gen_hdr.py")
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
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})
100 DEPENDS ${out_file} ${fq_deps_list}
102 endfunction(add_gen_header)
104 set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY")
106 # Rule which is essentially a wrapper over add_library to compile a set of
107 # sources to object files.
109 # add_object_library(
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(
118 "" # No option arguments
119 "" # Single value arguments
120 "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS" # Multivalue arguments
124 if(NOT ADD_OBJECT_SRCS)
125 message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")
128 get_fq_target_name(${target_name} fq_target_name)
135 target_include_directories(
138 "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
140 if(ADD_OBJECT_COMPILE_OPTIONS)
141 target_compile_options(
143 PRIVATE ${ADD_OBJECT_COMPILE_OPTIONS}
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})
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
160 get_target_property(obj_type ${obj_target} "TARGET_TYPE")
161 if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})))
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)
170 list(REMOVE_DUPLICATES all_object_files)
172 set_target_properties(
175 "TARGET_TYPE" ${OBJECT_LIBRARY_TARGET_TYPE}
176 "OBJECT_FILES" "${all_object_files}"
178 endfunction(add_object_library)
180 set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
182 # A rule for entrypoint object targets.
184 # add_entrypoint_object(
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`>
194 function(add_entrypoint_object target_name)
195 cmake_parse_arguments(
197 "ALIAS;REDIRECTED" # Optional argument
198 "NAME" # Single value arguments
199 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi value arguments
203 get_fq_target_name(${target_name} fq_target_name)
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.")
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.")
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.")
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(
232 "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
233 "OBJECT_FILES" "${all_objects}"
234 "OBJECT_FILES_RAW" "${all_objects_raw}"
239 if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
240 message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
242 if(NOT ADD_ENTRYPOINT_OBJ_HDRS)
243 message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
246 set(entrypoint_name ${target_name})
247 if(ADD_ENTRYPOINT_OBJ_NAME)
248 set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
251 set(objects_target_name "${fq_target_name}_objects")
254 ${objects_target_name}
255 # We want an object library as the objects will eventually get packaged into
256 # an archive (like libc.a).
258 ${ADD_ENTRYPOINT_OBJ_SRCS}
259 ${ADD_ENTRYPOINT_OBJ_HDRS}
261 target_compile_options(
262 ${objects_target_name}
265 -fpie ${LLVM_CXX_STD_default}
267 target_include_directories(
268 ${objects_target_name}
270 "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
273 ${objects_target_name}
274 libc.src.__support.common
277 if(ADD_ENTRYPOINT_OBJ_DEPENDS)
278 get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})
280 ${objects_target_name}
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
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.
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
299 get_target_property(obj_files ${dep_target} "OBJECT_FILES")
300 list(APPEND dep_objects ${obj_files})
301 endforeach(dep_target)
303 list(REMOVE_DUPLICATES dep_objects)
305 if(ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS)
306 target_compile_options(
307 ${objects_target_name}
308 PRIVATE ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}
312 set(object_file_raw "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_raw.o")
313 set(object_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.o")
315 set(input_objects $<TARGET_OBJECTS:${objects_target_name}>)
317 OUTPUT ${object_file_raw}
318 DEPENDS ${input_objects}
319 COMMAND ${CMAKE_LINKER} -r ${input_objects} -o ${object_file_raw}
322 set(alias_attributes "0,function,global")
323 if(ADD_ENTRYPOINT_OBJ_REDIRECTED)
324 set(alias_attributes "${alias_attributes},hidden")
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}
337 DEPENDS ${object_file}
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(
346 "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
347 "OBJECT_FILES" "${all_objects}"
348 "OBJECT_FILES_RAW" "${all_objects_raw}"
351 if(LLVM_LIBC_ENABLE_LINTING)
352 set(lint_timestamp "${CMAKE_CURRENT_BINARY_DIR}/.${target_name}.__lint_timestamp__")
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
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}
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__)
385 endfunction(add_entrypoint_object)
387 # A rule to build a library from a collection of entrypoint objects.
389 # add_entrypoint_library(
390 # DEPENDS <list of add_entrypoint_object targets>
392 function(add_entrypoint_library target_name)
393 cmake_parse_arguments(
395 "" # No optional arguments
396 "" # No single value arguments
397 "DEPENDS" # Multi-value arguments
400 if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
401 message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
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.")
410 get_target_property(target_obj_files ${dep} "OBJECT_FILES")
411 list(APPEND obj_list "${target_obj_files}")
413 list(REMOVE_DUPLICATES obj_list)
415 set(library_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${target_name}${CMAKE_STATIC_LIBRARY_SUFFIX}")
417 OUTPUT ${library_file}
418 COMMAND ${CMAKE_AR} -r ${library_file} ${obj_list}
424 DEPENDS ${library_file}
426 endfunction(add_entrypoint_library)
428 # Rule build a redirector object file.
429 function(add_redirector_object target_name)
430 cmake_parse_arguments(
432 "" # No optional arguments
433 "SRC" # The cpp file in which the redirector is defined.
434 "" # No multivalue arguments
437 if(NOT REDIRECTOR_OBJECT_SRC)
438 message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
444 ${REDIRECTOR_OBJECT_SRC}
446 target_compile_options(
450 endfunction(add_redirector_object)
452 # Rule to build a shared library of redirector objects.
453 function(add_redirector_library target_name)
454 cmake_parse_arguments(
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}>)
468 # TODO: Call the linker explicitly instead of calling the compiler driver to
469 # prevent DT_NEEDED on C++ runtime.
475 set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
477 target_link_libraries(
482 set_target_properties(
487 endfunction(add_redirector_library)
489 # Rule to add a libc unittest.
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>
499 function(add_libc_unittest target_name)
500 if(NOT LLVM_INCLUDE_TESTS)
504 cmake_parse_arguments(
506 "" # No optional arguments
507 "SUITE" # Single value arguments
508 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
511 if(NOT LIBC_UNITTEST_SRCS)
512 message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp files.")
514 if(NOT LIBC_UNITTEST_DEPENDS)
515 message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
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})
529 # TODO: Check if the dep is a normal CMake library target. If yes, then add it
530 # to the list of library_deps.
532 list(REMOVE_DUPLICATES library_deps)
534 get_fq_target_name(${target_name} fq_target_name)
538 ${LIBC_UNITTEST_SRCS}
539 ${LIBC_UNITTEST_HDRS}
541 target_include_directories(
546 ${LIBC_BUILD_DIR}/include
548 if(LIBC_UNITTEST_COMPILE_OPTIONS)
549 target_compile_options(
551 PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS}
556 target_link_libraries(${fq_target_name} PRIVATE ${library_deps})
559 set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
566 target_link_libraries(${fq_target_name} PRIVATE LibcUnitTest libc_test_utils)
569 TARGET ${fq_target_name}
571 COMMAND $<TARGET_FILE:${fq_target_name}>
573 if(LIBC_UNITTEST_SUITE)
575 ${LIBC_UNITTEST_SUITE}
579 endfunction(add_libc_unittest)
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)
586 # Rule to add a fuzzer test.
590 # SRCS <list of .cpp files for the test>
591 # HDRS <list of .h files for the test>
592 # DEPENDS <list of dependencies>
594 function(add_libc_fuzzer target_name)
595 cmake_parse_arguments(
597 "" # No optional arguments
598 "" # Single value arguments
599 "SRCS;HDRS;DEPENDS" # Multi-value arguments
602 if(NOT LIBC_FUZZER_SRCS)
603 message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp files.")
605 if(NOT LIBC_FUZZER_DEPENDS)
606 message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
609 get_fq_deps_list(fq_deps_list ${LIBC_FUZZER_DEPENDS})
611 foreach(dep IN LISTS fq_deps_list)
612 get_target_property(dep_type ${dep} "TARGET_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})
621 # TODO: Check if the dep is a normal CMake library target. If yes, then add it
622 # to the list of library_deps.
625 get_fq_target_name(${target_name} fq_target_name)
632 target_include_directories(
637 ${LIBC_BUILD_DIR}/include
641 target_link_libraries(${fq_target_name} PRIVATE ${library_deps})
644 set_target_properties(${fq_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
650 add_dependencies(libc-fuzzer ${fq_target_name})
651 endfunction(add_libc_fuzzer)
653 # Rule to add header only libraries.
655 # add_header_library(
657 # HDRS <list of .h files part of the library>
658 # DEPENDS <list of dependencies>
660 function(add_header_library target_name)
661 cmake_parse_arguments(
663 "" # No optional arguments
664 "" # No Single value arguments
665 "HDRS;DEPENDS" # Multi-value arguments
669 if(NOT ADD_HEADER_HDRS)
670 message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
673 get_fq_target_name(${target_name} fq_target_name)
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})
682 set(interface_target_name "${fq_target_name}_header_library__")
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})
691 add_custom_target(${fq_target_name})
692 add_dependencies(${fq_target_name} ${interface_target_name})
693 set_target_properties(
696 "TARGET_TYPE" "HDR_LIBRARY"
698 endfunction(add_header_library)