510 lines · plain
1set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY")2set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ")3set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT")4 5# Rule to check if a list of dependencies contains any entrypoint objects. Returns a list in entrypoint_deps.6function(check_entrypoint_deps entrypoint_deps)7 set(PUBLIC_DEPS "")8 set(fq_deps_list "")9 list(APPEND fq_deps_list ${ARGN})10 11 #don't warn for deps that are allowed, such as errno12 set(ALLOWED_DEPS13 "libc.src.errno.errno"14 "libc.src.setjmp.longjmp"15 )16 list(REMOVE_ITEM fq_deps_list ${ALLOWED_DEPS})17 18 foreach(dep IN LISTS fq_deps_list)19 if(NOT TARGET ${dep})20 continue()21 endif()22 23 get_target_property(target_type ${dep} "TARGET_TYPE")24 if(${target_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE})25 list(APPEND PUBLIC_DEPS ${dep})26 endif()27 endforeach()28 set(${entrypoint_deps} ${PUBLIC_DEPS} PARENT_SCOPE)29endfunction()30 31 32# Rule which is essentially a wrapper over add_library to compile a set of33# sources to object files.34# Usage:35# add_object_library(36# <target_name>37# HDRS <list of header files>38# SRCS <list of source files>39# [ALIAS] <If this object library is an alias for another object library.>40# DEPENDS <list of dependencies; Should be a single item for ALIAS libraries>41# COMPILE_OPTIONS <optional list of special compile options for this target>42# FLAGS <optional list of flags>43function(create_object_library fq_target_name)44 cmake_parse_arguments(45 "ADD_OBJECT"46 "ALIAS;NO_GPU_BUNDLE" # optional arguments47 "CXX_STANDARD" # Single value arguments48 "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS;FLAGS" # Multivalue arguments49 ${ARGN}50 )51 52 get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS})53 54 if(ADD_OBJECT_ALIAS)55 if(ADD_OBJECT_SRCS OR ADD_OBJECT_HDRS)56 message(FATAL_ERROR57 "${fq_target_name}: object library alias cannot have SRCS and/or HDRS.")58 endif()59 list(LENGTH fq_deps_list depends_size)60 if(NOT ${depends_size} EQUAL 1)61 message(FATAL_ERROR62 "${fq_targe_name}: object library alias should have exactly one DEPENDS.")63 endif()64 add_library(65 ${fq_target_name}66 ALIAS67 ${fq_deps_list}68 )69 return()70 endif()71 72 if(NOT ADD_OBJECT_SRCS)73 message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.")74 endif()75 76 if(NOT ADD_OBJECT_CXX_STANDARD)77 set(ADD_OBJECT_CXX_STANDARD ${CMAKE_CXX_STANDARD})78 endif()79 80 set(internal_target_name ${fq_target_name}.__internal__)81 set(public_packaging_for_internal "-DLIBC_COPT_PUBLIC_PACKAGING")82 83 _get_common_compile_options(compile_options "${ADD_OBJECT_FLAGS}")84 list(APPEND compile_options ${ADD_OBJECT_COMPILE_OPTIONS})85 86 add_library(87 ${fq_target_name}88 EXCLUDE_FROM_ALL89 OBJECT90 ${ADD_OBJECT_SRCS}91 ${ADD_OBJECT_HDRS}92 )93 target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})94 target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})95 target_compile_options(${fq_target_name} PRIVATE ${compile_options})96 97 #loop through the deps, check if any have the TARGET_TYPE of ENTRYPOINT_OBJ_TARGET_TYPE, and print a warning if they do.98 if(LIBC_CMAKE_VERBOSE_LOGGING)99 set(entrypoint_deps "")100 if(NOT "${fq_deps_list}" STREQUAL "")101 check_entrypoint_deps(entrypoint_deps ${fq_deps_list})102 endif()103 if(NOT "${entrypoint_deps}" STREQUAL "")104 message(WARNING "Object ${fq_target_name} depends on public entrypoint(s) ${entrypoint_deps}.105 Depending on public entrypoints is not allowed in internal code.")106 endif()107 endif()108 109 if(SHOW_INTERMEDIATE_OBJECTS)110 message(STATUS "Adding object library ${fq_target_name}")111 if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")112 foreach(dep IN LISTS ADD_OBJECT_DEPENDS)113 message(STATUS " ${fq_target_name} depends on ${dep}")114 endforeach()115 endif()116 endif()117 118 list(APPEND fq_deps_list libc.src.__support.macros.config)119 list(REMOVE_DUPLICATES fq_deps_list)120 add_dependencies(${fq_target_name} ${fq_deps_list})121 # Add deps as link libraries to inherit interface compile and link options.122 target_link_libraries(${fq_target_name} PUBLIC ${fq_deps_list})123 124 set_target_properties(125 ${fq_target_name}126 PROPERTIES127 TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE}128 CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD}129 DEPS "${fq_deps_list}"130 FLAGS "${ADD_OBJECT_FLAGS}"131 )132 133 # If we built a separate internal target we want to use those target objects134 # for testing instead of the exported target.135 set(target_objects ${fq_target_name})136 if(TARGET ${internal_target_name})137 set(target_objects ${internal_target_name})138 endif()139 140 set_target_properties(141 ${fq_target_name}142 PROPERTIES143 OBJECT_FILES "$<TARGET_OBJECTS:${target_objects}>"144 )145endfunction(create_object_library)146 147function(add_object_library target_name)148 add_target_with_flags(149 ${target_name}150 CREATE_TARGET create_object_library151 ${ARGN})152endfunction(add_object_library)153 154 155# A rule for entrypoint object targets.156# Usage:157# add_entrypoint_object(158# <target_name>159# [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias.160# [NAME] <the C name of the entrypoint if different from target_name>161# SRCS <list of .cpp files>162# HDRS <list of .h files>163# DEPENDS <list of dependencies>164# COMPILE_OPTIONS <optional list of special compile options for this target>165# SPECIAL_OBJECTS <optional list of special object targets added by the rule `add_object`>166# FLAGS <optional list of flags>167# )168function(create_entrypoint_object fq_target_name)169 cmake_parse_arguments(170 "ADD_ENTRYPOINT_OBJ"171 "ALIAS;REDIRECTED" # Optional argument172 "NAME;CXX_STANDARD" # Single value arguments173 "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS" # Multi value arguments174 ${ARGN}175 )176 177 set(entrypoint_target_type ${ENTRYPOINT_OBJ_TARGET_TYPE})178 list(FIND TARGET_ENTRYPOINT_NAME_LIST ${ADD_ENTRYPOINT_OBJ_NAME} entrypoint_name_index)179 if(${entrypoint_name_index} EQUAL -1)180 add_custom_target(${fq_target_name})181 set_target_properties(182 ${fq_target_name}183 PROPERTIES184 "ENTRYPOINT_NAME" ${ADD_ENTRYPOINT_OBJ_NAME}185 "TARGET_TYPE" ${entrypoint_target_type}186 "OBJECT_FILE" ""187 "OBJECT_FILE_RAW" ""188 "DEPS" ""189 "SKIPPED" "YES"190 )191 if(LIBC_CMAKE_VERBOSE_LOGGING)192 message(STATUS "Skipping libc entrypoint ${fq_target_name}.")193 endif()194 return()195 endif()196 197 set(internal_target_name ${fq_target_name}.__internal__)198 199 if(ADD_ENTRYPOINT_OBJ_ALIAS)200 # Alias targets help one add aliases to other entrypoint object targets.201 # One can use alias targets setup OS/machine independent entrypoint targets.202 list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size)203 if(NOT (${deps_size} EQUAL "1"))204 message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.")205 endif()206 list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target)207 get_fq_dep_name(fq_dep_name ${dep_target})208 209 if(SHOW_INTERMEDIATE_OBJECTS)210 message(STATUS "Adding entrypoint object ${fq_target_name} as an alias of"211 " ${fq_dep_name}")212 endif()213 214 if(NOT TARGET ${fq_dep_name})215 message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; "216 "Target ${target_name} will be ignored.")217 return()218 endif()219 220 get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE")221 if((NOT obj_type) OR (NOT ${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}))222 message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.")223 endif()224 225 get_target_property(object_file ${fq_dep_name} "OBJECT_FILE")226 get_target_property(object_file_raw ${fq_dep_name} "OBJECT_FILE_RAW")227 228 # If the system cannot build the GPU tests we simply make a dummy target.229 if(LIBC_TARGET_OS_IS_GPU AND LIBC_GPU_TESTS_DISABLED)230 add_custom_target(${internal_target_name})231 else()232 add_library(233 ${internal_target_name}234 EXCLUDE_FROM_ALL235 OBJECT236 ${object_file_raw}237 )238 endif()239 240 add_dependencies(${internal_target_name} ${fq_dep_name})241 add_library(242 ${fq_target_name}243 EXCLUDE_FROM_ALL244 OBJECT245 ${object_file}246 )247 add_dependencies(${fq_target_name} ${fq_dep_name} ${internal_target_name})248 set_target_properties(249 ${fq_target_name}250 PROPERTIES251 ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}252 TARGET_TYPE ${entrypoint_target_type}253 IS_ALIAS "YES"254 OBJECT_FILE ""255 OBJECT_FILE_RAW ""256 DEPS "${fq_dep_name}"257 FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"258 )259 return()260 endif()261 262 if(NOT ADD_ENTRYPOINT_OBJ_SRCS)263 message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.")264 endif()265 if(NOT ADD_ENTRYPOINT_OBJ_CXX_STANDARD)266 set(ADD_ENTRYPOINT_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD})267 endif()268 269 _get_common_compile_options(common_compile_options "${ADD_ENTRYPOINT_OBJ_FLAGS}")270 list(APPEND common_compile_options ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS})271 get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS})272 273 #loop through the deps, check if any have the TARGET_TYPE of entrypoint_target_type, and print a warning if they do.274 if(LIBC_CMAKE_VERBOSE_LOGGING)275 set(entrypoint_deps "")276 if(NOT "${fq_deps_list}" STREQUAL "")277 check_entrypoint_deps(entrypoint_deps ${fq_deps_list})278 endif()279 if(NOT "${entrypoint_deps}" STREQUAL "")280 message(WARNING "Entrypoint ${fq_target_name} depends on public entrypoint(s) ${entrypoint_deps}.281 Depending on public entrypoints is not allowed in internal code.")282 endif()283 endif()284 285 set(full_deps_list ${fq_deps_list} libc.src.__support.common)286 287 if(SHOW_INTERMEDIATE_OBJECTS)288 message(STATUS "Adding entrypoint object ${fq_target_name}")289 if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS")290 foreach(dep IN LISTS ADD_ENTRYPOINT_OBJ_DEPENDS)291 message(STATUS " ${fq_target_name} depends on ${dep}")292 endforeach()293 endif()294 endif()295 296 add_library(297 ${internal_target_name}298 # TODO: We don't need an object library for internal consumption.299 # A future change should switch this to a normal static library.300 EXCLUDE_FROM_ALL301 OBJECT302 ${ADD_ENTRYPOINT_OBJ_SRCS}303 ${ADD_ENTRYPOINT_OBJ_HDRS}304 )305 target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options})306 target_include_directories(${internal_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})307 target_include_directories(${internal_target_name} PRIVATE ${LIBC_SOURCE_DIR})308 add_dependencies(${internal_target_name} ${full_deps_list})309 target_link_libraries(${internal_target_name} ${full_deps_list})310 311 add_library(312 ${fq_target_name}313 # We want an object library as the objects will eventually get packaged into314 # an archive (like libc.a).315 EXCLUDE_FROM_ALL316 OBJECT317 ${ADD_ENTRYPOINT_OBJ_SRCS}318 ${ADD_ENTRYPOINT_OBJ_HDRS}319 )320 target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -DLIBC_COPT_PUBLIC_PACKAGING)321 target_include_directories(${fq_target_name} SYSTEM PRIVATE ${LIBC_INCLUDE_DIR})322 target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})323 add_dependencies(${fq_target_name} ${full_deps_list})324 target_link_libraries(${fq_target_name} ${full_deps_list})325 326 # Builtin recognition causes issues when trying to implement the builtin327 # functions themselves. The GPU backends do not use libcalls so we disable the328 # known problematic ones on the entrypoints that implement them.329 if(LIBC_TARGET_OS_IS_GPU)330 set(libc_builtins bcmp strlen memmem bzero memcmp memcpy memmem memmove331 memset strcmp strstr)332 if(${ADD_ENTRYPOINT_OBJ_NAME} IN_LIST libc_builtins)333 target_compile_options(${fq_target_name} PRIVATE -fno-builtin-${ADD_ENTRYPOINT_OBJ_NAME})334 endif()335 endif()336 337 set_target_properties(338 ${fq_target_name}339 PROPERTIES340 ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME}341 TARGET_TYPE ${entrypoint_target_type}342 OBJECT_FILE "$<TARGET_OBJECTS:${fq_target_name}>"343 CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}344 DEPS "${fq_deps_list}"345 FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"346 )347 348 if(TARGET ${internal_target_name})349 set_target_properties(350 ${internal_target_name}351 PROPERTIES352 CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD}353 FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}"354 )355 set_target_properties(356 ${fq_target_name}357 PROPERTIES358 # TODO: We don't need to list internal object files if the internal359 # target is a normal static library.360 OBJECT_FILE_RAW "$<TARGET_OBJECTS:${internal_target_name}>"361 )362 endif()363 364 if(LLVM_LIBC_ENABLE_LINTING AND TARGET ${internal_target_name})365 if(NOT LLVM_LIBC_CLANG_TIDY)366 message(FATAL_ERROR "Something is wrong! LLVM_LIBC_ENABLE_LINTING is "367 "ON but LLVM_LIBC_CLANG_TIDY is not set.")368 endif()369 370 # We only want a second invocation of clang-tidy to run371 # restrict-system-libc-headers if the compiler-resource-dir was set in372 # order to prevent false-positives due to a mismatch between the host373 # compiler and the compiled clang-tidy.374 if(COMPILER_RESOURCE_DIR)375 # We run restrict-system-libc-headers with --system-headers to prevent376 # transitive inclusion through compler provided headers.377 set(restrict_system_headers_check_invocation378 COMMAND ${LLVM_LIBC_CLANG_TIDY} --system-headers379 --checks="-*,llvmlibc-restrict-system-libc-headers"380 # We explicitly set the resource dir here to match the381 # resource dir of the host compiler.382 "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}"383 --quiet384 -p ${PROJECT_BINARY_DIR}385 ${ADD_ENTRYPOINT_OBJ_SRCS}386 )387 else()388 set(restrict_system_headers_check_invocation389 COMMAND ${CMAKE_COMMAND} -E echo "Header file check skipped")390 endif()391 392 add_custom_target(393 ${fq_target_name}.__lint__394 # --quiet is used to surpress warning statistics from clang-tidy like:395 # Suppressed X warnings (X in non-user code).396 # There seems to be a bug in clang-tidy where by even with --quiet some397 # messages from clang's own diagnostics engine leak through:398 # X warnings generated.399 # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress400 # these.401 COMMAND ${LLVM_LIBC_CLANG_TIDY}402 "--extra-arg=-fno-caret-diagnostics" --quiet403 # Path to directory containing compile_commands.json404 -p ${PROJECT_BINARY_DIR}405 ${ADD_ENTRYPOINT_OBJ_SRCS}406 # See above: this might be a second invocation of clang-tidy depending on407 # the conditions above.408 ${restrict_system_headers_check_invocation}409 # We have two options for running commands, add_custom_command and410 # add_custom_target. We don't want to run the linter unless source files411 # have changed. add_custom_target explicitly runs everytime therefore we412 # use add_custom_command. This function requires an output file and since413 # linting doesn't produce a file, we create a dummy file using a414 # crossplatform touch.415 COMMENT "Linting... ${fq_target_name}"416 DEPENDS ${internal_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS}417 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}418 )419 add_dependencies(libc-lint ${fq_target_name}.__lint__)420 endif()421 422endfunction(create_entrypoint_object)423 424function(add_entrypoint_object target_name)425 cmake_parse_arguments(426 "ADD_ENTRYPOINT_OBJ"427 "" # Optional arguments428 "NAME" # Single value arguments429 "" # Multi-value arguments430 ${ARGN}431 )432 433 if(NOT ADD_ENTRYPOINT_OBJ_NAME)434 set(ADD_ENTRYPOINT_OBJ_NAME ${target_name})435 endif()436 437 add_target_with_flags(438 ${target_name}439 NAME ${ADD_ENTRYPOINT_OBJ_NAME}440 CREATE_TARGET create_entrypoint_object441 ${ADD_ENTRYPOINT_OBJ_UNPARSED_ARGUMENTS}442 )443endfunction(add_entrypoint_object)444 445# A rule for external entrypoint targets.446# Usage:447# add_entrypoint_external(448# <target_name>449# DEPENDS <list of dependencies>450# )451function(add_entrypoint_external target_name)452 cmake_parse_arguments(453 "ADD_ENTRYPOINT_EXT"454 "" # No optional arguments455 "" # No single value arguments456 "DEPENDS" # Multi value arguments457 ${ARGN}458 )459 get_fq_target_name(${target_name} fq_target_name)460 set(entrypoint_name ${target_name})461 462 add_custom_target(${fq_target_name})463 set_target_properties(464 ${fq_target_name}465 PROPERTIES466 "ENTRYPOINT_NAME" ${entrypoint_name}467 "TARGET_TYPE" ${ENTRYPOINT_EXT_TARGET_TYPE}468 "DEPS" "${ADD_ENTRYPOINT_EXT_DEPENDS}"469 )470 471endfunction(add_entrypoint_external)472 473# Helper to define a function with multiple implementations474# - Computes flags to satisfy required/rejected features and arch,475# - Declares an entry point,476# - Attach the REQUIRE_CPU_FEATURES property to the target,477# - Add the fully qualified target to `${name}_implementations` global property for tests.478function(add_implementation name impl_name)479 cmake_parse_arguments(480 "ADD_IMPL"481 "" # Optional arguments482 "" # Single value arguments483 "REQUIRE;SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;MLLVM_COMPILE_OPTIONS" # Multi value arguments484 ${ARGN})485 486 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")487 # Note that '-mllvm' needs to be prefixed with 'SHELL:' to prevent CMake flag deduplication.488 foreach(opt IN LISTS ADD_IMPL_MLLVM_COMPILE_OPTIONS)489 list(APPEND ADD_IMPL_COMPILE_OPTIONS "SHELL:-mllvm ${opt}")490 endforeach()491 endif()492 493 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")494 # Prevent warning when passing x86 SIMD types as template arguments.495 # e.g. "warning: ignoring attributes on template argument ‘__m128i’ [-Wignored-attributes]"496 list(APPEND ADD_IMPL_COMPILE_OPTIONS "-Wno-ignored-attributes")497 endif()498 499 add_entrypoint_object(${impl_name}500 NAME ${name}501 SRCS ${ADD_IMPL_SRCS}502 HDRS ${ADD_IMPL_HDRS}503 DEPENDS ${ADD_IMPL_DEPENDS}504 COMPILE_OPTIONS ${libc_opt_high_flag} ${ADD_IMPL_COMPILE_OPTIONS}505 )506 get_fq_target_name(${impl_name} fq_target_name)507 set_target_properties(${fq_target_name} PROPERTIES REQUIRE_CPU_FEATURES "${ADD_IMPL_REQUIRE}")508 set_property(GLOBAL APPEND PROPERTY "${name}_implementations" "${fq_target_name}")509endfunction()510