Using Conan in a non-intrusive way in CMake-based projects
The Barbarians, CC BY-NC-SA 2.0 (https://www.flickr.com/photos/turatti/31861600644/)

Using Conan in a non-intrusive way in CMake-based projects

Ever since Software is eating the world an ongoing debate in the C and C++ development world is about proper dependency management and if a large amount of thirdparty dependencies are actually good or bad for the long-term maintainability of a project. Relating to C and C++ development, this is particularly interesting, because this doesn't seem to be an issue in many other programming languages.

For instance, if we look at the latest working model in frontend web development, it is not uncommon for a project to have dozens or even hundreds of dependencies. However, this is usually not an issue, since all those dependencies are managed using a dedicated package manager and get resolved automatically.

NodeJS, a commonly used web server written in JavaScript for example, uses a package manager called NPM. Ruby developers may use RubyGems, a package management framework for Ruby packages. Even relatively old languages such as PHP have something like Composer, a package management system for PHP packages.

On the other hand, maintaining a large chain of dependencies in a C or C++ based project can be quite cumbersome. In rare cases, it may even lead to software teams treating dependencies as enemies, which have to be deleted. For instance, it is told that the Excel team at Microsoft once aggressively neglected all dependencies to their product. However, unlike in the web development business, a proper package management in C or C++ is very complicated for a variety of reasons.

Often times, thirdparty libraries have to be treated specially: May be a custom configuration or only specific parts of a library is needed. Moreover, sometimes libraries need to be compiled with a specific compiler for ABI compatibility reasons, before they can be used on a software project. Not to mention, partially linking where no library files but the intermediate object files are needed for a consuming project. The different possible use cases seem to be endless here.

Introducing Conan as a dependency manager

A promising contender to tackle package dependency management is Conan, the successor of biicode. Conan is a decentralized, build system agnostic, package manager with a client-server architecture, where you define your dependencies (requirements in Conan-speak) in a dedicated configuration file and put it alongside your source code. Conan then needs to be called from the command line (once), to fetch all your dependencies either as pre-compiled binary artifacts or in source form and caches them locally. As Conan uses a client-server architecture, binary artifacts can be stored on a dedicated server, to further reduce the build times.

Using CMake to manage the build process of a software project, we don't want our package dependency manager to interfere with our build system, since they are two different responsibilities and we should strive for a separation of concerns here. Unfortunately, when using Conan in its latest version, this still is not achievable. According to their documentation, the preferred way of integrating Conan is to include a pre-populated conanbuildinfo.cmake right into your build system.

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(timer timer.cpp)
target_link_libraries(timer ${CONAN_LIBS})

However, as pointed out above, we don't want to mix two different responsibilities here: For one, we don't want to lock-in into a specific tool for managing our dependencies and we certainly don't want to adjust our existing configuration to fulfill the requirements of another build tool. Furthermore, we don't want to burden any clients using our software project to now add Conan as their package management tool as well.

Using cmake_paths generator

Another way of integrating Conan is to use the cmake_paths generator. This way, we don't need to touch our original build system configuration but let Conan produce a toolchain file that we can inject from outside as a parameter into our build system (as opposed to the conanbuildinfo.cmake, which needs to be referenced right in the configuration file). Unfortunately, in its current state this will fail for even simple cases, if the dependency itself has other dependencies (so called transitive dependencies).

# CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(HelloConanOpenCV)

find_package(OpenCV REQUIRED)

add_executable(hello_opencv hello.cc)
target_include_directories(hello_opencv PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(hello_opencv ${OpenCV_LIBS})

# conanfile.txt
[requires]
opencv/4.0.1@conan/stable

[generators]
cmake_paths

If we try to use this now, first calling Conan and then CMake to build our project, this will fail as the OpenCV dependency has been populated with CONAN_* variables that are unknown to our build system. Hence, in its current state, we are forced to populate those variables before calling our build system, which is prone to errors and unacceptable.

$ mkdir build && cd build
$ conan install --build=missing ..
$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=conan_paths.cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DCONAN_LIBJPEG_ROOT=.... \
    -DCONAN_ZLIB_ROOT=.... \
    -DCONAN_LIBPNG_ROOT=.... \
    -DCONAN_LIBTIFF_ROOT=.... \

As Conan is developed in public and Open-Source, I've looked into the implementation and found out that this information (the missing transitive dependencies) is indeed already stored in the DepsCppCmake data structure that is responsible for generating the toolchain file. Hence, this should be an easy fix. I've reported this issue to the Conan developers and fortunately they seem to agree and will provide a solution probably with the next upcoming version 1.13.

I'm available for software consultancy, training and mentoring. Please contact me, if you are interested in my services.

Luis Martínez de Bartolomé Izquierdo

Senior Software Engineer at JFrog

6 年

We couldn't make it for 1.13 :( but 1.14 will be soon!

要查看或添加评论,请登录

Kai Wolf的更多文章

  • Managing transitive dependencies with Conan and CMake

    Managing transitive dependencies with Conan and CMake

    Back when I looked into Conan the first time in 2017 the state-of-the-art for dependency management in the C and C++…

  • Heterogeneous computing on Android devices

    Heterogeneous computing on Android devices

    In today's technical world with all its different devices, ranging from small, intelligent wristwatches, smartphones…

  • Strange graphs and dynamic programming

    Strange graphs and dynamic programming

    There exists a YouTube channel called Numberphile that discusses all interesting sorts of mathematics. I really do…

  • Compile-time generation of RGB debug colors

    Compile-time generation of RGB debug colors

    Watching the latest episode of Handmade Hero on YouTube by Casey Muratori, there was an interesting application for…

  • Working with C++ code in Android effectively

    Working with C++ code in Android effectively

    If you are working with native (C or C++) code in your Android project, you are probably using CMake or ndk-build for…

  • Space carving of gemstones

    Space carving of gemstones

    Recently a potential customer approached me, asking me for a solution to 3D reconstruct inclusions and dirt in…

社区洞察

其他会员也浏览了