How settings and options of a recipe influence its package ID

In Conan, a package ID is a unique identifier for a package binary that takes into account all the factors that affect its binary compatibility. These factors include recipe options and settings as well as requirements or tool requirements.

Let’s see how settings and options affect the package ID and some examples where they should not.

How settings influence the package ID

Settings are development project-wide variables, like the compiler, its version, or the OS itself. These variable values have to be defined, they should match the values of our development environment, and they cannot have a default value like options do.

For example, let’s define a recipe that generates packages that are only OS dependent:

  1. from conan import ConanFile
  2. class Pkg(ConanFile):
  3. name = "pkg"
  4. version = "1.0.0"
  5. settings = "os" # Only OS setting affects the package ID

If we generate a package from this recipe for Linux we will get the following package ID:

  1. $ conan create . --settings os=Linux
  2. ...
  3. pkg/1.0.0: Package '9a4eb3c8701508aa9458b1a73d0633783ecc2270' created
  4. $ conan list pkg/1.0.0:*
  5. Local Cache
  6. pkg
  7. pkg/1.0.0
  8. revisions
  9. 476929a74c859bb5f646363a4900f7cf (2024-03-07 09:13:43 UTC)
  10. packages
  11. 9a4eb3c8701508aa9458b1a73d0633783ecc2270
  12. info
  13. settings
  14. os: Linux

If we do the same thing with Windows, now the package ID will be diffent:

  1. $ conan create . --settings os=Windows
  2. ...
  3. pkg/1.0.0: Package 'ebec3dc6d7f6b907b3ada0c3d3cdc83613a2b715' created
  4. $ conan list pkg/1.0.0:*
  5. Local Cache
  6. pkg
  7. pkg/1.0.0
  8. revisions
  9. 476929a74c859bb5f646363a4900f7cf (2024-03-07 09:13:43 UTC)
  10. packages
  11. 9a4eb3c8701508aa9458b1a73d0633783ecc2270
  12. info
  13. settings
  14. os: Linux
  15. ebec3dc6d7f6b907b3ada0c3d3cdc83613a2b715
  16. info
  17. settings
  18. os: Windows

Whenever a value of the settings or subsettings changes, the package ID will be different to reflect that.

The most common usage for settings is to model the different project-wide aspects that might influence the package ID. A recipe that does that will be:

  1. from conan import ConanFile
  2. class Pkg(ConanFile):
  3. name = "pkg"
  4. version = "1.0.0"
  5. settings = "os", "arch", "compiler", "build_type"

Now, compiling a package with different compiler versions will result into different package IDs:

  1. $ conan create . --settings compiler.version=192
  2. ...
  3. pkg/1.0.0: Package '4f267380690f99b3ef385199826c268f63147457' created
  4. $ conan create . --settings compiler.version=193
  5. ...
  6. pkg/1.0.0: Package 'c13a22a41ecd72caf9e556f68b406569547e0861' created
  7. $ conan list pkg/1.0.0:*
  8. Local Cache
  9. pkg
  10. pkg/1.0.0
  11. revisions
  12. f1f48830ecb04f3b328429b390fc5de8 (2024-03-07 09:21:07 UTC)
  13. packages
  14. 4f267380690f99b3ef385199826c268f63147457
  15. info
  16. settings
  17. arch: x86_64
  18. build_type: Release
  19. compiler: msvc
  20. compiler.cppstd: 14
  21. compiler.runtime: dynamic
  22. compiler.runtime_type: Release
  23. compiler.version: 192
  24. os: Windows
  25. c13a22a41ecd72caf9e556f68b406569547e0861
  26. info
  27. settings
  28. arch: x86_64
  29. build_type: Release
  30. compiler: msvc
  31. compiler.cppstd: 14
  32. compiler.runtime: dynamic
  33. compiler.runtime_type: Release
  34. compiler.version: 193
  35. os: Windows

Removing settings for a package used as a tool_require

There could be cases when a setting should not influence the resulting package ID. An example of this could be when a recipe packages a tool that would be used to build other packages via tool_requires

In that case, the value of the compiler used is needed for the compilation of the tool but not that relevant for consumers, as we only want to execute the tool to build other projects. So we could eventually remove the influence of the compiler from the pacakge ID:

  1. from conan import ConanFile
  2. class CMake(ConanFile):
  3. name = "cmake"
  4. version = "1.0.0"
  5. settings = "os", "arch", "compiler", "build_type" # Only OS and architecture influence the resulting package
  6. def build(self):
  7. # self.settings.compiler value will be used here to compile cmake
  8. def package_id(self):
  9. # Remove compiler setting from package ID
  10. del self.info.settings.compiler

Why not removing the setting from the settings attribute? Because the compiler value is still needed in the build() method to perform the compilation of the executable.

Note

In the case we are generating our own executables (our own apps, not a tool_require), removing the compiler setting from package ID is not recommended, as we would always want to know that the package was generated with a specific compiler.

However, in case we are packaging a tool that does not even require a compiler input for building (a python script for example), we could also directly remove the settings attribute:

  1. from conan import ConanFile
  2. class MyPythonScripts(ConanFile):
  3. name = "my-python-scripts"
  4. version = "1.0.0"
  5. # No settings this time

Or, if the tool is platform specific we can just keep the OS and architecture information:

  1. from conan import ConanFile
  2. class MyScripts(ConanFile):
  3. name = "my-scripts"
  4. version = "1.0.0"
  5. settings = "os", "arch"

How options influence the package ID

Options are used to specify characteristics that are particular to a single recipe, contrasting with settings that generally remain consistent across recipes within a project. They are usually a set of particular characteristics of a library executable or conan package may have.

For example, a shared option is a very common option used in recipes that can produce shared libraries. However, it could not be a setting as not all recipes produce shared libraries.

  1. from conan import ConanFile
  2. class Pkg(ConanFile):
  3. name = "pkg"
  4. version = "1.0.0"
  5. options = {"shared": [True, False]}
  6. default_options = {"shared": True}

As in the previous case with settings, the different values of an option will influence the package ID and therefore, generate different packages depending on it.

  1. $ conan create . --options shared=True
  2. ...
  3. pkg/1.0.0: Package '1744785cb24e3bdca70e27041dc5abd20476f947' created
  4. $ conan create . --options shared=False
  5. ...
  6. pkg/1.0.0: Package '55c609fe8808aa5308134cb5989d23d3caffccf2' created

In the same way, there might be “options” that are needed as input in a recipe to generate a package which shouldn’t be taken into account in the package ID. An example of this could be an option to control something that during the build phase but that does not influence the package result, like the verbosity of a compilation. In that case, the recipe should remove the option in the package_id() method:

However, the general advice is that options should always affect the package ID, and in case we would like to have an input to the recipe that should not affect it, it should be done via the conf section of your profile. Then in the recipe we should just add:

  1. from conan import ConanFile
  2. class MyPkg(ConanFile):
  3. name = "my-pkg"
  4. version = "1.0.0"
  5. def build(self):
  6. verbosity = self.conf.get("user.my-pkg:verbosity")
  7. self.output.info(f"Using verbosity level: {verbosity})
  8. ...

myprofile

  1. [conf]
  2. user.my-pkg:verbosity=silent

That way the package ID will be not affected, the recipe will be cleaner (without irrelevant options for package ID) and the input is easily managed via the profile’s conf section.

See also