xref: /llvm-project/libc/cmake/modules/LLVMLibCObjectRules.cmake (revision 431ea2d076f8a5ca35b2c293dd5d62f5ce083f45)
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#       [ALIAS] <If this object library is an alias for another object library.>
11#       DEPENDS <list of dependencies; Should be a single item for ALIAS libraries>
12#       COMPILE_OPTIONS <optional list of special compile options for this target>
13#       FLAGS <optional list of flags>
14function(create_object_library fq_target_name)
15  cmake_parse_arguments(
16    "ADD_OBJECT"
17    "ALIAS;NO_GPU_BUNDLE" # optional arguments
18    "CXX_STANDARD" # Single value arguments
19    "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS;FLAGS" # Multivalue arguments
20    ${ARGN}
21  )
22
23  get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS})
24
25  if(ADD_OBJECT_ALIAS)
26    if(ADD_OBJECT_SRCS OR ADD_OBJECT_HDRS)
27      message(FATAL_ERROR
28              "${fq_target_name}: object library alias cannot have SRCS and/or HDRS.")
29    endif()
30    list(LENGTH fq_deps_list depends_size)
31    if(NOT ${depends_size} EQUAL 1)
32      message(FATAL_ERROR
33              "${fq_targe_name}: object library alias should have exactly one DEPENDS.")
34    endif()
35    add_library(
36      ${fq_target_name}
37      ALIAS
38      ${fq_deps_list}
39    )
40    return()
41  endif()
42
43  if(NOT ADD_OBJECT_SRCS)
44    message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")
45  endif()
46
47  if(NOT ADD_OBJECT_CXX_STANDARD)
48    set(ADD_OBJECT_CXX_STANDARD ${CMAKE_CXX_STANDARD})
49  endif()
50
51  set(internal_target_name ${fq_target_name}.__internal__)
52  set(public_packaging_for_internal "-DLIBC_COPT_PUBLIC_PACKAGING")
53
54  _get_common_compile_options(compile_options "${ADD_OBJECT_FLAGS}")
55  list(APPEND compile_options ${ADD_OBJECT_COMPILE_OPTIONS})
56
57  add_library(
58    ${fq_target_name}
59    EXCLUDE_FROM_ALL
60    OBJECT
61    ${ADD_OBJECT_SRCS}
62    ${ADD_OBJECT_HDRS}
63  )
64  target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
65  target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
66  target_compile_options(${fq_target_name} PRIVATE ${compile_options})
67
68  if(SHOW_INTERMEDIATE_OBJECTS)
69    message(STATUS "Adding object library ${fq_target_name}")
70    if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
71      foreach(dep IN LISTS ADD_OBJECT_DEPENDS)
72        message(STATUS "  ${fq_target_name} depends on ${dep}")
73      endforeach()
74    endif()
75  endif()
76
77  list(APPEND fq_deps_list libc.src.__support.macros.config)
78  list(REMOVE_DUPLICATES fq_deps_list)
79  add_dependencies(${fq_target_name} ${fq_deps_list})
80  # Add deps as link libraries to inherit interface compile and link options.
81  target_link_libraries(${fq_target_name} PUBLIC ${fq_deps_list})
82
83  set_target_properties(
84    ${fq_target_name}
85    PROPERTIES
86      TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE}
87      CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD}
88      DEPS "${fq_deps_list}"
89      FLAGS "${ADD_OBJECT_FLAGS}"
90  )
91
92  # If we built a separate internal target we want to use those target objects
93  # for testing instead of the exported target.
94  set(target_objects ${fq_target_name})
95  if(TARGET ${internal_target_name})
96    set(target_objects ${internal_target_name})
97  endif()
98
99  set_target_properties(
100    ${fq_target_name}
101    PROPERTIES
102      OBJECT_FILES "$<TARGET_OBJECTS:${target_objects}>"
103  )
104endfunction(create_object_library)
105
106function(add_object_library target_name)
107  add_target_with_flags(
108    ${target_name}
109    CREATE_TARGET create_object_library
110    ${ARGN})
111endfunction(add_object_library)
112
113set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")
114set(ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE "ENTRYPOINT_OBJ_VENDOR")
115
116# A rule for entrypoint object targets.
117# Usage:
118#     add_entrypoint_object(
119#       <target_name>
120#       [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias.
121#       [NAME] <the C name of the entrypoint if different from target_name>
122#       SRCS <list of .cpp files>
123#       HDRS <list of .h files>
124#       DEPENDS <list of dependencies>
125#       COMPILE_OPTIONS <optional list of special compile options for this target>
126#       SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>
127#       FLAGS <optional list of flags>
128#     )
129function(create_entrypoint_object fq_target_name)
130  cmake_parse_arguments(
131    "ADD_ENTRYPOINT_OBJ"
132    "ALIAS;REDIRECTED;VENDOR" # Optional argument
133    "NAME;CXX_STANDARD" # Single value arguments
134    "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS"  # Multi value arguments
135    ${ARGN}
136  )
137
138  set(entrypoint_target_type ${ENTRYPOINT_OBJ_TARGET_TYPE})
139  if(${ADD_ENTRYPOINT_OBJ_VENDOR})
140    # TODO: We currently rely on external definitions of certain math functions
141    # provided by GPU vendors like AMD or Nvidia. We need to mark these so we
142    # don't end up running tests on these. In the future all of these should be
143    # implemented and this can be removed.
144    set(entrypoint_target_type ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})
145  endif()
146  list(FIND TARGET_ENTRYPOINT_NAME_LIST ${ADD_ENTRYPOINT_OBJ_NAME} entrypoint_name_index)
147  if(${entrypoint_name_index} EQUAL -1)
148    add_custom_target(${fq_target_name})
149    set_target_properties(
150      ${fq_target_name}
151      PROPERTIES
152        "ENTRYPOINT_NAME" ${ADD_ENTRYPOINT_OBJ_NAME}
153        "TARGET_TYPE" ${entrypoint_target_type}
154        "OBJECT_FILE" ""
155        "OBJECT_FILE_RAW" ""
156        "DEPS" ""
157        "SKIPPED" "YES"
158    )
159    if(LIBC_CMAKE_VERBOSE_LOGGING)
160      message(STATUS "Skipping libc entrypoint ${fq_target_name}.")
161    endif()
162    return()
163  endif()
164
165  set(internal_target_name ${fq_target_name}.__internal__)
166
167  if(ADD_ENTRYPOINT_OBJ_ALIAS)
168    # Alias targets help one add aliases to other entrypoint object targets.
169    # One can use alias targets setup OS/machine independent entrypoint targets.
170    list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size)
171    if(NOT (${deps_size} EQUAL "1"))
172      message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.")
173    endif()
174    list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target)
175    get_fq_dep_name(fq_dep_name ${dep_target})
176
177    if(SHOW_INTERMEDIATE_OBJECTS)
178      message(STATUS "Adding entrypoint object ${fq_target_name} as an alias of"
179              " ${fq_dep_name}")
180    endif()
181
182    if(NOT TARGET ${fq_dep_name})
183      message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; "
184                      "Target ${target_name} will be ignored.")
185      return()
186    endif()
187
188    get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE")
189    if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE} OR
190                               ${obj_type} STREQUAL ${ENTRYPOINT_OBJ_VENDOR_TARGET_TYPE})))
191      message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.")
192    endif()
193
194    get_target_property(object_file ${fq_dep_name} "OBJECT_FILE")
195    get_target_property(object_file_raw ${fq_dep_name} "OBJECT_FILE_RAW")
196
197    # If the system cannot build the GPU tests we simply make a dummy target.
198    if(LIBC_TARGET_OS_IS_GPU AND LIBC_GPU_TESTS_DISABLED)
199      add_custom_target(${internal_target_name})
200    else()
201      add_library(
202        ${internal_target_name}
203        EXCLUDE_FROM_ALL
204        OBJECT
205        ${object_file_raw}
206      )
207    endif()
208
209    add_dependencies(${internal_target_name} ${fq_dep_name})
210    add_library(
211      ${fq_target_name}
212      EXCLUDE_FROM_ALL
213      OBJECT
214      ${object_file}
215    )
216    add_dependencies(${fq_target_name} ${fq_dep_name} ${internal_target_name})
217    set_target_properties(
218      ${fq_target_name}
219      PROPERTIES
220        ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}
221        TARGET_TYPE ${entrypoint_target_type}
222        IS_ALIAS "YES"
223        OBJECT_FILE ""
224        OBJECT_FILE_RAW ""
225        DEPS "${fq_dep_name}"
226        FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
227    )
228    return()
229  endif()
230
231  if(NOT ADD_ENTRYPOINT_OBJ_SRCS)
232    message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")
233  endif()
234  if(NOT ADD_ENTRYPOINT_OBJ_CXX_STANDARD)
235    set(ADD_ENTRYPOINT_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD})
236  endif()
237
238  _get_common_compile_options(common_compile_options "${ADD_ENTRYPOINT_OBJ_FLAGS}")
239  list(APPEND common_compile_options ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS})
240  get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})
241  set(full_deps_list ${fq_deps_list} libc.src.__support.common)
242
243  if(SHOW_INTERMEDIATE_OBJECTS)
244    message(STATUS "Adding entrypoint object ${fq_target_name}")
245    if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")
246      foreach(dep IN LISTS ADD_ENTRYPOINT_OBJ_DEPENDS)
247        message(STATUS "  ${fq_target_name} depends on ${dep}")
248      endforeach()
249    endif()
250  endif()
251
252  add_library(
253    ${internal_target_name}
254    # TODO: We don't need an object library for internal consumption.
255    # A future change should switch this to a normal static library.
256    EXCLUDE_FROM_ALL
257    OBJECT
258    ${ADD_ENTRYPOINT_OBJ_SRCS}
259    ${ADD_ENTRYPOINT_OBJ_HDRS}
260  )
261  target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options})
262  target_include_directories(${internal_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
263  target_include_directories(${internal_target_name} PRIVATE ${LIBC_SOURCE_DIR})
264  add_dependencies(${internal_target_name} ${full_deps_list})
265  target_link_libraries(${internal_target_name} ${full_deps_list})
266
267  add_library(
268    ${fq_target_name}
269    # We want an object library as the objects will eventually get packaged into
270    # an archive (like libc.a).
271    EXCLUDE_FROM_ALL
272    OBJECT
273    ${ADD_ENTRYPOINT_OBJ_SRCS}
274    ${ADD_ENTRYPOINT_OBJ_HDRS}
275  )
276  target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -DLIBC_COPT_PUBLIC_PACKAGING)
277  target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})
278  target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
279  add_dependencies(${fq_target_name} ${full_deps_list})
280  target_link_libraries(${fq_target_name} ${full_deps_list})
281
282  # Builtin recognition causes issues when trying to implement the builtin
283  # functions themselves. The GPU backends do not use libcalls so we disable the
284  # known problematic ones on the entrypoints that implement them.
285  if(LIBC_TARGET_OS_IS_GPU)
286    set(libc_builtins bcmp strlen memmem bzero memcmp memcpy memmem memmove
287                      memset strcmp strstr)
288    if(${ADD_ENTRYPOINT_OBJ_NAME} IN_LIST libc_builtins)
289      target_compile_options(${fq_target_name} PRIVATE -fno-builtin-${ADD_ENTRYPOINT_OBJ_NAME})
290    endif()
291  endif()
292
293  set_target_properties(
294    ${fq_target_name}
295    PROPERTIES
296      ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}
297      TARGET_TYPE ${entrypoint_target_type}
298      OBJECT_FILE "$<TARGET_OBJECTS:${fq_target_name}>"
299      CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}
300      DEPS "${fq_deps_list}"
301      FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
302  )
303
304  if(TARGET ${internal_target_name})
305    set_target_properties(
306      ${internal_target_name}
307      PROPERTIES
308        CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}
309        FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"
310    )
311    set_target_properties(
312      ${fq_target_name}
313      PROPERTIES
314        # TODO: We don't need to list internal object files if the internal
315        # target is a normal static library.
316        OBJECT_FILE_RAW "$<TARGET_OBJECTS:${internal_target_name}>"
317    )
318  endif()
319
320  if(LLVM_LIBC_ENABLE_LINTING AND TARGET ${internal_target_name})
321    if(NOT LLVM_LIBC_CLANG_TIDY)
322      message(FATAL_ERROR "Something is wrong!  LLVM_LIBC_ENABLE_LINTING is "
323              "ON but LLVM_LIBC_CLANG_TIDY is not set.")
324    endif()
325
326    # We only want a second invocation of clang-tidy to run
327    # restrict-system-libc-headers if the compiler-resource-dir was set in
328    # order to prevent false-positives due to a mismatch between the host
329    # compiler and the compiled clang-tidy.
330    if(COMPILER_RESOURCE_DIR)
331      # We run restrict-system-libc-headers with --system-headers to prevent
332      # transitive inclusion through compler provided headers.
333      set(restrict_system_headers_check_invocation
334        COMMAND ${LLVM_LIBC_CLANG_TIDY} --system-headers
335        --checks="-*,llvmlibc-restrict-system-libc-headers"
336        # We explicitly set the resource dir here to match the
337        # resource dir of the host compiler.
338        "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}"
339        --quiet
340        -p ${PROJECT_BINARY_DIR}
341        ${ADD_ENTRYPOINT_OBJ_SRCS}
342      )
343    else()
344      set(restrict_system_headers_check_invocation
345        COMMAND ${CMAKE_COMMAND} -E echo "Header file check skipped")
346    endif()
347
348    add_custom_target(
349      ${fq_target_name}.__lint__
350      # --quiet is used to surpress warning statistics from clang-tidy like:
351      #     Suppressed X warnings (X in non-user code).
352      # There seems to be a bug in clang-tidy where by even with --quiet some
353      # messages from clang's own diagnostics engine leak through:
354      #     X warnings generated.
355      # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress
356      # these.
357      COMMAND ${LLVM_LIBC_CLANG_TIDY}
358              "--extra-arg=-fno-caret-diagnostics" --quiet
359              # Path to directory containing compile_commands.json
360              -p ${PROJECT_BINARY_DIR}
361              ${ADD_ENTRYPOINT_OBJ_SRCS}
362      # See above: this might be a second invocation of clang-tidy depending on
363      # the conditions above.
364      ${restrict_system_headers_check_invocation}
365      # We have two options for running commands, add_custom_command and
366      # add_custom_target. We don't want to run the linter unless source files
367      # have changed. add_custom_target explicitly runs everytime therefore we
368      # use add_custom_command. This function requires an output file and since
369      # linting doesn't produce a file, we create a dummy file using a
370      # crossplatform touch.
371      COMMENT "Linting... ${fq_target_name}"
372      DEPENDS ${internal_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}
373      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
374    )
375    add_dependencies(libc-lint ${fq_target_name}.__lint__)
376  endif()
377
378endfunction(create_entrypoint_object)
379
380function(add_entrypoint_object target_name)
381  cmake_parse_arguments(
382    "ADD_ENTRYPOINT_OBJ"
383    "" # Optional arguments
384    "NAME" # Single value arguments
385    "" # Multi-value arguments
386    ${ARGN}
387  )
388
389  if(NOT ADD_ENTRYPOINT_OBJ_NAME)
390    set(ADD_ENTRYPOINT_OBJ_NAME ${target_name})
391  endif()
392
393  add_target_with_flags(
394    ${target_name}
395    NAME ${ADD_ENTRYPOINT_OBJ_NAME}
396    CREATE_TARGET create_entrypoint_object
397    ${ADD_ENTRYPOINT_OBJ_UNPARSED_ARGUMENTS}
398  )
399endfunction(add_entrypoint_object)
400
401set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT")
402
403# A rule for external entrypoint targets.
404# Usage:
405#     add_entrypoint_external(
406#       <target_name>
407#       DEPENDS <list of dependencies>
408#     )
409function(add_entrypoint_external target_name)
410  cmake_parse_arguments(
411    "ADD_ENTRYPOINT_EXT"
412    "" # No optional arguments
413    "" # No single value arguments
414    "DEPENDS"  # Multi value arguments
415    ${ARGN}
416  )
417  get_fq_target_name(${target_name} fq_target_name)
418  set(entrypoint_name ${target_name})
419
420  add_custom_target(${fq_target_name})
421  set_target_properties(
422    ${fq_target_name}
423    PROPERTIES
424      "ENTRYPOINT_NAME" ${entrypoint_name}
425      "TARGET_TYPE" ${ENTRYPOINT_EXT_TARGET_TYPE}
426      "DEPS" "${ADD_ENTRYPOINT_EXT_DEPENDS}"
427  )
428
429endfunction(add_entrypoint_external)
430
431# Rule build a redirector object file.
432function(add_redirector_object target_name)
433  cmake_parse_arguments(
434    "REDIRECTOR_OBJECT"
435    "" # No optional arguments
436    "SRC" # The cpp file in which the redirector is defined.
437    "" # No multivalue arguments
438    ${ARGN}
439  )
440  if(NOT REDIRECTOR_OBJECT_SRC)
441    message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.")
442  endif()
443
444  add_library(
445    ${target_name}
446    EXCLUDE_FROM_ALL
447    OBJECT
448    ${REDIRECTOR_OBJECT_SRC}
449  )
450  target_compile_options(
451    ${target_name}
452    BEFORE PRIVATE -fPIC ${LIBC_COMPILE_OPTIONS_DEFAULT}
453  )
454endfunction(add_redirector_object)
455
456# Helper to define a function with multiple implementations
457# - Computes flags to satisfy required/rejected features and arch,
458# - Declares an entry point,
459# - Attach the REQUIRE_CPU_FEATURES property to the target,
460# - Add the fully qualified target to `${name}_implementations` global property for tests.
461function(add_implementation name impl_name)
462  cmake_parse_arguments(
463    "ADD_IMPL"
464    "" # Optional arguments
465    "" # Single value arguments
466    "REQUIRE;SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;MLLVM_COMPILE_OPTIONS" # Multi value arguments
467    ${ARGN})
468
469  if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
470    # Note that '-mllvm' needs to be prefixed with 'SHELL:' to prevent CMake flag deduplication.
471    foreach(opt IN LISTS ADD_IMPL_MLLVM_COMPILE_OPTIONS)
472      list(APPEND ADD_IMPL_COMPILE_OPTIONS "SHELL:-mllvm ${opt}")
473    endforeach()
474  endif()
475
476  if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
477    # Prevent warning when passing x86 SIMD types as template arguments.
478    # e.g. "warning: ignoring attributes on template argument ‘__m128i’ [-Wignored-attributes]"
479    list(APPEND ADD_IMPL_COMPILE_OPTIONS "-Wno-ignored-attributes")
480  endif()
481
482  add_entrypoint_object(${impl_name}
483    NAME ${name}
484    SRCS ${ADD_IMPL_SRCS}
485    HDRS ${ADD_IMPL_HDRS}
486    DEPENDS ${ADD_IMPL_DEPENDS}
487    COMPILE_OPTIONS ${libc_opt_high_flag} ${ADD_IMPL_COMPILE_OPTIONS}
488  )
489  get_fq_target_name(${impl_name} fq_target_name)
490  set_target_properties(${fq_target_name} PROPERTIES REQUIRE_CPU_FEATURES "${ADD_IMPL_REQUIRE}")
491  set_property(GLOBAL APPEND PROPERTY "${name}_implementations" "${fq_target_name}")
492endfunction()
493