Как правильно добавить библиотеки в CMakeLists.txt, когда имена пакетов отличаются между дистрибутивами Linux?
У меня есть проект, изначально настроенный для Arch Linux, с следующим кодом в CMakeLists:
# find c++utilities
find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.20.0 REQUIRED)
use_cpp_utilities(VISIBILITY PUBLIC)
# find qtutilities
find_package(${PACKAGE_NAMESPACE_PREFIX}qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES} 6.13.0 REQUIRED)
use_qt_utilities()
В Debian эти пакеты называются libmartchus-c++utilities-dev и libmartchus-qtutilities-dev. При попытке подменить названия и убрать переменные возникает ошибка:
CMake Error at src/CMakeLists.txt:137 (find_package):
By not providing "Findlibmartchus-c++utilities-dev.cmake" in
CMAKE_MODULE_PATH this project has asked CMake to find a package
configuration file provided by "libmartchus-c++utilities-dev", but
CMake did not find one.
Could not find a package configuration file provided by
"libmartchus-c++utilities-dev" with any of the following names:
libmartchus-c++utilities-devConfig.cmake
libmartchus-c++utilities-dev-config.cmake
Add the installation prefix of "libmartchus-c++utilities-dev" to
CMAKE_PREFIX_PATH or set "libmartchus-c++utilities-dev_DIR" to a
directory containing one of the above files. If
"libmartchus-c++utilities-dev" provides a separate development
package or SDK, be sure it has been installed.
-- Configuring incomplete, errors occurred!
Как правильно настроить CMakeLists.txt для сборки пакета в Debian?
Для решения проблемы с разными именами пакетов между дистрибутивами Linux в CMakeLists.txt необходимо использовать комбинацию стратегий поиска пакетов и условной компиляции. Основная проблема заключается в том, что разные дистрибутивы используют разные соглашения об именовании пакетов и предоставляют файлы конфигурации CMake по-разному.
Содержание
- Понимание проблемы
- Стратегии решения
- Рекомендуемый подход
- Пример реализации
- Дополнительные методы
- Тестирование и валидация
- Заключение
Понимание проблемы
Основная проблема заключается в том, что:
- Разные соглашения об именовании: Arch Linux использует
c++utilities, а Debian -libmartchus-c++utilities-dev - Отсутствие файлов конфигурации: В Debian пакеты обычно не предоставляют файлы
*Config.cmakeпо умолчанию - Разные пути установки: Пакеты могут находиться в разных системных путях
- Разные зависимости: В разных дистрибутивах могут требоваться разные версии зависимостей
Ошибка, которую вы получаете, возникает потому, что CMake ищет файл конфигурации пакета, который не предоставляется в стандартных пакетах Debian.
Стратегии решения
Есть несколько подходов к решению этой проблемы:
1. Использование нескольких find_package вызовов
Проверять наличие пакетов с разными именами и использовать первый доступный:
# Попробовать сначала Arch Linux стиль, затем Debian
if(NOT DEFINED CMAKE_CXXUTILITIES_FOUND)
find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.20.0 QUIET)
if(NOT ${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}_FOUND)
find_package(libmartchus-c++utilities-dev 5.20.0 QUIET)
endif()
if(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}_FOUND OR libmartchus-c++utilities-dev_FOUND)
set(CMAKE_CXXUTILITIES_FOUND TRUE)
endif()
endif()
if(CMAKE_CXXUTILITIES_FOUND)
if(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}_FOUND)
use_cpp_utilities(VISIBILITY PUBLIC)
elseif(libmartchus-c++utilities-dev_FOUND)
# Альтернативная логика для Debian
target_link_libraries(your_target PRIVATE libmartchus-c++utilities-dev)
endif()
endif()
2. Создание кастомных модулей поиска
Создать модули Find*.cmake для каждого дистрибутива:
cmake/
├── FindC++Utilities.cmake
├── FindQtUtilities.cmake
└── Modules/
└── FindDebianC++Utilities.cmake
3. Использование pkg-config
Большинство библиотек предоставляют файлы .pc для pkg-config, которые работают практически во всех дистрибутивах:
find_package(PkgConfig REQUIRED)
pkg_check_modules(CPPUTILITIES REQUIRED c++utilities)
pkg_check_modules(QTUTILITIES REQUIRED qtutilities)
Рекомендуемый подход
Наиболее надежным и переносимым решением является комбинация pkg-config и резервных механизмов поиска:
# Функция для поиска библиотек с разными именами
function(find_package_cross_distribution PACKAGE_NAME VERSION DEBIAN_NAME)
# Сначала попробовать pkg-config
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(${PACKAGE_NAME}_PKG ${PACKAGE_NAME} QUIET)
if(${PACKAGE_NAME}_PKG_FOUND)
set(${PACKAGE_NAME}_FOUND TRUE PARENT_SCOPE)
return()
endif()
endif()
# Попробовать стандартное имя
find_package(${PACKAGE_NAME} ${VERSION} QUIET)
if(${PACKAGE_NAME}_FOUND)
return()
endif()
# Попробовать Debian имя
if(DEFINED DEBIAN_NAME)
find_package(${DEBIAN_NAME} ${VERSION} QUIET)
if(${DEBIAN_NAME}_FOUND)
set(${PACKAGE_NAME}_FOUND TRUE PARENT_SCOPE)
set(${PACKAGE_NAME}_DEBIAN_NAME ${DEBIAN_NAME} PARENT_SCOPE)
return()
endif()
endif()
# Если ничего не найдено, вывести ошибку
message(FATAL_ERROR "Package ${PACKAGE_NAME} (version ${VERSION}) not found. "
"Please install it using your system package manager.")
endfunction()
# Использование функции
find_package_cross_distribution(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.20.0 libmartchus-c++utilities-dev)
find_package_cross_distribution(${PACKAGE_NAMESPACE_PREFIX}qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES} 6.13.0 libmartchus-qtutilities-dev)
# Условная логика использования
if(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}_FOUND)
use_cpp_utilities(VISIBILITY PUBLIC)
elseif(DEFINED c++utilities_DEBIAN_NAME)
# Для Debian использовать прямое подключение
target_link_libraries(your_target PRIVATE ${c++utilities_DEBIAN_NAME})
endif()
if(${PACKAGE_NAMESPACE_PREFIX}qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES}_FOUND)
use_qt_utilities()
elseif(DEFINED qtutilities_DEBIAN_NAME)
target_link_libraries(your_target PRIVATE ${qtutilities_DEBIAN_NAME})
endif()
Пример реализации
Вот полный пример для вашего CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0.0)
# Настройка переменных
set(PACKAGE_NAMESPACE_PREFIX "")
set(CONFIGURATION_PACKAGE_SUFFIX "")
set(CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES "")
# Определение дистрибутива
if(EXISTS /etc/debian_version)
set(DEBIAN_PACKAGE_NAMES TRUE)
message(STATUS "Detected Debian-based system")
elseif(EXISTS /etc/arch-release)
message(STATUS "Detected Arch Linux")
elseif(EXISTS /etc/redhat-release)
message(STATUS "Detected RedHat-based system")
endif()
# Функция для поиска пакетов
function(find_library_cross_distribution LIB_NAME VERSION ARCH_NAME DEBIAN_NAME)
# Сначала попробовать pkg-config
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
string(TOLOWER "${LIB_NAME}" LIB_LOWER)
pkg_check_modules(${LIB_NAME}_PKG ${LIB_LOWER} QUIET)
if(${LIB_NAME}_PKG_FOUND)
set(${LIB_NAME}_FOUND TRUE PARENT_SCOPE)
return()
endif()
endif()
# Попробовать Arch Linux имя
find_package(${ARCH_NAME} ${VERSION} QUIET)
if(${ARCH_NAME}_FOUND)
set(${LIB_NAME}_FOUND TRUE PARENT_SCOPE)
set(${LIB_NAME}_PACKAGE_NAME ${ARCH_NAME} PARENT_SCOPE)
return()
endif()
# Попробовать Debian имя
if(DEBIAN_PACKAGE_NAMES)
find_package(${DEBIAN_NAME} ${VERSION} QUIET)
if(${DEBIAN_NAME}_FOUND)
set(${LIB_NAME}_FOUND TRUE PARENT_SCOPE)
set(${LIB_NAME}_PACKAGE_NAME ${DEBIAN_NAME} PARENT_SCOPE)
return()
endif()
endif()
# Попробать найти вручную через pkg-config файлы
find_file(${LIB_NAME}_PC_FILE
NAMES ${LIB_NAME}.pc
PATHS /usr/lib/pkgconfig /usr/lib/*/pkgconfig /usr/share/pkgconfig
)
if(${LIB_NAME}_PC_FILE)
set(${LIB_NAME}_FOUND TRUE PARENT_SCOPE)
return()
endif()
# Если ничего не найдено
set(${LIB_NAME}_FOUND FALSE PARENT_SCOPE)
message(WARNING "Could not find ${LIB_NAME} (version ${VERSION})")
endfunction()
# Поиск c++utilities
find_library_cross_distribution(
CXX_UTILITIES
5.20.0
${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}
libmartchus-c++utilities-dev
)
# Поиск qtutilities
find_library_cross_distribution(
QT_UTILITIES
6.13.0
${PACKAGE_NAMESPACE_PREFIX}qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES}
libmartchus-qtutilities-dev
)
# Проверка найденных пакетов
if(CXX_UTILITIES_FOUND)
message(STATUS "Found c++utilities: ${CXX_UTILITIES_PACKAGE_NAME}")
if(DEFINED CXX_UTILITIES_PACKAGE_NAME)
if(CXX_UTILITIES_PACKAGE_NAME STREQUAL "libmartchus-c++utilities-dev")
# Для Debian использовать прямое подключение
find_library(CXX_UTILITIES_LIB
NAMES c++utilities
PATHS /usr/lib /usr/lib64 /usr/local/lib
)
if(CXX_UTILITIES_LIB)
target_link_libraries(your_target PRIVATE ${CXX_UTILITIES_LIB})
endif()
else()
# Для Arch использовать use_cpp_utilities
use_cpp_utilities(VISIBILITY PUBLIC)
endif()
else()
use_cpp_utilities(VISIBILITY PUBLIC)
endif()
else()
message(FATAL_ERROR "c++utilities not found. Please install it.")
endif()
if(QT_UTILITIES_FOUND)
message(STATUS "Found qtutilities: ${QT_UTILITIES_PACKAGE_NAME}")
if(DEFINED QT_UTILITIES_PACKAGE_NAME)
if(QT_UTILITIES_PACKAGE_NAME STREQUAL "libmartchus-qtutilities-dev")
# Для Debian использовать прямое подключение
find_library(QT_UTILITIES_LIB
NAMES qtutilities
PATHS /usr/lib /usr/lib64 /usr/local/lib
)
if(QT_UTILITIES_LIB)
target_link_libraries(your_target PRIVATE ${QT_UTILITIES_LIB})
endif()
else()
# Для Arch использовать use_qt_utilities
use_qt_utilities()
endif()
else()
use_qt_utilities()
endif()
else()
message(FATAL_ERROR "qtutilities not found. Please install it.")
endif()
Дополнительные методы
1. Использование переменных окружения
Добавьте поддержку переменных окружения для переопределения имен пакетов:
# Позволить пользователю переопределить имена пакетов
if(DEFINED ENV{CMAKE_CXXUTILITIES_NAME})
set(CXXUTILITIES_CUSTOM_NAME $ENV{CMAKE_CXXUTILITIES_NAME})
endif()
if(DEFINED CXXUTILITIES_CUSTOM_NAME)
find_package(${CXXUTILITIES_CUSTOM_NAME} 5.20.0 REQUIRED)
else()
# Использовать стандартную логику поиска
endif()
2. Создание конфигурационных файлов
Создайте файл config.cmake или config.h.in для определения доступных функций:
# config.cmake
option(USE_CXX_UTILITIES "Enable c++utilities support" ON)
option(USE_QT_UTILITIES "Enable qtutilities support" ON)
if(USE_CXX_UTILITIES)
# Логика поиска c++utilities
endif()
if(USE_QT_UTILITIES)
# Логика поиска qtutilities
endif()
3. Использование Conan или vcpkg
Для еще большей переносимости可以考虑 использование менеджеров пакетов:
find_package(CONAN REQUIRED)
conan_cmake_run(REQUIRES c++utilities/5.20.0 qtutilities/6.13.0
BASIC_SETUP
BUILD_MISSING)
Тестирование и валидация
Для тестирования вашего CMakeLists.txt на разных дистрибутивах:
-
Создайте Docker контейнеры с разными дистрибутивами:
bash# Dockerfile для теста на Debian FROM debian:bullseye RUN apt-get update && apt-get install -y cmake libmartchus-c++utilities-dev libmartchus-qtutilities-dev # Dockerfile для теста на Arch FROM archlinux:latest RUN pacman -Syu --noconfirm cmake c++utilities qtutilities -
Используйте CI/CD для автоматического тестирования:
yaml# .github/workflows/ci.yml name: Cross-distribution Build Test on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: distribution: [ubuntu-20.04, ubuntu-22.04, archlinux] steps: - uses: actions/checkout@v2 - name: Setup ${{ matrix.distribution }} uses: vmactions/archlinux@v0.1.4 - name: Install dependencies run: | # Установка зависимостей для конкретного дистрибутива - name: Configure run: cmake . - name: Build run: cmake --build . -
Добавьте тесты доступности пакетов:
cmake# В CMakeLists.txt add_test(NAME CheckCXXUtilities COMMAND ${CMAKE_COMMAND} -E echo "Testing c++utilities") add_test(NAME CheckQtUtilities COMMAND ${CMAKE_COMMAND} -E echo "Testing qtutilities") # Или более сложные тесты function(test_package_availability PACKAGE_NAME) find_package(${PACKAGE_NAME} QUIET) if(${PACKAGE_NAME}_FOUND) message(STATUS "✓ ${PACKAGE_NAME} is available") else() message(WARNING "✗ ${PACKAGE_NAME} is not available") endif() endfunction() test_package_availability(c++utilities) test_package_aviability(qtutilities)
Заключение
Для правильной настройки CMakeLists.txt для работы с разными дистрибутивами Linux:
- Используйте комбинацию стратегий поиска: pkg-config, find_package с разными именами, ручной поиск
- Создайте абстрактные функции для поиска пакетов с обработкой разных дистрибутивов
- Реализуйте условную логику для подключения библиотек по-разному в зависимости от найденных пакетов
- Добавьте поддержку переменных окружения для ручного переопределения имен пакетов
- Тестируйте на разных дистрибутивах с помощью Docker и CI/CD
Основные принципы:
- Гибкость: Позволить проекту работать на разных дистрибутивах
- Надежность: Предоставить несколько способов поиска пакетов
- Удобство: Дать пользователям возможность переопределять поведение
- Тестируемость: Обеспечить автоматическое тестирование на разных платформах
Такой подход гарантирует, что ваш проект будет успешно собираться на Arch Linux, Debian, Ubuntu и других дистрибутивах Linux без необходимости ручной правки CMakeLists.txt для каждой платформы.