As you will probably know, Conan has official support for integration with several build systems such as CMake, MSBuild or Meson amongst others. But maybe you don’t know that if you are using a build system that is not currently supported, Conan provides the tools to integrate it and build and consume packages that use it.
The code for this post is now available in the Conan examples repository. Feel free to clone it and experiment with the code.
Where do I start ?
Imagine that you want to create some packages using a specific build system and let others consume your packages and build them in case there are not binaries generated for their configuration. Conan has three features that can help you with that:
Conan generators. They provide your build system with all the information about dependencies in a suitable format.
Conan installer. Conan allows you to create packages for tools needed in the build process and installing them later with a
build_requiresto be able to invoke that tool from Conan. In our case, we want to install the tools to run our build system.
Conan build-helper. Build-helpers assist you in the process of translating settings such as
archto the build system. It can also invoke the build system tools to build our sources. To use the build helper inside a
conanfile.pywe will use a Python requires
Conan generator for the Waf build system
To test these tools, we have selected the Waf build system. Waf is a build-automation tool designed to help in the automatic compilation and installation of computer software. It is open-source software written in Python and is released under the terms of the BSD license.
Waf is a generic utility for building projects and project-specific details are stored in Python modules under
the name wscript. A Waf project must contain a top-level wscript where the commands that will make the
build happen are defined. Also, a configuration context will store data which may be re-used during the build.
Let’s see how a minimal implementation for that wscript would look for a C++ project where we want to build
an executable that depends on
As you can see, there are several commands defined here being
build() the ones that matter
most to us at this moment.
configurecommand has the responsibility to set several settings and find the location of the prerequisites. We have to modify the configuration context (
conf.env) variable to tell Waf where will it be able to find the includes and library files. Conan has all this information so we will need a tool that transforms that information in a way we can load in the wscript and that’s what a Conan generator is designed to do.
buildcommand will transform the source files into build files. Note that in the call to
bld.programwe can tell Waf which libraries we are linking with the
useargument. The Conan generator will have to provide this argument to Waf as well.
Waf provides us with the capability of loading python modules using the
load command. We can load the
Python code created by the Conan generator to modify the Waf configuration context. That way we can
include the information about all the dependencies.
Custom Conan generators
A custom generator in
Conan is a class that extends
Generator and implements two properties:
filenameshould return the name of the file that will be generated. In our case, we will generate a file called
contentshould return the contents of the file with the desired format. Here we will retrieve all that information from the
deps_build_infoproperty of the
Generatorclass. That property is a dictionary that has all the information required to link the library.
To use the generator in our consumers we will have to make a package that can be later loaded as a
build_requires. The implementation of the generator will go in
conanfile.py and can be as simple as
This generator will create the
waf_conan_libs_info.py file with all the dependencies information. We can
pass this information to Waf with the
load command in the wscript:
But that would only work if we have the Waf build tool in our path. However, we don’t know if our consumers are going to have it installed. We can solve this problem creating a Conan installer package.
Creating a package to install the build system
As we said, Waf is a build system written in Python so to use it we will need to download the
Python script from the Waf repository. We can create a Conan package that
downloads the tool and makes it available to perform our build. This would be the structure of the
conanfile.py for our installer:
Note that only the
os_build setting has been left from the settings of the
conanfile.py because it
does not make sense to create different installer packages depending for example on the
arch as the tool will be the same for all those configurations. After installing this package all
consumers that declare it as
build_requires will have this tool available on the path.
At this point, we are able to tell Waf about the libraries locations and we can invoke Waf from a
self.run() and manually passing settings like the
build_type. But there is a better
way of doing this that will be the missing piece of our puzzle: creating our own Conan build-helper.
Conan build-helper for Waf
Our build-helper will have two missions:
Generate all the information with the Conan build settings to a format Waf can understand. We will generate another Python module that sets build information that Conan has such as
compiler.runtimein Waf. The name of this file will be
Assist with the compilation of libraries and applications in the
build()method of a recipe. We will create a method that invokes the build system abstracting the calls to
self.runin the conanfile.
To create our own build-helper, we will use the
python_requires() feature of Conan. That way we will be
able to reuse python code existing in other
conanfile.py recipes. We will create a package with our
build-helper code and reuse it in the consumers importing them as a Python requires. There is a minimal
implementation of the Python requires in the conanfile but all the important code will reside in
waf_environment.py file that contains the
WafBuildEnvironment class. To learn a bit more about Python
Requires, please visit the Conan
As we said, all the important code is in the
WafBuildEnvironment class in
see an example of a simplified build-helper implementation that only takes into account the Conan
build_type. The configuration of the environment is made calling to the
configure method of the
We modify the configuration environment through the
conf.env variable setting all the relevant flags for
Release and Debug configurations depending on if we are building with Visual Studio or any other compiler.
We also define a
build method that runs the Waf build tool.
Putting it all together
Building the library
At this point, we are able to create a recipe that builds our library with the Waf build system. An example of the structure of the project would be as follows:
conanfile.py that declares the requirement of all the necessary tools for building the project.
wscript to build the library could be like this:
The information for the build system is passed through the loading of the
that was created by the build-helper.
Consuming the library
We could now consume the library even if we didn’t have Waf installed but for the sake of completeness, let’s
consume it using Waf as well. We will have to declare the needed
And create a
wscript that loads all the Conan information in the Waf environment.
Now, we could build our application using Conan:
At this point, you should have a general understanding of what Conan generators, build-helpers and installers are and how they can help you to integrate almost any build system in Conan. Now you can clone the Conan examples repository to see the implementation at a higher detail and start integrating your favourite build system in the Conan package manager.