Conan: Default option vs test package requirements()

Created on 1 Feb 2019  路  6Comments  路  Source: conan-io/conan

Consider the following test package:
conanfile.py

from conans import ConanFile

class TestConan(ConanFile):
    name = "Test"
    version = "0.0.1"
    options = {"opt": [True, False]}
    default_options = "opt=True"

    def requirements(self):
        print("conanfile.py opt =", self.options.opt) 

test_package/conanfile.py

from conans import ConanFile

class TestTestConan(ConanFile):
    version = "0.1"

    def requirements(self):
        print("test_package/conanfile.py opt =", self.options["Test"].opt) 

    def test(self):
        pass

Now try to create it:
conan create . user/channel -o Test:opt=True
...
test_package/conanfile.py opt = True
conanfile.py opt = True
OK

conan create . user/channel -o Test:opt=False
...
test_package/conanfile.py opt = False
conanfile.py opt = False
OK

conan create . user/channel
...
test_package/conanfile.py opt = None
conanfile.py opt = True

Why None?

question

All 6 comments

This is by-design, it is the way dependencies are resolved. When you are calling the requirements() method, you are constructing the graph. You are requesting to depend on a given package. That request might be overriden by other, downstream consumer specifying an updated version.

The options of your dependencies are not defined yet, the "Test" package has not even been retrieved at all, so it becomes impossible to know its default options. If you are specifying them explicitly, then you have it, but otherwise it is impossible.

Later in the process, for example, in the build() method of test_package/conanfile.py, you will get the right value.

Using the dependencies options in the requirements() is discouraged, it should only check its own options. We might consider raising an error in the future (Conan 2.0)

Using the dependencies options in the requirements() is discouraged, it should only check its own options. We might consider raising an error in the future (Conan 2.0)

Thanks for the clarification. I think it deserves at least a warning. Leaving opt == None is quite misleading because opt is expected to have only True/False values.

BTW, I'm trying to do the following:
Main package:

    options = {"modular_boost": [True, False]}
    default_options = "modular_boost=True"

    def requirements(self):
        if self.options.modular_boost:
            self.requires("boost_thread/1.66.0@bincrafters/stable")
            self.requires("boost_date_time/1.66.0@bincrafters/stable")
        else:
            self.requires("boost/1.66.0@conan/stable")

test_package:

    def requirements(self):
        if self.options["main_package_name"].modular_boost:
            self.requires("boost_test/1.66.0@bincrafters/stable")

If I cannot evaluate the main package options in requirements(), how to select the necessary boost package for testing? Always require modular and non-modular one?

self.options["main_package_name"] requires graph info, but it will be created just before to run build(), so you are not able to access dependencies info in requirements.

you could install boost_test in main package:

options = {"modular_boost": [True, False]}
    default_options = "modular_boost=True"

    def requirements(self):
        if self.options.modular_boost:
            self.requires("boost_thread/1.66.0@bincrafters/stable")
            self.requires("boost_date_time/1.66.0@bincrafters/stable")
            if self.develop:
                self.requires("boost_test/1.66.0@bincrafters/stable")
        else:
            self.requires("boost/1.66.0@conan/stable")

develop will be True only when building the package, in other words, when you are running the test_package as well.

@memsharded @uilianries thanks a lot for your help. Closing the issue.

@uilianries just for the record,

            if self.develop:
                self.requires("boost_test/1.66.0@bincrafters/stable")

is actually not an acceptable solution. The main package gets different package ids when self.develop is True/False (due to different requirements lists) and conan does not find a binary when I'm going to use it.

@memsharded

This is by-design, it is the way dependencies are resolved. When you are calling the requirements() method, you are constructing the graph. You are requesting to depend on a given package. That request might be overriden by other, downstream consumer specifying an updated version.

I cannot read a dependent package option in configure() method as well: self.options[package_name].option_name always returns None. Is this also by-design?

Was this page helpful?
0 / 5 - 0 ratings