1set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY")
2
3function(_get_common_compile_options output_var)
4  set(compile_options ${LLVM_CXX_STD_default} ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN})
5  if(NOT ${LIBC_TARGET_OS} STREQUAL "windows")
6    set(compile_options ${compile_options} -fpie -ffreestanding)
7  endif()
8  set(${output_var} ${compile_options} PARENT_SCOPE)
9endfunction()
10
11# Rule which is essentially a wrapper over add_library to compile a set of
12# sources to object files.
13# Usage:
14#     add_object_library(
15#       <target_name>
16#       HDRS <list of header files>
17#       SRCS <list of source files>
18#       DEPENDS <list of dependencies>
19#       COMPILE_OPTIONS <optional list of special compile options for this target>
20function(add_object_library target_name)
21  cmake_parse_arguments(
22    "ADD_OBJECT"
23    "" # No option arguments
24    "" # Single value arguments
25    "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS" # Multivalue arguments
26    ${ARGN}
27  )
28
29  if(NOT ADD_OBJECT_SRCS)
30    message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")
31  endif()
32
33  get_fq_target_name(${target_name} fq_target_name)
34  add_library(
35    ${fq_target_name}
36    EXCLUDE_FROM_ALL
37    OBJECT
38    ${ADD_OBJECT_SRCS}
39    ${ADD_OBJECT_HDRS}
40  )
41  target_include_directories(
42    ${fq_target_name}
43    PRIVATE
44      ${LIBC_BUILD_DIR}/include
45      ${LIBC_SOURCE_DIR}
46      ${LIBC_BUILD_DIR}
47  )
48  _get_common_compile_options(compile_options ${ADD_OBJECT_COMPILE_OPTIONS})
49  target_compile_options(${fq_target_name} PRIVATE ${compile_options})
50
51  get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS})
52  if(fq_deps_list)
53    add_dependencies(${fq_target_name} ${fq_deps_list})
54  endif()
55
56  set_target_properties(
57    ${fq_target_name}
58    PROPERTIES
59      "TARGET_TYPE" ${OBJECT_LIBRARY_TARGET_TYPE}
60      "OBJECT_FILES" "$<TARGET_OBJECTS:${fq_target_name}>"
61      "DEPS" "${fq_deps_list}"
62  )
63endfunction(add_object_library)
64
65set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
66
67# A rule for entrypoint object targets.
68# Usage:
69#     add_entrypoint_object(
70#       <target_name>
71#       [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias.
72#       [NAME] <the C name of the entrypoint if different from target_name>
73#       SRCS <list of .cpp files>
74#       HDRS <list of .h files>
75#       DEPENDS <list of dependencies>
76#       COMPILE_OPTIONS <optional list of special compile options for this target>
77#       SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>
78#     )
79function(add_entrypoint_object target_name)
80  cmake_parse_arguments(
81    "ADD_ENTRYPOINT_OBJ"
82    "ALIAS;REDIRECTED" # Optional argument
83    "NAME" # Single value arguments
84    "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS"  # Multi value arguments
85    ${ARGN}
86  )
87
88  get_fq_target_name(${target_name} fq_target_name)
89  set(entrypoint_name ${target_name})
90  if(ADD_ENTRYPOINT_OBJ_NAME)
91    set(entrypoint_name ${ADD_ENTRYPOINT_OBJ_NAME})
92  endif()
93
94  list(FIND TARGET_ENTRYPOINT_NAME_LIST ${entrypoint_name} entrypoint_name_index)
95  if(${entrypoint_name_index} EQUAL -1)
96    add_custom_target(${fq_target_name})
97    set_target_properties(
98      ${fq_target_name}
99      PROPERTIES
100        "ENTRYPOINT_NAME" ${entrypoint_name}
101        "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
102        "OBJECT_FILE" ""
103        "OBJECT_FILE_RAW" ""
104        "DEPS" ""
105        "SKIPPED" "YES"
106    )
107    message(STATUS "Skipping libc entrypoint ${fq_target_name}.")
108    return()
109  endif()
110
111  if(ADD_ENTRYPOINT_OBJ_ALIAS)
112    # Alias targets help one add aliases to other entrypoint object targets.
113    # One can use alias targets setup OS/machine independent entrypoint targets.
114    list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size)
115    if(NOT (${deps_size} EQUAL "1"))
116      message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.")
117    endif()
118    list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target)
119    get_fq_dep_name(fq_dep_name ${dep_target})
120    if(NOT TARGET ${fq_dep_name})
121      message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; "
122                      "Target ${target_name} will be ignored.")
123      return()
124    endif()
125
126    get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE")
127    if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})))
128      message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.")
129    endif()
130
131    add_custom_target(${fq_target_name})
132    add_dependencies(${fq_target_name} ${fq_dep_name})
133    get_target_property(object_file ${fq_dep_name} "OBJECT_FILE")
134    get_target_property(object_file_raw ${fq_dep_name} "OBJECT_FILE_RAW")
135    set_target_properties(
136      ${fq_target_name}
137      PROPERTIES
138        "ENTRYPOINT_NAME" ${entrypoint_name}
139        "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
140        "IS_ALIAS" "YES"
141        "OBJECT_FILE" ""
142        "OBJECT_FILE_RAW" ""
143        "DEPS" "${fq_dep_name}"
144    )
145    return()
146  endif()
147
148  if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
149    message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
150  endif()
151  if(NOT ADD_ENTRYPOINT_OBJ_HDRS)
152    message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.")
153  endif()
154
155  _get_common_compile_options(common_compile_options ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS})
156  set(internal_target_name ${fq_target_name}.__internal__)
157  set(include_dirs ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR})
158  get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})
159  set(full_deps_list ${fq_deps_list} libc.src.__support.common)
160
161  add_library(
162    ${internal_target_name}
163    # TODO: We don't need an object library for internal consumption.
164    # A future change should switch this to a normal static library.
165    EXCLUDE_FROM_ALL
166    OBJECT
167    ${ADD_ENTRYPOINT_OBJ_SRCS}
168    ${ADD_ENTRYPOINT_OBJ_HDRS}
169  )
170  target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options})
171  target_include_directories(${internal_target_name} PRIVATE ${include_dirs})
172  add_dependencies(${internal_target_name} ${full_deps_list})
173
174  add_library(
175    ${fq_target_name}
176    # We want an object library as the objects will eventually get packaged into
177    # an archive (like libc.a).
178    EXCLUDE_FROM_ALL
179    OBJECT
180    ${ADD_ENTRYPOINT_OBJ_SRCS}
181    ${ADD_ENTRYPOINT_OBJ_HDRS}
182  )
183  target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -DLLVM_LIBC_PUBLIC_PACKAGING)
184  target_include_directories(${fq_target_name} PRIVATE ${include_dirs})
185  add_dependencies(${fq_target_name} ${full_deps_list})
186
187  set_target_properties(
188    ${fq_target_name}
189    PROPERTIES
190      "ENTRYPOINT_NAME" ${entrypoint_name}
191      "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE}
192      "OBJECT_FILE" $<TARGET_OBJECTS:${fq_target_name}>
193      # TODO: We don't need to list internal object files if the internal
194      # target is a normal static library.
195      "OBJECT_FILE_RAW" $<TARGET_OBJECTS:${internal_target_name}>
196      "DEPS" "${fq_deps_list}"
197  )
198
199  if(LLVM_LIBC_ENABLE_LINTING)
200
201    # We only want a second invocation of clang-tidy to run
202    # restrict-system-libc-headers if the compiler-resource-dir was set in
203    # order to prevent false-positives due to a mismatch between the host
204    # compiler and the compiled clang-tidy.
205    if(COMPILER_RESOURCE_DIR)
206      # We run restrict-system-libc-headers with --system-headers to prevent
207      # transitive inclusion through compler provided headers.
208      set(restrict_system_headers_check_invocation
209        COMMAND $<TARGET_FILE:clang-tidy> --system-headers
210        --checks="-*,llvmlibc-restrict-system-libc-headers"
211        # We explicitly set the resource dir here to match the
212        # resource dir of the host compiler.
213        "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}"
214        --quiet
215        -p ${PROJECT_BINARY_DIR}
216        ${ADD_ENTRYPOINT_OBJ_SRCS}
217      )
218    else()
219      set(restrict_system_headers_check_invocation
220        COMMAND ${CMAKE_COMMAND} -E echo "Header file check skipped")
221    endif()
222
223    set(lint_timestamp "${CMAKE_CURRENT_BINARY_DIR}/.${target_name}.__lint_timestamp__")
224    add_custom_command(
225      OUTPUT ${lint_timestamp}
226      # --quiet is used to surpress warning statistics from clang-tidy like:
227      #     Suppressed X warnings (X in non-user code).
228      # There seems to be a bug in clang-tidy where by even with --quiet some
229      # messages from clang's own diagnostics engine leak through:
230      #     X warnings generated.
231      # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress
232      # these.
233      COMMAND $<TARGET_FILE:clang-tidy>
234              "--extra-arg=-fno-caret-diagnostics" --quiet
235              # Path to directory containing compile_commands.json
236              -p ${PROJECT_BINARY_DIR}
237              ${ADD_ENTRYPOINT_OBJ_SRCS}
238      # See above: this might be a second invocation of clang-tidy depending on
239      # the conditions above.
240      ${restrict_system_headers_check_invocation}
241      # We have two options for running commands, add_custom_command and
242      # add_custom_target. We don't want to run the linter unless source files
243      # have changed. add_custom_target explicitly runs everytime therefore we
244      # use add_custom_command. This function requires an output file and since
245      # linting doesn't produce a file, we create a dummy file using a
246      # crossplatform touch.
247      COMMAND "${CMAKE_COMMAND}" -E touch ${lint_timestamp}
248      COMMENT "Linting... ${target_name}"
249      DEPENDS clang-tidy ${internal_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
250      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
251    )
252
253    add_custom_target(${fq_target_name}.__lint__
254      DEPENDS ${lint_timestamp})
255    add_dependencies(lint-libc ${fq_target_name}.__lint__)
256    add_dependencies(${fq_target_name} ${fq_target_name}.__lint__)
257  endif()
258
259endfunction(add_entrypoint_object)
260
261# Rule build a redirector object file.
262function(add_redirector_object target_name)
263  cmake_parse_arguments(
264    "REDIRECTOR_OBJECT"
265    "" # No optional arguments
266    "SRC" # The cpp file in which the redirector is defined.
267    "" # No multivalue arguments
268    ${ARGN}
269  )
270  if(NOT REDIRECTOR_OBJECT_SRC)
271    message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
272  endif()
273
274  add_library(
275    ${target_name}
276    EXCLUDE_FROM_ALL
277    OBJECT
278    ${REDIRECTOR_OBJECT_SRC}
279  )
280  target_compile_options(
281    ${target_name}
282    BEFORE PRIVATE -fPIC ${LIBC_COMPILE_OPTIONS_DEFAULT}
283  )
284endfunction(add_redirector_object)
285