This section outlines all the practices and guidelines for the requirements()
and build_requirements()
methods. This includes everything
from handling "vendored" dependencies to what versions should be used.
- List Dependencies
- Accessing Dependencies
- Adherence to Build Service
- Handling "internal" dependencies
Since all ConanCenterIndex recipes are to build and/or package projects they are exclusively done in conanfile.py
. This offers a few
ways to add requirements. The most common way is requirements:
def requirements(self):
self.requires("fmt/9.1.0")
Note: With Conan 2.0, you'll also need to pay attention to new properties like the
transitive_header
attributed which is needed when a project include a dependencies header files in its public headers.
When a project supports a range of version of a dependency, it's generally advised to pick the most recent available in ConanCenter. This helps ensure there are fewer conflicts with other, up to-date, recipes that share the same requirement.
Many projects support enabling certain features by adding dependencies. In ConanCenterIndex this is done by adding an option, see naming recommendation, which should be set to match the upstream project's by default.
class ExampleConan(ConanFile):
options = {
"with_zlib": [True, False], # Possible values
}
default_options = {
"with_zlib": True, # Should match upstream's CMakeLists.txt `option(...)`
}
def requirements(self):
if self.options.with_zlib:
self.requires("zlib/1.2.13")
If a dependency was added (or removed) with a release, then the if
condition could check self.version
. Another common case is
self.settings.os
dependant requirements which need to be added for certain plaforms.
In ConanCenter we only assume CMake is available. If a project requires any other specific tool, those can be added as well. We like to do this with build_requirements:
def build_requirements(self):
self.tool_requires("ninja/1.1.0")
It's fairly common to need to pass information from a dependency to the project. This is the job of the generate()
method. This
is generally covered by the built-in generators like CMakeDeps
However the self.dependencies
are available.
Alternatively, a project may depend on a specific versions or configuration of a dependency. This use case is again covered by the
self.dependencies
within the
validate()
method. Additionally it's possible to suggest the option's values while the graph is built through configure()
this is not guaranteed and not a common practice.
Forcing options of dependencies inside a ConanCenter should be avoided, except if it is mandatory for the library to build. Our general belief is the users input should be the most important; it's unexpected for command line arguments to be over ruled by specifc recipes.
You need to use the validate()
method in order to ensure they check after the Conan graph is completely built.
Certain projects are dependent on the configuration (also known as options) of a dependency. This can be enforced in a recipe by
accessing the options
field of
the dependency.
def configure(self):
self.options["foobar"].enable_feature = True # This will still allow users to override this option
def validate(self):
if not self.dependencies["foobar"].options.enable_feature:
raise ConanInvalidConfiguration(f"{self.ref} requires foobar/*:enable_feature=True.")
Some project requirements need to respect a version constraint, this can be done as follows:
def validate(self):
if Version(self.dependencies["foobar"].ref.version) < "1.2":
raise ConanInvalidConfiguration(f"{self.ref} requires [foobar>=1.2] to build and work.")
The self.dependencies
are limited to generate()
and validate()
. This means configuring a projects build scripts
is a touch more complicated when working with unsupported build scripts.
In general, with CMake project, this can be very simple with the CMakeToolchain
, such as:
def generate(self):
tc = CMakeToolchain(self)
# deps_cpp_info, deps_env_info and deps_user_info are no longer used
if self.dependencies["dependency"].options.foobar:
tc.variables["DEPENDENCY_LIBPATH"] = self.dependencies["dependency"].cpp_info.libdirs
This pattern can be recreated for less common build system by, generating a script to call configure or capture the required values in a YAML files for example.
Note: This needs to be saved to disk because the
conan install
andconan build
commands can be separated when developing packages so for this reason theclass
may not persists the information. This is a very common workflow, even used in ConanCenter in other areas such as testing.
from conan import ConanFile
from conan.tools.files import save, load
class ExampleConan(ConanFile):
_optional_build_args = []
@property
def _optional_build_args_filename(self):
return os.path.join(self.recipe_folder, self.folders.generators, "build_args.yml")
def generate(self):
# This is required as `self.dependencies` is not available in `build()` or `test()`
if self.dependencies["foobar"].options.with_compression:
self._optional_build_args.append("--enable-foobar-compression")
save(self, self._optional_build_args_filename, file)
def build(self):
opts_args = load(self, self._optional_build_args_filename)
# Some magic setup
self.run(f"./configure.sh {opts_args}")
Note: This was adding in Conan 1.55 to the generators... we need to write docs for when that's available
It's very rare we layout "rules", most often it's guidelines, however in order to ensure graph and the package generated are usable for consumer, we do impose some limits on Conan features to provide a smoother first taste to using Conan.
Note: These are very specific to the ConanCenter being the default remote and may not be relevant to your specifc use case.
- Version ranges are generally not allowed (see below for exemption).
- Specify explicit RREV (recipe revision) of dependencies is not allowed.
- Only ConanCenter recipes are allowed in
requires
/requirements()
andbuild_requires
/build_requirements()
. python_requires
are not allowed.
Version ranges are a useful Conan feature, documentation here. With the introduction of Conan 2.0, we are currently working to allow the use of version ranges and are allowing this for a handful of dependencies. Currently, these are (except if the recipe needs a newer lower/upper version bound for specific reasons):
- OpenSSL:
[>=1.1 <4]
for libraries known to be compatible with OpenSSL 1.x and 3.x - CMake:
[>3.XX <4]
, where3.XX
is the minimum version of CMake required by the relevant build scripts. Note that CCI recipes assume 3.15 is installed in the system, so add this version range only when a requirement for a newer version is needed. - Libcurl:
[>=7.78 <9]
- Zlib:
[>=1.2.11 <2]
- Libpng:
[>=1.6 <2]
- Expat:
[>=2.6.2 <3]
- Libxml2:
[>=2.12.5 <3]
- Libuv:
[>=1 <2]
- qt5:
[~5.15]
, if your library depends on qt5, only the 5.15 minor version is allowed - qt6:
[>=6.x <7]
, where 6.x is the lower bound of your needed qt6 version - c-ares:
[>=1.27 <2]
Warning: With Conan 1.x, version ranges adhere to a much more strict sematic version spec, OpenSSL 1.1.x does not follow this so the client will not resolve to that range and will pick a 3.x version. In order to select a lower version you can user the defunct
--require-override openssl/1.1.1t@
from the command line, or override from the recipe withself.requires(openssl/1.1.1t, override=True)
to ensure a lower version is picked.
Conan maintainers may introduce this for other dependencies over time. Outside of the cases outlined above, version ranges are not allowed in ConanCenter recipes.
You might also see version ranges in some PR by CCI maintainers.
These are being done on a case-by-case basis, and are being rolled out in phases to ensure that they do not cause problems to users. Please do not open PRs that exclusively add version ranges to dependencies, unless they are solving current conflicts, in which case we welcome them and they will be prioritized.
Vendoring in library source code should be removed (in a best effort basis) to avoid potential ODR violations. If upstream takes care to rename symbols, it may be acceptable.