Skip to content
2 changes: 2 additions & 0 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr

# FIXME
down_options = node.conanfile.up_options
if require.options is not None:
down_options.update_options(Options(options_values=require.options))
self._prepare_node(new_node, profile_host, profile_build, down_options)

require.process_package_type(new_node)
Expand Down
13 changes: 8 additions & 5 deletions conans/model/requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Requirement:
"""
def __init__(self, ref, *, headers=True, libs=True, build=False, run=None, visible=True,
transitive_headers=None, transitive_libs=None, test=False, package_id_mode=None,
force=False, override=False, direct=True):
force=False, override=False, direct=True, options=None):
# * prevents the usage of more positional parameters, always ref + **kwargs
# By default this is a generic library requirement
self.ref = ref
Expand All @@ -27,6 +27,7 @@ def __init__(self, ref, *, headers=True, libs=True, build=False, run=None, visib
self.force = force
self.override = override
self.direct = direct
self.options = options

def __repr__(self):
return repr(self.__dict__)
Expand Down Expand Up @@ -184,9 +185,10 @@ class BuildRequirements:
def __init__(self, requires):
self._requires = requires

def __call__(self, ref, package_id_mode=None, visible=False):
def __call__(self, ref, package_id_mode=None, visible=False, options=None):
# TODO: Check which arguments could be user-defined
self._requires.build_require(ref, package_id_mode=package_id_mode, visible=visible)
self._requires.build_require(ref, package_id_mode=package_id_mode, visible=visible,
options=options)


class TestRequirements:
Expand Down Expand Up @@ -233,11 +235,12 @@ def __call__(self, str_ref, **kwargs):
raise ConanException("Duplicated requirement: {}".format(ref))
self._requires[req] = req

def build_require(self, ref, raise_if_duplicated=True, package_id_mode=None, visible=False):
def build_require(self, ref, raise_if_duplicated=True, package_id_mode=None, visible=False,
options=None):
# FIXME: This raise_if_duplicated is ugly, possibly remove
ref = RecipeReference.loads(ref)
req = Requirement(ref, headers=False, libs=False, build=True, run=True, visible=visible,
package_id_mode=package_id_mode)
package_id_mode=package_id_mode, options=options)
if raise_if_duplicated and self._requires.get(req):
raise ConanException("Duplicated requirement: {}".format(ref))
self._requires[req] = req
Expand Down
96 changes: 96 additions & 0 deletions conans/test/integration/options/test_options_build_requires.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import textwrap

from conans.test.assets.genconanfile import GenConanfile
from conans.test.utils.tools import TestClient


def test_build_requires_options_different():
# copied from https://github.com/conan-io/conan/pull/9839
client = TestClient()

conanfile_openssl_1_1_1 = GenConanfile("openssl", "1.1.1")
conanfile_openssl_3_0_0 = GenConanfile("openssl", "3.0.0") \
.with_option("no_fips", [True, False]) \
.with_default_option("no_fips", True)
conanfile_cmake = GenConanfile("cmake", "0.1") \
.with_requires("openssl/1.1.1")
conanfile_consumer = GenConanfile("consumer", "0.1") \
.with_build_requires("cmake/0.1") \
.with_requires("openssl/3.0.0")

client.save({"openssl_1_1_1.py": conanfile_openssl_1_1_1,
"openssl_3_0_0.py": conanfile_openssl_3_0_0,
"conanfile_cmake.py": conanfile_cmake,
"conanfile.py": conanfile_consumer})

client.run("create openssl_1_1_1.py")
client.run("create openssl_3_0_0.py")
client.run("create conanfile_cmake.py")
client.run("install conanfile.py")
Comment thread
memsharded marked this conversation as resolved.


def test_different_options_values():
c = TestClient()
protobuf = textwrap.dedent("""
from conans import ConanFile
class Proto(ConanFile):
options = {"shared": [True, False]}
default_options = {"shared": False}

def package_info(self):
self.output.info("MYOPTION: {}-{}".format(self.context, self.options.shared))
""")

c.save({"protobuf/conanfile.py": protobuf,
"consumer/conanfile.py": GenConanfile().with_requires("protobuf/1.0")
.with_build_requires("protobuf/1.0")})

c.run("create protobuf protobuf/1.0@")
c.run("create protobuf protobuf/1.0@ -o protobuf:shared=True")
c.run("install consumer")
assert "protobuf/1.0: MYOPTION: host-False" in c.out
assert "protobuf/1.0: MYOPTION: build-False" in c.out
# specifying it in the profile works
c.run("install consumer -o protobuf:shared=True")
assert "protobuf/1.0: MYOPTION: host-True" in c.out
assert "protobuf/1.0: MYOPTION: build-False" in c.out
c.run("install consumer -o protobuf:shared=True -o:b protobuf:shared=False")
assert "protobuf/1.0: MYOPTION: host-True" in c.out
assert "protobuf/1.0: MYOPTION: build-False" in c.out
c.run("install consumer -o protobuf:shared=False -o:b protobuf:shared=True")
assert "protobuf/1.0: MYOPTION: host-False" in c.out
assert "protobuf/1.0: MYOPTION: build-True" in c.out
c.run("install consumer -o:b protobuf:shared=True")
assert "protobuf/1.0: MYOPTION: host-False" in c.out
assert "protobuf/1.0: MYOPTION: build-True" in c.out


def test_different_options_values_recipe():
c = TestClient()
protobuf = textwrap.dedent("""
from conans import ConanFile
class Proto(ConanFile):
options = {"shared": [True, False]}
default_options = {"shared": False}

def package_info(self):
self.output.info("MYOPTION: {}-{}".format(self.context, self.options.shared))
""")
consumer_recipe = textwrap.dedent("""
from conans import ConanFile
class Consumer(ConanFile):
def requirements(self):
self.requires("protobuf/1.0", options={{"protobuf:shared": {host}}})
def build_requirements(self):
self.build_requires("protobuf/1.0", options={{"protobuf:shared": {build}}})
""")
c.save({"conanfile.py": protobuf})

c.run("create . protobuf/1.0@")
c.run("create . protobuf/1.0@ -o protobuf:shared=True")

for host, build in ((True, True), (True, False), (False, True), (False, False)):
c.save({"conanfile.py": consumer_recipe.format(host=host, build=build)})
c.run("install .")
assert f"protobuf/1.0: MYOPTION: host-{host}" in c.out
assert f"protobuf/1.0: MYOPTION: build-{build}" in c.out