How do I properly add libraries to CMakeLists.txt when package names differ between Linux distributions?
I have a project originally configured for Arch Linux with the following code in 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()
In Debian, these packages are named libmartchus-c++utilities-dev and libmartchus-qtutilities-dev. When I try to substitute the names and remove the variables, I get an error:
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!
How do I properly configure CMakeLists.txt to build the package in Debian?
Solving Package Naming Issues Between Linux Distributions in CMakeLists.txt
To solve the problem of different package names between Linux distributions in CMakeLists.txt, you need to use a combination of package search strategies and conditional compilation. The main issue is that different distributions use different package naming conventions and provide CMake configuration files differently.
Table of Contents
- Understanding the Problem
- Solution Strategies
- Recommended Approach
- Implementation Example
- Additional Methods
- Testing and Validation
- Conclusion
Understanding the Problem
The main issues are:
- Different naming conventions: Arch Linux uses
c++utilities, while Debian useslibmartchus-c++utilities-dev - Missing configuration files: Debian packages typically don’t provide
*Config.cmakefiles by default - Different installation paths: Packages may be located in different system paths
- Different dependencies: Different distributions may require different versions of dependencies
The error you’re getting occurs because CMake is looking for a package configuration file that isn’t provided in standard Debian packages.
Solution Strategies
There are several approaches to solve this problem:
1. Using Multiple find_package Calls
Check for packages with different names and use the first available one:
# Try Arch Linux style first, then 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)
# Alternative logic for Debian
target_link_libraries(your_target PRIVATE libmartchus-c++utilities-dev)
endif()
endif()
2. Creating Custom Find Modules
Create Find*.cmake modules for each distribution:
cmake/
├── FindC++Utilities.cmake
├── FindQtUtilities.cmake
└── Modules/
└── FindDebianC++Utilities.cmake
3. Using pkg-config
Most libraries provide .pc files for pkg-config, which work across almost all distributions:
find_package(PkgConfig REQUIRED)
pkg_check_modules(CPPUTILITIES REQUIRED c++utilities)
pkg_check_modules(QTUTILITIES REQUIRED qtutilities)
Recommended Approach
The most reliable and portable solution is a combination of pkg-config and fallback search mechanisms:
# Function to search for libraries with different names
function(find_package_cross_distribution PACKAGE_NAME VERSION DEBIAN_NAME)
# First try 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()
# Try standard name
find_package(${PACKAGE_NAME} ${VERSION} QUIET)
if(${PACKAGE_NAME}_FOUND)
return()
endif()
# Try Debian name
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()
# If nothing found, show error
message(FATAL_ERROR "Package ${PACKAGE_NAME} (version ${VERSION}) not found. "
"Please install it using your system package manager.")
endfunction()
# Using the function
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)
# Conditional usage logic
if(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}_FOUND)
use_cpp_utilities(VISIBILITY PUBLIC)
elseif(DEFINED c++utilities_DEBIAN_NAME)
# For Debian, use direct linking
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()
Implementation Example
Here’s a complete example for your CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0.0)
# Configure variables
set(PACKAGE_NAMESPACE_PREFIX "")
set(CONFIGURATION_PACKAGE_SUFFIX "")
set(CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES "")
# Detect distribution
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 to search for libraries
function(find_library_cross_distribution LIB_NAME VERSION ARCH_NAME DEBIAN_NAME)
# First try 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()
# Try Arch Linux name
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()
# Try Debian name
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()
# Try to find manually via pkg-config files
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()
# If nothing found
set(${LIB_NAME}_FOUND FALSE PARENT_SCOPE)
message(WARNING "Could not find ${LIB_NAME} (version ${VERSION})")
endfunction()
# Search for c++utilities
find_library_cross_distribution(
CXX_UTILITIES
5.20.0
${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX}
libmartchus-c++utilities-dev
)
# Search for qtutilities
find_library_cross_distribution(
QT_UTILITIES
6.13.0
${PACKAGE_NAMESPACE_PREFIX}qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES}
libmartchus-qtutilities-dev
)
# Check found packages
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")
# For Debian, use direct linking
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()
# For Arch, use 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")
# For Debian, use direct linking
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()
# For Arch, use use_qt_utilities
use_qt_utilities()
endif()
else()
use_qt_utilities()
endif()
else()
message(FATAL_ERROR "qtutilities not found. Please install it.")
endif()
Additional Methods
1. Using Environment Variables
Add support for environment variables to override package names:
# Allow users to override package names
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()
# Use standard search logic
endif()
2. Creating Configuration Files
Create config.cmake or config.h.in files to define available features:
# 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 search logic
endif()
if(USE_QT_UTILITIES)
# qtutilities search logic
endif()
3. Using Conan or vcpkg
For even better portability, consider using package managers:
find_package(CONAN REQUIRED)
conan_cmake_run(REQUIRES c++utilities/5.20.0 qtutilities/6.13.0
BASIC_SETUP
BUILD_MISSING)
Testing and Validation
To test your CMakeLists.txt on different distributions:
-
Create Docker containers with different distributions:
bash# Dockerfile for Debian test FROM debian:bullseye RUN apt-get update && apt-get install -y cmake libmartchus-c++utilities-dev libmartchus-qtutilities-dev # Dockerfile for Arch test FROM archlinux:latest RUN pacman -Syu --noconfirm cmake c++utilities qtutilities -
Use CI/CD for automated testing:
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: | # Install dependencies for specific distribution - name: Configure run: cmake . - name: Build run: cmake --build . -
Add package availability tests:
cmake# In 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") # Or more complex tests 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_availability(qtutilities)
Conclusion
To properly configure CMakeLists.txt to work across different Linux distributions:
- Use a combination of search strategies: pkg-config, find_package with different names, manual search
- Create abstract functions for package search with handling for different distributions
- Implement conditional logic for linking libraries differently based on found packages
- Add environment variable support for manually overriding package names
- Test on different distributions using Docker and CI/CD
Key principles:
- Flexibility: Allow the project to work on different distributions
- Reliability: Provide multiple ways to search for packages
- Convenience: Give users the ability to override behavior
- Testability: Ensure automated testing across different platforms
This approach ensures that your project will successfully build on Arch Linux, Debian, Ubuntu, and other Linux distributions without needing to manually edit CMakeLists.txt for each platform.