From 54cc742c63697526f481b7990ae40a147e0b1e9b Mon Sep 17 00:00:00 2001 From: James Date: Wed, 1 Mar 2023 12:40:48 +0100 Subject: [PATCH] fix definition of patterns for dependencies options in configure() (#13263) * fix definition of patterns for dependencies options in configure() * fix test --- conans/model/options.py | 19 +-- .../test/integration/options/options_test.py | 111 +++++++++++++++++- .../transitive_options_affect_id_test.py | 2 +- .../profile_loader/profile_loader_test.py | 4 +- conans/test/unittests/model/options_test.py | 6 +- 5 files changed, 117 insertions(+), 25 deletions(-) diff --git a/conans/model/options.py b/conans/model/options.py index 88263c13514..f8bd4c34266 100644 --- a/conans/model/options.py +++ b/conans/model/options.py @@ -1,5 +1,5 @@ from conans.errors import ConanException -from conans.model.recipe_ref import RecipeReference, ref_matches +from conans.model.recipe_ref import ref_matches _falsey_options = ["false", "none", "0", "off", ""] @@ -308,22 +308,7 @@ def __delattr__(self, field): self._package_options.__delattr__(field) def __getitem__(self, item): - # FIXME: Kept for configure => self.options["xxx"].shared = True - # To access dependencies options like ``options["mydep"]``. This will no longer be - # a read case, only for defining values. Read access will be via self.dependencies["dep"] - if isinstance(item, str): - if "/" not in item: # FIXME: To allow patterns like "*" or "foo*" - item += "/*" - item = RecipeReference.loads(item) - - return self.get(item, is_consumer=False) - - def get(self, ref, is_consumer): - ret = _PackageOptions() - for pattern, options in self._deps_package_options.items(): - if ref_matches(ref, pattern, is_consumer): - ret.update(options) - return self._deps_package_options.setdefault(ref.repr_notime(), ret) + return self._deps_package_options.setdefault(item, _PackageOptions()) def scope(self, ref): """ when there are free options like "shared=True", they apply to the "consumer" package diff --git a/conans/test/integration/options/options_test.py b/conans/test/integration/options/options_test.py index d949035c369..a6b92d9f02a 100644 --- a/conans/test/integration/options/options_test.py +++ b/conans/test/integration/options/options_test.py @@ -384,7 +384,7 @@ class Pkg(ConanFile): version = "0.1" requires = "pkg/0.1" def configure(self): - self.options["pkg"].backend = 1 + self.options["pkg*"].backend = 1 """) c.save({"conanfile.py": consumer}) c.run("install . -s os=Windows") @@ -395,3 +395,112 @@ def configure(self): # This fails in Conan 1.X c.run("create . -s os=Windows -o pkg*:backend=3") assert "pkg/0.1: Package with option:3!" in c.out + + +class TestMultipleOptionsPatterns: + """ + https://github.com/conan-io/conan/issues/13240 + """ + + dep = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + version = "1.0" + options = {"shared": [True, False]} + default_options = {"shared": False} + def package_info(self): + self.output.info(f"SHARED: {self.options.shared}!!") + """) + + def test_multiple_options_patterns_cli(self): + """ + https://github.com/conan-io/conan/issues/13240 + """ + c = TestClient() + consumer = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + settings = "os", "arch", "compiler", "build_type" + + def requirements(self): + self.requires("dep1/1.0") + self.requires("dep2/1.0") + self.requires("dep3/1.0") + self.requires("dep4/1.0") + """) + c.save({"dep/conanfile.py": self.dep, + "pkg/conanfile.py": consumer}) + for d in (1, 2, 3, 4): + c.run(f"export dep --name dep{d}") + + # match in order left to right + c.run('install pkg -o *:shared=True -o dep1*:shared=False -o dep2*:shared=False -b missing') + assert "dep1/1.0: SHARED: False!!" in c.out + assert "dep2/1.0: SHARED: False!!" in c.out + assert "dep3/1.0: SHARED: True!!" in c.out + assert "dep4/1.0: SHARED: True!!" in c.out + + # All match in order, left to right + c.run('install pkg -o dep1*:shared=False -o dep2*:shared=False -o *:shared=True -b missing') + assert "dep1/1.0: SHARED: True!!" in c.out + 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 + + def test_multiple_options_patterns(self): + c = TestClient() + configure_consumer = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + settings = "os", "arch", "compiler", "build_type" + + def requirements(self): + self.requires("dep1/1.0") + self.requires("dep2/1.0") + self.requires("dep3/1.0") + self.requires("dep4/1.0") + + def configure(self): + self.options["*"].shared = True + self.options["dep1*"].shared = False + self.options["dep2*"].shared = False + """) + c.save({"dep/conanfile.py": self.dep, + "pkg/conanfile.py": configure_consumer}) + for d in (1, 2, 3, 4): + c.run(f"export dep --name dep{d}") + + c.run("install pkg --build=missing") + assert "dep1/1.0: SHARED: False!!" in c.out + assert "dep2/1.0: SHARED: False!!" in c.out + assert "dep3/1.0: SHARED: True!!" in c.out + assert "dep4/1.0: SHARED: True!!" in c.out + + def test_multiple_options_patterns_order(self): + c = TestClient() + configure_consumer = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + settings = "os", "arch", "compiler", "build_type" + + def requirements(self): + self.requires("dep1/1.0") + self.requires("dep2/1.0") + self.requires("dep3/1.0") + self.requires("dep4/1.0") + + def configure(self): + self.options["dep1*"].shared = False + self.options["dep2*"].shared = False + self.options["*"].shared = True + """) + c.save({"dep/conanfile.py": self.dep, + "pkg/conanfile.py": configure_consumer}) + for d in (1, 2, 3, 4): + c.run(f"export dep --name dep{d}") + + c.run("install pkg --build=missing") + assert "dep1/1.0: SHARED: True!!" in c.out + 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 diff --git a/conans/test/integration/package_id/transitive_options_affect_id_test.py b/conans/test/integration/package_id/transitive_options_affect_id_test.py index 6b158df1f46..e920869ac73 100644 --- a/conans/test/integration/package_id/transitive_options_affect_id_test.py +++ b/conans/test/integration/package_id/transitive_options_affect_id_test.py @@ -35,7 +35,7 @@ def package_id(self): if self.dependencies["pkg"].package_type == "static-library": self.output.info("EMBED MODE!!") self.info.requires["pkg"].package_id = None - self.info.options["pkg"].affect = self.dependencies["pkg"].options.affect + self.info.options["pkg/*"].affect = self.dependencies["pkg"].options.affect """) client.save({"conanfile.py": app}) client.run("create . -o pkg*:noaffect=1 -o pkg*:affect=False") diff --git a/conans/test/unittests/client/profile_loader/profile_loader_test.py b/conans/test/unittests/client/profile_loader/profile_loader_test.py index 9fb6b4ba3d7..07d9c4ee318 100644 --- a/conans/test/unittests/client/profile_loader/profile_loader_test.py +++ b/conans/test/unittests/client/profile_loader/profile_loader_test.py @@ -103,8 +103,8 @@ def save_profile(txt, name): profile = profile_loader.load_profile("./profile4.txt", tmp) assert profile.settings == {"os": "1"} - assert profile.options["zlib/1.2.11"].aoption == 1 - assert profile.options["zlib/*"].otheroption == 12 + assert profile.options["zlib*"].aoption == 1 + assert profile.options["zlib*"].otheroption == 12 assert profile.tool_requires == {"*": [RecipeReference.loads("one/1.5@lasote/stable"), RecipeReference.loads("two/1.2@lasote/stable")]} diff --git a/conans/test/unittests/model/options_test.py b/conans/test/unittests/model/options_test.py index 2c8bb37de6a..10b1a115f19 100644 --- a/conans/test/unittests/model/options_test.py +++ b/conans/test/unittests/model/options_test.py @@ -129,10 +129,8 @@ def test_load(self): assert sut.path != "whatever" # Non validating assert sut.static == "True" assert sut.static != "whatever" # Non validating - assert sut["zlib/1*"].option == 8 - assert sut["zlib/1.2.11"].option == 8 - assert sut["zlib/*"].option != "whatever" # Non validating - assert sut["*/*"].common == "value" + assert sut["zlib*"].option == 8 + assert sut["zlib*"].option != "whatever" # Non validating assert sut["*"].common == "value" assert sut["*"].common != "whatever" # Non validating