2391ea50b0db552e1c869047ba8e00ffca265c86
[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(SINGLE_OBJECT_TARGET_TYPE "LIBC_SINGLE_OBJECT")
98
99 # Function to generate single object file.
100 # Usage:
101 #     add_object(
102 #       <target_name>
103 #       SRC <source file to compile>
104 #       DEPENDS <list of dependencies>
105 #       COMPILE_OPTIONS <optional list of special compile options for this target>
106 function(add_object target_name)
107   cmake_parse_arguments(
108     "ADD_OBJECT"
109     "" # No option arguments
110     "SRC" # Single value arguments
111     "COMPILE_OPTIONS;DEPENDS" # Multivalue arguments
112     ${ARGN}
113   )
114
115   if(NOT ADD_OBJECT_SRC)
116     message(FATAL_ERROR "'add_object' rules requires a SRC to be specified.")
117   endif()
118
119   add_library(
120     ${target_name}
121     OBJECT
122     ${ADD_OBJECT_SRC}
123   )
124   target_include_directories(
125     ${target_name}
126     PRIVATE
127       "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
128   )
129   if(ADD_OBJECT_COMPILE_OPTIONS)
130     target_compile_options(
131       ${target_name}
132       PRIVATE ${ADD_OBJECT_COMPILE_OPTIONS}
133     )
134   endif()
135   if(ADD_OBJECT_DEPENDS)
136     add_dependencies(
137       ${target_name}
138       ${ADD_OBJECT_DEPENDS}
139     )
140   endif()
141   set_target_properties(
142     ${target_name}
143     PROPERTIES
144       "TARGET_TYPE" ${SINGLE_OBJECT_TARGET_TYPE}
145       "OBJECT_FILE" $<TARGET_OBJECTS:${target_name}>
146   )
147 endfunction(add_object)
148
149 set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
150
151 # A rule for entrypoint object targets.
152 # Usage:
153 #     add_entrypoint_object(
154 #       <target_name>
155 #       [REDIRECTED] # Specified if the entrypoint is redirected.
156 #       [NAME] <the C name of the entrypoint if different from target_name>
157 #       SRCS <list of .cpp files>
158 #       HDRS <list of .h files>
159 #       DEPENDS <list of dependencies>
160 #       COMPILE_OPTIONS <optional list of special compile options for this target>
161 #       SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>
162 #     )
163 function(add_entrypoint_object target_name)
164   cmake_parse_arguments(
165     "ADD_ENTRYPOINT_OBJ"
166     "REDIRECTED" # Optional argument
167     "NAME" # Single value arguments
168     "SRCS;HDRS;SPECIAL_OBJECTS;DEPENDS;COMPILE_OPTIONS"  # Multi value arguments
169     ${ARGN}
170   )
171   if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
172     message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
173   endif()
174   if(NOT ADD_ENTRYPOINT_OBJ_HDRS)
175     message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
176   endif()
177
178   set(entrypoint_name ${target_name})
179   if(ADD_ENTRYPOINT_OBJ_NAME)
180     set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
181   endif()
182
183   add_library(
184     "${target_name}_objects"
185     # We want an object library as the objects will eventually get packaged into
186     # an archive (like libc.a).
187     OBJECT
188     ${ADD_ENTRYPOINT_OBJ_SRCS}
189     ${ADD_ENTRYPOINT_OBJ_HDRS}
190   )
191   target_compile_options(
192     ${target_name}_objects
193     BEFORE
194     PRIVATE
195       -fpie ${LLVM_CXX_STD_default}
196   )
197   target_include_directories(
198     ${target_name}_objects
199     PRIVATE
200       "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR}"
201   )
202   add_dependencies(
203     ${target_name}_objects
204     support_common_h
205   )
206   if(ADD_ENTRYPOINT_OBJ_DEPENDS)
207     add_dependencies(
208       ${target_name}_objects
209       ${ADD_ENTRYPOINT_OBJ_DEPENDS}
210     )
211   endif()
212   if(ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS)
213     target_compile_options(
214       ${target_name}_objects
215       PRIVATE ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS}
216     )
217   endif()
218
219   set(object_file_raw "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_raw.o")
220   set(object_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.o")
221
222   set(input_objects $<TARGET_OBJECTS:${target_name}_objects>)
223   if(ADD_ENTRYPOINT_OBJ_SPECIAL_OBJECTS)
224     foreach(obj_target IN LISTS ADD_ENTRYPOINT_OBJ_SPECIAL_OBJECTS)
225       get_target_property(obj_type ${obj_target} "TARGET_TYPE")
226       if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${SINGLE_OBJECT_TARGET_TYPE})))
227         message(FATAL_ERROR "Unexpected target type for 'SPECIAL_OBJECT' - should be a target introduced by the `add_object` rule.")
228       endif()
229       list(APPEND input_objects $<TARGET_OBJECTS:${obj_target}>)
230     endforeach(obj_target)
231   endif()
232
233   add_custom_command(
234     OUTPUT ${object_file_raw}
235     DEPENDS ${input_objects}
236     COMMAND ${CMAKE_LINKER} -r ${input_objects} -o ${object_file_raw}
237   )
238
239   set(alias_attributes "0,function,global")
240   if(ADD_ENTRYPOINT_OBJ_REDIRECTED)
241     set(alias_attributes "${alias_attributes},hidden")
242   endif()
243
244   add_custom_command(
245     OUTPUT ${object_file}
246     # We llvm-objcopy here as GNU-binutils objcopy does not support the 'hidden' flag.
247     DEPENDS ${object_file_raw} ${llvm-objcopy}
248     COMMAND $<TARGET_FILE:llvm-objcopy> --add-symbol "${entrypoint_name}=.llvm.libc.entrypoint.${entrypoint_name}:${alias_attributes}" ${object_file_raw} ${object_file}
249   )
250
251   add_custom_target(
252     ${target_name}
253     ALL
254     DEPENDS ${object_file}
255   )
256   set_target_properties(
257     ${target_name}
258     PROPERTIES
259       "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
260       "OBJECT_FILE" ${object_file}
261       "OBJECT_FILE_RAW" ${object_file_raw}
262   )
263 endfunction(add_entrypoint_object)
264
265 # A rule to build a library from a collection of entrypoint objects.
266 # Usage:
267 #     add_entrypoint_library(
268 #       DEPENDS <list of add_entrypoint_object targets>
269 #     )
270 function(add_entrypoint_library target_name)
271   cmake_parse_arguments(
272     "ENTRYPOINT_LIBRARY"
273     "" # No optional arguments
274     "" # No single value arguments
275     "DEPENDS" # Multi-value arguments
276     ${ARGN}
277   )
278   if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
279     message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
280   endif()
281
282   set(obj_list "")
283   foreach(dep IN LISTS ENTRYPOINT_LIBRARY_DEPENDS)
284     get_target_property(dep_type ${dep} "TARGET_TYPE")
285     string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint)
286     if(NOT dep_is_entrypoint)
287       message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is not an 'add_entrypoint_object' target.")
288     endif()
289     get_target_property(target_obj_file ${dep} "OBJECT_FILE")
290     list(APPEND obj_list "${target_obj_file}")
291   endforeach(dep)
292
293   set(library_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${target_name}${CMAKE_STATIC_LIBRARY_SUFFIX}")
294   add_custom_command(
295     OUTPUT ${library_file}
296     COMMAND ${CMAKE_AR} -r ${library_file} ${obj_list}
297     DEPENDS ${obj_list}
298   )
299   add_custom_target(
300     ${target_name}
301     ALL
302     DEPENDS ${library_file}
303   )
304 endfunction(add_entrypoint_library)
305
306 # Rule build a redirector object file.
307 function(add_redirector_object target_name)
308   cmake_parse_arguments(
309     "REDIRECTOR_OBJECT"
310     "" # No optional arguments
311     "SRC" # The cpp file in which the redirector is defined.
312     "" # No multivalue arguments
313     ${ARGN}
314   )
315   if(NOT REDIRECTOR_OBJECT_SRC)
316     message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
317   endif()
318
319   add_library(
320     ${target_name}
321     OBJECT
322     ${REDIRECTOR_OBJECT_SRC}
323   )
324   target_compile_options(
325     ${target_name}
326     BEFORE PRIVATE -fPIC
327   )
328 endfunction(add_redirector_object)
329
330 # Rule to build a shared library of redirector objects.
331 function(add_redirector_library target_name)
332   cmake_parse_arguments(
333     "REDIRECTOR_LIBRARY"
334     ""
335     ""
336     "DEPENDS"
337     ${ARGN}
338   )
339
340   set(obj_files "")
341   foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
342     # TODO: Ensure that each dep is actually a add_redirector_object target.
343     list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
344   endforeach(dep)
345
346   # TODO: Call the linker explicitly instead of calling the compiler driver to
347   # prevent DT_NEEDED on C++ runtime.
348   add_library(
349     ${target_name}
350     SHARED
351     ${obj_files}
352   )
353   set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
354
355   target_link_libraries(
356     ${target_name}
357     -nostdlib -lc -lm
358   )
359
360   set_target_properties(
361     ${target_name}
362     PROPERTIES
363       LINKER_LANGUAGE "C"
364   )
365 endfunction(add_redirector_library)
366
367 # Rule to add a libc unittest.
368 # Usage
369 #    add_libc_unittest(
370 #      <target name>
371 #      SUITE <name of the suite this test belongs to>
372 #      SRCS  <list of .cpp files for the test>
373 #      HDRS  <list of .h files for the test>
374 #      DEPENDS <list of dependencies>
375 #      COMPILE_OPTIONS <list of special compile options for this target>
376 #    )
377 function(add_libc_unittest target_name)
378   if(NOT LLVM_INCLUDE_TESTS)
379     return()
380   endif()
381   
382   cmake_parse_arguments(
383     "LIBC_UNITTEST"
384     "" # No optional arguments
385     "SUITE" # Single value arguments
386     "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS" # Multi-value arguments
387     ${ARGN}
388   )
389   if(NOT LIBC_UNITTEST_SRCS)
390     message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp files.")
391   endif()
392   if(NOT LIBC_UNITTEST_DEPENDS)
393     message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
394   endif()
395
396   set(library_deps "")
397   foreach(dep IN LISTS LIBC_UNITTEST_DEPENDS)
398     get_target_property(dep_type ${dep} "TARGET_TYPE")
399     if (dep_type)
400       string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint)
401       if(dep_is_entrypoint)
402         get_target_property(obj_file ${dep} "OBJECT_FILE_RAW")
403         list(APPEND library_deps ${obj_file})
404         continue()
405       endif()
406     endif()
407     # TODO: Check if the dep is a normal CMake library target. If yes, then add it
408     # to the list of library_deps.
409   endforeach(dep)
410
411   add_executable(
412     ${target_name}
413     EXCLUDE_FROM_ALL
414     ${LIBC_UNITTEST_SRCS}
415     ${LIBC_UNITTEST_HDRS}
416   )
417   target_include_directories(
418     ${target_name}
419     PRIVATE
420       ${LIBC_SOURCE_DIR}
421       ${LIBC_BUILD_DIR}
422       ${LIBC_BUILD_DIR}/include
423   )
424   if(LIBC_UNITTEST_COMPILE_OPTIONS)
425     target_compile_options(
426       ${target_name}
427       PRIVATE ${LIBC_UNITTEST_COMPILE_OPTIONS}
428     )
429   endif()
430
431   if(library_deps)
432     target_link_libraries(${target_name} PRIVATE ${library_deps})
433   endif()
434
435   set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
436
437   add_dependencies(
438     ${target_name}
439     ${LIBC_UNITTEST_DEPENDS}
440   )
441
442   target_link_libraries(${target_name} PRIVATE LibcUnitTest libc_test_utils)
443
444   add_custom_command(
445     TARGET ${target_name}
446     POST_BUILD
447     COMMAND $<TARGET_FILE:${target_name}>
448   )
449   if(LIBC_UNITTEST_SUITE)
450     add_dependencies(
451       ${LIBC_UNITTEST_SUITE}
452       ${target_name}
453     )
454   endif()
455 endfunction(add_libc_unittest)
456
457 function(add_libc_testsuite suite_name)
458   add_custom_target(${suite_name})
459   add_dependencies(check-libc ${suite_name})
460 endfunction(add_libc_testsuite)
461
462 # Rule to add a fuzzer test.
463 # Usage
464 #    add_libc_fuzzer(
465 #      <target name>
466 #      SRCS  <list of .cpp files for the test>
467 #      HDRS  <list of .h files for the test>
468 #      DEPENDS <list of dependencies>
469 #    )
470 function(add_libc_fuzzer target_name)
471   cmake_parse_arguments(
472     "LIBC_FUZZER"
473     "" # No optional arguments
474     "" # Single value arguments
475     "SRCS;HDRS;DEPENDS" # Multi-value arguments
476     ${ARGN}
477   )
478   if(NOT LIBC_FUZZER_SRCS)
479     message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp files.")
480   endif()
481   if(NOT LIBC_FUZZER_DEPENDS)
482     message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
483   endif()
484
485   set(library_deps "")
486   foreach(dep IN LISTS LIBC_FUZZER_DEPENDS)
487     get_target_property(dep_type ${dep} "TARGET_TYPE")
488     if (dep_type)
489       string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint)
490       if(dep_is_entrypoint)
491         get_target_property(obj_file ${dep} "OBJECT_FILE_RAW")
492         list(APPEND library_deps ${obj_file})
493         continue()
494       endif()
495     endif()
496     # TODO: Check if the dep is a normal CMake library target. If yes, then add it
497     # to the list of library_deps.
498   endforeach(dep)
499
500   add_executable(
501     ${target_name}
502     EXCLUDE_FROM_ALL
503     ${LIBC_FUZZER_SRCS}
504     ${LIBC_FUZZER_HDRS}
505   )
506   target_include_directories(
507     ${target_name}
508     PRIVATE
509       ${LIBC_SOURCE_DIR}
510       ${LIBC_BUILD_DIR}
511       ${LIBC_BUILD_DIR}/include
512   )
513
514   if(library_deps)
515     target_link_libraries(${target_name} PRIVATE ${library_deps})
516   endif()
517
518   set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
519
520   add_dependencies(
521     ${target_name}
522     ${LIBC_FUZZER_DEPENDS}
523   )
524   add_dependencies(libc-fuzzer ${target_name})
525 endfunction(add_libc_fuzzer)
526
527 # Rule to add header only libraries.
528 # Usage
529 #    add_header_library(
530 #      <target name>
531 #      HDRS  <list of .h files part of the library>
532 #      DEPENDS <list of dependencies>
533 #    )
534 function(add_header_library target_name)
535   cmake_parse_arguments(
536     "ADD_HEADER"
537     "" # No optional arguments
538     "" # No Single value arguments
539     "HDRS;DEPENDS" # Multi-value arguments
540     ${ARGN}
541   )
542
543   if(NOT ADD_HEADER_HDRS)
544     message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
545   endif()
546
547   set(FULL_HDR_PATHS "")
548   # TODO: Remove this foreach block when we can switch to the new
549   # version of the CMake policy CMP0076.
550   foreach(hdr IN LISTS ADD_HEADER_HDRS)
551     list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr})
552   endforeach()
553
554   set(interface_target_name "${target_name}_header_library__")
555
556   add_library(${interface_target_name} INTERFACE)
557   target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS})
558   if(ADD_HEADER_DEPENDS)
559     add_dependencies(${interface_target_name} ${ADD_HEADER_DEPENDS})
560   endif()
561
562   add_custom_target(${target_name})
563   add_dependencies(${target_name} ${interface_target_name})
564   set_target_properties(
565     ${target_name}
566     PROPERTIES
567       "TARGET_TYPE" "HDR_LIBRARY"
568   )
569 endfunction(add_header_library)