xref: /llvm-project/libc/cmake/modules/LLVMLibCLibraryRules.cmake (revision 2bf58f5d27a233e63e58d644ff7aff126ee99aa7)
1function(collect_object_file_deps target result)
2  # NOTE: This function does add entrypoint targets to |result|.
3  # It is expected that the caller adds them separately.
4  set(all_deps "")
5  get_target_property(target_type ${target} "TARGET_TYPE")
6  if(NOT target_type)
7    return()
8  endif()
9
10  if(${target_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})
11    list(APPEND all_deps ${target})
12    get_target_property(deps ${target} "DEPS")
13    foreach(dep IN LISTS deps)
14      collect_object_file_deps(${dep} dep_targets)
15      list(APPEND all_deps ${dep_targets})
16    endforeach(dep)
17    list(REMOVE_DUPLICATES all_deps)
18    set(${result} ${all_deps} PARENT_SCOPE)
19    return()
20  endif()
21
22  if(${target_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE} OR
23     ${target_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})
24    set(entrypoint_target ${target})
25    get_target_property(is_alias ${entrypoint_target} "IS_ALIAS")
26    if(is_alias)
27      get_target_property(aliasee ${entrypoint_target} "DEPS")
28      if(NOT aliasee)
29        message(FATAL_ERROR
30                "Entrypoint alias ${entrypoint_target} does not have an aliasee.")
31      endif()
32      set(entrypoint_target ${aliasee})
33    endif()
34    get_target_property(deps ${target} "DEPS")
35    foreach(dep IN LISTS deps)
36      collect_object_file_deps(${dep} dep_targets)
37      list(APPEND all_deps ${dep_targets})
38    endforeach(dep)
39    list(REMOVE_DUPLICATES all_deps)
40    set(${result} ${all_deps} PARENT_SCOPE)
41    return()
42  endif()
43
44  if(${target_type} STREQUAL ${ENTRYPOINT_EXT_TARGET_TYPE})
45    # It is not possible to recursively extract deps of external dependencies.
46    # So, we just accumulate the direct dep and return.
47    get_target_property(deps ${target} "DEPS")
48    set(${result} ${deps} PARENT_SCOPE)
49    return()
50  endif()
51endfunction(collect_object_file_deps)
52
53function(get_all_object_file_deps result fq_deps_list)
54  set(all_deps "")
55  foreach(dep ${fq_deps_list})
56    get_target_property(dep_type ${dep} "TARGET_TYPE")
57    if(NOT ((${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}) OR
58            (${dep_type} STREQUAL ${ENTRYPOINT_EXT_TARGET_TYPE}) OR
59            (${dep_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})))
60      message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is "
61                          "not an 'add_entrypoint_object' or 'add_entrypoint_external' target.")
62    endif()
63    collect_object_file_deps(${dep} recursive_deps)
64    list(APPEND all_deps ${recursive_deps})
65    # Add the entrypoint object target explicitly as collect_object_file_deps
66    # only collects object files from non-entrypoint targets.
67    if(${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE} OR
68       ${dep_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})
69      set(entrypoint_target ${dep})
70      get_target_property(is_alias ${entrypoint_target} "IS_ALIAS")
71      if(is_alias)
72        get_target_property(aliasee ${entrypoint_target} "DEPS")
73        if(NOT aliasee)
74          message(FATAL_ERROR
75                  "Entrypoint alias ${entrypoint_target} does not have an aliasee.")
76        endif()
77        set(entrypoint_target ${aliasee})
78      endif()
79    endif()
80    list(APPEND all_deps ${entrypoint_target})
81  endforeach(dep)
82  list(REMOVE_DUPLICATES all_deps)
83  set(${result} ${all_deps} PARENT_SCOPE)
84endfunction()
85
86# A rule to build a library from a collection of entrypoint objects and bundle
87# it in a single LLVM-IR bitcode file.
88# Usage:
89#     add_gpu_entrypoint_library(
90#       DEPENDS <list of add_entrypoint_object targets>
91#     )
92function(add_bitcode_entrypoint_library target_name base_target_name)
93  cmake_parse_arguments(
94    "ENTRYPOINT_LIBRARY"
95    "" # No optional arguments
96    "" # No single value arguments
97    "DEPENDS" # Multi-value arguments
98    ${ARGN}
99  )
100  if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
101    message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
102                        "of 'add_entrypoint_object' targets.")
103  endif()
104
105  get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
106  get_all_object_file_deps(all_deps "${fq_deps_list}")
107
108  set(objects "")
109  foreach(dep IN LISTS all_deps)
110    set(object $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
111    list(APPEND objects ${object})
112  endforeach()
113
114  add_executable(${target_name} ${objects})
115  target_link_options(${target_name} PRIVATE
116                      "-r" "-nostdlib" "-flto" "-Wl,--lto-emit-llvm")
117endfunction(add_bitcode_entrypoint_library)
118
119# A rule to build a library from a collection of entrypoint objects.
120# Usage:
121#     add_entrypoint_library(
122#       DEPENDS <list of add_entrypoint_object targets>
123#     )
124#
125# NOTE: If one wants an entrypoint to be available in a library, then they will
126# have to list the entrypoint target explicitly in the DEPENDS list. Implicit
127# entrypoint dependencies will not be added to the library.
128function(add_entrypoint_library target_name)
129  cmake_parse_arguments(
130    "ENTRYPOINT_LIBRARY"
131    "" # No optional arguments
132    "" # No single value arguments
133    "DEPENDS" # Multi-value arguments
134    ${ARGN}
135  )
136  if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
137    message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
138                        "of 'add_entrypoint_object' targets.")
139  endif()
140
141  get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
142  get_all_object_file_deps(all_deps "${fq_deps_list}")
143
144  set(objects "")
145  foreach(dep IN LISTS all_deps)
146    list(APPEND objects $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
147  endforeach(dep)
148
149  add_library(
150    ${target_name}
151    STATIC
152    ${objects}
153  )
154  set_target_properties(${target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${LIBC_LIBRARY_DIR})
155endfunction(add_entrypoint_library)
156
157# Rule to build a shared library of redirector objects.
158function(add_redirector_library target_name)
159  cmake_parse_arguments(
160    "REDIRECTOR_LIBRARY"
161    ""
162    ""
163    "DEPENDS"
164    ${ARGN}
165  )
166
167  set(obj_files "")
168  foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
169    # TODO: Ensure that each dep is actually a add_redirector_object target.
170    list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
171  endforeach(dep)
172
173  # TODO: Call the linker explicitly instead of calling the compiler driver to
174  # prevent DT_NEEDED on C++ runtime.
175  add_library(
176    ${target_name}
177    EXCLUDE_FROM_ALL
178    SHARED
179    ${obj_files}
180  )
181  set_target_properties(${target_name}  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${LIBC_LIBRARY_DIR})
182  target_link_libraries(${target_name}  -nostdlib -lc -lm)
183  set_target_properties(${target_name}  PROPERTIES LINKER_LANGUAGE "C")
184endfunction(add_redirector_library)
185
186set(HDR_LIBRARY_TARGET_TYPE "HDR_LIBRARY")
187
188# Internal function, used by `add_header_library`.
189function(create_header_library fq_target_name)
190  cmake_parse_arguments(
191    "ADD_HEADER"
192    "" # Optional arguments
193    "" # Single value arguments
194    "HDRS;DEPENDS;FLAGS;COMPILE_OPTIONS" # Multi-value arguments
195    ${ARGN}
196  )
197
198  if(NOT ADD_HEADER_HDRS)
199    message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
200  endif()
201
202  if(SHOW_INTERMEDIATE_OBJECTS)
203    message(STATUS "Adding header library ${fq_target_name}")
204    if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
205      foreach(dep IN LISTS ADD_HEADER_DEPENDS)
206        message(STATUS "  ${fq_target_name} depends on ${dep}")
207      endforeach()
208    endif()
209  endif()
210
211  add_library(${fq_target_name} INTERFACE)
212  target_sources(${fq_target_name} INTERFACE ${ADD_HEADER_HDRS})
213  if(ADD_HEADER_DEPENDS)
214    add_dependencies(${fq_target_name} ${ADD_HEADER_DEPENDS})
215
216    # `*.__copied_hdr__` is created only to copy the header files to the target
217    # location, not to be linked against.
218    set(link_lib "")
219    foreach(dep ${ADD_HEADER_DEPENDS})
220      if (NOT dep MATCHES "__copied_hdr__")
221        list(APPEND link_lib ${dep})
222      endif()
223    endforeach()
224
225    target_link_libraries(${fq_target_name} INTERFACE ${link_lib})
226  endif()
227  if(ADD_HEADER_COMPILE_OPTIONS)
228    target_compile_options(${fq_target_name} INTERFACE ${ADD_HEADER_COMPILE_OPTIONS})
229  endif()
230  set_target_properties(
231    ${fq_target_name}
232    PROPERTIES
233      INTERFACE_FLAGS "${ADD_HEADER_FLAGS}"
234      TARGET_TYPE "${HDR_LIBRARY_TARGET_TYPE}"
235      DEPS "${ADD_HEADER_DEPENDS}"
236      FLAGS "${ADD_HEADER_FLAGS}"
237  )
238endfunction(create_header_library)
239
240# Rule to add header only libraries.
241# Usage
242#    add_header_library(
243#      <target name>
244#      HDRS  <list of .h files part of the library>
245#      DEPENDS <list of dependencies>
246#      FLAGS <list of flags>
247#    )
248
249function(add_header_library target_name)
250  add_target_with_flags(
251    ${target_name}
252    CREATE_TARGET create_header_library
253    ${ARGN}
254  )
255endfunction(add_header_library)
256