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

list --profile argument #15697

Merged
merged 5 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
34 changes: 32 additions & 2 deletions conan/api/subapi/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from conans.errors import ConanException, NotFoundException
from conans.model.info import load_binary_info
from conans.model.package_ref import PkgReference
from conans.model.recipe_ref import RecipeReference
from conans.model.recipe_ref import RecipeReference, ref_matches
from conans.search.search import get_cache_packages_binary_info, filter_packages
from conans.util.dates import timelimit

Expand Down Expand Up @@ -87,7 +87,35 @@ def filter_packages_configurations(pkg_configurations, query):
"""
return filter_packages(query, pkg_configurations)

def select(self, pattern, package_query=None, remote=None, lru=None):
@staticmethod
def filter_packages_profile(packages, profile, ref):
result = {}
profile_settings = profile.processed_settings.serialize()
# Options are those for dependencies, like *:shared=True
profile_options = profile.options._deps_package_options
for pref, data in packages.items():
settings = data.get("settings", {})
settings_match = options_match = True
for k, v in settings.items(): # Only the defined settings that don't match
value = profile_settings.get(k)
if value is not None and value != v:
settings_match = False
break
options = data.get("options", {})
for k, v in options.items():
for pattern, pattern_options in profile_options.items():
if ref_matches(ref, pattern, None):
value = pattern_options.get_safe(k)
if value is not None and value != v:
options_match = False
break

if settings_match and options_match:
result[pref] = data

return result

def select(self, pattern, package_query=None, remote=None, lru=None, profile=None):
if package_query and pattern.package_id and "*" not in pattern.package_id:
raise ConanException("Cannot specify '-p' package queries, "
"if 'package_id' is not a pattern")
Expand Down Expand Up @@ -150,6 +178,8 @@ def msg_format(msg, item, total):
packages = self.packages_configurations(rrev, remote)
if package_query is not None:
packages = self.filter_packages_configurations(packages, package_query)
if profile is not None:
packages = self.filter_packages_profile(packages, profile, rrev)
prefs = packages.keys()
prefs = pattern.filter_prefs(prefs)
packages = {pref: conf for pref, conf in packages.items() if pref in prefs}
Expand Down
23 changes: 18 additions & 5 deletions conan/cli/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ def list(conan_api: ConanAPI, parser, *args):
parser.add_argument('-p', '--package-query', default=None, action=OnceArgument,
help="List only the packages matching a specific query, e.g, os=Windows AND "
"(arch=x86 OR compiler=gcc)")
parser.add_argument('-fp', '--filter-profile', action="append",
help="Profiles to filter the binaries")
parser.add_argument('-fs', '--filter-settings', action="append",
help="Settings to filter the binaries")
parser.add_argument('-fo', '--filter-options', action="append",
help="Options to filter the binaries")
parser.add_argument("-r", "--remote", default=None, action="append",
help="Remote names. Accepts wildcards ('*' means all the remotes available)")
parser.add_argument("-c", "--cache", action='store_true', help="Search in the local cache")
Expand All @@ -227,10 +233,13 @@ def list(conan_api: ConanAPI, parser, *args):

if args.pattern is None and args.graph is None:
raise ConanException("Missing pattern or graph json file")
if args.pattern and args.graph:
raise ConanException("Cannot define both the pattern and the graph json file")
if args.graph and args.lru:
raise ConanException("Cannot define lru when loading a graph json file")
if args.graph: # a few arguments are not compatible with this
if args.pattern:
raise ConanException("Cannot define both the pattern and the graph json file")
if args.lru:
raise ConanException("Cannot define lru when loading a graph json file")
if args.filter_profile or args.filter_settings or args.filter_options:
raise ConanException("Filtering binaries cannot be done when loading a graph json file")
if (args.graph_recipes or args.graph_binaries) and not args.graph:
raise ConanException("--graph-recipes and --graph-binaries require a --graph input")
if args.remote and args.lru:
Expand All @@ -245,8 +254,12 @@ def list(conan_api: ConanAPI, parser, *args):
pkglist = MultiPackagesList()
if args.cache or not args.remote:
try:
profile = conan_api.profiles.get_profile(args.filter_profile or [],
args.filter_settings,
args.filter_options) \
if args.filter_profile or args.filter_settings or args.filter_options else None
cache_list = conan_api.list.select(ref_pattern, args.package_query, remote=None,
lru=args.lru)
lru=args.lru, profile=profile)
except Exception as e:
pkglist.add_error("Local Cache", str(e))
else:
Expand Down
61 changes: 61 additions & 0 deletions conans/test/integration/command_v2/list_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,3 +793,64 @@ def test_list_compact_no_settings_no_options(self):
""")

assert expected == expected_output


class TestListBinaryFilter:
def test_list_filter(self):
c = TestClient()
c.save({"pkg/conanfile.py": GenConanfile("pkg", "1.0").with_settings("os", "arch")
.with_shared_option(False),
"header/conanfile.py": GenConanfile("header", "1.0"),
"profile_linux": "[settings]\nos=Linux",
"profile_armv8": "[settings]\narch=armv8",
"profile_shared": "[options]\n*:shared=True"})
c.run("create pkg -s os=Windows -s arch=x86")
c.run("create pkg -s os=Linux -s arch=armv8")
c.run("create pkg -s os=Macos -s arch=armv8 -o shared=True")
c.run("create header")

c.run("list *:* -fp=profile_linux --format=json")
result = json.loads(c.stdout)
header = result["Local Cache"]["header/1.0"]["revisions"]["747cc49983b14bdd00df50a0671bd8b3"]
assert header["packages"] == {"da39a3ee5e6b4b0d3255bfef95601890afd80709": {"info": {}}}
pkg = result["Local Cache"]["pkg/1.0"]["revisions"]["03591c8b22497dd74214e08b3bf2a56f"]
assert len(pkg["packages"]) == 1
settings = pkg["packages"]["2d46abc802bbffdf2af11591e3e452bc6149ea2b"]["info"]["settings"]
assert settings == {"arch": "armv8", "os": "Linux"}

# for linux + x86 only the header-only is a match
c.run("list *:* -fp=profile_linux -fs=arch=x86 --format=json")
result = json.loads(c.stdout)
header = result["Local Cache"]["header/1.0"]["revisions"]["747cc49983b14bdd00df50a0671bd8b3"]
assert header["packages"] == {"da39a3ee5e6b4b0d3255bfef95601890afd80709": {"info": {}}}
pkg = result["Local Cache"]["pkg/1.0"]["revisions"]["03591c8b22497dd74214e08b3bf2a56f"]
assert pkg["packages"] == {}

c.run("list *:* -fp=profile_armv8 --format=json")
result = json.loads(c.stdout)
header = result["Local Cache"]["header/1.0"]["revisions"]["747cc49983b14bdd00df50a0671bd8b3"]
assert header["packages"] == {"da39a3ee5e6b4b0d3255bfef95601890afd80709": {"info": {}}}
pkg = result["Local Cache"]["pkg/1.0"]["revisions"]["03591c8b22497dd74214e08b3bf2a56f"]
assert len(pkg["packages"]) == 2
settings = pkg["packages"]["2d46abc802bbffdf2af11591e3e452bc6149ea2b"]["info"]["settings"]
assert settings == {"arch": "armv8", "os": "Linux"}
settings = pkg["packages"]["2a67a51fbf36a4ee345b2125dd2642be60ffd3ec"]["info"]["settings"]
assert settings == {"arch": "armv8", "os": "Macos"}

c.run("list *:* -fp=profile_shared --format=json")
result = json.loads(c.stdout)
header = result["Local Cache"]["header/1.0"]["revisions"]["747cc49983b14bdd00df50a0671bd8b3"]
assert header["packages"] == {"da39a3ee5e6b4b0d3255bfef95601890afd80709": {"info": {}}}
pkg = result["Local Cache"]["pkg/1.0"]["revisions"]["03591c8b22497dd74214e08b3bf2a56f"]
assert len(pkg["packages"]) == 1
settings = pkg["packages"]["2a67a51fbf36a4ee345b2125dd2642be60ffd3ec"]["info"]["settings"]
assert settings == {"arch": "armv8", "os": "Macos"}

c.run("list *:* -fs os=Windows -fo *:shared=False --format=json")
result = json.loads(c.stdout)
header = result["Local Cache"]["header/1.0"]["revisions"]["747cc49983b14bdd00df50a0671bd8b3"]
assert header["packages"] == {"da39a3ee5e6b4b0d3255bfef95601890afd80709": {"info": {}}}
pkg = result["Local Cache"]["pkg/1.0"]["revisions"]["03591c8b22497dd74214e08b3bf2a56f"]
assert len(pkg["packages"]) == 1
settings = pkg["packages"]["d2e97769569ac0a583d72c10a37d5ca26de7c9fa"]["info"]["settings"]
assert settings == {"arch": "x86", "os": "Windows"}