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 not allowed.
- 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. However, in the context of ConanCenter they pose a few key challenges when being used generally to consume packages, most notably:
-
Non-Deterministic
package-id
: With version ranges the newest compatible package may yield a differentpackage_id
than the one built and published by ConanCenter resulting in frustrating error "no binaries found". For more context see this excellent explanation. -
Build Reproducibility: If consumers try to download and build the recipe at a later time, it may resolve to a different package version that may generate a different binary (that may or may not be compatible). In order to prevent these types of issues, we have decided to only allow exact requirements versions. This is a complicated issue, check this thread for more information.
Vendoring in library source code should be removed (best effort) to avoid potential ODR violations. If upstream takes care to rename symbols, it may be acceptable.