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