Skip to content

Latest commit

 

History

History
201 lines (145 loc) · 10.7 KB

dependencies.md

File metadata and controls

201 lines (145 loc) · 10.7 KB

Dependencies

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.

Contents

List 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.

Optional Requirements

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.

Build Requirements

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")

Accessing Dependencies

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.

Handling Requirement's Options

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.")

Verifying Dependency's Version

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.")

Passing Requirement's info to build()

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 and conan build commands can be separated when developing packages so for this reason the class 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}")

Overriding the provided properties from the consumer

Note: This was adding in Conan 1.55 to the generators... we need to write docs for when that's available

Adherence to Build Service

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() and build_requires/build_requirements().
  • python_requires are not allowed.

Version Ranges

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:

  • OpenSSL: [>=1.1 <4] for libraries known to be compatible with OpenSSL 1.x and 3.x
  • CMake: [>3.XX <4], where 3.XX is the minimum version of CMake required by the relevant build scripts
  • Libcurl: [>=X.YY <9], where X.YY is the minimum version of Libcurl required, starting from 7.78

Note: You might also see Zlib ranges in some PR by CCI maintainers. We're adding them little by little to avoid missing binaries and conflict errors. Please do not open PRs moving Zlib to ranges for now, we'll update this page when PRs are free to add new ranges.

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 with self.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.

Handling "internal" dependencies

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.