НейроАгент

Как настроить CMake для разных дистрибутивов Linux

Решение проблемы с разными именами пакетов в CMakeLists.txt для Arch Linux и Debian. Использование pkg-config, find_package и кросс-дистрибутивной настройки сборки.

Вопрос

Как правильно добавить библиотеки в CMakeLists.txt, когда имена пакетов отличаются между дистрибутивами Linux?

У меня есть проект, изначально настроенный для Arch Linux, с следующим кодом в CMakeLists:

cmake
# 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 по-разному.

Содержание

Понимание проблемы

Основная проблема заключается в том, что:

  1. Разные соглашения об именовании: Arch Linux использует c++utilities, а Debian - libmartchus-c++utilities-dev
  2. Отсутствие файлов конфигурации: В Debian пакеты обычно не предоставляют файлы *Config.cmake по умолчанию
  3. Разные пути установки: Пакеты могут находиться в разных системных путях
  4. Разные зависимости: В разных дистрибутивах могут требоваться разные версии зависимостей

Ошибка, которую вы получаете, возникает потому, что CMake ищет файл конфигурации пакета, который не предоставляется в стандартных пакетах Debian.

Стратегии решения

Есть несколько подходов к решению этой проблемы:

1. Использование нескольких find_package вызовов

Проверять наличие пакетов с разными именами и использовать первый доступный:

cmake
# Попробовать сначала 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, которые работают практически во всех дистрибутивах:

cmake
find_package(PkgConfig REQUIRED)

pkg_check_modules(CPPUTILITIES REQUIRED c++utilities)
pkg_check_modules(QTUTILITIES REQUIRED qtutilities)

Рекомендуемый подход

Наиболее надежным и переносимым решением является комбинация pkg-config и резервных механизмов поиска:

cmake
# Функция для поиска библиотек с разными именами
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
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. Использование переменных окружения

Добавьте поддержку переменных окружения для переопределения имен пакетов:

cmake
# Позволить пользователю переопределить имена пакетов
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 для определения доступных функций:

cmake
# 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

Для еще большей переносимости可以考虑 использование менеджеров пакетов:

cmake
find_package(CONAN REQUIRED)
conan_cmake_run(REQUIRES c++utilities/5.20.0 qtutilities/6.13.0
                BASIC_SETUP
                BUILD_MISSING)

Тестирование и валидация

Для тестирования вашего CMakeLists.txt на разных дистрибутивах:

  1. Создайте 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
    
  2. Используйте 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 .
    
  3. Добавьте тесты доступности пакетов:

    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:

  1. Используйте комбинацию стратегий поиска: pkg-config, find_package с разными именами, ручной поиск
  2. Создайте абстрактные функции для поиска пакетов с обработкой разных дистрибутивов
  3. Реализуйте условную логику для подключения библиотек по-разному в зависимости от найденных пакетов
  4. Добавьте поддержку переменных окружения для ручного переопределения имен пакетов
  5. Тестируйте на разных дистрибутивах с помощью Docker и CI/CD

Основные принципы:

  • Гибкость: Позволить проекту работать на разных дистрибутивах
  • Надежность: Предоставить несколько способов поиска пакетов
  • Удобство: Дать пользователям возможность переопределять поведение
  • Тестируемость: Обеспечить автоматическое тестирование на разных платформах

Такой подход гарантирует, что ваш проект будет успешно собираться на Arch Linux, Debian, Ubuntu и других дистрибутивах Linux без необходимости ручной правки CMakeLists.txt для каждой платформы.