Skip to content

Commit cd2e2cf

Browse files
authored
Ensure multi-tagged wheels are correctly identified as universal (#2692)
1 parent b2800bb commit cd2e2cf

6 files changed

Lines changed: 69 additions & 13 deletions

File tree

changes/2690.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
macOS wheels that are tagged with *both* `universal2` and individual architecture tags are now correctly processed as part of the architecture merging process.

src/briefcase/platforms/macOS/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,6 @@ def _install_app_requirements(
263263
""",
264264
)
265265

266-
# Find all the packages with binary components.
267-
# We can ignore any -universal2 packages; they're already fat.
268-
binary_packages = self.find_binary_packages(
269-
host_app_packages_path,
270-
universal_suffix="_universal2",
271-
)
272-
273266
# Install dependencies for the architecture that isn't the host architecture
274267
other_arch = {
275268
"arm64": "x86_64",
@@ -284,6 +277,13 @@ def _install_app_requirements(
284277
self.tools.shutil.rmtree(other_app_packages_path)
285278
self.tools.os.mkdir(other_app_packages_path)
286279

280+
# Find all the packages with binary components.
281+
# We can ignore any -universal2 packages; they're already fat.
282+
binary_packages = self.find_binary_packages(
283+
host_app_packages_path,
284+
universal_suffix="_universal2",
285+
other_suffix=f"_{other_arch}",
286+
)
287287
if binary_packages:
288288
with self.console.wait_bar(
289289
f"Installing binary app requirements for {other_arch}..."

src/briefcase/platforms/macOS/utils.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,15 @@ def find_binary_packages(
6262
self,
6363
install_path: Path,
6464
universal_suffix: str | None = None,
65+
other_suffix: str | None = None,
6566
) -> list[tuple[str, str]]:
6667
"""Find the packages that have been installed that have binary components.
6768
6869
:param install_path: The path into which packages have been installed.
6970
:param universal_suffix: The tag suffix that indicates a universal wheel.
71+
:param other_suffix: The tag suffix for the *other* architecture. Presence of
72+
this architecture implies a universal wheel, even if it isn't tagged as
73+
such.
7074
:returns: A list of (package name, version) tuples, describing the packages that
7175
are in the install path that are non-universal and non-pure.
7276
"""
@@ -78,15 +82,20 @@ def find_binary_packages(
7882
with (distinfo / "WHEEL").open("r", encoding="utf-8") as f:
7983
wheel_data = email.message_from_string(f.read())
8084
is_purelib = wheel_data.get("Root-Is-Purelib", "false") == "true"
81-
tag = wheel_data["Tag"]
85+
tags = wheel_data.get_all("Tag")
8286

8387
# If the wheel is pure, it's not a binary package
8488
if is_purelib:
8589
continue
8690

8791
# If the tag ends with the universal tag, the binary package can be used on
8892
# all targets and doesn't need additional processing.
89-
if universal_suffix and tag.endswith(universal_suffix):
93+
if universal_suffix and any(tag.endswith(universal_suffix) for tag in tags):
94+
continue
95+
96+
# If the wheel is tagged with the *other* architecture, it's a universal
97+
# wheel even though it isn't tagged as such
98+
if other_suffix and any(tag.endswith(other_suffix) for tag in tags):
9099
continue
91100

92101
# The wheel is a single platform binary wheel.

tests/platforms/macOS/app/test_create.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ def test_install_app_packages(
421421
create_command.find_binary_packages.assert_called_once_with(
422422
bundle_path / f"app_packages.{host_arch}",
423423
universal_suffix="_universal2",
424+
other_suffix=f"_{other_arch}",
424425
)
425426

426427
# A request was made to install requirements
@@ -553,6 +554,7 @@ def test_min_os_version(create_command, first_app_templated, old_config, tmp_pat
553554
create_command.find_binary_packages.assert_called_once_with(
554555
bundle_path / "app_packages.arm64",
555556
universal_suffix="_universal2",
557+
other_suffix="_x86_64",
556558
)
557559

558560
# A request was made to install requirements
@@ -700,6 +702,7 @@ def test_default_min_os_version(
700702
create_command.find_binary_packages.assert_called_once_with(
701703
bundle_path / "app_packages.arm64",
702704
universal_suffix="_universal2",
705+
other_suffix="_x86_64",
703706
)
704707

705708
# A request was made to install requirements
@@ -838,6 +841,7 @@ def test_install_app_packages_no_binary(
838841
create_command.find_binary_packages.assert_called_once_with(
839842
bundle_path / f"app_packages.{host_arch}",
840843
universal_suffix="_universal2",
844+
other_suffix=f"_{other_arch}",
841845
)
842846

843847
# A request was made to install requirements
@@ -938,6 +942,7 @@ def test_install_app_packages_failure(create_command, first_app_templated, tmp_p
938942
create_command.find_binary_packages.assert_called_once_with(
939943
bundle_path / "app_packages.arm64",
940944
universal_suffix="_universal2",
945+
other_suffix="_x86_64",
941946
)
942947

943948
# A request was made to install requirements

tests/platforms/macOS/test_AppPackagesMergeMixin__find_binary_packages.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,55 @@ def test_find_binary_packages(dummy_command, tmp_path):
4949
version="9.9.9",
5050
tag="macOS_13_arm64",
5151
)
52+
# Packages with multiple binary tags (see #2690)
53+
# Multiple tags, where universal2 duplicates explicit tagging
54+
create_installed_package(
55+
tmp_path / "app-packages",
56+
"multi-tagged-binary-package-1",
57+
version="3.4.7",
58+
tag=["macOS_11_x86-64", "macOS_11_arm64", "macOS_11_universal2"],
59+
)
60+
# Universal2 even though it isn't tagged as such
61+
create_installed_package(
62+
tmp_path / "app-packages",
63+
"multi-tagged-binary-package-2",
64+
version="3.4.8",
65+
tag=["macOS_11_x86-64", "macOS_11_arm64"],
66+
)
67+
# Multiple tagged, but not a universal pair
68+
create_installed_package(
69+
tmp_path / "app-packages",
70+
"multi-tagged-binary-package-3",
71+
version="3.4.9",
72+
tag=["macOS_11_arm64", "macOS_11_ppc"],
73+
)
74+
# Multiple tagged, includes universal and host, but *not* other
75+
create_installed_package(
76+
tmp_path / "app-packages",
77+
"multi-tagged-binary-package-4",
78+
version="3.4.10",
79+
tag=["macOS_11_arm64", "macOS_11_universal2"],
80+
)
81+
# Multiple tagged, includes universal and other, but *not* host
82+
create_installed_package(
83+
tmp_path / "app-packages",
84+
"multi-tagged-binary-package-5",
85+
version="3.4.11",
86+
tag=["macOS_11_x86_64", "macOS_11_universal2"],
87+
)
5288

5389
binary_packages = dummy_command.find_binary_packages(
5490
tmp_path / "app-packages",
5591
universal_suffix="_universal2",
92+
other_suffix="_x86-64",
5693
)
5794

5895
# Binary wheels are discovered. We don't care about the order they are returned in,
5996
# just that they're all found.
60-
assert len(binary_packages) == 2
6197
assert set(binary_packages) == {
6298
("binary-package-1", "3.4.5"),
6399
("binary-package-2", "3.4.6"),
100+
("multi-tagged-binary-package-3", "3.4.9"),
64101
}
65102

66103

tests/utils.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,13 @@ def _download_file(url, download_path, role):
193193
def distinfo_metadata(
194194
package: str = "dummy",
195195
version: str = "1.2.3",
196-
tag: str = "py3-none-any",
196+
tag: str | list[str] = "py3-none-any",
197197
):
198198
"""Generate the content for a distinfo folder.
199199
200200
:param package: The name of the package.
201201
:param version: The version number of the package.
202-
:param tag: The packaging tag for the package.
202+
:param tag: The packaging tag (or tags) for the package.
203203
"""
204204
content = []
205205

@@ -221,7 +221,11 @@ def distinfo_metadata(
221221
wheel["Wheel-Version"] = "1.0"
222222
wheel["Generator"] = "test-case"
223223
wheel["Root-Is-Purelib"] = "true" if tag == "py3-none-any" else "false"
224-
wheel["Tag"] = tag
224+
if isinstance(tag, str):
225+
wheel["Tag"] = tag
226+
else:
227+
for value in tag:
228+
wheel["Tag"] = value
225229
content.append((f"{package}-{version}.dist-info/WHEEL", str(wheel)))
226230

227231
# RECORD

0 commit comments

Comments
 (0)