[C++] Getting Started With Conan

Managing external dependencies and packages in C++ Projects was always a bit poor - at least for me. So far, the most satisfactory solution has been installing dependencies manually and using CMake’s find_package(…). The downside here still is, that you need to provide the root-variables for each package, so CMake can find the dependency. For instance: Googletest needs GTEST_ROOT with the corresponding location.

Once the project grows and the number of dependency increases, this introduces more and more variables. Every user needs to provide them for building the project, which makes this approach vague.

Other approaches with git submodules or CMake's external_project(...) weren't satisfying as well (multiple downloads in different projects of the same dependencies, big CMakeLists, longer build process, etc.).

After searching for best practices for C++ Package Management I came accross Conan, the C/C++ Package Manager.
This post covers the installation of Conan and a first basic example.

 

Installing Conan

The recommended way to install is using pip (if you are on Windows, make sure python and pip is installed). Then run:

pip install conan

Verify the installation by simply running

conan

and you'll see the help output from Conan.

 

Conan remote packages

Conan works with remotes to access packages. With this post we are using the default remote, conancenter. In future posts I'll dive deeper into Conan by using own libraries, creating Conan packages and setting up other remotes.

All available packages on a remote. After installing the default and only remote is conancenter as you can see by running:

conan remote list

To find available packages here, You can either checkout conancenter or use the search command for a specific one (like googletest which we'll use later):

conan search gtest -r conancenter
 

The First Project

You can find this Getting Started Project on Github.

We'll set up a simple project, where we just build a dummy unittest (see ./gtest/ExampleTest.cc) by using googletest. The project structure then looks like:

├── CMakeLists.txt
├── conanfile.txt
└── gtest
    ├── CMakeLists.txt
    └── ExampleTest.cc

Conan works with a conanfile.txt (or in an advanced way with conanfile.py), where are all dependencies from our project are listed. The syntax for required packages is <package>/<version> or by targeting other remotes/repositories it's <package>/<version>@<user>/<channel>. For now we'll continue by using the default user and channel. And as generator we want to use CMake.

Writing The Conanfile

The conanfile.txt contains the following content to access googletest in our project:

[requires]
gtest/1.10.0

[generators]
cmake

Writing CMakeLists

Now consider the two CMakeLists.txt. First we include the from Conan generated conanbuildinfo.cmake and execute the macro conan_basic_setup(). Then the dependend libraries are stored in ${CONAN_LIBS}, which is used in the ./gtest/CMakeLists.txt.

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

enable_testing()
add_subdirectory(${PROJECT_SOURCE_DIR}/gtest)


# ./gtest/CMakeLists:
set(target unittests)

add_executable(${target} ${CMAKE_CURRENT_SOURCE_DIR}/ExampleTest.cc)
target_link_libraries(${target} ${CONAN_LIBS})

add_test(NAME ${target} COMMAND ${target})

Before Building With GCC >= 5.1

If you are using GCC compiler >= 5.1 run the following command prior building

conan profile update settings.compiler.libcxx=libstdc++11 default

Conan sets up a default profile. Compiler, OS, arch, build_type, etc. are stored here. For the scope of this post we adjust the libcxx from libstdc++ to libstdc++11. For more information see 5. from the Conan Getting Started.

Use conan profile show default to show the default settings or conan profile -h to see further options and the file location.

Building The First Project

Before building the project with cmake we'll run conan. Run the following steps accordingly:

$ mkdir build
$ cd build 
$ conan install .. --build missing
$ cmake -S ./.. -B ./
$ cmake --build ./
$ ctest -VV

This build passes and we can run the unittest (which is just dummy unittest, see ./gtest/ExampleTest.cc). The output after running ctest shall be the following.

 

Conclusion

At first I was a bit sceptical. But after using Conan and getting familiar with it, I started to like it. In my opinion this is a great option to get rid of big CMakeLists.txt, multiple CMake variables which i need to provide to a build or manual pre-installed libraries/frameworks.

In future posts I'll write about setting up own libraries and remotes to have a better practice for using Conan. Also if you want to use it profassionally with closed source libraries, the anser is here JFrog Artifactory.

That's it for now.

Best Thomas

Previous
Previous

[C++] Protobuf: Project Setup With Conan and CMake