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