|
16 | 16 | A starlark implementation of the wheel platform tag parsing to get the target platform. |
17 | 17 | """ |
18 | 18 |
|
19 | | -load("//python/private:version.bzl", "version") |
20 | | -load(":parse_whl_name.bzl", "parse_whl_name") |
21 | | - |
22 | 19 | # The order of the dictionaries is to keep definitions with their aliases next to each |
23 | 20 | # other |
24 | 21 | _CPU_ALIASES = { |
@@ -47,153 +44,6 @@ _OS_PREFIXES = { |
47 | 44 | "win": "windows", |
48 | 45 | } # buildifier: disable=unsorted-dict-items |
49 | 46 |
|
50 | | -def _get_priority(*, tag, values, allow_wildcard = True): |
51 | | - for priority, wp in enumerate(values): |
52 | | - # TODO @aignas 2025-06-16: move the matcher validation out of this |
53 | | - # TODO @aignas 2025-06-21: test the 'cp*' matching |
54 | | - head, sep, tail = wp.partition("*") |
55 | | - if "*" in tail: |
56 | | - fail("only a single '*' can be present in the matcher") |
57 | | - if not allow_wildcard and sep: |
58 | | - fail("'*' is not allowed in the matcher") |
59 | | - |
60 | | - for p in tag.split("."): |
61 | | - if not sep and p == head: |
62 | | - return priority |
63 | | - elif sep and p.startswith(head) and p.endswith(tail): |
64 | | - return priority |
65 | | - |
66 | | - return None |
67 | | - |
68 | | -def select_whl(*, whls, python_version, platforms, want_abis, implementation = "cp", limit = 1, logger = None): |
69 | | - """Select a whl that is the most suitable for the given platform. |
70 | | -
|
71 | | - Args: |
72 | | - whls: {type}`list[struct]` a list of candidates which have a `filename` |
73 | | - attribute containing the `whl` filename. |
74 | | - python_version: {type}`str` the target python version. |
75 | | - platforms: {type}`list[str]` the target platform identifiers that may contain |
76 | | - a single `*` character. |
77 | | - implementation: {type}`str` TODO |
78 | | - want_abis: {type}`str` TODO |
79 | | - limit: {type}`int` number of wheels to return. Defaults to 1. |
80 | | - logger: {type}`struct` the logger instance. |
81 | | -
|
82 | | - Returns: |
83 | | - {type}`list[struct] | struct | None`, a single struct from the `whls` input |
84 | | - argument or `None` if a match is not found. If the `limit` is greater than |
85 | | - one, then we will return a list. |
86 | | - """ |
87 | | - py_version = version.parse(python_version, strict = True) |
88 | | - |
89 | | - # Get the minor version instead |
90 | | - # TODO @aignas 2025-06-27: do this more efficiently |
91 | | - py_version = version.parse("{0}.{1}".format(*py_version.release), strict = True) |
92 | | - candidates = {} |
93 | | - |
94 | | - for whl in whls: |
95 | | - parsed = parse_whl_name(whl.filename) |
96 | | - |
97 | | - suffix = "" |
98 | | - if parsed.abi_tag.startswith(implementation): |
99 | | - v = parsed.abi_tag[2:] |
100 | | - min_whl_py_version = version.parse( |
101 | | - "{}.{}".format(v[0], v[1:].strip("tmu")), |
102 | | - strict = False, |
103 | | - ) |
104 | | - if not min_whl_py_version: |
105 | | - if logger: |
106 | | - logger.warn(lambda: "Discarding the wheel ('{}') because we could not parse the version from the abi tag".format(whl.filename)) |
107 | | - continue |
108 | | - |
109 | | - if parsed.abi_tag.endswith("t"): |
110 | | - suffix = "t" |
111 | | - |
112 | | - if not version.is_eq(py_version, min_whl_py_version): |
113 | | - if logger: |
114 | | - logger.debug(lambda: "Discarding the wheel ('{}') because the min version supported based on the wheel ABI tag '{}' ({}) is not compatible with the provided target Python version '{}'".format( |
115 | | - whl.filename, |
116 | | - parsed.abi_tag, |
117 | | - min_whl_py_version.string, |
118 | | - py_version.string, |
119 | | - )) |
120 | | - continue |
121 | | - else: |
122 | | - if parsed.python_tag.startswith("py"): |
123 | | - pass |
124 | | - elif not parsed.python_tag.startswith(implementation): |
125 | | - if logger: |
126 | | - logger.debug(lambda: "Discarding the wheel because the implementation '{}' is not compatible with target implementation '{}'".format( |
127 | | - parsed.python_tag, |
128 | | - implementation, |
129 | | - )) |
130 | | - continue |
131 | | - |
132 | | - if parsed.python_tag == "py2.py3": |
133 | | - min_version = "2" |
134 | | - else: |
135 | | - min_version = parsed.python_tag[2:] |
136 | | - |
137 | | - if len(min_version) > 1: |
138 | | - min_version = "{}.{}".format(min_version[0], min_version[1:]) |
139 | | - |
140 | | - min_whl_py_version = version.parse(min_version, strict = True) |
141 | | - if not version.is_ge(py_version, min_whl_py_version): |
142 | | - if logger: |
143 | | - logger.debug(lambda: "Discarding the wheel because the min version supported based on the wheel ABI tag '{}' ({}) is not compatible with the provided target Python version '{}'".format( |
144 | | - parsed.abi_tag, |
145 | | - min_whl_py_version.string, |
146 | | - py_version.string, |
147 | | - )) |
148 | | - continue |
149 | | - |
150 | | - abi_priority = _get_priority( |
151 | | - tag = parsed.abi_tag, |
152 | | - values = want_abis, |
153 | | - allow_wildcard = False, |
154 | | - ) |
155 | | - if abi_priority == None: |
156 | | - if logger: |
157 | | - logger.debug(lambda: "The abi '{}' does not match given list: {}".format( |
158 | | - parsed.abi_tag, |
159 | | - want_abis, |
160 | | - )) |
161 | | - continue |
162 | | - platform_priority = _get_priority( |
163 | | - tag = parsed.platform_tag, |
164 | | - values = platforms, |
165 | | - ) |
166 | | - if platform_priority == None: |
167 | | - if logger: |
168 | | - logger.debug(lambda: "The platform_tag '{}' does not match given list: {}".format( |
169 | | - parsed.platform_tag, |
170 | | - platforms, |
171 | | - )) |
172 | | - continue |
173 | | - |
174 | | - key = ( |
175 | | - # Ensure that we chose the highest compatible version |
176 | | - parsed.python_tag.startswith(implementation), |
177 | | - platform_priority, |
178 | | - # prefer abi_tags in this order |
179 | | - version.key(min_whl_py_version), |
180 | | - abi_priority, |
181 | | - suffix, |
182 | | - ) |
183 | | - candidates.setdefault(key, whl) |
184 | | - |
185 | | - if not candidates: |
186 | | - return None |
187 | | - |
188 | | - sorted_candidates = [i[1] for i in sorted(candidates.items())] |
189 | | - if logger: |
190 | | - logger.debug(lambda: "Sorted candidates:\n{}".format( |
191 | | - "\n".join([c.filename for c in sorted_candidates]), |
192 | | - )) |
193 | | - results = sorted_candidates[-limit:] if sorted_candidates else None |
194 | | - |
195 | | - return results[-1] if limit == 1 else results |
196 | | - |
197 | 47 | def whl_target_platforms(platform_tag, abi_tag = ""): |
198 | 48 | """Parse the wheel abi and platform tags and return (os, cpu) tuples. |
199 | 49 |
|
|
0 commit comments