How to package header-only libraries
Without unit tests
Packaging a header only library, without requiring to build and run unit tests for it within conan, can be
done with a very simple recipe. Assuming you have the recipe in the source repo root folder, and the headers
in a subfolder called include
, you could do:
from conans import ConanFile
class HelloConan(ConanFile):
name = "Hello"
version = "0.1"
# No settings/options are necessary, this is header only
exports_sources = "include/*"
no_copy_source = True
def package(self):
self.copy("*.h")
If you want to package an external repository, you can use the source()
method to do a clone or download
instead of the exports_sources
fields.
There is no need for
settings
, as changing them will not affect the final package artifactsThere is no need for
build()
method, as header-only are not builtThere is no need for a custom
package_info()
method. The default one already adds “include” subfolder to the include pathno_copy_source = True
will disable the copy of the source folder to the build directory as there is no need to do so because source code is not modified at all by theconfigure()
orbuild()
methods.Note that this recipe has no other dependencies, settings or options. If it had any of those, it would be very convenient to add the
package_id()
method, to ensure that only one package with always the same ID is create irrespective of the configurations and dependencies:
def package_id(self):
self.info.header_only()
Package is created with:
$ conan create . user/channel
With unit tests
If you want to run the library unit test while packaging, you would need this recipe:
from conans import ConanFile, CMake
class HelloConan(ConanFile):
name = "Hello"
version = "0.1"
settings = "os", "compiler", "arch", "build_type"
exports_sources = "include/*", "CMakeLists.txt", "example.cpp"
no_copy_source = True
def build(self): # this is not building a library, just tests
cmake = CMake(self)
cmake.configure()
cmake.build()
cmake.test()
def package(self):
self.copy("*.h")
def package_id(self):
self.info.header_only()
Tip
If you are cross building your library or app you’ll probably need
to skip the unit tests because your target binary cannot be executed in current building host.
To do it you can use tools.get_env() in combination with
CONAN_RUN_TESTS env variable, defined as False
in profile for cross building and replace cmake.test()
with:
if tools.get_env("CONAN_RUN_TESTS", True):
cmake.test()
Which will use a CMakeLists.txt
file in the root folder:
project(Package CXX)
cmake_minimum_required(VERSION 2.8.12)
include_directories("include")
add_executable(example example.cpp)
enable_testing()
add_test(NAME example
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin
COMMAND example)
and some example.cpp
file, which will be our “unit test” of the library:
#include <iostream>
#include "hello.h"
int main() {
hello();
}
This will use different compilers and versions, as configured by conan settings (in command line or profiles), but will always generate just 1 output package, always with the same ID.
The necessary files for the unit tests, must be
exports_sources
too (or retrieved fromsource()
method)If the package had dependencies, via
requires
, it would be necessary to add thegenerators = "cmake"
to the package recipe and adding theconanbuildinfo.cmake
file to the testing CMakeLists.txt:
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(example example.cpp)
target_link_libraries(example ${CONAN_LIBS}) # not necessary if dependencies are also header-only
Package is created with:
$ conan create . user/channel
Note
This with/without tests is referring to running full unitary tests over the library, which is different to the test functionality that checks the integrityg of the package. The above examples are describing the approaches for unit-testing the library within the recipe. In either case, it is recommended to have a test_package folder, so the conan create command checks the package once it is created. Check the packaging getting started guide