oneAPI Threading Building Blocks (oneTBB) — популярная библиотека для параллельного программирования на C++ с открытым исходным кодом, опубликована на GitHub. Пару лет назад команда разработки решилась на глобальный рефакторинг библиотеки (проект TBB revamp), в который удалось вписать долгожданную смену системы сборки с GNU Makefiles на CMake. Свежая версия вышла в релиз в рамках инициативы oneAPI, обновив имя на oneTBB. В этой статье я расскажу про то, как подключить oneTBB в CMake-проект и как собрать, протестировать и установить oneTBB.
Как подключить oneTBB в свой CMake-проект
Эта часть может быть полезна тем, кто хочет использовать библиотеку oneTBB в своём проекте.
Существует два основных способа подключения стороннего проекта: как готовый пакет и как исходный код.
Способ 1. Подключение готового пакета oneTBB
Для подключения пакета нужна функция find_package(TBB <...>). Такой способ работает для тех пакетов oneTBB, в которых есть соответствующая поддержка — TBBConfig-файлы. Официальные oneAPI пакеты распространяются с такой поддержкой из коробки. Архивы, прикреплённые к релизам oneTBB на GitHub, тоже поддерживают такой способ, но нужно дать CMake’у знать, куда распакован пакет, через переменную CMAKE_PREFIX_PATH. Некоторые сторонние дистрибутивы oneTBB также поддерживают этот способ. На самом деле, поддержка TBBConfig-файлов появилась задолго до релиза oneTBB, это произошло в одном из обновлений TBB 2017. Реализация в oneTBB во многом совместима с реализацией из предыдущих версий.
После успешного вызова find_package(TBB <...>) в проекте станут доступны импортированные таргеты TBB::tbb, TBB::tbbmalloc и т.д. в зависимости от запрошенных и доступных компонентов. Эти таргеты можно подключить к своему таргету — target_link_libraries(my_app PRIVATE TBB::tbb). Несмотря на «link» в названии функции, она добавит зависимость не только на стадию линковки, но и на стадию компиляции (путь до заголовочных файлов oneTBB подставится в строку компиляции).
Код
# Создаём приложение
add_executable(my_app)
target_sources(my_app PRIVATE my_app.cpp)
# Ищем готовый пакет oneTBB 2021.5.0 (или новее)
find_package(TBB 2021.5.0 REQUIRED)
# Подключаем импортированный таргет TBB::tbb к приложению
target_link_libraries(my_app PRIVATE TBB::tbb)Детальнее посмотреть и попробовать этот способ можно на примере, в котором:
- my_app.cpp — исходник приложения, вызывающего - TBB_runtime_version()и печатающего результат в консоль;
- CMakeLists.txt — CMake-конфигурация для сборки приложения, где и показан способ подключения oneTBB; 
- run.sh — скрипт для запуска примера на линуксе; 
- README.md — небольшое описание примера. 
В этом примере используется готовый пакет со страницы oneTBB/releases, который ставится в текущую папку. Такая CMake-конфигурация будет работать и в случае установки oneTBB из многих других источников.
Во время сборки можно заметить, как подключается библиотека:
- на этапе компиляции подключаются заголовочные файлы: - -isystem <onetbb-root>/include
- на этапе линковки проставляется - rpathи подключается библиотека:- -Wl,-rpath,<onetbb-root>/lib/intel64/gcc4.8 <onetbb-root>/lib/intel64/gcc4.8/libtbb.so.12
Так как путь до библиотеки уже есть в rpath, для запуска не нужно выставлять какие-то дополнительные переменные окружения. С помощью ldd можно проверить, что oneTBB «виден»:
> ldd ./build/my_app | grep tbb
        libtbb.so.12 => <onetbb-root>/lib/intel64/gcc4.8/libtbb.so.12 (0x00007f867622c000)
Способ 2. Подключение oneTBB в виде исходного кода
Если подходящего пакета нет или требуется сборка с особыми параметрами, то можно использовать исходный код oneTBB, собирая его вместе с проектом.
До вызова функции, подключающей oneTBB, можно дополнительно настроить конфигурацию с помощью переменных, которые описаны в начале oneTBB/cmake/README.md (TBB_STRICT, TBB_TEST и т.д.), общие переменные CMAKE_BUILD_TYPE, CMAKE_CXX_STANDARD и другие также будут учитываться при построении oneTBB.
В самом простом варианте можно скачать исходники библиотеки в папку с проектом и добавить в CMakeLists.txt команду add_subdirectory(<onetbb-sources-dir>). Дальше можно использовать таргеты из oneTBB проекта и подключать их к своим таргетам, как в способе 1 —  target_link_libraries(my_app PRIVATE TBB::tbb).
Код
# Создаём приложение
add_executable(my_app)
target_sources(my_app PRIVATE my_app.cpp)
# Настраиваем конфигурацию oneTBB - явно отключаем тесты
option(TBB_TEST OFF)
# Подключаем в проект подпапку с исходниками oneTBB,
# которая должна быть предварительно скачана в проект
add_subdirectory(oneTBB)
# Подключаем таргет TBB::tbb к приложению
target_link_libraries(my_app PRIVATE TBB::tbb)Вместо add_subdirectory можно использовать функции FetchContent_Declare и FetchContent_MakeAvailable из модуля FetchContent, что позволит автоматически скачивать исходники oneTBB, например с GitHub.
Код
# Создаём таргет приложения
add_executable(my_app)
target_sources(my_app PRIVATE my_app.cpp)
# Загружаем исходники oneTBB 2021.5.0 с GitHub на этапе конфигурации
include(FetchContent)
FetchContent_Declare(
  onetbb
  GIT_REPOSITORY https://github.com/oneapi-src/oneTBB.git
  GIT_TAG v2021.5.0
)
# Настраиваем конфигурацию oneTBB - явно отключаем тесты
option(TBB_TEST OFF)
# Делаем исходники oneTBB доступными в текущем проекте
# (аналогично add_subdirectory)
FetchContent_MakeAvailable(onetbb)
# Подключаем таргет TBB::tbb к приложению
target_link_libraries(my_app PRIVATE TBB::tbb)
Детальнее посмотреть и попробовать этот способ можно на примере, в котором:
- my_app.cpp — исходник приложения, вызывающего - TBB_runtime_version()и печатающего результат в консоль;
- CMakeLists.txt — CMake-конфигурация для сборки приложения, где и показан способ подключения oneTBB с помощью модуля - FetchContent;
- run.sh — скрипт для запуска примера на линуксе; 
- README.md — небольшое описание примера. 
При построении явно используется таргет my_app, который через зависимость вызывает построение таргета TBB::tbb. Если не использовать никакого таргета или использовать таргет all, то построятся все таргеты из проекта oneTBB, попавшие в all, а не только те, на которые есть зависимость.
Как сконфигурировать, построить и протестировать oneTBB
Эта часть может быть полезна тем, кто хочет самостоятельно собирать oneTBB, делать какие-то доработки и предлагать изменения в библиотеку.
Подробное и актуальное описание системы сборки можете найти в oneTBB/cmake/README.md. Ниже кратко опишу основные шаги.
Конфигурация
Конфигурируется проект типично —  командой cmake. Настроить конфигурацию можно как общими переменными (CMAKE_BUILD_TYPE, CMAKE_CXX_STANDARD и т.д.), так и проектными (TBB_STRICT, TBB_TEST и т.д.).
Построение
Строится проект также типично: например, командой cmake --build. Проект включает в себя несколько компонентов, для каждого из которых есть таргет, если компонент поддерживается на системе и не отключен при конфигурации: tbb, tbbmalloc, tbbmalloc_proxy, разные варианты tbbbind. Каждый из этих таргетов строится в динамическую библиотеку. Статическую версию тоже можно построить (BUILD_SHARED_LIBS), но разработчики oneTBB не рекомендуют и официально не поддерживают такой вариант.
В проект входят Python-bindings (таргет python_build), которые включаются опцией TBB4PY_BUILD и собираются через Setuptools под капотом. Для сборки нужен Python 3.5+ и SWIG.
Если тесты не отключены через TBB_TEST=OFF, то для каждого из них создаётся таргет, который можно построить отдельно.
Тестирование
При включенной опции TBB_TEST в проект включаются тесты. Набор тестов зависит от включенных компонентов и от платформы. Тесты будут включать дополнительные сценарии, если в системе установлен Intel® Software Development Emulator. Построенные тесты можно запустить с помощью утилиты ctest.
Установка
Подробная инструкция установки из исходников с примерами описана в oneTBB/INSTALL.md.
Для установки можно использовать таргет install, либо напрямую вызывать cmake_install.cmake скрипт, с помощью которого можно установить конкретный компонент:runtime, devel или tbb4py.
Настроить путь установки можно на этапе конфигурации проекта через переменную CMAKE_INSTALL_PREFIX. По умолчанию установится в /usr/local на Unix и в C:\Program Files\TBB на Windows.
Кроме того, в проекте реализована простая zip конфигурация для упаковщика CPack. После конфигурации и сборки проекта можно сделать oneTBB пакет в виде zip-архива. В распакованном виде такой пакет можно подключать к проектам, как описано выше в способе 1.
Более подробную и актуальную информацию о системе сборки (и не только) можно найти в репозитории oneTBB на GitHub. Проект активный, поэтому баг-репорты, фича-реквесты или просто вопросы приветствуются через Issues, а свой вклад можно внести через Pull Request.
 
          