Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal for important! options #15571

Merged
merged 6 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions conans/model/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class _PackageOption:
def __init__(self, name, value, possible_values=None):
self._name = name
self._value = value # Value None = not defined
self.strong = False
# possible_values only possible origin is recipes
if possible_values is None:
self._possible_values = None
Expand All @@ -27,10 +28,11 @@ def __init__(self, name, value, possible_values=None):
def dumps(self, scope=None):
if self._value is None:
return None
strong = "!" if self.strong else ""
if scope:
return "%s:%s=%s" % (scope, self._name, self._value)
return "%s:%s%s=%s" % (scope, self._name, strong, self._value)
else:
return "%s=%s" % (self._name, self._value)
return "%s%s=%s" % (self._name, strong, self._value)

def copy_conaninfo_option(self):
# To generate a copy without validation, for package_id info.options value
Expand Down Expand Up @@ -182,12 +184,19 @@ def __setitem__(self, item, value):

def _set(self, item, value):
# programmatic way to define values, for Conan codebase
strong = item[-1] == "!"
item = item[:-1] if strong else item

current_value = self._data.get(item)
if self._freeze and current_value.value is not None and current_value != value:
raise ConanException(f"Incorrect attempt to modify option '{item}' "
f"from '{current_value}' to '{value}'")
self._ensure_exists(item)
self._data.setdefault(item, _PackageOption(item, None)).value = value
v = self._data.setdefault(item, _PackageOption(item, None))
new_value_strong = strong or (isinstance(value, _PackageOption) and value.strong)
if new_value_strong or not v.strong:
v.value = value
v.strong = new_value_strong

def items(self):
result = []
Expand Down
66 changes: 66 additions & 0 deletions conans/test/integration/options/options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,3 +666,69 @@ def test_transitive_options_conanfile_py_create(self, client):
client.save({"app/conanfile.py": conanfile})
client.run("create app --build=missing")
self.check(client, False)


class TestStrongOptions:
@pytest.mark.parametrize("pkg", ["liba", "libb", "app"])
def test_strong_options(self, pkg):
c = TestClient()

liba = GenConanfile("liba", "0.1").with_option("myoption", [1, 2, 3])
libb = GenConanfile("libb", "0.1").with_requires("liba/0.1")
app = GenConanfile().with_requires("libb/0.1")
if pkg == "liba":
liba.with_default_option("myoption!", 2)
elif pkg == "libb":
libb.with_default_option("*:myoption!", 2)
elif pkg == "app":
app.with_default_option("*:myoption!", 2)
package_id = textwrap.dedent("""
def package_id(self):
self.output.info(f"MYOPTION: {self.info.options.myoption}")
""")
liba = str(liba) + textwrap.indent(package_id, " ")

c.save({"liba/conanfile.py": liba,
"libb/conanfile.py": libb,
"app/conanfile.py": app})
c.run("export liba")
c.run("export libb")

c.run("graph info app -o *:myoption=3")
assert "liba/0.1: MYOPTION: 2" in c.out

# And the profile can always strong-override the option
c.run("graph info app -o *:myoption!=3")
assert "liba/0.1: MYOPTION: 3" in c.out

def test_profile_shows_strong(self):
c = TestClient()
c.run("profile show -o *:myoption!=3")
assert "*:myoption!=3" in c.out

def test_strong_options_recipe_priority(self):
c = TestClient()

liba = GenConanfile("liba", "0.1").with_option("myoption", [1, 2, 3, 4])\
.with_default_option("myoption!", 1)
libb = GenConanfile("libb", "0.1").with_requires("liba/0.1")\
.with_default_option("*:myoption!", 2)
app = GenConanfile().with_requires("libb/0.1").with_default_option("*:myoption!", 3)

package_id = textwrap.dedent("""
def package_id(self):
self.output.info(f"MYOPTION: {self.info.options.myoption}")
""")
liba = str(liba) + textwrap.indent(package_id, " ")

c.save({"liba/conanfile.py": liba,
"libb/conanfile.py": libb,
"app/conanfile.py": app})
c.run("export liba")
c.run("export libb")

c.run("graph info app")
assert "liba/0.1: MYOPTION: 3" in c.out

c.run("graph info app -o *:myoption!=4")
assert "liba/0.1: MYOPTION: 4" in c.out