Using Conan in a non-intrusive way in CMake-based projects
Kai Wolf 13 February 2019
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.
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 generators
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).
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.
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.
Image reference: The Barbarians, CC BY-NC-SA 2.0 (https://www.flickr.com/photos/turatti/31861600644/)