1 function(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.
5 get_target_property(target_type ${target} "TARGET_TYPE")
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})
17 list(REMOVE_DUPLICATES all_deps)
18 set(${result} ${all_deps} PARENT_SCOPE)
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")
27 get_target_property(aliasee ${entrypoint_target} "DEPS")
30 "Entrypoint alias ${entrypoint_target} does not have an aliasee.")
32 set(entrypoint_target ${aliasee})
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})
39 list(REMOVE_DUPLICATES all_deps)
40 set(${result} ${all_deps} PARENT_SCOPE)
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)
51 endfunction(collect_object_file_deps)
53 function(get_all_object_file_deps result fq_deps_list)
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.")
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")
72 get_target_property(aliasee ${entrypoint_target} "DEPS")
75 "Entrypoint alias ${entrypoint_target} does not have an aliasee.")
77 set(entrypoint_target ${aliasee})
80 list(APPEND all_deps ${entrypoint_target})
82 list(REMOVE_DUPLICATES all_deps)
83 set(${result} ${all_deps} PARENT_SCOPE)
86 # A rule to build a library from a collection of entrypoint objects and bundle
87 # it into a GPU fatbinary. Usage is the same as 'add_entrypoint_library'.
89 # add_gpu_entrypoint_library(
90 # DEPENDS <list of add_entrypoint_object targets>
92 function(add_gpu_entrypoint_library target_name base_target_name)
93 cmake_parse_arguments(
95 "" # No optional arguments
96 "" # No single value arguments
97 "DEPENDS" # Multi-value arguments
100 if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
101 message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
102 "of 'add_entrypoint_object' targets.")
105 get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
106 get_all_object_file_deps(all_deps "${fq_deps_list}")
108 # The GPU 'libc' needs to be exported in a format that can be linked with
109 # offloading langauges like OpenMP or CUDA. This wraps every GPU object into a
110 # fat binary and adds them to a static library.
112 foreach(dep IN LISTS all_deps)
113 set(object $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
114 string(FIND ${dep} "." last_dot_loc REVERSE)
115 math(EXPR name_loc "${last_dot_loc} + 1")
116 string(SUBSTRING ${dep} ${name_loc} -1 name)
117 if(LIBC_TARGET_ARCHITECTURE_IS_NVPTX)
118 set(prefix --image=arch=generic,triple=nvptx64-nvidia-cuda,feature=+ptx63)
119 elseif(LIBC_TARGET_ARCHITECTURE_IS_AMDGPU)
120 set(prefix --image=arch=generic,triple=amdgcn-amd-amdhsa)
123 # Use the 'clang-offload-packager' to merge these files into a binary blob.
125 OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/binary/${name}.gpubin"
126 COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/binary
127 COMMAND ${LIBC_CLANG_OFFLOAD_PACKAGER}
128 "${prefix},file=$<JOIN:${object},,file=>" -o
129 ${CMAKE_CURRENT_BINARY_DIR}/binary/${name}.gpubin
130 DEPENDS ${dep} ${base_target_name}
131 COMMENT "Packaging LLVM offloading binary for '${object}'"
133 add_custom_target(${dep}.__gpubin__ DEPENDS ${dep}
134 "${CMAKE_CURRENT_BINARY_DIR}/binary/${name}.gpubin")
135 if(TARGET clang-offload-packager)
136 add_dependencies(${dep}.__gpubin__ clang-offload-packager)
139 # CMake does not permit setting the name on object files. In order to have
140 # human readable names we create an empty stub file with the entrypoint
141 # name. This empty file will then have the created binary blob embedded.
143 OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/stubs/${name}.cpp"
144 COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stubs
145 COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/stubs/${name}.cpp
146 DEPENDS ${dep} ${dep}.__gpubin__ ${base_target_name}
148 add_custom_target(${dep}.__stub__
149 DEPENDS ${dep}.__gpubin__ "${CMAKE_CURRENT_BINARY_DIR}/stubs/${name}.cpp")
151 add_library(${dep}.__fatbin__
152 EXCLUDE_FROM_ALL OBJECT
153 "${CMAKE_CURRENT_BINARY_DIR}/stubs/${name}.cpp"
156 # This is always compiled for the LLVM host triple instead of the native GPU
157 # triple that is used by default in the build.
158 target_compile_options(${dep}.__fatbin__ BEFORE PRIVATE -nostdlib)
159 target_compile_options(${dep}.__fatbin__ PRIVATE
160 --target=${LLVM_HOST_TRIPLE}
161 "SHELL:-Xclang -fembed-offload-object=${CMAKE_CURRENT_BINARY_DIR}/binary/${name}.gpubin")
162 add_dependencies(${dep}.__fatbin__
163 ${dep} ${dep}.__stub__ ${dep}.__gpubin__ ${base_target_name})
165 # Set the list of newly create fat binaries containing embedded device code.
166 list(APPEND objects $<TARGET_OBJECTS:${dep}.__fatbin__>)
174 set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${LIBC_LIBRARY_DIR})
175 endfunction(add_gpu_entrypoint_library)
177 # A rule to build a library from a collection of entrypoint objects and bundle
178 # it in a single LLVM-IR bitcode file.
180 # add_gpu_entrypoint_library(
181 # DEPENDS <list of add_entrypoint_object targets>
183 function(add_bitcode_entrypoint_library target_name base_target_name)
184 cmake_parse_arguments(
186 "" # No optional arguments
187 "" # No single value arguments
188 "DEPENDS" # Multi-value arguments
191 if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
192 message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
193 "of 'add_entrypoint_object' targets.")
196 get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
197 get_all_object_file_deps(all_deps "${fq_deps_list}")
200 foreach(dep IN LISTS all_deps)
201 set(object $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
202 list(APPEND objects ${object})
205 set(output ${CMAKE_CURRENT_BINARY_DIR}/${target_name}.bc)
208 COMMAND ${LIBC_LLVM_LINK} ${objects} -o ${output}
209 DEPENDS ${all_deps} ${base_target_name}
210 COMMENT "Linking LLVM-IR bitcode for ${base_target_name}"
213 add_custom_target(${target_name} DEPENDS ${output} ${all_deps})
214 set_target_properties(${target_name} PROPERTIES TARGET_OBJECT ${output})
216 add_dependencies(${target_name} llvm-link)
218 endfunction(add_bitcode_entrypoint_library)
220 # A rule to build a library from a collection of entrypoint objects.
222 # add_entrypoint_library(
223 # DEPENDS <list of add_entrypoint_object targets>
226 # NOTE: If one wants an entrypoint to be available in a library, then they will
227 # have to list the entrypoint target explicitly in the DEPENDS list. Implicit
228 # entrypoint dependencies will not be added to the library.
229 function(add_entrypoint_library target_name)
230 cmake_parse_arguments(
232 "" # No optional arguments
233 "" # No single value arguments
234 "DEPENDS" # Multi-value arguments
237 if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
238 message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
239 "of 'add_entrypoint_object' targets.")
242 get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
243 get_all_object_file_deps(all_deps "${fq_deps_list}")
246 foreach(dep IN LISTS all_deps)
247 list(APPEND objects $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
255 set_target_properties(${target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${LIBC_LIBRARY_DIR})
256 endfunction(add_entrypoint_library)
258 # Rule to build a shared library of redirector objects.
259 function(add_redirector_library target_name)
260 cmake_parse_arguments(
269 foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
270 # TODO: Ensure that each dep is actually a add_redirector_object target.
271 list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
274 # TODO: Call the linker explicitly instead of calling the compiler driver to
275 # prevent DT_NEEDED on C++ runtime.
282 set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${LIBC_LIBRARY_DIR})
283 target_link_libraries(${target_name} -nostdlib -lc -lm)
284 set_target_properties(${target_name} PROPERTIES LINKER_LANGUAGE "C")
285 endfunction(add_redirector_library)
287 set(HDR_LIBRARY_TARGET_TYPE "HDR_LIBRARY")
289 # Internal function, used by `add_header_library`.
290 function(create_header_library fq_target_name)
291 cmake_parse_arguments(
293 "" # Optional arguments
294 "" # Single value arguments
295 "HDRS;DEPENDS;FLAGS;COMPILE_OPTIONS" # Multi-value arguments
299 if(NOT ADD_HEADER_HDRS)
300 message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
303 if(SHOW_INTERMEDIATE_OBJECTS)
304 message(STATUS "Adding header library ${fq_target_name}")
305 if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
306 foreach(dep IN LISTS ADD_HEADER_DEPENDS)
307 message(STATUS " ${fq_target_name} depends on ${dep}")
312 add_library(${fq_target_name} INTERFACE)
313 target_sources(${fq_target_name} INTERFACE ${ADD_HEADER_HDRS})
314 if(ADD_HEADER_DEPENDS)
315 add_dependencies(${fq_target_name} ${ADD_HEADER_DEPENDS})
317 # `*.__copied_hdr__` is created only to copy the header files to the target
318 # location, not to be linked against.
320 foreach(dep ${ADD_HEADER_DEPENDS})
321 if (NOT dep MATCHES "__copied_hdr__")
322 list(APPEND link_lib ${dep})
326 target_link_libraries(${fq_target_name} INTERFACE ${link_lib})
328 if(ADD_HEADER_COMPILE_OPTIONS)
329 target_compile_options(${fq_target_name} INTERFACE ${ADD_HEADER_COMPILE_OPTIONS})
331 set_target_properties(
334 INTERFACE_FLAGS "${ADD_HEADER_FLAGS}"
335 TARGET_TYPE "${HDR_LIBRARY_TARGET_TYPE}"
336 DEPS "${ADD_HEADER_DEPENDS}"
337 FLAGS "${ADD_HEADER_FLAGS}"
339 endfunction(create_header_library)
341 # Rule to add header only libraries.
343 # add_header_library(
345 # HDRS <list of .h files part of the library>
346 # DEPENDS <list of dependencies>
347 # FLAGS <list of flags>
350 function(add_header_library target_name)
351 add_target_with_flags(
353 CREATE_TARGET create_header_library
356 endfunction(add_header_library)