## Copyright 2009-2021 Intel Corporation ## SPDX-License-Identifier: Apache-2.0 #=============================================================================== # This script will attempt to find TBB and set up a TBB target. # # The user may specify a version and lists of required and optional components: # # find_package(TBB 2017.0 EXACT REQUIRED # tbb tbbmalloc # OPTIONAL_COMPONENTS tbbmalloc_proxy # QUIET) # # If this target exists already, the script will attempt to re-use it, but fail # if version or components do not match the user-specified requirements. # # If all the required component targets (e.g. TBB::tbb) exist, the script will # attempt to create a target TBB and link existing component targets to it. # It will fail if the component target version does not match the user-specified # requirements. # # The user may specify the following variables to help the search process: # - TBB_ROOT # - TBB_INCLUDE_DIR # # After the script has run successfully, there is a target TBB, as well as # component targets TBB::, e.g. TBB::tbbmalloc. # # The targets will attempt to link to release versions of TBB in release mode, # and debug versions in debug mode. # # In addition to the targets, the script defines: # # TBB_FOUND # TBB_INCLUDE_DIRS # #=============================================================================== # We use INTERFACE libraries, which are only supported in 3.x cmake_minimum_required(VERSION 3.5) # These two are used to automatically find the root and include directories. set(_TBB_INCLUDE_SUBDIR "include") set(_TBB_HEADER "tbb/tbb.h") # Initialize cache variable; but use existing non-cache variable as the default, # and fall back to the environment variable. if (NOT TBB_ROOT) set(TBB_ROOT "$ENV{TBB_ROOT}") endif() set(TBB_ROOT "${TBB_ROOT}" CACHE PATH "The root path of TBB.") #=============================================================================== # Error messages that respect the user's wishes about peace and quiet. #=============================================================================== function(rk_tbb_status) if (NOT TBB_FIND_QUIETLY) message(STATUS "${ARGV}") endif() endfunction() function(rk_tbb_warning) if (NOT TBB_FIND_QUIETLY) message(WARNING "${ARGV}") endif() endfunction() macro(rk_tbb_error) if (TBB_FIND_REQUIRED) message(FATAL_ERROR "${ARGV}") else() rk_tbb_warning("${ARGV}") endif() return() endmacro() #=============================================================================== # Extract a list of required and optional components. #=============================================================================== macro(rk_tbb_list_components) # cmake provides the TBB_FIND_COMPONENTS and # TBB_FIND_REQUIRED_ variables based on the invocation # of find_package. if (TBB_FIND_COMPONENTS STREQUAL "") set(_REQUIRED_COMPONENTS "tbb") set(_OPTIONAL_COMPONENTS "tbbmalloc" "tbbmalloc_proxy" "tbbbind" "tbbpreview") else() set(_REQUIRED_COMPONENTS "") set(_OPTIONAL_COMPONENTS "") foreach (C IN LISTS TBB_FIND_COMPONENTS) if (${TBB_FIND_REQUIRED_${C}}) list(APPEND _REQUIRED_COMPONENTS ${C}) else() list(APPEND _OPTIONAL_COMPONENTS ${C}) endif() endforeach() endif() rk_tbb_status("Looking for TBB components ${_REQUIRED_COMPONENTS}" " (${_OPTIONAL_COMPONENTS})") endmacro() #=============================================================================== # List components that are available, and check if any REQUIRED components # are missing. #=============================================================================== macro(rk_tbb_check_components) set(_TBB_MISSING_COMPONENTS "") set(_TBB_AVAILABLE_COMPONENTS "") foreach (C IN LISTS _REQUIRED_COMPONENTS) if (TARGET TBB::${C}) list(APPEND _TBB_AVAILABLE_COMPONENTS ${C}) else() list(APPEND _TBB_MISSING_COMPONENTS ${C}) endif() endforeach() foreach (C IN LISTS _OPTIONAL_COMPONENTS) if (TARGET TBB::${C}) list(APPEND _TBB_AVAILABLE_COMPONENTS ${C}) endif() endforeach() endmacro() #=============================================================================== # Check the version of the TBB root we found. #=============================================================================== macro(rk_tbb_check_version) # Extract the version we found in our root. if(EXISTS "${TBB_INCLUDE_DIR}/oneapi/tbb/version.h") set(_TBB_VERSION_HEADER "oneapi/tbb/version.h") elseif(EXISTS "${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h") set(_TBB_VERSION_HEADER "tbb/tbb_stddef.h") elseif(EXISTS "${TBB_INCLUDE_DIR}/tbb/version.h") set(_TBB_VERSION_HEADER "tbb/version.h") else() rk_tbb_error("Missing TBB version information. Could not find" "tbb/tbb_stddef.h or tbb/version.h in ${TBB_INCLUDE_DIR}") endif() file(READ ${TBB_INCLUDE_DIR}/${_TBB_VERSION_HEADER} VERSION_HEADER_CONTENT) string(REGEX MATCH "#define TBB_VERSION_MAJOR ([0-9]+)" DUMMY "${VERSION_HEADER_CONTENT}") set(TBB_VERSION_MAJOR ${CMAKE_MATCH_1}) string(REGEX MATCH "#define TBB_VERSION_MINOR ([0-9]+)" DUMMY "${VERSION_HEADER_CONTENT}") set(TBB_VERSION_MINOR ${CMAKE_MATCH_1}) set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") set(TBB_VERSION_STRING "${TBB_VERSION}") # If the user provided information about required versions, check them! if (TBB_FIND_VERSION) if (${TBB_FIND_VERSION_EXACT} AND NOT TBB_VERSION VERSION_EQUAL ${TBB_FIND_VERSION}) rk_tbb_error("Requested exact TBB version ${TBB_FIND_VERSION}," " but found ${TBB_VERSION}") elseif(TBB_VERSION VERSION_LESS ${TBB_FIND_VERSION}) rk_tbb_error("Requested minimum TBB version ${TBB_FIND_VERSION}," " but found ${TBB_VERSION}") endif() endif() rk_tbb_status("Found TBB version ${TBB_VERSION} at ${TBB_ROOT}") endmacro() #=============================================================================== # Reuse existing targets. # NOTE: This must be a macro, as we rely on return() to exit this script. #=============================================================================== macro(rk_tbb_reuse_existing_target_components) rk_tbb_check_components() if (_TBB_MISSING_COMPONENTS STREQUAL "") rk_tbb_status("Found existing TBB component targets: ${_TBB_AVAILABLE_COMPONENTS}") # Get TBB_INCLUDE_DIR if not already set to check for the version of the # existing component targets (making the assumption that they all have # the same version) if (NOT TBB_INCLUDE_DIR) list(GET _TBB_AVAILABLE_COMPONENTS 0 first_target) get_target_property(TBB_INCLUDE_DIR TBB::${first_target} INTERFACE_INCLUDE_DIRECTORIES) foreach(TGT IN LISTS _TBB_AVAILABLE_COMPONENTS) get_target_property(_TGT_INCLUDE_DIR TBB::${TGT} INTERFACE_INCLUDE_DIRECTORIES) if (NOT _TGT_INCLUDE_DIR STREQUAL "${TBB_INCLUDE_DIR}") rk_tbb_error("Existing TBB component targets have inconsistent include directories.") endif() endforeach() endif() find_path(TBB_INCLUDE_DIR NAMES "${_TBB_HEADER}" PATHS "${TBB_INCLUDE_DIRS}") # Extract TBB_ROOT from the include path so that rk_tbb_check_version # prints the correct tbb location string(REPLACE "/${_TBB_INCLUDE_SUBDIR}" "" TBB_ROOT "${TBB_INCLUDE_DIR}") rk_tbb_check_version() # Add target TBB and link all available components if (NOT TARGET TBB) add_library(TBB INTERFACE) foreach(C IN LISTS _TBB_AVAILABLE_COMPONENTS) target_link_libraries(TBB INTERFACE TBB::${C}) endforeach() endif() set(TBB_FOUND TRUE) set(TBB_INCLUDE_DIRS "${TBB_INCLUDE_DIR}") return() elseif ((TARGET TBB) OR (NOT _TBB_AVAILABLE_COMPONENTS STREQUAL "")) rk_tbb_error("Ignoring existing TBB targets because required components are missing: ${_TBB_MISSING_COMPONENTS}") endif() endmacro() #=============================================================================== # Find the root directory if a manual override is not specified. # Sets TBB_ROOT in the parent scope, but does not check for failure. #=============================================================================== function(rk_tbb_find_root) if (NOT TBB_ROOT OR TBB_ROOT STREQUAL "") set(TBB_HINTS "") set(TBB_PATHS "") if (WIN32) # workaround for parentheses in variable name / CMP0053 set(PROGRAMFILESx86 "PROGRAMFILES(x86)") set(PROGRAMFILES32 "$ENV{${PROGRAMFILESx86}}") if(NOT PROGRAMFILES32) set(PROGRAMFILES32 "$ENV{PROGRAMFILES}") endif() if(NOT PROGRAMFILES32) set(PROGRAMFILES32 "C:/Program Files (x86)") endif() set(TBB_PATHS "${PROJECT_SOURCE_DIR}/../tbb" "${PROGRAMFILES32}/IntelSWTools/compilers_and_libraries/windows/tbb" "${PROGRAMFILES32}/Intel/Composer XE/tbb" "${PROGRAMFILES32}/Intel/compilers_and_libraries/windows/tbb") else() set(TBB_HINTS "/usr/local") set(TBB_PATHS "${PROJECT_SOURCE_DIR}/tbb" "/opt/intel/composerxe/tbb" "/opt/intel/compilers_and_libraries/tbb" "/opt/intel/compilers_and_libraries/linux/tbb" "/opt/intel/tbb") endif() set(TBB_ROOT "TBB_ROOT-NOTFOUND") find_path(TBB_ROOT NAMES "${_TBB_INCLUDE_SUBDIR}/${_TBB_HEADER}" HINTS ${TBB_HINTS} PATHS ${TBB_PATHS} NO_PACKAGE_ROOT_PATH) endif() endfunction() #=============================================================================== # Find the include directory if a manual override is not specified. # Assumes TBB_ROOT to be set. #=============================================================================== function(rk_tbb_find_include_directory) find_path(TBB_INCLUDE_DIR NAMES "${_TBB_HEADER}" HINTS "${TBB_ROOT}/${_TBB_INCLUDE_SUBDIR}" NO_PACKAGE_ROOT_PATH) endfunction() #=============================================================================== # Find a specific library and create a target for it. #=============================================================================== function(rk_tbb_find_library COMPONENT_NAME BUILD_CONFIG) set(LIB_VAR "${COMPONENT_NAME}_LIBRARY_${BUILD_CONFIG}") set(BIN_DIR_VAR "${COMPONENT_NAME}_BIN_DIR_${BUILD_CONFIG}") set(DLL_VAR "${COMPONENT_NAME}_DLL_${BUILD_CONFIG}") if (BUILD_CONFIG STREQUAL "DEBUG") set(LIB_NAME "${COMPONENT_NAME}_debug") else() set(LIB_NAME "${COMPONENT_NAME}") endif() unset(LIB_PATHS) if (WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(TBB_ARCH intel64) else() set(TBB_ARCH ia32) endif() if(MSVC10) set(TBB_VCVER vc10) elseif(MSVC11) set(TBB_VCVER vc11) elseif(MSVC12) set(TBB_VCVER vc12) else() set(TBB_VCVER vc14) endif() set(LIB_PATHS ${TBB_ROOT}/lib/${TBB_ARCH}/${TBB_VCVER} ${TBB_ROOT}/lib ) # On window, also search the DLL so that the client may install it. set(DLL_NAME "${LIB_NAME}.dll") # lib name with version suffix to handle oneTBB tbb12.dll set(LIB_NAME_VERSION "") if (${COMPONENT_NAME} STREQUAL "tbb") if (BUILD_CONFIG STREQUAL "DEBUG") set(LIB_NAME_VERSION "tbb12_debug") else() set(LIB_NAME_VERSION "tbb12") endif() endif() set(DLL_NAME_VERSION "${LIB_NAME_VERSION}.dll") find_file(BIN_FILE NAMES ${DLL_NAME} ${DLL_NAME_VERSION} PATHS "${TBB_ROOT}/bin/${TBB_ARCH}/${TBB_VCVER}" "${TBB_ROOT}/bin" "${TBB_ROOT}/redist/${TBB_ARCH}/${TBB_VCVER}" "${TBB_ROOT}/../redist/${TBB_ARCH}/tbb/${TBB_VCVER}" "${TBB_ROOT}/../redist/${TBB_ARCH}_win/tbb/${TBB_VCVER}" NO_DEFAULT_PATH) get_filename_component(${BIN_DIR_VAR} ${BIN_FILE} DIRECTORY) set(${DLL_VAR} "${BIN_FILE}" CACHE PATH "${COMPONENT_NAME} ${BUILD_CONFIG} dll path") elseif(APPLE) set(LIB_PATHS ${TBB_ROOT}/lib) else() file(GLOB LIB_PATHS PATHS ${TBB_ROOT}/lib/intel64/gcc*) list(REVERSE LIB_PATHS) list(APPEND LIB_PATHS ${TBB_ROOT}/lib ${TBB_ROOT}/lib/x86_64-linux-gnu ${TBB_ROOT}/lib64 ${TBB_ROOT}/libx86_64-linux-gnu) endif() # We prefer finding the versioned file on Unix so that the library path # variable will not point to a symlink. This makes installing TBB as a # dependency easier. if (UNIX) set(LIB_NAME lib${LIB_NAME}.so.2 ${LIB_NAME}) endif() find_library(${LIB_VAR} NAMES ${LIB_NAME} PATHS ${LIB_PATHS} NO_DEFAULT_PATH) # Hide this variable if we found something, otherwise display it for # easy override. if(${LIB_VAR}) mark_as_advanced(${LIB_VAR}) endif() if(${BIN_DIR_VAR}) mark_as_advanced(${BIN_DIR_VAR}) endif() if(${DLL_VAR}) mark_as_advanced(${DLL_VAR}) endif() endfunction() #=============================================================================== # Find the given component. # This macro attempts to find both release and debug versions, and falls back # appropriately if only one can be found. # On success, it creates a target ${TARGET}::${COMPONENT_NAME} and links # it to the overall ${TARGET}. # # For more information on the variables set here, see # https://cmake.org/cmake/help/v3.17/manual/cmake-developer.7.html#a-sample-find-module #=============================================================================== function(rk_tbb_find_and_link_component COMPONENT_NAME) set(COMPONENT_TARGET "TBB::${COMPONENT_NAME}") rk_tbb_find_library("${COMPONENT_NAME}" RELEASE) rk_tbb_find_library("${COMPONENT_NAME}" DEBUG) if (${COMPONENT_NAME}_LIBRARY_RELEASE OR ${COMPONENT_NAME}_LIBRARY_DEBUG) # Note: We *must* use SHARED here rather than UNKNOWN as our # IMPORTED_NO_SONAME trick a few lines down does not work with # UNKNOWN. add_library(${COMPONENT_TARGET} SHARED IMPORTED) if (${COMPONENT_NAME}_LIBRARY_RELEASE) set_property(TARGET ${COMPONENT_TARGET} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) if(WIN32) set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_LOCATION_RELEASE "${${COMPONENT_NAME}_DLL_RELEASE}") set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_IMPLIB_RELEASE "${${COMPONENT_NAME}_LIBRARY_RELEASE}") else() set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_LOCATION_RELEASE "${${COMPONENT_NAME}_LIBRARY_RELEASE}") endif() endif() if (${COMPONENT_NAME}_LIBRARY_DEBUG) set_property(TARGET ${COMPONENT_TARGET} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) if(WIN32) set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_LOCATION_DEBUG "${${COMPONENT_NAME}_DLL_DEBUG}") set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_IMPLIB_DEBUG "${${COMPONENT_NAME}_LIBRARY_DEBUG}") else() set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_LOCATION_DEBUG "${${COMPONENT_NAME}_LIBRARY_DEBUG}") endif() endif() set_target_properties(${COMPONENT_TARGET} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${TBB_INCLUDE_DIR}" INTERFACE_COMPILE_DEFINITIONS "__TBB_NO_IMPLICIT_LINKAGE=1" ) if(NOT WIN32) # Note: IMPORTED_NO_SONAME must be set or cmake will attempt # to link to the full path of libtbb.so. Instead, we # rely on the linker to find libtbb.so.2. set_target_properties(${COMPONENT_TARGET} PROPERTIES IMPORTED_NO_SONAME TRUE ) endif() target_link_libraries(TBB INTERFACE ${COMPONENT_TARGET}) endif() endfunction() #=============================================================================== # Note: The order of these is important. # Some of these macros create variables that are used in later calls. rk_tbb_list_components() rk_tbb_reuse_existing_target_components() rk_tbb_find_root() if (NOT EXISTS "${TBB_ROOT}") rk_tbb_error("Unable to find root directory ${TBB_ROOT}") endif() mark_as_advanced(TBB_ROOT) # Hide, we found something. rk_tbb_find_include_directory() if (NOT EXISTS "${TBB_INCLUDE_DIR}") rk_tbb_error("Unable to find include directory ${TBB_INCLUDE_DIR}") endif() mark_as_advanced(TBB_INCLUDE_DIR) # Hide, we found something. rk_tbb_check_version() add_library(TBB INTERFACE) foreach(C IN LISTS _REQUIRED_COMPONENTS _OPTIONAL_COMPONENTS) rk_tbb_find_and_link_component(${C}) endforeach() rk_tbb_check_components() if (_TBB_MISSING_COMPONENTS) rk_tbb_error("Cannot find required components: " "${_TBB_MISSING_COMPONENTS}") endif() set(TBB_FOUND TRUE) set(TBB_INCLUDE_DIRS "${TBB_INCLUDE_DIR}")