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