set(OBJECT_LIBRARY_TARGET_TYPE "OBJECT_LIBRARY") function(_get_common_compile_options output_var flags) list(FIND flags ${FMA_OPT_FLAG} fma) if(${fma} LESS 0) list(FIND flags "${FMA_OPT_FLAG}__ONLY" fma) endif() if((${fma} GREATER -1) AND (LIBC_TARGET_ARCHITECTURE_IS_RISCV64 OR (LIBC_CPU_FEATURES MATCHES "FMA"))) set(ADD_FMA_FLAG TRUE) endif() list(FIND flags ${ROUND_OPT_FLAG} round) if(${round} LESS 0) list(FIND flags "${ROUND_OPT_FLAG}__ONLY" round) endif() if((${round} GREATER -1) AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) set(ADD_SSE4_2_FLAG TRUE) endif() set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT} ${ARGN}) if(LLVM_COMPILER_IS_GCC_COMPATIBLE) list(APPEND compile_options "-fpie") list(APPEND compile_options "-ffreestanding") list(APPEND compile_options "-fno-builtin") list(APPEND compile_options "-fno-exceptions") list(APPEND compile_options "-fno-lax-vector-conversions") list(APPEND compile_options "-fno-unwind-tables") list(APPEND compile_options "-fno-asynchronous-unwind-tables") list(APPEND compile_options "-fno-rtti") list(APPEND compile_options "-Wall") list(APPEND compile_options "-Wextra") list(APPEND compile_options "-Wimplicit-fallthrough") list(APPEND compile_options "-Wwrite-strings") list(APPEND compile_options "-Wextra-semi") if(NOT CMAKE_COMPILER_IS_GNUCXX) list(APPEND compile_options "-Wnewline-eof") list(APPEND compile_options "-Wnonportable-system-include-path") list(APPEND compile_options "-Wstrict-prototypes") list(APPEND compile_options "-Wthread-safety") endif() if(ADD_FMA_FLAG) if(LIBC_TARGET_ARCHITECTURE_IS_X86) list(APPEND compile_options "-mavx2") list(APPEND compile_options "-mfma") elseif(LIBC_TARGET_ARCHITECTURE_IS_RISCV64) list(APPEND compile_option "-D__LIBC_RISCV_USE_FMA") endif() endif() if(ADD_SSE4_2_FLAG) list(APPEND compile_options "-msse4.2") endif() elseif(MSVC) list(APPEND compile_options "/EHs-c-") list(APPEND compile_options "/GR-") if(ADD_FMA_FLAG) list(APPEND compile_options "/arch:AVX2") endif() endif() if (LIBC_TARGET_ARCHITECTURE_IS_GPU) list(APPEND compile_options "-nogpulib") list(APPEND compile_options "-fvisibility=hidden") endif() set(${output_var} ${compile_options} PARENT_SCOPE) endfunction() # Obtains NVPTX specific arguments for compilation. # The PTX feature is primarily based on the CUDA toolchain version. We want to # be able to target NVPTX without an existing CUDA installation, so we need to # set this manually. This simply sets the PTX feature to the minimum required # for the features we wish to use on that target. The minimum PTX features used # here roughly corresponds to the CUDA 9.0 release. # Adjust as needed for desired PTX features. function(get_nvptx_compile_options output_var gpu_arch) set(nvptx_options "") list(APPEND nvptx_options "-march=${gpu_arch}") list(APPEND nvptx_options "-Wno-unknown-cuda-version") if(${gpu_arch} STREQUAL "sm_35") list(APPEND nvptx_options "--cuda-feature=+ptx60") elseif(${gpu_arch} STREQUAL "sm_37") list(APPEND nvptx_options "--cuda-feature=+ptx60") elseif(${gpu_arch} STREQUAL "sm_50") list(APPEND nvptx_options "--cuda-feature=+ptx60") elseif(${gpu_arch} STREQUAL "sm_52") list(APPEND nvptx_options "--cuda-feature=+ptx60") elseif(${gpu_arch} STREQUAL "sm_53") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_60") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_61") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_62") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_70") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_72") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_75") list(APPEND nvptx_options "--cuda-feature=+ptx63") elseif(${gpu_arch} STREQUAL "sm_80") list(APPEND nvptx_options "--cuda-feature=+ptx72") elseif(${gpu_arch} STREQUAL "sm_86") list(APPEND nvptx_options "--cuda-feature=+ptx72") else() message(FATAL_ERROR "Unknown Nvidia GPU architecture '${gpu_arch}'") endif() if(LIBC_CUDA_ROOT) list(APPEND nvptx_options "--cuda-path=${LIBC_CUDA_ROOT}") endif() set(${output_var} ${nvptx_options} PARENT_SCOPE) endfunction() # Builds the object target for the GPU. # This compiles the target for all supported architectures and embeds it into # host binary for installing. The internal target contains the GPU code directly # compiled for a single architecture used internally. # Usage: # _build_gpu_objects( # # # SRCS # HDRS # DEPENDS # COMPILE_OPTIONS # FLAGS # ) function(_build_gpu_objects fq_target_name internal_target_name) cmake_parse_arguments( "ADD_GPU_OBJ" "" # No optional arguments "NAME;CXX_STANDARD" # Single value arguments "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS" # Multi value arguments ${ARGN} ) set(include_dirs ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR}) set(common_compile_options ${ADD_GPU_OBJ_COMPILE_OPTIONS}) if(NOT ADD_GPU_OBJ_CXX_STANDARD) set(ADD_GPU_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() foreach(add_gpu_obj_src ${ADD_GPU_OBJ_SRCS}) # The packaged version will be built for every target GPU architecture. We do # this so we can support multiple accelerators on the same machine. foreach(gpu_arch ${LIBC_GPU_ARCHITECTURES}) get_filename_component(src_name ${add_gpu_obj_src} NAME) set(gpu_target_name ${fq_target_name}.${src_name}.${gpu_arch}) set(compile_options ${ADD_GPU_OBJ_COMPILE_OPTIONS}) # Derive the triple from the specified architecture. if("${gpu_arch}" IN_LIST all_amdgpu_architectures) set(gpu_target_triple "amdgcn-amd-amdhsa") list(APPEND compile_options "-mcpu=${gpu_arch}") elseif("${gpu_arch}" IN_LIST all_nvptx_architectures) set(gpu_target_triple "nvptx64-nvidia-cuda") get_nvptx_compile_options(nvptx_options ${gpu_arch}) list(APPEND compile_options "${nvptx_options}") else() message(FATAL_ERROR "Unknown GPU architecture '${gpu_arch}'") endif() list(APPEND compile_options "--target=${gpu_target_triple}") list(APPEND compile_options "-emit-llvm") # Build the library for this target architecture. We always emit LLVM-IR for # packaged GPU binaries. add_library(${gpu_target_name} EXCLUDE_FROM_ALL OBJECT ${add_gpu_obj_src} ${ADD_GPU_OBJ_HDRS} ) target_compile_options(${gpu_target_name} PRIVATE ${compile_options}) target_include_directories(${gpu_target_name} PRIVATE ${include_dirs}) target_compile_definitions(${gpu_target_name} PRIVATE LIBC_COPT_PUBLIC_PACKAGING) set_target_properties( ${gpu_target_name} PROPERTIES CXX_STANDARD ${ADD_GPU_OBJ_CXX_STANDARD} ) if(ADD_GPU_OBJ_DEPENDS) add_dependencies(${gpu_target_name} ${ADD_GPU_OBJ_DEPENDS}) endif() # Append this target to a list of images to package into a single binary. set(input_file $) list(APPEND packager_images --image=file=${input_file},arch=${gpu_arch},triple=${gpu_target_triple}) list(APPEND gpu_target_names ${gpu_target_name}) endforeach() # After building the target for the desired GPUs we must package the output # into a fatbinary, see https://clang.llvm.org/docs/OffloadingDesign.html for # more information. set(packaged_target_name ${fq_target_name}.${src_name}.__gpu__) set(packaged_output_name ${CMAKE_CURRENT_BINARY_DIR}/${fq_target_name}.${src_name}.gpubin) add_custom_command(OUTPUT ${packaged_output_name} COMMAND ${LIBC_CLANG_OFFLOAD_PACKAGER} ${packager_images} -o ${packaged_output_name} DEPENDS ${gpu_target_names} ${add_gpu_obj_src} ${ADD_GPU_OBJ_HDRS} COMMENT "Packaging LLVM offloading binary") add_custom_target(${packaged_target_name} DEPENDS ${packaged_output_name}) list(APPEND packaged_gpu_names ${packaged_target_name}) list(APPEND packaged_gpu_binaries ${packaged_output_name}) endforeach() # We create an empty 'stub' file for the host to contain the embedded device # code. This will be packaged into 'libcgpu.a'. # TODO: In the future we will want to combine every architecture for a target # into a single bitcode file and use that. For now we simply build for # every single one and let the offloading linker handle it. string(FIND ${fq_target_name} "." last_dot_loc REVERSE) math(EXPR name_loc "${last_dot_loc} + 1") string(SUBSTRING ${fq_target_name} ${name_loc} -1 target_name) set(stub_filename "${target_name}.cpp") add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename}" COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stubs/ COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename} DEPENDS ${gpu_target_names} ${ADD_GPU_OBJ_SRCS} ${ADD_GPU_OBJ_HDRS} ) set(stub_target_name ${fq_target_name}.__stub__) add_custom_target(${stub_target_name} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename}) add_library( ${fq_target_name} # We want an object library as the objects will eventually get packaged into # an archive (like libcgpu.a). EXCLUDE_FROM_ALL OBJECT ${CMAKE_CURRENT_BINARY_DIR}/stubs/${stub_filename} ) target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -nostdlib) foreach(packaged_gpu_binary ${packaged_gpu_binaries}) target_compile_options(${fq_target_name} PRIVATE "SHELL:-Xclang -fembed-offload-object=${packaged_gpu_binary}") endforeach() target_include_directories(${fq_target_name} PRIVATE ${include_dirs}) add_dependencies(${fq_target_name} ${full_deps_list} ${packaged_gpu_names} ${stub_target_name}) # We only build the internal target for a single supported architecture. if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU OR LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX) add_library( ${internal_target_name} EXCLUDE_FROM_ALL OBJECT ${ADD_GPU_OBJ_SRCS} ${ADD_GPU_OBJ_HDRS} ) target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options} --target=${LIBC_GPU_TARGET_TRIPLE}) if(LIBC_GPU_TARGET_ARCHITECTURE_IS_AMDGPU) target_compile_options(${internal_target_name} PRIVATE -mcpu=${LIBC_GPU_TARGET_ARCHITECTURE} -flto) elseif(LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX) get_nvptx_compile_options(nvptx_options ${LIBC_GPU_TARGET_ARCHITECTURE}) target_compile_options(${internal_target_name} PRIVATE ${nvptx_options}) endif() target_include_directories(${internal_target_name} PRIVATE ${include_dirs}) if(full_deps_list) add_dependencies(${internal_target_name} ${full_deps_list}) endif() endif() endfunction() # Rule which is essentially a wrapper over add_library to compile a set of # sources to object files. # Usage: # add_object_library( # # HDRS # SRCS # [ALIAS] # DEPENDS # COMPILE_OPTIONS # FLAGS function(create_object_library fq_target_name) cmake_parse_arguments( "ADD_OBJECT" "ALIAS;NO_GPU_BUNDLE" # optional arguments "CXX_STANDARD" # Single value arguments "SRCS;HDRS;COMPILE_OPTIONS;DEPENDS;FLAGS" # Multivalue arguments ${ARGN} ) get_fq_deps_list(fq_deps_list ${ADD_OBJECT_DEPENDS}) if(ADD_OBJECT_ALIAS) if(ADD_OBJECT_SRCS OR ADD_OBJECT_HDRS) message(FATAL_ERROR "${fq_target_name}: object library alias cannot have SRCS and/or HDRS.") endif() list(LENGTH fq_deps_list depends_size) if(NOT ${depends_size} EQUAL 1) message(FATAL_ERROR "${fq_targe_name}: object library alias should have exactly one DEPENDS.") endif() add_library( ${fq_target_name} ALIAS ${fq_deps_list} ) return() endif() if(NOT ADD_OBJECT_SRCS) message(FATAL_ERROR "'add_object_library' rule requires SRCS to be specified.") endif() # The GPU build uses a separate internal file. if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT ${ADD_OBJECT_NO_GPU_BUNDLE}) set(internal_target_name ${fq_target_name}.__internal__) else() set(internal_target_name ${fq_target_name}) endif() _get_common_compile_options( compile_options "${ADD_OBJECT_FLAGS}" ${ADD_OBJECT_COMPILE_OPTIONS} ) # GPU builds require special handling for the objects because we want to # export several different targets at once, e.g. for both Nvidia and AMD. if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT ${ADD_OBJECT_NO_GPU_BUNDLE}) _build_gpu_objects( ${fq_target_name} ${internal_target_name} SRCS ${ADD_OBJECT_SRCS} HDRS ${ADD_OBJECT_HDRS} DEPENDS ${fq_deps_list} CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD} COMPILE_OPTIONS ${compile_options} ) else() add_library( ${fq_target_name} EXCLUDE_FROM_ALL OBJECT ${ADD_OBJECT_SRCS} ${ADD_OBJECT_HDRS} ) target_include_directories( ${fq_target_name} PRIVATE ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR} ) target_compile_options(${fq_target_name} PRIVATE ${compile_options}) endif() if(SHOW_INTERMEDIATE_OBJECTS) message(STATUS "Adding object library ${fq_target_name}") if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") foreach(dep IN LISTS ADD_OBJECT_DEPENDS) message(STATUS " ${fq_target_name} depends on ${dep}") endforeach() endif() endif() if(fq_deps_list) add_dependencies(${fq_target_name} ${fq_deps_list}) endif() if(NOT ADD_OBJECT_CXX_STANDARD) set(ADD_OBJECT_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() set_target_properties( ${fq_target_name} PROPERTIES TARGET_TYPE ${OBJECT_LIBRARY_TARGET_TYPE} CXX_STANDARD ${ADD_OBJECT_CXX_STANDARD} DEPS "${fq_deps_list}" FLAGS "${ADD_OBJECT_FLAGS}" ) if(TARGET ${internal_target_name}) set_target_properties( ${fq_target_name} PROPERTIES OBJECT_FILES "$" ) endif() endfunction(create_object_library) # Internal function, used by `add_object_library`. function(expand_flags_for_object_library target_name flags) cmake_parse_arguments( "EXPAND_FLAGS" "IGNORE_MARKER" # Optional arguments "" # Single-value arguments "DEPENDS;FLAGS" # Multi-value arguments ${ARGN} ) list(LENGTH flags nflags) if(NOT ${nflags}) create_object_library( ${target_name} DEPENDS ${EXPAND_FLAGS_DEPENDS} FLAGS ${EXPAND_FLAGS_FLAGS} ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} ) return() endif() list(GET flags 0 flag) list(REMOVE_AT flags 0) extract_flag_modifier(${flag} real_flag modifier) if(NOT "${modifier}" STREQUAL "NO") expand_flags_for_object_library( ${target_name} "${flags}" DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" ) endif() if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") return() endif() set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) list(REMOVE_ITEM NEW_FLAGS ${flag}) get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) # Only target with `flag` has `.__NO_flag` target, `flag__NO` and # `flag__ONLY` do not. if("${modifier}" STREQUAL "") set(TARGET_NAME "${target_name}.__NO_${flag}") else() set(TARGET_NAME "${target_name}") endif() expand_flags_for_object_library( ${TARGET_NAME} "${flags}" DEPENDS "${NEW_DEPS}" IGNORE_MARKER FLAGS "${NEW_FLAGS}" IGNORE_MARKER "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" ) endfunction(expand_flags_for_object_library) function(add_object_library target_name) cmake_parse_arguments( "ADD_TO_EXPAND" "" # Optional arguments "" # Single value arguments "DEPENDS;FLAGS" # Multi-value arguments ${ARGN} ) get_fq_target_name(${target_name} fq_target_name) if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") endif() get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) list(SORT flags) if(SHOW_INTERMEDIATE_OBJECTS AND flags) message(STATUS "Object library ${fq_target_name} has FLAGS: ${flags}") endif() expand_flags_for_object_library( ${fq_target_name} "${flags}" DEPENDS "${fq_deps_list}" IGNORE_MARKER FLAGS "${flags}" IGNORE_MARKER ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} ) endfunction(add_object_library) set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ") # A rule for entrypoint object targets. # Usage: # add_entrypoint_object( # # [ALIAS|REDIRECTED] # Specified if the entrypoint is redirected or an alias. # [NAME] # SRCS # HDRS # DEPENDS # COMPILE_OPTIONS # SPECIAL_OBJECTS # FLAGS # ) function(create_entrypoint_object fq_target_name) cmake_parse_arguments( "ADD_ENTRYPOINT_OBJ" "ALIAS;REDIRECTED" # Optional argument "NAME;CXX_STANDARD" # Single value arguments "SRCS;HDRS;DEPENDS;COMPILE_OPTIONS;FLAGS" # Multi value arguments ${ARGN} ) list(FIND TARGET_ENTRYPOINT_NAME_LIST ${ADD_ENTRYPOINT_OBJ_NAME} entrypoint_name_index) if(${entrypoint_name_index} EQUAL -1) add_custom_target(${fq_target_name}) set_target_properties( ${fq_target_name} PROPERTIES "ENTRYPOINT_NAME" ${ADD_ENTRYPOINT_OBJ_NAME} "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} "OBJECT_FILE" "" "OBJECT_FILE_RAW" "" "DEPS" "" "SKIPPED" "YES" ) if(LIBC_CMAKE_VERBOSE_LOGGING) message(STATUS "Skipping libc entrypoint ${fq_target_name}.") endif() return() endif() if(ADD_ENTRYPOINT_OBJ_ALIAS) # Alias targets help one add aliases to other entrypoint object targets. # One can use alias targets setup OS/machine independent entrypoint targets. list(LENGTH ADD_ENTRYPOINT_OBJ_DEPENDS deps_size) if(NOT (${deps_size} EQUAL "1")) message(FATAL_ERROR "An entrypoint alias should have exactly one dependency.") endif() list(GET ADD_ENTRYPOINT_OBJ_DEPENDS 0 dep_target) get_fq_dep_name(fq_dep_name ${dep_target}) if(SHOW_INTERMEDIATE_OBJECTS) message(STATUS "Adding entrypoint object ${fq_target_name} as an alias of" " ${fq_dep_name}") endif() if(NOT TARGET ${fq_dep_name}) message(WARNING "Aliasee ${fq_dep_name} for entrypoint alias ${target_name} missing; " "Target ${target_name} will be ignored.") return() endif() get_target_property(obj_type ${fq_dep_name} "TARGET_TYPE") if((NOT obj_type) OR (NOT (${obj_type} STREQUAL ${ENTRYPOINT_OBJ_TARGET_TYPE}))) message(FATAL_ERROR "The aliasee of an entrypoint alias should be an entrypoint.") endif() add_custom_target(${fq_target_name}) add_dependencies(${fq_target_name} ${fq_dep_name}) get_target_property(object_file ${fq_dep_name} "OBJECT_FILE") get_target_property(object_file_raw ${fq_dep_name} "OBJECT_FILE_RAW") set_target_properties( ${fq_target_name} PROPERTIES ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME} TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE} IS_ALIAS "YES" OBJECT_FILE "" OBJECT_FILE_RAW "" DEPS "${fq_dep_name}" FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) return() endif() if(NOT ADD_ENTRYPOINT_OBJ_SRCS) message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.") endif() if(NOT ADD_ENTRYPOINT_OBJ_HDRS) message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.") endif() if(NOT ADD_ENTRYPOINT_OBJ_CXX_STANDARD) set(ADD_ENTRYPOINT_OBJ_CXX_STANDARD ${CMAKE_CXX_STANDARD}) endif() _get_common_compile_options( common_compile_options "${ADD_ENTRYPOINT_OBJ_FLAGS}" ${ADD_ENTRYPOINT_OBJ_COMPILE_OPTIONS} ) set(internal_target_name ${fq_target_name}.__internal__) set(include_dirs ${LIBC_BUILD_DIR}/include ${LIBC_SOURCE_DIR} ${LIBC_BUILD_DIR}) get_fq_deps_list(fq_deps_list ${ADD_ENTRYPOINT_OBJ_DEPENDS}) set(full_deps_list ${fq_deps_list} libc.src.__support.common) if(SHOW_INTERMEDIATE_OBJECTS) message(STATUS "Adding entrypoint object ${fq_target_name}") if(${SHOW_INTERMEDIATE_OBJECTS} STREQUAL "DEPS") foreach(dep IN LISTS ADD_OBJECT_DEPENDS) message(STATUS " ${fq_target_name} depends on ${dep}") endforeach() endif() endif() # GPU builds require special handling for the objects because we want to # export several different targets at once, e.g. for both Nvidia and AMD. if(LIBC_TARGET_ARCHITECTURE_IS_GPU) _build_gpu_objects( ${fq_target_name} ${internal_target_name} SRCS ${ADD_ENTRYPOINT_OBJ_SRCS} HDRS ${ADD_ENTRYPOINT_OBJ_HDRS} COMPILE_OPTIONS ${common_compile_options} CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} DEPENDS ${full_deps_list} FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) else() add_library( ${internal_target_name} # TODO: We don't need an object library for internal consumption. # A future change should switch this to a normal static library. EXCLUDE_FROM_ALL OBJECT ${ADD_ENTRYPOINT_OBJ_SRCS} ${ADD_ENTRYPOINT_OBJ_HDRS} ) target_compile_options(${internal_target_name} BEFORE PRIVATE ${common_compile_options}) target_include_directories(${internal_target_name} PRIVATE ${include_dirs}) add_dependencies(${internal_target_name} ${full_deps_list}) add_library( ${fq_target_name} # We want an object library as the objects will eventually get packaged into # an archive (like libc.a). EXCLUDE_FROM_ALL OBJECT ${ADD_ENTRYPOINT_OBJ_SRCS} ${ADD_ENTRYPOINT_OBJ_HDRS} ) target_compile_options(${fq_target_name} BEFORE PRIVATE ${common_compile_options} -DLIBC_COPT_PUBLIC_PACKAGING) target_include_directories(${fq_target_name} PRIVATE ${include_dirs}) add_dependencies(${fq_target_name} ${full_deps_list}) endif() set_target_properties( ${fq_target_name} PROPERTIES ENTRYPOINT_NAME ${ADD_ENTRYPOINT_OBJ_NAME} TARGET_TYPE ${ENTRYPOINT_OBJ_TARGET_TYPE} OBJECT_FILE "$" CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} DEPS "${fq_deps_list}" FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) if(TARGET ${internal_target_name}) set_target_properties( ${internal_target_name} PROPERTIES CXX_STANDARD ${ADD_ENTRYPOINT_OBJ_CXX_STANDARD} FLAGS "${ADD_ENTRYPOINT_OBJ_FLAGS}" ) set_target_properties( ${fq_target_name} PROPERTIES # TODO: We don't need to list internal object files if the internal # target is a normal static library. OBJECT_FILE_RAW "$" ) endif() if(LLVM_LIBC_ENABLE_LINTING AND TARGET ${internal_target_name}) if(NOT LLVM_LIBC_CLANG_TIDY) message(FATAL_ERROR "Something is wrong! LLVM_LIBC_ENABLE_LINTING is " "ON but LLVM_LIBC_CLANG_TIDY is not set.") endif() # We only want a second invocation of clang-tidy to run # restrict-system-libc-headers if the compiler-resource-dir was set in # order to prevent false-positives due to a mismatch between the host # compiler and the compiled clang-tidy. if(COMPILER_RESOURCE_DIR) # We run restrict-system-libc-headers with --system-headers to prevent # transitive inclusion through compler provided headers. set(restrict_system_headers_check_invocation COMMAND ${LLVM_LIBC_CLANG_TIDY} --system-headers --checks="-*,llvmlibc-restrict-system-libc-headers" # We explicitly set the resource dir here to match the # resource dir of the host compiler. "--extra-arg=-resource-dir=${COMPILER_RESOURCE_DIR}" --quiet -p ${PROJECT_BINARY_DIR} ${ADD_ENTRYPOINT_OBJ_SRCS} ) else() set(restrict_system_headers_check_invocation COMMAND ${CMAKE_COMMAND} -E echo "Header file check skipped") endif() add_custom_target( ${fq_target_name}.__lint__ # --quiet is used to surpress warning statistics from clang-tidy like: # Suppressed X warnings (X in non-user code). # There seems to be a bug in clang-tidy where by even with --quiet some # messages from clang's own diagnostics engine leak through: # X warnings generated. # Until this is fixed upstream, we use -fno-caret-diagnostics to surpress # these. COMMAND ${LLVM_LIBC_CLANG_TIDY} "--extra-arg=-fno-caret-diagnostics" --quiet # Path to directory containing compile_commands.json -p ${PROJECT_BINARY_DIR} ${ADD_ENTRYPOINT_OBJ_SRCS} # See above: this might be a second invocation of clang-tidy depending on # the conditions above. ${restrict_system_headers_check_invocation} # We have two options for running commands, add_custom_command and # add_custom_target. We don't want to run the linter unless source files # have changed. add_custom_target explicitly runs everytime therefore we # use add_custom_command. This function requires an output file and since # linting doesn't produce a file, we create a dummy file using a # crossplatform touch. COMMENT "Linting... ${fq_target_name}" DEPENDS ${internal_target_name} ${ADD_ENTRYPOINT_OBJ_SRCS} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_dependencies(libc-lint ${fq_target_name}.__lint__) endif() endfunction(create_entrypoint_object) # Internal function, used by `add_entrypoint_object`. function(expand_flags_for_entrypoint_object target_name flags) cmake_parse_arguments( "EXPAND_FLAGS" "IGNORE_MARKER" # Optional arguments "" # Single-value arguments "DEPENDS;FLAGS" # Multi-value arguments ${ARGN} ) list(LENGTH flags nflags) if(NOT ${nflags}) create_entrypoint_object( ${target_name} DEPENDS ${EXPAND_FLAGS_DEPENDS} FLAGS ${EXPAND_FLAGS_FLAGS} ${EXPAND_FLAGS_UNPARSED_ARGUMENTS} ) return() endif() list(GET flags 0 flag) list(REMOVE_AT flags 0) extract_flag_modifier(${flag} real_flag modifier) if(NOT "${modifier}" STREQUAL "NO") expand_flags_for_entrypoint_object( ${target_name} "${flags}" DEPENDS "${EXPAND_FLAGS_DEPENDS}" IGNORE_MARKER FLAGS "${EXPAND_FLAGS_FLAGS}" IGNORE_MARKER "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" ) endif() if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY") return() endif() set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS}) list(REMOVE_ITEM NEW_FLAGS ${flag}) get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS}) # Only target with `flag` has `.__NO_flag` target, `flag__NO` and # `flag__ONLY` do not. if("${modifier}" STREQUAL "") set(TARGET_NAME "${target_name}.__NO_${flag}") else() set(TARGET_NAME "${target_name}") endif() expand_flags_for_entrypoint_object( ${TARGET_NAME} "${flags}" DEPENDS "${NEW_DEPS}" IGNORE_MARKER FLAGS "${NEW_FLAGS}" IGNORE_MARKER "${EXPAND_FLAGS_UNPARSED_ARGUMENTS}" ) endfunction(expand_flags_for_entrypoint_object) function(add_entrypoint_object target_name) cmake_parse_arguments( "ADD_TO_EXPAND" "" # Optional arguments "NAME" # Single value arguments "DEPENDS;FLAGS" # Multi-value arguments ${ARGN} ) get_fq_target_name(${target_name} fq_target_name) if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")) message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}") endif() get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS}) get_flags_from_dep_list(deps_flag_list ${fq_deps_list}) list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list}) remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags) list(SORT flags) if(SHOW_INTERMEDIATE_OBJECTS AND flags) message(STATUS "Entrypoint object ${fq_target_name} has FLAGS: ${flags}") endif() if(NOT ADD_TO_EXPAND_NAME) set(ADD_TO_EXPAND_NAME ${target_name}) endif() expand_flags_for_entrypoint_object( ${fq_target_name} "${flags}" NAME ${ADD_TO_EXPAND_NAME} IGNORE_MARKER DEPENDS "${fq_deps_list}" IGNORE_MARKER FLAGS "${flags}" IGNORE_MARKER ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS} ) endfunction(add_entrypoint_object) set(ENTRYPOINT_EXT_TARGET_TYPE "ENTRYPOINT_EXT") # A rule for external entrypoint targets. # Usage: # add_entrypoint_external( # # DEPENDS # ) function(add_entrypoint_external target_name) cmake_parse_arguments( "ADD_ENTRYPOINT_EXT" "" # No optional arguments "" # No single value arguments "DEPENDS" # Multi value arguments ${ARGN} ) get_fq_target_name(${target_name} fq_target_name) set(entrypoint_name ${target_name}) add_custom_target(${fq_target_name}) set_target_properties( ${fq_target_name} PROPERTIES "ENTRYPOINT_NAME" ${entrypoint_name} "TARGET_TYPE" ${ENTRYPOINT_EXT_TARGET_TYPE} "DEPS" "${ADD_ENTRYPOINT_EXT_DEPENDS}" ) endfunction(add_entrypoint_external) # Rule build a redirector object file. function(add_redirector_object target_name) cmake_parse_arguments( "REDIRECTOR_OBJECT" "" # No optional arguments "SRC" # The cpp file in which the redirector is defined. "" # No multivalue arguments ${ARGN} ) if(NOT REDIRECTOR_OBJECT_SRC) message(FATAL_ERROR "'add_redirector_object' rule requires SRC option listing one source file.") endif() add_library( ${target_name} EXCLUDE_FROM_ALL OBJECT ${REDIRECTOR_OBJECT_SRC} ) target_compile_options( ${target_name} BEFORE PRIVATE -fPIC ${LIBC_COMPILE_OPTIONS_DEFAULT} ) endfunction(add_redirector_object)