1function(collect_object_file_deps target result)
2  set(all_deps "")
3  get_target_property(target_type ${target} "TARGET_TYPE")
4  if(NOT target_type)
5    return()
6  endif()
7
8  if(${target_type} STREQUAL ${OBJECT_LIBRARY_TARGET_TYPE})
9    list(APPEND all_deps ${target})
10    get_target_property(deps ${target} "DEPS")
11    foreach(dep IN LISTS deps)
12      collect_object_file_deps(${dep} dep_targets)
13      list(APPEND all_deps ${dep_targets})
14    endforeach(dep)
15    set(${result} ${all_deps} PARENT_SCOPE)
16    return()
17  endif()
18
19  if(${target_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})
20    set(entrypoint_target ${target})
21    get_target_property(is_alias ${entrypoint_target} "IS_ALIAS")
22    if(is_alias)
23      get_target_property(aliasee ${entrypoint_target} "DEPS")
24      if(NOT aliasee)
25        message(FATAL_ERROR
26                "Entrypoint alias ${entrypoint_target} does not have an aliasee.")
27      endif()
28      set(entrypoint_target ${aliasee})
29    endif()
30    list(APPEND all_deps ${entrypoint_target})
31    get_target_property(deps ${target} "DEPS")
32    foreach(dep IN LISTS deps)
33      collect_object_file_deps(${dep} dep_targets)
34      list(APPEND all_deps ${dep_targets})
35    endforeach(dep)
36    set(${result} ${all_deps} PARENT_SCOPE)
37    return()
38  endif()
39
40  if(${target_type} STREQUAL ${ENTRYPOINT_EXT_TARGET_TYPE})
41    # It is not possible to recursively extract deps of external dependencies.
42    # So, we just accumulate the direct dep and return.
43    get_target_property(deps ${target} "DEPS")
44    set(${result} ${deps} PARENT_SCOPE)
45    return()
46  endif()
47endfunction(collect_object_file_deps)
48
49# A rule to build a library from a collection of entrypoint objects.
50# Usage:
51#     add_entrypoint_library(
52#       DEPENDS <list of add_entrypoint_object targets>
53#     )
54#
55# NOTE: If one wants an entrypoint to be available in a library, then they will
56# have to list the entrypoint target explicitly in the DEPENDS list. Implicit
57# entrypoint dependencies will not be added to the library.
58function(add_entrypoint_library target_name)
59  cmake_parse_arguments(
60    "ENTRYPOINT_LIBRARY"
61    "" # No optional arguments
62    "" # No single value arguments
63    "DEPENDS" # Multi-value arguments
64    ${ARGN}
65  )
66  if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
67    message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
68                        "of 'add_entrypoint_object' targets.")
69  endif()
70
71  get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
72  set(all_deps "")
73  foreach(dep IN LISTS fq_deps_list)
74    get_target_property(dep_type ${dep} "TARGET_TYPE")
75    if(NOT ((${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}) OR (${dep_type} STREQUAL ${ENTRYPOINT_EXT_TARGET_TYPE})))
76      message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is "
77                          "not an 'add_entrypoint_object' or 'add_entrypoint_external' target.")
78    endif()
79    collect_object_file_deps(${dep} recursive_deps)
80    list(APPEND all_deps ${recursive_deps})
81  endforeach(dep)
82  list(REMOVE_DUPLICATES all_deps)
83  set(objects "")
84  foreach(dep IN LISTS all_deps)
85    list(APPEND objects $<$<STREQUAL:$<TARGET_NAME_IF_EXISTS:${dep}>,${dep}>:$<TARGET_OBJECTS:${dep}>>)
86  endforeach(dep)
87
88  add_library(
89    ${target_name}
90    STATIC
91    ${objects}
92  )
93  set_target_properties(${target_name}  PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
94endfunction(add_entrypoint_library)
95
96# Rule to build a shared library of redirector objects.
97function(add_redirector_library target_name)
98  cmake_parse_arguments(
99    "REDIRECTOR_LIBRARY"
100    ""
101    ""
102    "DEPENDS"
103    ${ARGN}
104  )
105
106  set(obj_files "")
107  foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
108    # TODO: Ensure that each dep is actually a add_redirector_object target.
109    list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
110  endforeach(dep)
111
112  # TODO: Call the linker explicitly instead of calling the compiler driver to
113  # prevent DT_NEEDED on C++ runtime.
114  add_library(
115    ${target_name}
116    EXCLUDE_FROM_ALL
117    SHARED
118    ${obj_files}
119  )
120  set_target_properties(${target_name}  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
121  target_link_libraries(${target_name}  -nostdlib -lc -lm)
122  set_target_properties(${target_name}  PROPERTIES LINKER_LANGUAGE "C")
123endfunction(add_redirector_library)
124
125set(HDR_LIBRARY_TARGET_TYPE "HDR_LIBRARY")
126
127# Internal function, used by `add_header_library`.
128function(create_header_library fq_target_name)
129  cmake_parse_arguments(
130    "ADD_HEADER"
131    "" # Optional arguments
132    "" # Single value arguments
133    "HDRS;DEPENDS;FLAGS" # Multi-value arguments
134    ${ARGN}
135  )
136
137  if(NOT ADD_HEADER_HDRS)
138    message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
139  endif()
140
141  set(FULL_HDR_PATHS "")
142  # TODO: Remove this foreach block when we can switch to the new
143  # version of the CMake policy CMP0076.
144  foreach(hdr IN LISTS ADD_HEADER_HDRS)
145    list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr})
146  endforeach()
147
148  if(SHOW_INTERMEDIATE_OBJECTS)
149    message(STATUS "Adding header library ${fq_target_name}")
150    if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
151      foreach(dep IN LISTS ADD_HEADER_DEPENDS)
152        message(STATUS "  ${fq_target_name} depends on ${dep}")
153      endforeach()
154    endif()
155  endif()
156  set(interface_target_name "${fq_target_name}.__header_library__")
157
158  add_library(${interface_target_name} INTERFACE)
159  target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS})
160  if(ADD_HEADER_DEPENDS)
161    add_dependencies(${interface_target_name} ${ADD_HEADER_DEPENDS})
162  endif()
163  set_target_properties(
164    ${interface_target_name}
165    PROPERTIES
166      INTERFACE_FLAGS "${ADD_HEADER_FLAGS}"
167  )
168
169  add_custom_target(${fq_target_name})
170  add_dependencies(${fq_target_name} ${interface_target_name})
171  set_target_properties(
172    ${fq_target_name}
173    PROPERTIES
174      TARGET_TYPE "${HDR_LIBRARY_TARGET_TYPE}"
175      DEPS "${ADD_HEADER_DEPENDS}"
176      FLAGS "${ADD_HEADER_FLAGS}"
177  )
178endfunction(create_header_library)
179
180# Rule to add header only libraries.
181# Usage
182#    add_header_library(
183#      <target name>
184#      HDRS  <list of .h files part of the library>
185#      DEPENDS <list of dependencies>
186#      FLAGS <list of flags>
187#    )
188
189# Internal function, used by `add_header_library`.
190function(expand_flags_for_header_library target_name flags)
191  cmake_parse_arguments(
192    "EXPAND_FLAGS"
193    "IGNORE_MARKER" # Optional arguments
194    "" # Single-value arguments
195    "DEPENDS;FLAGS" # Multi-value arguments
196    ${ARGN}
197  )
198
199  list(LENGTH flags nflags)
200  if(NOT ${nflags})
201    create_header_library(
202      ${target_name}
203      DEPENDS ${EXPAND_FLAGS_DEPENDS}
204      FLAGS ${EXPAND_FLAGS_FLAGS}
205      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
206    )
207    return()
208  endif()
209
210  list(GET flags 0 flag)
211  list(REMOVE_AT flags 0)
212  extract_flag_modifier(${flag} real_flag modifier)
213
214  if(NOT "${modifier}" STREQUAL "NO")
215    expand_flags_for_header_library(
216      ${target_name}
217      "${flags}"
218      DEPENDS ${EXPAND_FLAGS_DEPENDS} IGNORE_MARKER
219      FLAGS ${EXPAND_FLAGS_FLAGS} IGNORE_MARKER
220      ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
221    )
222  endif()
223
224  if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")
225    return()
226  endif()
227
228  set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})
229  list(REMOVE_ITEM NEW_FLAGS ${flag})
230  get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})
231
232  # Only target with `flag` has `.__NO_flag` target, `flag__NO` and
233  # `flag__ONLY` do not.
234  if("${modifier}" STREQUAL "")
235    set(TARGET_NAME "${target_name}.__NO_${flag}")
236  else()
237    set(TARGET_NAME "${target_name}")
238  endif()
239
240  expand_flags_for_header_library(
241    ${TARGET_NAME}
242    "${flags}"
243    DEPENDS ${NEW_DEPS} IGNORE_MARKER
244    FLAGS ${NEW_FLAGS} IGNORE_MARKER
245    ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}
246  )
247endfunction(expand_flags_for_header_library)
248
249function(add_header_library target_name)
250  cmake_parse_arguments(
251    "ADD_TO_EXPAND"
252    "" # Optional arguments
253    "" # Single value arguments
254    "DEPENDS;FLAGS" # Multi-value arguments
255    ${ARGN}
256  )
257
258  get_fq_target_name(${target_name} fq_target_name)
259
260  if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))
261    message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")
262  endif()
263
264  get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})
265  get_flags_from_dep_list(deps_flag_list ${fq_deps_list})
266
267  list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})
268  remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)
269  list(SORT flags)
270
271  if(SHOW_INTERMEDIATE_OBJECTS AND flags)
272    message(STATUS "Header library ${fq_target_name} has FLAGS: ${flags}")
273  endif()
274
275  expand_flags_for_header_library(
276    ${fq_target_name}
277    "${flags}"
278    DEPENDS ${fq_deps_list} IGNORE_MARKER
279    FLAGS ${flags} IGNORE_MARKER
280    ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}
281  )
282endfunction(add_header_library)
283