Build a simple CMake project using Conan

Let’s get started with an example: We are going to create a string compressor application that uses one of the most popular C++ libraries: Zlib.

We’ll use CMake as build system in this case but keep in mind that Conan works with any build system and is not limited to using CMake. You can check more examples with other build systems in the Read More section.

Please, first clone the sources to recreate this project, you can find them in the examples2 repository in GitHub:

  1. $ git clone https://github.com/conan-io/examples2.git
  2. $ cd examples2/tutorial/consuming_packages/simple_cmake_project

We start from a very simple C language project with this structure:

  1. .
  2. ├── CMakeLists.txt
  3. └── src
  4. └── main.c

This project contains a basic CMakeLists.txt including the zlib dependency and the source code for the string compressor program in main.c.

Let’s have a look at the main.c file:

main.c

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <zlib.h>
  5. int main(void) {
  6. char buffer_in [256] = {"Conan is a MIT-licensed, Open Source package manager for C and C++ development "
  7. "for C and C++ development, allowing development teams to easily and efficiently "
  8. "manage their packages and dependencies across platforms and build systems."};
  9. char buffer_out [256] = {0};
  10. z_stream defstream;
  11. defstream.zalloc = Z_NULL;
  12. defstream.zfree = Z_NULL;
  13. defstream.opaque = Z_NULL;
  14. defstream.avail_in = (uInt) strlen(buffer_in);
  15. defstream.next_in = (Bytef *) buffer_in;
  16. defstream.avail_out = (uInt) sizeof(buffer_out);
  17. defstream.next_out = (Bytef *) buffer_out;
  18. deflateInit(&defstream, Z_BEST_COMPRESSION);
  19. deflate(&defstream, Z_FINISH);
  20. deflateEnd(&defstream);
  21. printf("Uncompressed size is: %lu\n", strlen(buffer_in));
  22. printf("Compressed size is: %lu\n", strlen(buffer_out));
  23. printf("ZLIB VERSION: %s\n", zlibVersion());
  24. return EXIT_SUCCESS;
  25. }

Also, the contents of CMakeLists.txt are:

CMakeLists.txt

  1. cmake_minimum_required(VERSION 3.15)
  2. project(compressor C)
  3. find_package(ZLIB REQUIRED)
  4. add_executable(${PROJECT_NAME} src/main.c)
  5. target_link_libraries(${PROJECT_NAME} ZLIB::ZLIB)

Our application relies on the Zlib library. Conan, by default, tries to install libraries from a remote server called ConanCenter. You can search there for libraries and also check the available versions. In our case, after checking the available versions for Zlib we choose to use one of the latest versions: zlib/1.2.11.

The easiest way to install the Zlib library and find it from our project with Conan is using a conanfile.txt file. Let’s create one with the following content:

conanfile.txt

  1. [requires]
  2. zlib/1.2.11
  3. [generators]
  4. CMakeDeps
  5. CMakeToolchain

As you can see we added two sections to this file with a syntax similar to an INI file.

  • [requires] section is where we declare the libraries we want to use in the project, in this case, zlib/1.2.11.

  • [generators] section tells Conan to generate the files that the compilers or build systems will use to find the dependencies and build the project. In this case, as our project is based in CMake, we will use CMakeDeps to generate information about where the Zlib library files are installed and CMakeToolchain to pass build information to CMake using a CMake toolchain file.

Besides the conanfile.txt, we need a Conan profile to build our project. Conan profiles allow users to define a configuration set for things like the compiler, build configuration, architecture, shared or static libraries, etc. Conan, by default, will not try to detect a profile automatically, so we need to create one. To let Conan try to guess the profile, based on the current operating system and installed tools, please run:

  1. conan profile detect --force

This will detect the operating system, build architecture and compiler settings based on the environment. It will also set the build configuration as Release by default. The generated profile will be stored in the Conan home folder with name default and will be used by Conan in all commands by default unless another profile is specified via the command line. An example of the output of this command for MacOS would be:

  1. $ conan profile detect --force
  2. Found apple-clang 14.0
  3. apple-clang>=13, using the major as version
  4. Detected profile:
  5. [settings]
  6. arch=x86_64
  7. build_type=Release
  8. compiler=apple-clang
  9. compiler.cppstd=gnu17
  10. compiler.libcxx=libc++
  11. compiler.version=14
  12. os=Macos

Note

A note about the detected C++ standard by Conan

Conan will always set the default C++ standard as the one that the detected compiler version uses by default, except for the case of macOS using apple-clang. In this case, for apple-clang>=11, it sets compiler.cppstd=gnu17. If you want to use a different C++ standard, you can edit the default profile file directly. First, get the location of the default profile using:

  1. $ conan profile path default
  2. /Users/user/.conan2/profiles/default

Then open and edit the file and set compiler.cppstd to the C++ standard you want to use.

Note

Using a compiler other than the auto-detected one

If you want to change a Conan profile to use a compiler different from the default one, you need to change the compiler setting and also tell Conan explicitly where to find it using the tools.build:compiler_executables configuration.

We will use Conan to install Zlib and generate the files that CMake needs to find this library and build our project. We will generate those files in the folder build. To do that, run:

  1. $ conan install . --output-folder=build --build=missing

You will get something similar to this as the output of that command:

  1. $ conan install . --output-folder=build --build=missing
  2. ...
  3. -------- Computing dependency graph ----------
  4. zlib/1.2.11: Not found in local cache, looking in remotes...
  5. zlib/1.2.11: Checking remote: conancenter
  6. zlib/1.2.11: Trying with 'conancenter'...
  7. Downloading conanmanifest.txt
  8. Downloading conanfile.py
  9. Downloading conan_export.tgz
  10. Decompressing conan_export.tgz
  11. zlib/1.2.11: Downloaded recipe revision f1fadf0d3b196dc0332750354ad8ab7b
  12. Graph root
  13. conanfile.txt: /home/conan/examples2/tutorial/consuming_packages/simple_cmake_project/conanfile.txt
  14. Requirements
  15. zlib/1.2.11#f1fadf0d3b196dc0332750354ad8ab7b - Downloaded (conancenter)
  16. -------- Computing necessary packages ----------
  17. Requirements
  18. zlib/1.2.11#f1fadf0d3b196dc0332750354ad8ab7b:cdc9a35e010a17fc90bb845108cf86cfcbce64bf#dd7bf2a1ab4eb5d1943598c09b616121 - Download (conancenter)
  19. -------- Installing packages ----------
  20. Installing (downloading, building) binaries...
  21. zlib/1.2.11: Retrieving package cdc9a35e010a17fc90bb845108cf86cfcbce64bf from remote 'conancenter'
  22. Downloading conanmanifest.txt
  23. Downloading conaninfo.txt
  24. Downloading conan_package.tgz
  25. Decompressing conan_package.tgz
  26. zlib/1.2.11: Package installed cdc9a35e010a17fc90bb845108cf86cfcbce64bf
  27. zlib/1.2.11: Downloaded package revision dd7bf2a1ab4eb5d1943598c09b616121
  28. -------- Finalizing install (deploy, generators) ----------
  29. conanfile.txt: Generator 'CMakeToolchain' calling 'generate()'
  30. conanfile.txt: Generator 'CMakeDeps' calling 'generate()'
  31. conanfile.txt: Generating aggregated env files

As you can see in the output, there are a couple of things that happened:

  • Conan installed the Zlib library from the remote server, which should be the Conan Center server by default if the library is available. This server stores both the Conan recipes, which are the files that define how libraries must be built, and the binaries that can be reused so we don’t have to build from sources every time.

  • Conan generated several files under the build folder. Those files were generated by both the CMakeToolchain and CMakeDeps generators we set in the conanfile.txt. CMakeDeps generates files so that CMake finds the Zlib library we have just downloaded. On the other side, CMakeToolchain generates a toolchain file for CMake so that we can transparently build our project with CMake using the same settings that we detected for our default profile.

Now we are ready to build and run our compressor app:

Windows

  1. $ cd build
  2. # assuming Visual Studio 15 2017 is your VS version and that it matches your default profile
  3. $ cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake"
  4. $ cmake --build . --config Release
  5. ...
  6. [100%] Built target compressor
  7. $ Release\compressor.exe
  8. Uncompressed size is: 233
  9. Compressed size is: 147
  10. ZLIB VERSION: 1.2.11

Linux, macOS

  1. $ cd build
  2. $ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
  3. $ cmake --build .
  4. ...
  5. [100%] Built target compressor
  6. $ ./compressor
  7. Uncompressed size is: 233
  8. Compressed size is: 147
  9. ZLIB VERSION: 1.2.11

Note that CMakeToolchain might generate CMake presets files, that allows users with a modern CMake (>=3.23) to use them with cmake --preset instead of passing the toolchain file argument. See Building with CMake presets

See also