Getting started
This section introduces how to create your own Conan packages, explain conanfile.py recipes and the commands to build packages from sources in your computer.
Important
This is a tutorial section. You are encouraged to execute these commands.
For this concrete example, you will need CMake installed in your path.
It is not strictly required by Conan to create packages, you can use
other build systems (as VS, Meson, Autotools and even your own) to do that, without any dependency
to CMake.
Some of the features used in this section are experimental, like CMakeToolchain
or cmake_layout()
,
and they might change in future releases. There are other alternative tools that are stable, please check
the reference section for more information.
Using the conan new command will create a “Hello World” C++ library example project for us:
$ mkdir hellopkg && cd hellopkg
$ conan new hello/0.1 --template=cmake_lib
File saved: conanfile.py
File saved: CMakeLists.txt
File saved: src/hello.cpp
File saved: src/hello.h
File saved: test_package/conanfile.py
File saved: test_package/CMakeLists.txt
File saved: test_package/src/example.cpp
The generated files are:
conanfile.py: On the root folder, there is a conanfile.py which is the main recipe file, responsible for defining how the package is built and consumed.
CMakeLists.txt: A simple generic CMakeLists.txt, with nothing specific about Conan in it.
src folder: the src folder that contains the simple C++ “hello” library.
(optional) test_package folder: contains an example application that will require and link with the created package. It is not mandatory, but it is useful to check that our package is correctly created.
Let’s have a look at the package recipe conanfile.py:
from conans import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
class HelloConan(ConanFile):
name = "hello"
version = "0.1"
# Binary configuration
settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]}
default_options = {"shared": False, "fPIC": True}
# Sources are located in the same place as this recipe, copy them to the recipe
exports_sources = "CMakeLists.txt", "src/*"
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.libs = ["hello"]
Let’s explain a little bit about this recipe:
The binary configuration is composed by
settings
andoptions
. See more in this section. When something changes in the configuration, the resulting binary built and packaged will be different:settings
are project wide configuration, that cannot be defaulted in recipes, like the OS or the architecture.options
are package specific configuration and can be defaulted in recipes, in this case we have the option of creating the package as a shared or static library, being static the default.
The
exports_sources
attribute defines which sources are exported together with the recipe, these sources become part of the package recipe (there are other mechanisms that don’t do this, will be explained later.The
config_options()
method (together withconfigure()
one) allows to fine tune the binary configuration model, for example, in Windows there is nofPIC
option, so it can be removed.The
generate()
method prepares the build of the package from source. In this case, it could be simplified to an attributegenerators = "CMakeToolchain"
, but it is left to show this important method. In this case, the execution ofCMakeToolchain
generate()
method will create a conan_toolchain.cmake file that maps the Conansettings
andoptions
to CMake syntax.The
build()
method uses theCMake
wrapper to call CMake commands, it is a thin layer that will manage to pass in this case the-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
argument, plus other possible arguments, like-DCMAKE_BUILD_TYPE=<config>
if necessary. It will configure the project and build it from source. The actual arguments that will be used are obtained from a generatedCMakePresets.json
file.The
package()
method copies artifacts (headers, libs) from the build folder to the final package folder. It can be done with bare “copy” commands, but in this case it is leveraging the already existing CMake install functionality (if the CMakeLists.txt didn’t implement it, it is easy to writeself.copy()
commands in thispackage()
method.Finally, the
package_info()
method defines that consumers must link with a “hello” library when using this package. Other information as include or lib paths can be defined as well. This information is used for files created by generators (asCMakeDeps
) to be used by consumers. Although this method implies some potential duplication with the build system output (CMake could generate xxx-config.cmake files), it is important to define this, as Conan packages can be consumed by any other build system, not only CMake.
The contents of the test_package
folder is not critical now for understanding how packages are created, the important
bits are:
test_package
folder is different from unit or integration tests. These tests are “package” tests, and validate that the package is properly created, and that the package consumers will be able to link against it and reuse it.It is a small Conan project itself, it contains its own
conanfile.py
, and its source code including build scripts, that depends on the package being created, and builds and execute a small application that requires the library in the package.It doesn’t belong to the package. It only exist in the source repository, not in the package.
Let’s build the package from sources with the current default configuration (default profile), and then let the test_package
folder test the package:
$ conan create . demo/testing
...
hello/0.1: Hello World Release!
hello/0.1: _M_X64 defined
...
If “Hello world Release!” is displayed, it worked. This is what has happened:
The conanfile.py together with the contents of the src folder have been copied (exported in Conan terms) to the local Conan cache.
A new build from source for the
hello/0.1@demo/testing
package starts, calling thegenerate()
,build()
andpackage()
methods. This creates the binary package in the Conan cache.Moves to the test_package folder and executes a
conan install
+conan build
+test()
method, to check if the package was correctly created.
We can now validate that the recipe and the package binary are in the cache:
$ conan search
Existing package recipes:
hello/0.1@demo/testing
$ conan search hello/0.1@demo/testing
Existing packages for recipe hello/0.1@demo/testing:
Package_ID: 3fb49604f9c2f729b85ba3115852006824e72cab
[options]
shared: False
[settings]
arch: x86_64
build_type: Release
...
The conan create command receives the same command line parameters as conan install so you can pass to it the same settings and options. If we execute the following lines, we will create new package binaries for those configurations:
$ conan create . demo/testing -s build_type=Debug
...
hello/0.1: Hello World Debug!
$ conan create . demo/testing -o hello:shared=True
...
hello/0.1: Hello World Release!
These new package binaries will be also stored in the Conan cache, ready to be used by any project in this computer, we can see them with:
$ conan search hello/0.1@demo/testing
Existing packages for recipe hello/0.1@demo/testing:
Package_ID: 127af201a4cdf8111e2e08540525c245c9b3b99e
[options]
shared: True
[settings]
arch: x86_64
build_type: Release
...
Package_ID: 3fb49604f9c2f729b85ba3115852006824e72cab
[options]
shared: False
[settings]
arch: x86_64
build_type: Release
...
Package_ID: d057732059ea44a47760900cb5e4855d2bea8714
[options]
shared: False
[settings]
arch: x86_64
build_type: Debug
...
Any doubts? Please check out our FAQ section or open a Github issue