From 13f50821ad73de4e46131bc4bb9c7c7987db9e9e Mon Sep 17 00:00:00 2001 From: memsharded Date: Wed, 1 Dec 2021 14:03:02 +0100 Subject: [PATCH 1/8] proof of concept of adding options per-requirement --- conans/client/graph/graph_builder.py | 2 + conans/model/requires.py | 13 ++- .../options/test_options_build_requires.py | 96 +++++++++++++++++++ 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 conans/test/integration/options/test_options_build_requires.py diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index bf7c9de4151..709358b1971 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -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) diff --git a/conans/model/requires.py b/conans/model/requires.py index e354b54e23f..902d51018e2 100644 --- a/conans/model/requires.py +++ b/conans/model/requires.py @@ -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 @@ -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__) @@ -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: @@ -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 diff --git a/conans/test/integration/options/test_options_build_requires.py b/conans/test/integration/options/test_options_build_requires.py new file mode 100644 index 00000000000..b0c6a53bc56 --- /dev/null +++ b/conans/test/integration/options/test_options_build_requires.py @@ -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") + + +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 From 03b2549574151505b757c1fab5c05da0362791e4 Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 9 Dec 2021 10:04:38 +0100 Subject: [PATCH 2/8] review --- .../test/integration/options/test_options_build_requires.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conans/test/integration/options/test_options_build_requires.py b/conans/test/integration/options/test_options_build_requires.py index b0c6a53bc56..63efb8a451f 100644 --- a/conans/test/integration/options/test_options_build_requires.py +++ b/conans/test/integration/options/test_options_build_requires.py @@ -27,6 +27,10 @@ def test_build_requires_options_different(): client.run("create openssl_3_0_0.py") client.run("create conanfile_cmake.py") client.run("install conanfile.py") + # This test used to crash, not crashing means ok + assert "openssl/1.1.1" in client.out + assert "openssl/3.0.0: Already installed!" in client.out + assert "cmake/0.1: Already installed!" in client.out def test_different_options_values(): From 2f4d14aeaf71b216916711c9596a2d76e6f33faf Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 9 Dec 2021 10:10:20 +0100 Subject: [PATCH 3/8] add test docstrings --- .../options/test_options_build_requires.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/conans/test/integration/options/test_options_build_requires.py b/conans/test/integration/options/test_options_build_requires.py index 63efb8a451f..9227dacdc73 100644 --- a/conans/test/integration/options/test_options_build_requires.py +++ b/conans/test/integration/options/test_options_build_requires.py @@ -6,6 +6,7 @@ def test_build_requires_options_different(): # copied from https://github.com/conan-io/conan/pull/9839 + # This is a test that crashed in 1.X, because of conflicting options client = TestClient() conanfile_openssl_1_1_1 = GenConanfile("openssl", "1.1.1") @@ -33,7 +34,13 @@ def test_build_requires_options_different(): assert "cmake/0.1: Already installed!" in client.out -def test_different_options_values(): +def test_different_options_values_profile(): + """ + consumer -> protobuf (library) + \\--(build)-> protobuf (protoc) + protobuf by default is a static library (shared=False) + The profile or CLI args can select for each one (library and protoc) the "shared" value + """ c = TestClient() protobuf = textwrap.dedent(""" from conans import ConanFile @@ -69,7 +76,14 @@ def package_info(self): assert "protobuf/1.0: MYOPTION: build-True" in c.out +# TODO: Make possible to work for direct dependencies without having to specify the protobuf: def test_different_options_values_recipe(): + """ + consumer -> protobuf (library) + \\--(build)-> protobuf (protoc) + protobuf by default is a static library (shared=False) + The "consumer" conanfile.py can use ``self.requires(...,options=)`` to define protobuf:shared + """ c = TestClient() protobuf = textwrap.dedent(""" from conans import ConanFile From e0a0598d5d44730892fa557fb14678eb41ec79dd Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 9 Dec 2021 16:45:17 +0100 Subject: [PATCH 4/8] wip --- conans/client/graph/graph_builder.py | 14 +++-- .../options/test_options_build_requires.py | 52 +++++++++++++++++-- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index 709358b1971..aa55aa2a5c1 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -249,10 +249,18 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr new_node.recipe = recipe_status new_node.remote = remote - # FIXME - down_options = node.conanfile.up_options + # The consumer "up_options" are the options that come from downstream to this node if require.options is not None: - down_options.update_options(Options(options_values=require.options)) + # If the consumer has specified requires(options=xxx), we need to use it + # It will have less priority than downstream consumers + down_options = Options(options_values=require.options) + down_options.scope(new_ref.name) + # TODO: discuss, together with build_require override, if it is "build" or "visible" + if not require.build: # Build-requirements do NOT propagate options from downstream + down_options.update_options(node.conanfile.up_options) + else: + down_options = Options() if require.build else node.conanfile.up_options + self._prepare_node(new_node, profile_host, profile_build, down_options) require.process_package_type(new_node) diff --git a/conans/test/integration/options/test_options_build_requires.py b/conans/test/integration/options/test_options_build_requires.py index 9227dacdc73..6380befc496 100644 --- a/conans/test/integration/options/test_options_build_requires.py +++ b/conans/test/integration/options/test_options_build_requires.py @@ -1,5 +1,7 @@ import textwrap +import pytest + from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.tools import TestClient @@ -76,8 +78,8 @@ def package_info(self): assert "protobuf/1.0: MYOPTION: build-True" in c.out -# TODO: Make possible to work for direct dependencies without having to specify the protobuf: -def test_different_options_values_recipe(): +@pytest.mark.parametrize("scope", ["protobuf:", ""]) +def test_different_options_values_recipe(scope): """ consumer -> protobuf (library) \\--(build)-> protobuf (protoc) @@ -98,9 +100,9 @@ def package_info(self): from conans import ConanFile class Consumer(ConanFile): def requirements(self): - self.requires("protobuf/1.0", options={{"protobuf:shared": {host}}}) + self.requires("protobuf/1.0", options={{"{scope}shared": {host}}}) def build_requirements(self): - self.build_requires("protobuf/1.0", options={{"protobuf:shared": {build}}}) + self.build_requires("protobuf/1.0", options={{"{scope}shared": {build}}}) """) c.save({"conanfile.py": protobuf}) @@ -108,7 +110,47 @@ def build_requirements(self): 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.save({"conanfile.py": consumer_recipe.format(host=host, build=build, scope=scope)}) 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 + + +def test_different_options_values_recipe_priority(): + """ + consumer ---> mypkg ---> protobuf (library) + \\--(build)-> protobuf (protoc) + protobuf by default is a static library (shared=1) + "consumer" defines a protobuf:shared=3 value, that must be respected for HOST context + But build context, it is assigned by "mypkg", and build-require is private + """ + c = TestClient() + protobuf = textwrap.dedent(""" + from conans import ConanFile + class Proto(ConanFile): + options = {"shared": [1, 2, 3]} + default_options = {"shared": 1} + + def package_id(self): + self.output.info("MYOPTION: {}-{}".format(self.context, self.options.shared)) + """) + my_pkg = textwrap.dedent(""" + from conans import ConanFile + class Consumer(ConanFile): + def requirements(self): + self.requires("protobuf/1.0", options={"shared": 2}) + def build_requirements(self): + self.build_requires("protobuf/1.0", options={"shared": 2}) + """) + c.save({"protobuf/conanfile.py": protobuf, + "mypkg/conanfile.py": my_pkg, + "consumer/conanfile.py": GenConanfile().with_requires("mypkg/1.0") + .with_default_option("protobuf:shared", 3)}) + + c.run("create protobuf protobuf/1.0@ -o protobuf:shared=2") + c.run("create protobuf protobuf/1.0@ -o protobuf:shared=3") + c.run("create mypkg mypkg/1.0@") + + c.run("install consumer") + assert f"protobuf/1.0: MYOPTION: host-3" in c.out + assert f"protobuf/1.0: MYOPTION: build-2" in c.out From b6c1c67bd32244b2dcb637f510497ed2ce9c1a96 Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 9 Dec 2021 18:52:20 +0100 Subject: [PATCH 5/8] wip --- conans/client/graph/graph_builder.py | 2 +- conans/test/assets/genconanfile.py | 2 +- .../command_v2/test_info_build_order.py | 14 ++++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index aa55aa2a5c1..871e1757eba 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -256,7 +256,7 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr down_options = Options(options_values=require.options) down_options.scope(new_ref.name) # TODO: discuss, together with build_require override, if it is "build" or "visible" - if not require.build: # Build-requirements do NOT propagate options from downstream + if require.visible: # Only visible requirements propagate options from downstream down_options.update_options(node.conanfile.up_options) else: down_options = Options() if require.build else node.conanfile.up_options diff --git a/conans/test/assets/genconanfile.py b/conans/test/assets/genconanfile.py index c014bccd426..2fb90d595aa 100644 --- a/conans/test/assets/genconanfile.py +++ b/conans/test/assets/genconanfile.py @@ -285,7 +285,7 @@ def _default_options_render(self): def _build_requirements_render(self): lines = [] for ref, kwargs in self._build_requirements: - args = ", ".join("{}={}".format(k, f'"{v}"' if not isinstance(v, bool) else v) + args = ", ".join("{}={}".format(k, f'"{v}"' if not isinstance(v, (bool, dict)) else v) for k, v in kwargs.items()) lines.append(' self.build_requires("{}", {})'.format(ref, args)) return "def build_requirements(self):\n{}\n".format("\n".join(lines)) diff --git a/conans/test/integration/command_v2/test_info_build_order.py b/conans/test/integration/command_v2/test_info_build_order.py index 6fce4d97d05..10b17bd2296 100644 --- a/conans/test/integration/command_v2/test_info_build_order.py +++ b/conans/test/integration/command_v2/test_info_build_order.py @@ -106,11 +106,13 @@ def test_info_build_order_build_require(): def test_info_build_order_options(): c = TestClient() + # The normal default_options do NOT propagate to build_requires, it is necessary to use + # self.requires(..., options=xxx) c.save({"tool/conanfile.py": GenConanfile().with_option("myopt", [1, 2, 3]), - "dep1/conanfile.py": GenConanfile().with_build_requires("tool/0.1").with_default_option( - "tool:myopt", 1), - "dep2/conanfile.py": GenConanfile().with_build_requires("tool/0.1").with_default_option( - "tool:myopt", 2), + "dep1/conanfile.py": GenConanfile().with_build_requirement("tool/0.1", + options={"myopt": 1}), + "dep2/conanfile.py": GenConanfile().with_build_requirement("tool/0.1", + options={"myopt": 2}), "consumer/conanfile.txt": "[requires]\ndep1/0.1\ndep2/0.1"}) c.run("export tool --name=tool --version=0.1") c.run("export dep1 --name=dep1 --version=0.1") @@ -151,7 +153,7 @@ def test_info_build_order_options(): ], [ { - "ref": "dep1/0.1#36716458443ac8c76bf2e905323b331c", + "ref": "dep1/0.1#7a778ad1b2527b6de9fc16575086cc24", "depends": [ "tool/0.1#b6299fc637530d547c7eaa047d1da91d" ], @@ -168,7 +170,7 @@ def test_info_build_order_options(): ] }, { - "ref": "dep2/0.1#d7154a7eee8e107438768c1542ca1b70", + "ref": "dep2/0.1#e02c590340355284e2febc6edb210c24", "depends": [ "tool/0.1#b6299fc637530d547c7eaa047d1da91d" ], From b7dfbf9c789111c3f80e8988acd242d90b823e3b Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 9 Dec 2021 18:54:24 +0100 Subject: [PATCH 6/8] apply visible=True option --- conans/client/graph/graph_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index 6145d4ce690..dfc7c10b21c 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -260,7 +260,7 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr if require.visible: # Only visible requirements propagate options from downstream down_options.update_options(node.conanfile.up_options) else: - down_options = Options() if require.build else node.conanfile.up_options + down_options = node.conanfile.up_options if require.visible else Options() self._prepare_node(new_node, profile_host, profile_build, down_options) From a35857b8117ee3d3f1fd48fc426e05dd77afb91b Mon Sep 17 00:00:00 2001 From: memsharded Date: Fri, 10 Dec 2021 14:02:54 +0100 Subject: [PATCH 7/8] adding graph tests for visible=False not overriden, not forced --- conans/client/graph/graph_builder.py | 3 +- .../graph/core/graph_manager_test.py | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index dfc7c10b21c..30e9a5b2068 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -257,7 +257,8 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr down_options = Options(options_values=require.options) down_options.scope(new_ref.name) # TODO: discuss, together with build_require override, if it is "build" or "visible" - if require.visible: # Only visible requirements propagate options from downstream + if require.visible and context == CONTEXT_HOST: + # Only visible requirements in the host context propagate options from downstream down_options.update_options(node.conanfile.up_options) else: down_options = node.conanfile.up_options if require.visible else Options() diff --git a/conans/test/integration/graph/core/graph_manager_test.py b/conans/test/integration/graph/core/graph_manager_test.py index 5fb4c76866c..2a0a9c70f26 100644 --- a/conans/test/integration/graph/core/graph_manager_test.py +++ b/conans/test/integration/graph/core/graph_manager_test.py @@ -856,6 +856,33 @@ def test_diamond_reverse_order_conflict(self): app = deps_graph.root dep1 = app.dependencies[0].dst dep2 = app.dependencies[1].dst + self._check_node(app, "app/0.1", deps=[dep1, dep2]) + self._check_node(dep1, "dep1/2.0#123", deps=[], dependents=[app]) + # dep2 no dependency, it was not resolved due to conflict + self._check_node(dep2, "dep2/1.0#123", deps=[], dependents=[app]) + + def test_invisible_not_forced(self): + # app -> libb0.1 -(visible=False)----> liba0.1 (NOT forced to lib0.2) + # \-> -----(force not used)-------> liba0.2 + self.recipe_cache("liba/0.1") + self.recipe_cache("liba/0.2") + self.recipe_conanfile("libb/0.1", GenConanfile().with_requirement("liba/0.1", + visible=False)) + consumer = self.consumer_conanfile(GenConanfile("app", "0.1").with_require("libb/0.1") + .with_requirement("liba/0.2", force=True)) + deps_graph = self.build_consumer(consumer) + + self.assertEqual(4, len(deps_graph.nodes)) + app = deps_graph.root + libb = app.dependencies[0].dst + liba2 = app.dependencies[1].dst + liba = libb.dependencies[0].dst + + # TODO: No Revision??? Because of consumer? + self._check_node(app, "app/0.1", deps=[libb, liba2]) + self._check_node(libb, "libb/0.1#123", deps=[liba], dependents=[app]) + self._check_node(liba, "liba/0.1#123", dependents=[libb]) + self._check_node(liba2, "liba/0.2#123", dependents=[app]) class PureOverrideTest(GraphManagerTest): @@ -891,6 +918,26 @@ def test_discarded_override(self): # TODO: No Revision??? Because of consumer? self._check_node(app, "app/0.1", deps=[]) + def test_invisible_not_overriden(self): + # app -> libb0.1 -(visible=False)----> liba0.1 (NOT overriden to lib0.2) + # \-> -----(override not used)------->/ + self.recipe_cache("liba/0.1") + self.recipe_conanfile("libb/0.1", GenConanfile().with_requirement("liba/0.1", + visible=False)) + consumer = self.consumer_conanfile(GenConanfile("app", "0.1").with_require("libb/0.1") + .with_requirement("liba/0.2", override=True)) + deps_graph = self.build_consumer(consumer) + + self.assertEqual(3, len(deps_graph.nodes)) + app = deps_graph.root + libb = app.dependencies[0].dst + liba = libb.dependencies[0].dst + + # TODO: No Revision??? Because of consumer? + self._check_node(app, "app/0.1", deps=[libb]) + self._check_node(libb, "libb/0.1#123", deps=[liba], dependents=[app]) + self._check_node(liba, "liba/0.1#123", dependents=[libb]) + class TestProjectApp(GraphManagerTest): """ From 5bcc32d8058671bde64786fdc0aedff79c8ef372 Mon Sep 17 00:00:00 2001 From: memsharded Date: Fri, 10 Dec 2021 18:50:20 +0100 Subject: [PATCH 8/8] review --- conans/client/graph/graph_builder.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py index 16e00120832..71ce2485cce 100644 --- a/conans/client/graph/graph_builder.py +++ b/conans/client/graph/graph_builder.py @@ -252,11 +252,14 @@ def _create_new_node(self, node, require, graph, profile_host, profile_build, gr # The consumer "up_options" are the options that come from downstream to this node if require.options is not None: - # If the consumer has specified requires(options=xxx), we need to use it + # If the consumer has specified "requires(options=xxx)", we need to use it # It will have less priority than downstream consumers down_options = Options(options_values=require.options) down_options.scope(new_ref.name) - # TODO: discuss, together with build_require override, if it is "build" or "visible" + # At the moment, the behavior is the most restrictive one: default_options and + # options["dep"].opt=value only propagate to visible and host dependencies + # we will evaluate if necessary a potential "build_options", but recall that it is + # now possible to do "self.build_requires(..., options={k:v})" to specify it if require.visible and context == CONTEXT_HOST: # Only visible requirements in the host context propagate options from downstream down_options.update_options(node.conanfile.up_options)