@@ -6,7 +6,9 @@ import os
66import sys
77
88from msys2_devtools .db import ExtTarFile
9+ from msys2_devtools .utils import vercmp
910from fastprogress .fastprogress import progress_bar
11+ from concurrent .futures import ThreadPoolExecutor
1012
1113
1214def find_dbs (target_dir : str ) -> list [str ]:
@@ -67,21 +69,92 @@ def get_buildinfo(path: str) -> dict:
6769 return parse_buildinfo (buildinfo ), files
6870
6971
72+ def parse_vercmp (comparison_expression ) -> tuple [str , str | None , str | None ]:
73+ operators = ['>=' , '<=' , '=' , '>' , '<' ]
74+
75+ package_name = comparison_expression
76+ operator = None
77+ version = None
78+
79+ for op in operators :
80+ if op in comparison_expression :
81+ parts = comparison_expression .split (op , 1 )
82+ package_name = parts [0 ].strip ()
83+ version = parts [1 ].strip ()
84+ operator = op
85+ break
86+
87+ return package_name , operator , version
88+
89+
90+ def parse_full_package_name (full_name : str ) -> tuple [str , str ]:
91+ name = full_name .rsplit ("-" , 3 )[0 ]
92+ version = full_name [len (name ) + 1 :].rsplit ("-" , 1 )[0 ]
93+ return name , version
94+
95+
96+ def package_matches (full_name : str , expression : str ) -> bool :
97+ package_name , package_version = parse_full_package_name (full_name )
98+ ex_name , ex_operator , ex_version = parse_vercmp (expression )
99+ if ex_name != package_name :
100+ return False
101+
102+ if ex_operator is None or ex_version is None :
103+ return True
104+
105+ if ex_operator == "=" :
106+ return ex_version == package_version
107+ elif ex_operator == ">" :
108+ return vercmp (package_version , ex_version ) > 0
109+ elif ex_operator == "<" :
110+ return vercmp (package_version , ex_version ) < 0
111+ elif ex_operator == ">=" :
112+ return vercmp (package_version , ex_version ) >= 0
113+ elif ex_operator == "<=" :
114+ return vercmp (package_version , ex_version ) <= 0
115+ else :
116+ raise ValueError (f"Unknown operator: { ex_operator } " )
117+
118+
70119def main (argv ):
71120 parser = argparse .ArgumentParser (
72121 description = "List things about the package contents" , allow_abbrev = False
73122 )
74123 parser .add_argument ("root" , help = "path to root dir" )
124+ parser .add_argument ("--built-with-package" ,
125+ help = "filter packages that had this package installed during build time "
126+ "(optionally with a version constraint, e.g. 'foo>=1.0')" )
127+ parser .add_argument ("--contains-file" ,
128+ help = "filter packages that contain files matching this glob pattern (e.g. '*.a')" )
129+
75130 args = parser .parse_args (argv [1 :])
76131
77- something = "mingw-w64-ucrt-x86_64-crt-git-12.0.0.r619.g850703ae4-1-any"
132+ installed_filter = args .built_with_package
133+ file_pattern = args .contains_file
78134
79135 found = set ()
80136 paths = get_package_paths (args .root )
81- for p in progress_bar (paths , leave = False ):
137+
138+ def process_path (p ):
82139 buildinfo , files = get_buildinfo (p )
83- has_archive = any (f .endswith (".a" ) for f in files )
84- if something in buildinfo ["installed" ] and has_archive :
140+ return buildinfo , files
141+
142+ with ThreadPoolExecutor (4 ) as executor :
143+ for buildinfo , files in progress_bar (executor .map (process_path , paths ), total = len (paths ), leave = False ):
144+ if installed_filter is not None :
145+ for installed_full_name in buildinfo ["installed" ]:
146+ if package_matches (installed_full_name , installed_filter ):
147+ break
148+ else :
149+ continue
150+
151+ if file_pattern is not None :
152+ for file in files :
153+ if fnmatch .fnmatch (file , file_pattern ):
154+ break
155+ else :
156+ continue
157+
85158 found .update (buildinfo ["pkgbase" ])
86159
87160 for f in sorted (found ):
0 commit comments