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