Skip to content

Commit

Permalink
no user/channel pattern matches (conan-io#14338)
Browse files Browse the repository at this point in the history
* no user/channel pattern matches

* do tests for --build and options patterns
  • Loading branch information
memsharded authored and mkmkme committed Jul 26, 2023
1 parent 2efaf71 commit 0561011
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 6 deletions.
7 changes: 1 addition & 6 deletions conans/client/graph/build_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,9 @@ def __init__(self, params):
else:
if param.startswith("missing:"):
clean_pattern = param[len("missing:"):]
clean_pattern = clean_pattern[:-1] if param.endswith("@") else clean_pattern
clean_pattern = clean_pattern.replace("@#", "#")
self.build_missing_patterns.append(clean_pattern)
else:
# Remove the @ at the end, to match for
# "conan install --requires=pkg/0.1@ --build=pkg/0.1@"
clean_pattern = param[:-1] if param.endswith("@") else param
clean_pattern = clean_pattern.replace("@#", "#")
clean_pattern = param
if clean_pattern and clean_pattern[0] in ["!", "~"]:
self._excluded_patterns.append(clean_pattern[1:])
else:
Expand Down
10 changes: 10 additions & 0 deletions conans/model/recipe_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,19 @@ def matches(self, pattern, is_consumer):
pattern = pattern[1:]
negate = True

no_user_channel = False
if pattern.endswith("@"): # it means we want to match only without user/channel
pattern = pattern[:-1]
no_user_channel = True
elif "@#" in pattern:
pattern = pattern.replace("@#", "#")
no_user_channel = True

condition = ((pattern == "&" and is_consumer) or
fnmatch.fnmatchcase(str(self), pattern) or
fnmatch.fnmatchcase(self.repr_notime(), pattern))
if no_user_channel:
condition = condition and not self.user and not self.channel
if negate:
return not condition
return condition
Expand Down
37 changes: 37 additions & 0 deletions conans/test/integration/command/create_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,43 @@ def test_create_build_missing():
assert "ERROR: Missing prebuilt package for 'dep/1.0'" in c.out


def test_create_no_user_channel():
""" test the --build=pattern and --build=missing:pattern syntax to build missing packages
without user/channel
"""
c = TestClient()
c.save({"dep/conanfile.py": GenConanfile(),
"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep1/0.1", "dep2/0.1@user",
"dep3/0.1@user/channel")})
c.run("export dep --name=dep1 --version=0.1")
c.run("export dep --name=dep2 --version=0.1 --user=user")
c.run("export dep --name=dep3 --version=0.1 --user=user --channel=channel")

# First test the ``--build=missing:pattern``
c.run("create pkg --build=missing:*@", assert_error=True)
c.assert_listed_binary({"dep1/0.1": (NO_SETTINGS_PACKAGE_ID, "Build"),
"dep2/0.1": (NO_SETTINGS_PACKAGE_ID, "Missing"),
"dep3/0.1": (NO_SETTINGS_PACKAGE_ID, "Missing")})
c.run("create pkg --build=missing:!*@", assert_error=True)
c.assert_listed_binary({"dep1/0.1": (NO_SETTINGS_PACKAGE_ID, "Missing"),
"dep2/0.1": (NO_SETTINGS_PACKAGE_ID, "Build"),
"dep3/0.1": (NO_SETTINGS_PACKAGE_ID, "Build")})

# Now lets make sure they exist
c.run("create pkg --build=missing")

# Now test the --build=pattern
c.run("create pkg --build=*@")
c.assert_listed_binary({"dep1/0.1": (NO_SETTINGS_PACKAGE_ID, "Build"),
"dep2/0.1": (NO_SETTINGS_PACKAGE_ID, "Cache"),
"dep3/0.1": (NO_SETTINGS_PACKAGE_ID, "Cache")})
# The --build=* needs to be said: "build all except those that have user/channel
c.run("create pkg --build=* --build=!*@")
c.assert_listed_binary({"dep1/0.1": (NO_SETTINGS_PACKAGE_ID, "Cache"),
"dep2/0.1": (NO_SETTINGS_PACKAGE_ID, "Build"),
"dep3/0.1": (NO_SETTINGS_PACKAGE_ID, "Build")})


def test_create_format_json():
"""
Tests the ``conan create . -f json`` result
Expand Down
40 changes: 40 additions & 0 deletions conans/test/integration/options/options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,46 @@ def test_transitive_options_conanfile_py_create(self, client):
self.check(client)


def test_options_no_user_channel_patterns():
c = TestClient()
conanfile = textwrap.dedent("""\
from conan import ConanFile
class Pkg(ConanFile):
options = {"myoption": [1, 2, 3]}
def configure(self):
self.output.info(f"MYOPTION: {self.options.myoption}")
""")
c.save({"dep/conanfile.py": conanfile,
"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep1/0.1", "dep2/0.1@user",
"dep3/0.1@user/channel")})
c.run("export dep --name=dep1 --version=0.1")
c.run("export dep --name=dep2 --version=0.1 --user=user")
c.run("export dep --name=dep3 --version=0.1 --user=user --channel=channel")

c.run("graph info pkg -o *:myoption=3 -o *@:myoption=1")
assert "dep1/0.1: MYOPTION: 1" in c.out
assert "dep2/0.1@user: MYOPTION: 3" in c.out
assert "dep3/0.1@user/channel: MYOPTION: 3" in c.out

# Recall that order is also important latest matching pattern wins
c.run("graph info pkg -o *@:myoption=1 -o *:myoption=1")
assert "dep1/0.1: MYOPTION: 1" in c.out
assert "dep2/0.1@user: MYOPTION: 1" in c.out
assert "dep3/0.1@user/channel: MYOPTION: 1" in c.out

# This is a bit weird negation approach, but it works = all packages that have user channel
c.run("graph info pkg -o *:myoption=3 -o ~*@:myoption=1")
assert "dep1/0.1: MYOPTION: 3" in c.out
assert "dep2/0.1@user: MYOPTION: 1" in c.out
assert "dep3/0.1@user/channel: MYOPTION: 1" in c.out

# Which is identical to '~*@' == '*@*'
c.run("graph info pkg -o *:myoption=3 -o *@*:myoption=1")
assert "dep1/0.1: MYOPTION: 3" in c.out
assert "dep2/0.1@user: MYOPTION: 1" in c.out
assert "dep3/0.1@user/channel: MYOPTION: 1" in c.out


class TestTransitiveOptionsSharedInvisible:
"""
https://github.com/conan-io/conan/issues/13854
Expand Down
20 changes: 20 additions & 0 deletions conans/test/integration/settings/per_package_settings_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,23 @@ class Pkg(ConanFile):
client.run("create . --name=consumer --version=0.1 --user=user --channel=testing %s -s compiler.libcxx=libstdc++ "
"-s pkg*:compiler.libcxx=libstdc++11" % settings)
self.assertIn("consumer/0.1@user/testing: Created package", client.out)

def test_per_package_setting_all_packages_without_user_channel(self):
client = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile
class Pkg(ConanFile):
settings = "os"
def configure(self):
self.output.info(f"I am a {self.settings.os} pkg!!!")
""")
client.save({"conanfile.py": conanfile})
client.run("create . --name=pkg1 --version=0.1 -s os=Windows")
client.run("create . --name=pkg2 --version=0.1 --user=user -s os=Linux")
client.run("create . --name=pkg3 --version=0.1 --user=user --channel=channel -s os=Linux")
client.save({"conanfile.py": GenConanfile().with_requires("pkg1/0.1", "pkg2/0.1@user",
"pkg3/0.1@user/channel")})
client.run("install . -s os=Linux -s *@:os=Windows")
assert "pkg1/0.1: I am a Windows pkg!!!" in client.out
assert "pkg2/0.1@user: I am a Linux pkg!!!" in client.out
assert "pkg3/0.1@user/channel: I am a Linux pkg!!!" in client.out

0 comments on commit 0561011

Please sign in to comment.