From d1034172551d9e4bd126552c7d6e57aabf57bbe4 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 9 May 2023 16:45:32 +0200 Subject: [PATCH 1/3] fix propagation of options like *:shared=True defined in recipes --- conans/model/options.py | 2 +- .../test/integration/options/options_test.py | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/conans/model/options.py b/conans/model/options.py index d548e0aadd0..e0a6e7af757 100644 --- a/conans/model/options.py +++ b/conans/model/options.py @@ -390,7 +390,7 @@ def get_upstream_options(self, down_options, own_ref, is_consumer): # compute now the necessary to propagate all down - self + self deps upstream_options = Options() for pattern, options in down_options._deps_package_options.items(): - if ref_matches(own_ref, pattern, is_consumer=is_consumer): + if ref_matches(own_ref, pattern, is_consumer=is_consumer) and "*" not in pattern: # Remove the exact match to this package, don't further propagate up continue self._deps_package_options.setdefault(pattern, _PackageOptions()).update_options(options) diff --git a/conans/test/integration/options/options_test.py b/conans/test/integration/options/options_test.py index 2714d314b8e..db93aecb89f 100644 --- a/conans/test/integration/options/options_test.py +++ b/conans/test/integration/options/options_test.py @@ -505,3 +505,69 @@ def configure(self): assert "dep2/1.0: SHARED: True!!" in c.out assert "dep3/1.0: SHARED: True!!" in c.out assert "dep4/1.0: SHARED: True!!" in c.out + + +class TestTransitiveOptionsShared: + def test_transitive_options_shared_cli(self): + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep/0.1"), + "app/conanfile.txt": "[requires]\npkg/0.1"}) + c.run("export dep") + c.run("export pkg") + c.run("install app --build=missing -o *:shared=True") + c.run("list dep*:*") + assert "shared: True" in c.out + + def test_transitive_options_shared_profile(self): + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep/0.1"), + "app/conanfile.txt": "[requires]\npkg/0.1", + "profile": "[options]\n*:shared=True"}) + c.run("export dep") + c.run("export pkg") + c.run("install app --build=missing -pr=profile") + c.run("list dep*:*") + assert "shared: True" in c.out + + def test_transitive_options_conanfile_txt(self): + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep/0.1"), + "app/conanfile.txt": "[requires]\npkg/0.1\n[options]\n*:shared=True\n"}) + c.run("export dep") + c.run("export pkg") + c.run("install app --build=missing") + c.run("list dep*:*") + assert "shared: True" in c.out + + def test_transitive_options_conanfile_py(self): + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep/0.1"), + "app/conanfile.py": GenConanfile().with_requires("pkg/0.1") + .with_default_option("*:shared", True)}) + c.run("export dep") + c.run("export pkg") + c.run("install app --build=missing") + c.run("list dep*:*") + assert "shared: True" in c.out + + def test_transitive_options_conanfile_py_create(self): + c = TestClient() + c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep/0.1"), + "app/conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1") + .with_default_option("*:shared", + True)}) + c.run("export dep") + c.run("export pkg") + c.run("create app --build=missing") + c.run("list dep*:*") + assert "shared: True" in c.out From a17d7adbbc874583f45e4f42ec729b804608aefa Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 9 May 2023 23:53:47 +0200 Subject: [PATCH 2/3] better test --- .../test/integration/options/options_test.py | 105 ++++++++---------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/conans/test/integration/options/options_test.py b/conans/test/integration/options/options_test.py index db93aecb89f..bc18613408a 100644 --- a/conans/test/integration/options/options_test.py +++ b/conans/test/integration/options/options_test.py @@ -508,66 +508,55 @@ def configure(self): class TestTransitiveOptionsShared: - def test_transitive_options_shared_cli(self): + """ + https://github.com/conan-io/conan/issues/13854 + """ + @pytest.fixture() + def client(self): c = TestClient() - c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), + c.save({"dep2/conanfile.py": GenConanfile("dep2", "0.1").with_shared_option(False), + "dep1/conanfile.py": GenConanfile("dep1", "0.1").with_shared_option(False) + .with_requires("dep2/0.1"), "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) - .with_requires("dep/0.1"), + .with_requires("dep1/0.1"), "app/conanfile.txt": "[requires]\npkg/0.1"}) - c.run("export dep") - c.run("export pkg") - c.run("install app --build=missing -o *:shared=True") - c.run("list dep*:*") - assert "shared: True" in c.out - - def test_transitive_options_shared_profile(self): - c = TestClient() - c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), - "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) - .with_requires("dep/0.1"), - "app/conanfile.txt": "[requires]\npkg/0.1", - "profile": "[options]\n*:shared=True"}) - c.run("export dep") - c.run("export pkg") - c.run("install app --build=missing -pr=profile") - c.run("list dep*:*") - assert "shared: True" in c.out - - def test_transitive_options_conanfile_txt(self): - c = TestClient() - c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), - "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) - .with_requires("dep/0.1"), - "app/conanfile.txt": "[requires]\npkg/0.1\n[options]\n*:shared=True\n"}) - c.run("export dep") + c.run("export dep2") + c.run("export dep1") c.run("export pkg") - c.run("install app --build=missing") - c.run("list dep*:*") - assert "shared: True" in c.out - - def test_transitive_options_conanfile_py(self): - c = TestClient() - c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), - "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) - .with_requires("dep/0.1"), - "app/conanfile.py": GenConanfile().with_requires("pkg/0.1") - .with_default_option("*:shared", True)}) - c.run("export dep") - c.run("export pkg") - c.run("install app --build=missing") - c.run("list dep*:*") - assert "shared: True" in c.out + return c - def test_transitive_options_conanfile_py_create(self): - c = TestClient() - c.save({"dep/conanfile.py": GenConanfile("dep", "0.1").with_shared_option(False), - "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) - .with_requires("dep/0.1"), - "app/conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1") - .with_default_option("*:shared", - True)}) - c.run("export dep") - c.run("export pkg") - c.run("create app --build=missing") - c.run("list dep*:*") - assert "shared: True" in c.out + @staticmethod + def check(client): + client.run("list dep1/*:*") + assert "shared: True" in client.out + client.run("list dep2/*:*") + assert "shared: True" in client.out + client.run("list pkg/*:*") + assert "shared: True" in client.out + + def test_transitive_options_shared_cli(self, client): + client.run("install app --build=missing -o *:shared=True") + self.check(client) + + def test_transitive_options_shared_profile(self, client): + client.save({"profile": "[options]\n*:shared=True"}) + client.run("install app --build=missing -pr=profile") + self.check(client) + + def test_transitive_options_conanfile_txt(self, client): + client.save({"app/conanfile.txt": "[requires]\npkg/0.1\n[options]\n*:shared=True\n"}) + client.run("install app --build=missing") + self.check(client) + + def test_transitive_options_conanfile_py(self, client): + client.save({"app/conanfile.py": GenConanfile().with_requires("pkg/0.1") + .with_default_option("*:shared", True)}) + client.run("install app/conanfile.py --build=missing") + self.check(client) + + def test_transitive_options_conanfile_py_create(self, client): + conanfile = GenConanfile("app", "0.1").with_requires("pkg/0.1") \ + .with_default_option("*:shared", True) + client.save({"app/conanfile.py": conanfile}) + client.run("create app --build=missing") + self.check(client) From 298e3dfbafc6af0286b77440be3c8864651c360a Mon Sep 17 00:00:00 2001 From: memsharded Date: Wed, 10 May 2023 10:21:22 +0200 Subject: [PATCH 3/3] review --- conans/model/options.py | 8 +- .../test/integration/options/options_test.py | 80 +++++++++++++++++-- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/conans/model/options.py b/conans/model/options.py index e0a6e7af757..80c4f0c870f 100644 --- a/conans/model/options.py +++ b/conans/model/options.py @@ -390,9 +390,11 @@ def get_upstream_options(self, down_options, own_ref, is_consumer): # compute now the necessary to propagate all down - self + self deps upstream_options = Options() for pattern, options in down_options._deps_package_options.items(): - if ref_matches(own_ref, pattern, is_consumer=is_consumer) and "*" not in pattern: - # Remove the exact match to this package, don't further propagate up - continue + if ref_matches(own_ref, pattern, is_consumer=is_consumer): + # Remove the exact match-name to this package, don't further propagate up + pattern_name = pattern.split("/", 1)[0] + if "*" not in pattern_name: + continue self._deps_package_options.setdefault(pattern, _PackageOptions()).update_options(options) upstream_options._deps_package_options = self._deps_package_options diff --git a/conans/test/integration/options/options_test.py b/conans/test/integration/options/options_test.py index bc18613408a..6b99a4d7d07 100644 --- a/conans/test/integration/options/options_test.py +++ b/conans/test/integration/options/options_test.py @@ -514,12 +514,18 @@ class TestTransitiveOptionsShared: @pytest.fixture() def client(self): c = TestClient() - c.save({"dep2/conanfile.py": GenConanfile("dep2", "0.1").with_shared_option(False), + c.save({"toollib/conanfile.py": GenConanfile("toollib", "0.1").with_shared_option(False), + "tool/conanfile.py": GenConanfile("tool", "0.1").with_shared_option(False) + .with_requires("toollib/0.1"), + "dep2/conanfile.py": GenConanfile("dep2", "0.1").with_shared_option(False) + .with_tool_requires("tool/0.1"), "dep1/conanfile.py": GenConanfile("dep1", "0.1").with_shared_option(False) .with_requires("dep2/0.1"), "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) .with_requires("dep1/0.1"), "app/conanfile.txt": "[requires]\npkg/0.1"}) + c.run("export toollib") + c.run("export tool") c.run("export dep2") c.run("export dep1") c.run("export pkg") @@ -527,12 +533,14 @@ def client(self): @staticmethod def check(client): - client.run("list dep1/*:*") - assert "shared: True" in client.out - client.run("list dep2/*:*") - assert "shared: True" in client.out - client.run("list pkg/*:*") - assert "shared: True" in client.out + # tools and libs in build context do not get propagated the options + for dep in ("toollib", "tool"): + client.run(f"list {dep}/*:*") + assert "shared: False" in client.out + # But the whole host context does + for dep in ("dep1", "dep2", "pkg"): + client.run(f"list {dep}/*:*") + assert "shared: True" in client.out def test_transitive_options_shared_cli(self, client): client.run("install app --build=missing -o *:shared=True") @@ -560,3 +568,61 @@ def test_transitive_options_conanfile_py_create(self, client): client.save({"app/conanfile.py": conanfile}) client.run("create app --build=missing") self.check(client) + + +class TestTransitiveOptionsSharedInvisible: + """ + https://github.com/conan-io/conan/issues/13854 + When a requirement is visible=False + """ + @pytest.fixture() + def client(self): + c = TestClient() + c.save({"dep2/conanfile.py": GenConanfile("dep2", "0.1").with_shared_option(False), + "dep1/conanfile.py": GenConanfile("dep1", "0.1").with_shared_option(False) + .with_requirement("dep2/0.1", + visible=False), + "pkg/conanfile.py": GenConanfile("pkg", "0.1").with_shared_option(False) + .with_requires("dep1/0.1"), + "app/conanfile.txt": "[requires]\npkg/0.1"}) + c.run("export dep2") + c.run("export dep1") + c.run("export pkg") + return c + + @staticmethod + def check(client, value): + for dep in ("dep1", "pkg"): + client.run(f"list {dep}/*:*") + assert f"shared: True" in client.out + + # dep2 cannot be affected from downstream conanfile consumers options, only from profile + client.run(f"list dep2/*:*") + assert f"shared: {value}" in client.out + + def test_transitive_options_shared_cli(self, client): + client.run("install app --build=missing -o *:shared=True") + self.check(client, True) + + def test_transitive_options_shared_profile(self, client): + client.save({"profile": "[options]\n*:shared=True"}) + client.run("install app --build=missing -pr=profile") + self.check(client, True) + + def test_transitive_options_conanfile_txt(self, client): + client.save({"app/conanfile.txt": "[requires]\npkg/0.1\n[options]\n*:shared=True\n"}) + client.run("install app --build=missing") + self.check(client, False) + + def test_transitive_options_conanfile_py(self, client): + client.save({"app/conanfile.py": GenConanfile().with_requires("pkg/0.1") + .with_default_option("*:shared", True)}) + client.run("install app/conanfile.py --build=missing") + self.check(client, False) + + def test_transitive_options_conanfile_py_create(self, client): + conanfile = GenConanfile("app", "0.1").with_requires("pkg/0.1") \ + .with_default_option("*:shared", True) + client.save({"app/conanfile.py": conanfile}) + client.run("create app --build=missing") + self.check(client, False)