In the past I wrote an article that explains how to consume packages using Conan. In this article I’m going to explain how we can create our own packages with Conan.

I’m going to create a simple cpp library and make a conan package out of it.

Sample package

The first step is to create a folder for the library:

1
2
mkdir MyLib
cd MyLib

Then we can create a Conan recipe:

1
conan new MyLib/0.1

As you might have already figured out. We are creating a package named MyLib, and we are setting the version to 0.1. This command generates a file named conanfile.py.

The generated file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from conans import ConanFile, CMake, tools


class MylibConan(ConanFile):
    name = "MyLib"
    version = "0.1"
    license = "<Put the package license here>"
    author = "<Put your name here> <And your email here>"
    url = "<Package recipe repository url here, for issues about the package>"
    description = "<Description of Mylib here>"
    topics = ("<Put some tag here>", "<here>", "<and here>")
    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False]}
    default_options = "shared=False"
    generators = "cmake"

    def source(self):
        self.run("git clone https://github.com/memsharded/hello.git")
        self.run("cd hello && git checkout static_shared")
        # This small hack might be useful to guarantee proper /MT /MD linkage
        # in MSVC if the packaged project doesn't have variables to set it
        # properly
        tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(MyHello)",
                              '''PROJECT(MyHello)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()''')

    def build(self):
        cmake = CMake(self)
        cmake.configure(source_folder="hello")
        cmake.build()

        # Explicit way:
        # self.run('cmake %s/hello %s'
        #          % (self.source_folder, cmake.command_line))
        # self.run("cmake --build . %s" % cmake.build_config)

    def package(self):
        self.copy("*.h", dst="include", src="hello")
        self.copy("*hello.lib", dst="lib", keep_path=False)
        self.copy("*.dll", dst="bin", keep_path=False)
        self.copy("*.so", dst="lib", keep_path=False)
        self.copy("*.dylib", dst="lib", keep_path=False)
        self.copy("*.a", dst="lib", keep_path=False)

    def package_info(self):
        self.cpp_info.libs = ["hello"]

Most of the properties of the MylibConan class are self-explanatory. I’m going to explain the ones that are not that obvious.

  • settings - The default value means that if the os, compiler, build type or cpu architecture change. A different binary will be generated.
  • options - Allows specifing arbitrary options that can be used while building the package. In this case it could be used to define if a library will be built as a shared library.

There is a source method defined. This method can be used to retrieve source code from github or other sources. We will be using code in the current folder, so we won’t need to download anything.

The build method does what you would expect. It builds the project using cmake.

The package method can be used to move all artifacts (libraries, headers) to a single folder.

Lastly, the package_info method defines the name that will be used by the consumers of this package.

Library code

Before we modify conanfile.py to fit our needs, let’s create a very simple library that does nothing:

1
2
3
4
5
# Inside MyLib folder
mkdir src
cd src
touch MyLib.h
touch MyLib.cpp

MyLib.h:

1
2
3
4
class MyLib {
 public:
  void doNothing();
};

MyLib.cpp

1
2
3
4
5
#include "MyLib.h"

void MyLib::doNothing() {
  // Nothing
}

Make it a Cmake project:

1
touch CMakeLists.txt

CMakeLists.txt

1
2
3
4
5
6
7
# Minimum version of CMake required to build this project
cmake_minimum_required(VERSION 3.0)

# Name of the project
project(MyLib)

add_library(MyLib MyLib.cpp)

Run a Cmake build:

1
2
3
4
mkdir build
cd build
cmake ../src/
make

Now that we have our build ready, we can tune conanfile.py.

Conanfile.py

Here is a new version with comments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from conans import ConanFile, CMake, tools


class MylibConan(ConanFile):
    # Removed a bunch of attributes that are not needed, but you might want to
    # Add them for a real project
    name = "MyLib"
    version = "0.1"
    generators = "cmake"
    # If the source code is going to be in the same repo as the Conan recipe,
    # there is no need to define a `source` method. The source folder can be
    # defined like this
    exports_sources = "src/*"

    def build(self):
        cmake = CMake(self)
        # The CMakeLists.txt file must be in `source_folder`
        cmake.configure(source_folder="src")
        cmake.build()

    def package(self):
        # Copy headers to the include folder and libraries to the lib folder
        self.copy("*.h", dst="include", src="src")
        self.copy("*.a", dst="lib", keep_path=False)

    def package_info(self):
        self.cpp_info.libs = ["MyLib"]

To build the package:

1
conan create . myself/MyLib

If everything goes well you will see a line like this in the output:

1
2
3
...
MyLib/0.1@src/MyLib: Package folder /home/me/.conan/data/MyLib/0.1/src/MyLib/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9
...

You can go to that folder to see the contents of the package.

In my next article, I will explain how we can publish a package so it can be used by anyone.

[ c++  programming  dependency_management  ]
Dependency injection with Dagger and Bazel
Concurrent data structures
Pointers to members in C++
Operator overloading in C++
C++ value categories