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()
39endfunction(collect_object_file_deps)
40
41# A rule to build a library from a collection of entrypoint objects.
42# Usage:
43#     add_entrypoint_library(
44#       DEPENDS <list of add_entrypoint_object targets>
45#     )
46#
47# NOTE: If one wants an entrypoint to be availabe in a library, then they will
48# have to list the entrypoint target explicitly in the DEPENDS list. Implicit
49# entrypoint dependencies will not be added to the library.
50function(add_entrypoint_library target_name)
51  cmake_parse_arguments(
52    "ENTRYPOINT_LIBRARY"
53    "" # No optional arguments
54    "" # No single value arguments
55    "DEPENDS" # Multi-value arguments
56    ${ARGN}
57  )
58  if(NOT ENTRYPOINT_LIBRARY_DEPENDS)
59    message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list "
60                        "of 'add_entrypoint_object' targets.")
61  endif()
62
63  get_fq_deps_list(fq_deps_list ${ENTRYPOINT_LIBRARY_DEPENDS})
64  set(all_deps "")
65  foreach(dep IN LISTS fq_deps_list)
66    get_target_property(dep_type ${dep} "TARGET_TYPE")
67    if(NOT (${dep_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}))
68      message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is "
69                          "not an 'add_entrypoint_object' target.")
70    endif()
71    collect_object_file_deps(${dep} recursive_deps)
72    list(APPEND all_deps ${recursive_deps})
73  endforeach(dep)
74  list(REMOVE_DUPLICATES all_deps)
75  set(objects "")
76  foreach(dep IN LISTS all_deps)
77    list(APPEND objects $<TARGET_OBJECTS:${dep}>)
78  endforeach(dep)
79  add_library(
80    ${target_name}
81    STATIC
82    ${objects}
83  )
84  set_target_properties(
85    ${target_name}
86    PROPERTIES
87      ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
88  )
89endfunction(add_entrypoint_library)
90
91# Rule to build a shared library of redirector objects.
92function(add_redirector_library target_name)
93  cmake_parse_arguments(
94    "REDIRECTOR_LIBRARY"
95    ""
96    ""
97    "DEPENDS"
98    ${ARGN}
99  )
100
101  set(obj_files "")
102  foreach(dep IN LISTS REDIRECTOR_LIBRARY_DEPENDS)
103    # TODO: Ensure that each dep is actually a add_redirector_object target.
104    list(APPEND obj_files $<TARGET_OBJECTS:${dep}>)
105  endforeach(dep)
106
107  # TODO: Call the linker explicitly instead of calling the compiler driver to
108  # prevent DT_NEEDED on C++ runtime.
109  add_library(
110    ${target_name}
111    EXCLUDE_FROM_ALL
112    SHARED
113    ${obj_files}
114  )
115  set_target_properties(${target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
116
117  target_link_libraries(
118    ${target_name}
119    -nostdlib -lc -lm
120  )
121
122  set_target_properties(
123    ${target_name}
124    PROPERTIES
125      LINKER_LANGUAGE "C"
126  )
127endfunction(add_redirector_library)
128
129set(HDR_LIBRARY_TARGET_TYPE "HDR_LIBRARY")
130
131# Rule to add header only libraries.
132# Usage
133#    add_header_library(
134#      <target name>
135#      HDRS  <list of .h files part of the library>
136#      DEPENDS <list of dependencies>
137#    )
138function(add_header_library target_name)
139  cmake_parse_arguments(
140    "ADD_HEADER"
141    "" # No optional arguments
142    "" # No Single value arguments
143    "HDRS;DEPENDS" # Multi-value arguments
144    ${ARGN}
145  )
146
147  if(NOT ADD_HEADER_HDRS)
148    message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.")
149  endif()
150
151  get_fq_target_name(${target_name} fq_target_name)
152
153  set(FULL_HDR_PATHS "")
154  # TODO: Remove this foreach block when we can switch to the new
155  # version of the CMake policy CMP0076.
156  foreach(hdr IN LISTS ADD_HEADER_HDRS)
157    list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr})
158  endforeach()
159
160  set(interface_target_name "${fq_target_name}.__header_library__")
161
162  add_library(${interface_target_name} INTERFACE)
163  target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS})
164  get_fq_deps_list(fq_deps_list ${ADD_HEADER_DEPENDS})
165  if(ADD_HEADER_DEPENDS)
166    add_dependencies(${interface_target_name} ${fq_deps_list})
167  endif()
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" "${fq_deps_list}"
176  )
177endfunction(add_header_library)
178