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