Conan: Managing package compatibility based on options

Created on 17 Oct 2018  路  15Comments  路  Source: conan-io/conan

There is a very good and elaborate explanation about how to manage versioning with respect to package version, however I cannot find how to do the same with respect to package options.

Let me give you an example:
Let's have a packages A, B and C. C depends on B and B depends on A.
Package A has a boolean option bool_option and enum option enum_option. For example, its conanfile could look like this:

class AConan(ConanFile):
    name = 'A'
    version = '1.0.0'
    options = {
        'bool_option': [True, False],
        'enum_option': ['option_one', 'option_two', 'option_three']
    }
    default_options = {
        'bool_option': True,
        'enum_option': 'option_one'
    }

Now, let's say that B needs to be rebuilt if A's bool_option is changed from default, for example from C's perspective, i.e. user installs package C with -o A:bool_option=False. How can I define within B's recipe that in that case B's package_id must be different than in case when A:bool_option=True? How can I define that B's package_id does not depend on A:bool_option?

Currently with conan v1.8.3 I am observing following behaviour:

  • if I change A:bool_option while installing C - conan calculates different package ID for B than if keeping A:bool_option default
  • if I change A:enum_option while installing C - conan calculates same package ID for B as if using default A:enum_option

This particular behaviour currently suits my setup, but in general I would like to know how can I control which options of package's dependencies influence package's ID.

Please let me know if I didn't explain my issue correctly.

look into

All 15 comments

In the method package_id you can model whatever behavior you want; nevertheless, by default, the behavior should be the same for a _boolean_ option and for a _string_ one. Let us check it, it could be a bug.

In the method package_id you can model whatever behavior you want

What syntax should I use to denote that B's package_id depends on A:bool_option? Or if depends by default (I would prefer that it doesn't and that dependency is opt-in), what syntax should I use to denote that B's package_id does not depend on A:enum_option?

@jgsogo , it's probably not a bug in conan. I am observing described behaviour because my bool_option adds additional dependency to A, i.e.

class AConan(ConanFile):
    name = 'A'
    version = '1.0.0'
    options = {
        'bool_option': [True, False],
        'enum_option': ['option_one', 'option_two', 'option_three']
    }
    default_options = {
        'bool_option': True,
        'enum_option': 'option_one'
    }

    def requirements(self):
        if self.bool_option:
            self.requires('D/1.0.0@user/testing')

Then, enabling bool_option while installing C changes the dependency list of B because A has additional dependency, thus changes its package ID.

However, in some cases B can remain the same binary, except that it is now linking against different A binary and transitively to D.

If I change the A's dynamic requirement to D to be private, then it still does not work, as both A and D are static libraries (as is B), so C needs to be aware that it needs to link with D, as well as with B and A.

How to achieve having dynamic dependencies, without the need of changing ID's of the packages in between, as demonstrated with my example?

Also, to get back to the original question, if A is like following:

class AConan(ConanFile):
    name = 'A'
    version = '1.0.0'
    options = {
        'bool_option': [True, False],
        'enum_option': ['option_one', 'option_two', 'option_three']
    }
    default_options = {
        'bool_option': True,
        'enum_option': 'option_one'
    }

    def package_info(self):
        if self.bool_option:
            self.cpp_info.defines.append('HAS_BOOL_OPTION')

Then B should be able to express whether it cares about HAS_BOOL_OPTION preprocessor definition. If none of B's sources have #ifdef HAS_BOOL_OPTION within them or do not use A's public headers that have that in them, then changing A:bool_option does not need to change B's package_id. However, B could have different behaviour if A:bool_option changes. How can B express that in its package_id method?

I cannot find similar case anywhere in the documentation. I am not sure if that is even possible with conan, or if there are some tricks to achieve that.

I will invoke @memsharded 馃儚, as he is working hard with dependencies, packages_ids and has a clearer vision of all this. package_id are a very sensible part in the conan model and I don't want to generate noise here.

Wouldn't this be possible using this ?

def package_id(self):
    self.info.requires["LibA"].full_package_mode()

@danimtb , as far as I understand, full_package_mode defines that whenever anything in package version changes, different package_id should be generated - this includes major version, minor version, bugfix version, user and channel, but not the package's options (or am I wrong?).

In general, I am looking for solution that will keep semantic versioning scheme for calculating package ID's when it comes to changing package version, but I would also like to change my package_id when a specific option of one of my dependencies change.

full_package_mode() makes package ID change if requirement package ID changes, that should include options too as you can see in the table from the link you posted: https://docs.conan.io/en/latest/creating_packages/define_abi_compatibility.html#versioning-schema

Could you give it a try with the example you used above?

@danimtb, yes you are right. Indeed, with full_package_mode whenever A's package_id changes, B's package_id will change too. But I am asking for more fine-grained control over this.

Specifically, I want the B's package_id to change only if:

  • A's major version is changed, but not its minor or bugfix versions or user/channel, as long as settings and options are unchanged

    • this means that if A's minor version changes, it's package_id will change, but in that case I don't want B's package_id to change

  • A's bool_option option is changed, but not if A's enum_option changes

    • both those changes will generate different package_id for A, but B should not change it's package_id if only A:enum_option has changed

It appears that following works

def package_id(self):
    self.info.options.bool_option = self.options["LibA"].bool_option

Even though B does not have bool_option at all. It appears that you can dynamically add variables to self.info.options object within package_id method.

I'm not sure if you can rely on this behavior. Those options won't be taken into account by Conan (they are not included in the conanfile::options attribute) and we may change something in the future and your recipe could fail... it could led you to UB.

Indeed, but this works exactly as I want at the moment (Conan v1.8.4). It would be great if there was an official way to do that (that is why I raised this issue in the first place), so such hacks would not be necessary.
Or, even better, you could simply say that this behaviour is the official way of changing package's ID based on option of dependency package and add that to the documentation.

In both cases, there is a real need for having ability to change package_id based on option of another package and conan should provide a way to satisfy it.

I'm tagging this to milestone 1.10, this way we won't forget it. Release v1.9 is almost closed and we won't have time in these days to discuss this... and it is important to come back with a solid answer.

Hi @DoDoENT,

I have prepared a repository with test files and results describing the scenario you proposed and how to achieve the desired behavior: https://github.com/danimtb/conan-manange_compatibility_options

The proper configuration to achieve your desired behavior would be this one:

In recipe B (that depends on A)

def package_id(self):
        self.info.options["top"].bool_option = self.options["top"].bool_option

I have compared the results using the above package_id() with the results using full_package_mode().

I strongly recommend you to read the information in the repository and to use the pacakge_id() above. We need to update the documentation with examples like this one.

I am closing this issue and will open a new one in the docs repo (https://github.com/conan-io/docs/issues/950) to update the information. Please keep commenting or reopen this if you feel there is still something pending to be solved. Thanks! 馃槃

OK, thanks a lot.

Was this page helpful?
0 / 5 - 0 ratings