From 27356e47f1ea63039eee5fdabbf0ea0ea689955c Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Fri, 5 Jul 2024 08:23:37 -0700 Subject: [PATCH 1/7] Zero out minor version of platform tags for macOS 11 and later Add tests for target release versions Tests using env need to copy environ to ensure that subprocess works correctly --- Changelog.md | 3 +++ delocate/delocating.py | 12 +++++++++++- delocate/tests/test_scripts.py | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index ca7c860c..9a59e3cd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -34,6 +34,9 @@ rules on making a good Changelog. - Existing libraries causing DelocationError were not shown due to bad string formatting. [#216](https://github.com/matthew-brett/delocate/pull/216) +- Wheels for macOS 11 and later were using invalid literal versions in tags + instead of the macOS release version required by Python packagers. + [#219](https://github.com/matthew-brett/delocate/pull/219) ## [0.11.0] - 2024-03-22 diff --git a/delocate/delocating.py b/delocate/delocating.py index fe9e7087..d806a8cc 100644 --- a/delocate/delocating.py +++ b/delocate/delocating.py @@ -808,8 +808,18 @@ def _calculate_minimum_wheel_name( f"Failed to find any binary with the required architecture: {e}" ) from e prefix = wheel_name.rsplit("-", 1)[0] + + # Wheel platform tags MUST use the macOS release version, not the literal + # version provided by macOS. Since macOS 11 the minor version number is not + # part of the macOS release version and MUST be zero for tagging purposes. + def get_release_version(version: Version) -> str: + """Return the macOS release version from the given actual version.""" + if require_target_macos_version is not None: + version = max(version, require_target_macos_version) + return f"{version.major}_{0 if version.major >= 11 else version.minor}" + platform_tag = ".".join( - f"macosx_{version.major}_{version.minor}_{arch}" + f"macosx_{get_release_version(version)}_{arch}" for arch, version in arch_version.items() ) return f"{prefix}-{platform_tag}.whl", problematic_libs diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 1874a4ff..c5dc26c0 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -855,7 +855,7 @@ def test_delocate_wheel_verify_name_universal2_verify_crash_env_var( ["delocate-wheel", whl_10_9], check=False, cwd=tmp_path, - env={"MACOSX_DEPLOYMENT_TARGET": "10.9"}, + env={**os.environ, "MACOSX_DEPLOYMENT_TARGET": "10.9"}, ) assert result.returncode != 0 assert "Library dependencies do not satisfy target MacOS" in result.stderr @@ -863,3 +863,21 @@ def test_delocate_wheel_verify_name_universal2_verify_crash_env_var( assert "module2.abi3.so has a minimum target of 11.0" not in result.stderr assert "MACOSX_DEPLOYMENT_TARGET=12.0" in result.stderr assert "--require-target-macos-version 12.0" in result.stderr + + +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_delocate_wheel_macos_release_minor_version( + plat_wheel: PlatWheel, script_runner: ScriptRunner, tmp_path: Path +) -> None: + script_runner.run( + ["delocate-wheel", plat_wheel.whl, "-vv"], + env={**os.environ, "MACOSX_DEPLOYMENT_TARGET": "13.1"}, + check=True, + ) + + # Should create a 13.0 wheel instead of the requested 13.1 + assert {tmp_path / "plat-1.0-cp311-cp311-macosx_13_0_x86_64.whl"} == set( + file for file in tmp_path.iterdir() if file.suffix == ".whl" + ) From 8628a197545b3c28640fdf9c008dec3654e0ec6b Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Sat, 6 Jul 2024 01:23:45 -0700 Subject: [PATCH 2/7] Only mention MACOSX_DEPLOYMENT_TARGET to configure the supported version `--require-target-macos-version` could cause confusion in its current state. It was probably a mistake to add it. --- delocate/delocating.py | 3 +-- delocate/tests/test_scripts.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/delocate/delocating.py b/delocate/delocating.py index d806a8cc..d79943d6 100644 --- a/delocate/delocating.py +++ b/delocate/delocating.py @@ -862,8 +862,7 @@ def _check_and_update_wheel_name( "Library dependencies do not satisfy target MacOS" f" version {require_target_macos_version}:\n" f"{problematic_files_str}" - f"\nUse '--require-target-macos-version {min_valid_version}'" - " or set the environment variable" + "\nSet the environment variable" f" 'MACOSX_DEPLOYMENT_TARGET={min_valid_version}'" " to update minimum supported macOS for this wheel." ) diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index c5dc26c0..9b050f54 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -831,7 +831,6 @@ def test_delocate_wheel_verify_name_universal2_verify_crash( assert "Library dependencies do not satisfy target MacOS" in result.stderr assert "libam1.dylib has a minimum target of 12.0" in result.stderr assert "MACOSX_DEPLOYMENT_TARGET=12.0" in result.stderr - assert "--require-target-macos-version 12.0" in result.stderr @pytest.mark.xfail( # type: ignore[misc] @@ -862,7 +861,6 @@ def test_delocate_wheel_verify_name_universal2_verify_crash_env_var( assert "libam1.dylib has a minimum target of 12.0" in result.stderr assert "module2.abi3.so has a minimum target of 11.0" not in result.stderr assert "MACOSX_DEPLOYMENT_TARGET=12.0" in result.stderr - assert "--require-target-macos-version 12.0" in result.stderr @pytest.mark.xfail( # type: ignore[misc] From ada04a2228d55a745440c027bd3ac94b71be4fbb Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Sat, 6 Jul 2024 09:04:39 -0700 Subject: [PATCH 3/7] Warn on deceptive platform version numbers --- delocate/delocating.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/delocate/delocating.py b/delocate/delocating.py index d79943d6..3d5c4871 100644 --- a/delocate/delocating.py +++ b/delocate/delocating.py @@ -816,6 +816,17 @@ def get_release_version(version: Version) -> str: """Return the macOS release version from the given actual version.""" if require_target_macos_version is not None: version = max(version, require_target_macos_version) + elif version.major >= 11 and version.minor > 0: + # Warn when the platform tag is given a deceptive version number. + logger.warning( + "Wheel will be tagged as supporting macOS %i," + " but will not support macOS versions older than %i.%i\n\t" + "Configure MACOSX_DEPLOYMENT_TARGET to suppress this warning.", + version.major, + version.major, + version.minor, + ) + return f"{version.major}_{0 if version.major >= 11 else version.minor}" platform_tag = ".".join( From df2fb6fd1c51b74ade06b606b7200ab2fda7b1bf Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Sun, 7 Jul 2024 03:19:11 -0700 Subject: [PATCH 4/7] Test warning for deceptive platform tags Add macOS 12.1 test data --- delocate/tests/data/liba_12_1.dylib | Bin 0 -> 39704 bytes delocate/tests/data/make_libs.sh | 1 + delocate/tests/test_scripts.py | 25 +++++++++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 delocate/tests/data/liba_12_1.dylib diff --git a/delocate/tests/data/liba_12_1.dylib b/delocate/tests/data/liba_12_1.dylib new file mode 100644 index 0000000000000000000000000000000000000000..608596eb73f80ee20c32202c205473523509a379 GIT binary patch literal 39704 zcmeHQdu&_Rc|Q~-Kbi-*EUDc**(7%2c}PgTEGFx6Ircj&V@E)L5dWJhV2hwOE+zQw1!cuT4O}cup-=mp+*sGTDNvros)8dkt(TO z?f0E~E_p9W#jYH$7;p}J?z!K2eCPXq=ljmN=Zg3E?GNAmPPljKJ_Q{a3g65;ysh(4C#neb3GzgHx`*UjpT z=mvRTFX23r&ZJ_A5m{Kxuj}{B0!v4Wws=+Z<~*A?7E45VEHRu!rD}d5$#2dK`0oAt`pwY|B9lT;Tx(LY6N-eT7ZJE6ZNs`Izar|25H#CMO4!^d zEdnXY6++~^r+eRCt*hIzxg0Vn1jUtE*0A*+lO}oW<4b1rDB{+t{CI3A%md9l@L2Q& z?pZ51ksicv34_rh>7|p5^?@N_NIZ^wg|TOmUML_RPI3$G`w%HSE+hPiwIkk%xK`MN z_WnABqz3i#qeGLg{q#>ePRzfYKl|Gg2b-Ib=R*{_xo!n`w~Bij1Gb^X{aZ+c{6r(5 zhTu=U#Q1Ny6K?2Yk7v`V$DuIA$j+UQS^87`4n(WIJNiIP{r(f_j6V9<(-Jb&W^6km z_3H;N)P9Gg2`3d40tx|zfI>hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9C$0MSR8xZBSndMDZ1_%_)ATuW_6ndfL1&~ut-jIIUNPz#8_L|_|Q_!YE zg9W;54$|y zS~&E^@MD&FoA&v_r6M}+n!+%Q!>Y-M2bZAx#h$!YoYLMF1C^gV2zZTO;LC$DuJ=O% zNDH{BEYx|yGf5v_nDw=lWj{Va0C5rHW`uUoD4zigMdN*w$sEe&LQiOCJYS=4CQu8- z$PqQ?Oq*HM$f$_RQiaQGXkvVpdA_z=lt5;3yu#qDagG>2q0M?uK7=A;pV`k0#%soS z-Xu_2xbDjGAH!-)R42yg!0i*J)z5k62j51EsZjo0kW_w!8WzO8*>*BS*?(hZ(|sua zCc2+9J|nuA>_e6uE^!Zu<~%2ll0@GjMDmjzwiqB#8GDdl5X~nSJSSl<(q>cOVlo}U6eS%g=p+gW3l-QQ9?Zw$>CmF5KaxY z{>sc@juJ#>9Pd*zztj|-J zt5FLUFs1#Bv?3#bBmc}eOL$e+7y45*;}^m@7ma_Xv_^r7y;y61A+!}UBjo5Uh4Q#B zXog(}$9!zJ$KN!?z^ZQtsFwdC>X{E9Il8gzqgKEC_{Gp^OnavXmyGSG32iQxl|Doh zvoHyJZx#Dq6JG<1Q5mjE{h);|RtD%U{DP+Q1@u}n#PV3cI!hl(qLfOCC@~D_8$|=( z*%8~IQ~ip45Koku>k(EV+&k@!Rwn98$|s(lD)LpvvtBtY!@*D5U%5Z)`Hsv6{Rn-^ zj|+F0*p`gX(hVN@mdzy&^|;I-BQQ0P7@(vb|gr4+q|IJ^;Qeb@8pm%U`w!(-vs7F+SEa ztITg&oNUVDd_8twDr_64(C`*URGizPPT4>xbe>Bf7fnm*Tn=3aoCsTU_rI*ZalQC$0~Q>%-!@U0ms@ zarM1QHn@J$u-S0CK4_qv7u}feX5CHa-Rzt0hZrlmn^5-8Zny^e zGsEhupLT6w-v_7fH}qa`vmdxO&o;T1-2a3cR;ATfZ*y(h(zMTY?=y`3fz=Lq+nY4k zPJGo{&b!Zb7kh-UNABV5=^czcxB06sSEk%u_|YAL=e=No4%x$q)CYZkOf&+KpIrzt zM&Gp`0fAd~m;9!j=wblhV-t;u@*>h4=F}@l{lAOvY{}1`1Ye|hz{EGcx1qhM#S(ZI z(TUfM6pQ%u1{C0N#lMj?1d#@BVXhg>iC%m*JcGSo{ou`++`KukLh$COA4(f#3f6+g zbAU*}TBLZC6zOJ}($gfhqiL8@vrBJ|=1iio;T9=&+9LJJ)I4p#o1@v5>d|sZDeVg= zy-TL%x8mO17D?0oi^}P{afF<>s*6HEA)pXY2q**;0tx|zfI>hapb$_9Cy)5Kssx1QY@a0fm4hapb$_9 zCy)5Kssx1QY@a0fm4Boag4F6uOl|N*wrofoE zMt1cpjm#BlVpqS|#9Z$qZpXm|2N2J1U{|joeQ_gm{W;>_Bd#Fcy@@r@!Bw0OCXU4t zQGP&AXGe9=pWC;$Khwf_pgo?9gyVW=Hm&pFa752&2ln;*d2=kP*{gNLl4(8^PV4O> zdIn9jqoUO05eP(%gi}0|3db_(?uZr$=+E<5BEvH$#&j(f`-rM7dUE(9d9}yO3d z*0(oeXVVN>ViBIsq+*GY?#RA=UM+-%yv}f()XOAOkV@0qIi!kZw9y-9)fI3`-D1j< zgg|=Gb)}PPniHN?2)?`pTP}SoAAMkTc>)*xu_V%~(QJZkBktATUXXuG&e0|Jq zT-qXJjj)5VX*(RCO3Sjnx4=o#dIq*19g1am7)}&PCepDe6i?}yBdO#u$HW>ALnCwDxFM(y%li+D`esW=gV7IziDU3-f5I)K_ z?lZw+1MbG+3CwUcE`G1{FqH3nPp*Ry!#B^i49Qvkk2`U@_lqSobiU zZvZ+G=$oU<%#bzPm%{#_udn3x`4*b|zOgaC*YEZDDfs&QhrM2(&+F~;`}{ty&wJPh zYTD)Zqj3uklDQoV#bKOtL*aQzFSVHTUxU8il@sURP|ia*aE9tHw43F}Bwdp9OOp1| z*<%P7Bu(e$P}qzmirSYX9RyAMeH~``L0RsX^f5^vmh|@|JtpaYmGrcv@52E&LOw}H zBwdp9>yq|$n*Cjnbf2W(m2^(hcVKBFelwCjDCv@!~h2r5lv??QMg0WbS4vpZITveGlA}qG@a2z0gpqB4g;g} zo+!}yNFLZ>Eks^}k9(eGB#jhIjD z{ohqZ?=)#hZ3dcFXsRIhr6D6&n-R(V$R)^ZX{g9t$OLGZo2H?kO3xU73oyxDsXwG$9K zp^x5q+?1U_&4c={^AY?1==r$f5Y;=Hx_eM9p4R&I(C!PH9LI+G`kKpeiyR$tY%&7a z$&Q7iVmE@#61HP&ZI5Wb(J5_XC_Bt!*hGl^u(biB4F+WE@1TFQQ!8&^G-TUn#=30k zYfU@OI-9;oe>3(pf~S1@Q0@$S-0(u@X{cebgoY<(%)hr_YB z9(5pl%|1J@8BAxe2@^Xz9*Kt&BiLDC(^cM{*+Avz#9} z+Bd+(_OPw|Y6EW)FlK8gkM|ko!c_z1u~-x@OdPA)b@FUW!;}*At|e|$BCxJ07B(Q? zvq%Bg$EGzJ&V(JxRBXmn)GlfTPjuf)FS39?w#=IS9h9w7^O}{`C++TLOKJHyu?rjZ z>5s{j)havH5&@~#RSrx(npg=b?7~;r^GuZnnGH|dqtaa&CNA1 zb3Lw)-Z0iZF~8O`$Sqfs)??!9 zT&oi=Z__-UOpJUS+-M|8K;rjtN*REd#5a8;nOaNzo8*aY!?9GlYJIu+j$y8EPJ?Wf z!*2aW0PBes@Ru`_cpV+E&rLm@(gu^mmK7d=pW7VkS6SBK9@qEqam}^9EfvN3>om8@ z>Qkri+8nWD=?TZ*0D3BadZFp6{0xot{lMM&GMd82&C39*XO&tU6~`O->+Clhx|4&7zGw!v6w^_w;}O literal 0 HcmV?d00001 diff --git a/delocate/tests/data/make_libs.sh b/delocate/tests/data/make_libs.sh index 469af2de..4035dcb6 100755 --- a/delocate/tests/data/make_libs.sh +++ b/delocate/tests/data/make_libs.sh @@ -48,6 +48,7 @@ fi $CXX_64 -o liba.dylib -dynamiclib a.cc $CXX_M1 -o libam1.dylib -dynamiclib a.cc MACOSX_DEPLOYMENT_TARGET=12.0 $CXX_M1 -o libam1_12.dylib -dynamiclib a.cc +MACOSX_DEPLOYMENT_TARGET=12.1 $CXX_64 -o liba_12_1.dylib -dynamiclib a.cc $CXX_64 -o a.o -c a.cc ar rcs liba.a a.o $CXX_64 -o libb.dylib -dynamiclib b.cc -L. -la diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 9b050f54..1e592470 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -879,3 +879,28 @@ def test_delocate_wheel_macos_release_minor_version( assert {tmp_path / "plat-1.0-cp311-cp311-macosx_13_0_x86_64.whl"} == set( file for file in tmp_path.iterdir() if file.suffix == ".whl" ) + + +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_delocate_wheel_macos_release_version_warning( + plat_wheel: PlatWheel, script_runner: ScriptRunner, tmp_path: Path +) -> None: + with InWheel(plat_wheel.whl, plat_wheel.whl) as wheel_tmp_path: + shutil.copy( + DATA_PATH / "liba_12_1.dylib", # macOS library targeting 12.1 + Path(wheel_tmp_path, "fakepkg1/"), + ) + + result = script_runner.run( + ["delocate-wheel", plat_wheel.whl, "-vv"], check=True + ) + + assert "will be tagged as supporting macOS 12" in result.stderr + assert "will not support macOS versions older than 12.1" in result.stderr + + # Should create a 12.0 wheel instead of 12.1 + assert {tmp_path / "plat-1.0-cp311-cp311-macosx_12_0_x86_64.whl"} == set( + file for file in tmp_path.iterdir() if file.suffix == ".whl" + ) From 3e661d76bd1caaa2a40b2118490cb70bd81b9dab Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Sun, 7 Jul 2024 18:20:27 -0700 Subject: [PATCH 5/7] Refactor wheel name calculation Simplify code involving architectures by working with their unpacked forms Fixed code and tests which mislabeled `intel` as `universal` --- Changelog.md | 2 + delocate/delocating.py | 183 +++++++++++++++++++++------------ delocate/tests/test_scripts.py | 6 +- 3 files changed, 121 insertions(+), 70 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9a59e3cd..51555552 100644 --- a/Changelog.md +++ b/Changelog.md @@ -37,6 +37,8 @@ rules on making a good Changelog. - Wheels for macOS 11 and later were using invalid literal versions in tags instead of the macOS release version required by Python packagers. [#219](https://github.com/matthew-brett/delocate/pull/219) +- Fixed regression in `intel` platform support. + [#219](https://github.com/matthew-brett/delocate/pull/219) ## [0.11.0] - 2024-03-22 diff --git a/delocate/delocating.py b/delocate/delocating.py index 3d5c4871..e2d2875f 100644 --- a/delocate/delocating.py +++ b/delocate/delocating.py @@ -36,6 +36,7 @@ from macholib.MachO import MachO # type: ignore[import-untyped] from packaging.utils import parse_wheel_filename from packaging.version import Version +from typing_extensions import Final from .libsana import ( DelocationError, @@ -665,7 +666,7 @@ def _get_archs_and_version_from_wheel_name( return platform_requirements -def _get_problematic_libs( +def _get_incompatible_libs( required_version: Optional[Version], version_lib_dict: Dict[Version, List[Path]], arch: str, @@ -706,6 +707,73 @@ def _get_problematic_libs( return bad_libraries +def _unpack_architectures( + architecture_versions: Mapping[str, Version], +) -> dict[str, Version]: + """Return architecture versions derived from their universal forms. + + Examples + -------- + >>> _unpack_architectures({"arm64": Version("11.0")}) + {'arm64': } + >>> _unpack_architectures({"universal2": Version("10.5")}) + {'x86_64': , 'arm64': } + >>> _unpack_architectures({"intel": Version("10.5")}) + {'i386': , 'x86_64': } + >>> _unpack_architectures({}) + {} + """ + architecture_versions = {**architecture_versions} + if "universal2" in architecture_versions: + architecture_versions["x86_64"] = architecture_versions["universal2"] + architecture_versions["arm64"] = max( + architecture_versions["universal2"], Version("11.0") + ) + del architecture_versions["universal2"] + if "intel" in architecture_versions: + architecture_versions["i386"] = architecture_versions["intel"] + architecture_versions["x86_64"] = architecture_versions["intel"] + del architecture_versions["intel"] + return architecture_versions + + +def _pack_architectures( + architecture_versions: Mapping[str, Version], +) -> dict[str, Version]: + """Return architecture versions combined into their universal forms. + + Examples + -------- + >>> _pack_architectures({"arm64": Version("11.0")}) + {'arm64': } + >>> _pack_architectures({"i386": Version("10.5"), "x86_64": Version("10.5")}) + {'intel': } + >>> _pack_architectures({"x86_64": Version("10.5"), "arm64": Version("11.0")}) + {'universal2': } + >>> _pack_architectures({"x86_64": Version("11.0"), "arm64": Version("12.0")}) + {'x86_64': , 'arm64': } + >>> _pack_architectures({"i386": Version("11.0"), "x86_64": Version("11.0"), "arm64": Version("11.0")}) + {'i386': , 'universal2': } + >>> _pack_architectures({}) + {} + """ # noqa: E501 + architecture_versions = {**architecture_versions} + if {"x86_64", "arm64"}.issubset(architecture_versions.keys()) and ( + architecture_versions["x86_64"] == architecture_versions["arm64"] + or architecture_versions["arm64"] == Version("11.0") + ): + architecture_versions["universal2"] = architecture_versions["x86_64"] + del architecture_versions["x86_64"] + del architecture_versions["arm64"] + if {"i386", "x86_64"}.issubset( + architecture_versions.keys() + ) and architecture_versions["i386"] == architecture_versions["x86_64"]: + architecture_versions["intel"] = architecture_versions["i386"] + del architecture_versions["i386"] + del architecture_versions["x86_64"] + return architecture_versions + + def _calculate_minimum_wheel_name( wheel_name: str, wheel_dir: Path, @@ -730,84 +798,62 @@ def _calculate_minimum_wheel_name( str The updated wheel name. set[tuple[Path, Version]] - A set of libraries that require a more modern macOS version than the - provided one. + Any libraries requiring a more modern macOS version than + `require_target_macos_version`. """ # get platform tag from wheel name using packaging if wheel_name.endswith("any.whl"): # universal wheel, no need to update the platform tag return wheel_name, set() - arch_version = _get_archs_and_version_from_wheel_name(wheel_name) + wheel_arch_version: Final = _unpack_architectures( + _get_archs_and_version_from_wheel_name(wheel_name) + ) # get the architecture and minimum macOS version from the libraries # in the wheel - version_info_dict: Dict[str, Dict[Version, List[Path]]] = {} + all_library_versions: Dict[str, Dict[Version, List[Path]]] = {} for lib in wheel_dir.glob("**/*"): for arch, version in _get_macos_min_version(lib): - version_info_dict.setdefault(arch.lower(), {}).setdefault( + all_library_versions.setdefault(arch.lower(), {}).setdefault( version, [] ).append(lib) - version_dkt = { - arch: max(version) for arch, version in version_info_dict.items() + logger.debug( + "Bundled library info: %s arch=%s target=%s", + lib.name, + arch, + version, + ) + + # Derive architecture requirements from bundled libraries + arch_version = { + arch: max(version_libraries.keys()) + for arch, version_libraries in all_library_versions.items() } - problematic_libs: set[tuple[Path, Version]] = set() - - try: - for arch, version in list(arch_version.items()): - if arch == "universal2": - if version_dkt["arm64"] == Version("11.0"): - arch_version["universal2"] = max( - version, version_dkt["x86_64"] - ) - else: - arch_version["universal2"] = max( - version, version_dkt["arm64"], version_dkt["x86_64"] - ) - problematic_libs.update( - _get_problematic_libs( - require_target_macos_version, - version_info_dict["arm64"], - "arm64", - ) - ) - problematic_libs.update( - _get_problematic_libs( - require_target_macos_version, - version_info_dict["x86_64"], - "x86_64", - ) - ) - elif arch == "universal": - arch_version["universal"] = max( - version, version_dkt["i386"], version_dkt["x86_64"] - ) - problematic_libs.update( - _get_problematic_libs( - require_target_macos_version, - version_info_dict["i386"], - "i386", - ), - _get_problematic_libs( - require_target_macos_version, - version_info_dict["x86_64"], - "x86_64", - ), - ) - else: - arch_version[arch] = max(version, version_dkt[arch]) - problematic_libs.update( - _get_problematic_libs( - require_target_macos_version, - version_info_dict[arch], - arch, - ) - ) - except KeyError as e: + # Compare libraries to target macOS version and track incompatibilities + incompatible_libs: set[tuple[Path, Version]] = set() + for arch, version_libraries in all_library_versions.items(): + incompatible_libs.update( + _get_incompatible_libs( + require_target_macos_version, version_libraries, arch + ) + ) + + # Sanity check, wheels tagged with architectures should have at least one + # bundled library matching that architecture. + missing_architectures: Final = ( + wheel_arch_version.keys() - arch_version.keys() + ) + if missing_architectures: raise DelocationError( - f"Failed to find any binary with the required architecture: {e}" - ) from e - prefix = wheel_name.rsplit("-", 1)[0] + "Failed to find any binary with the required architecture: " + f"""{",".join(missing_architectures)!r}""" + ) + + # Limit architecture tags to whatever the wheel already claimed to support. + # Use versions derived from bundled libraries instead of previous wheel tag. + for arch in arch_version.keys() - wheel_arch_version.keys(): + del arch_version[arch] # Wheel platform tags MUST use the macOS release version, not the literal # version provided by macOS. Since macOS 11 the minor version number is not @@ -829,11 +875,12 @@ def get_release_version(version: Version) -> str: return f"{version.major}_{0 if version.major >= 11 else version.minor}" - platform_tag = ".".join( + platform_tag: Final = ".".join( f"macosx_{get_release_version(version)}_{arch}" - for arch, version in arch_version.items() + for arch, version in _pack_architectures(arch_version).items() ) - return f"{prefix}-{platform_tag}.whl", problematic_libs + prefix: Final = wheel_name.rsplit("-", 1)[0] + return f"{prefix}-{platform_tag}.whl", incompatible_libs def _check_and_update_wheel_name( @@ -1044,7 +1091,9 @@ def delocate_wheel( if len(copied_libs) or not in_place: if remove_old: os.remove(in_wheel) + logger.info("Deleted:%s", in_wheel) dir2zip(wheel_dir, out_wheel_) + logger.info("Output:%s", out_wheel_) return stripped_lib_dict(copied_libs, wheel_dir + os.path.sep) diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 1e592470..07adbae6 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -763,7 +763,7 @@ def test_delocate_wheel_verify_name_universal_ok( DATA_PATH / "np-1.6.0_intel_lib__compiled_base.so", tmp_path / "plat/fakepkg1/np-1.6.0_intel_lib__compiled_base.so", ) - whl_10_9 = tmp_path / "plat-1.0-cp311-cp311-macosx_10_9_universal.whl" + whl_10_9 = tmp_path / "plat-1.0-cp311-cp311-macosx_10_9_intel.whl" dir2zip(tmp_path / "plat", whl_10_9) script_runner.run( [ @@ -789,12 +789,12 @@ def test_delocate_wheel_missing_architecture( ) -> None: shutil.copy( plat_wheel.whl, - tmp_path / "plat2-1.0-cp311-cp311-macosx_10_9_universal.whl", + tmp_path / "plat2-1.0-cp311-cp311-macosx_10_9_intel.whl", ) result = script_runner.run( [ "delocate-wheel", - tmp_path / "plat2-1.0-cp311-cp311-macosx_10_9_universal.whl", + tmp_path / "plat2-1.0-cp311-cp311-macosx_10_9_intel.whl", ], check=False, cwd=tmp_path, From ae65b6de12136053a4d4069c38ef0558702b31b2 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Mon, 8 Jul 2024 07:45:27 -0700 Subject: [PATCH 6/7] Deprecate `--require-target-macos-version` This flag is redundant and does not mesh well with the MACOSX_DEPLOYMENT_TARGET environment variable. --- Changelog.md | 6 ++++++ delocate/cmd/delocate_wheel.py | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 51555552..ea1bb906 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,12 @@ rules on making a good Changelog. version to `delocate==0.11.0`. [#215](https://github.com/matthew-brett/delocate/pull/215) +### Deprecated + +- `--require-target-macos-version` has been deprecated. + `MACOSX_DEPLOYMENT_TARGET` should be used instead of this flag. + [#219](https://github.com/matthew-brett/delocate/pull/219) + ### Fixed - Existing libraries causing DelocationError were not shown due to bad string diff --git a/delocate/cmd/delocate_wheel.py b/delocate/cmd/delocate_wheel.py index 8d2e1e33..a564917b 100755 --- a/delocate/cmd/delocate_wheel.py +++ b/delocate/cmd/delocate_wheel.py @@ -2,6 +2,9 @@ """Copy, relink library dependencies for wheel. Overwrites the wheel in-place by default. + +This script respects the MACOSX_DEPLOYMENT_TARGET environment variable. +Set MACOSX_DEPLOYMENT_TARGET to verify and target a specific macOS release. """ # vim: ft=python @@ -69,7 +72,8 @@ parser.add_argument( "--require-target-macos-version", type=Version, - help="Verify if platform tag in wheel name is proper", + help="Verify if platform tag in wheel name is proper (deprecated)" + "\nConfigure MACOSX_DEPLOYMENT_TARGET instead of using this flag", default=None, ) From 19b5121e7565b2fde88d34eb548be9a59af4c666 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Fri, 19 Jul 2024 16:26:31 -0700 Subject: [PATCH 7/7] Improve platform tag documentation and warnings Add relevant architecture to the deceptive tag warning. --- delocate/delocating.py | 18 ++++++++++++------ delocate/tests/test_scripts.py | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/delocate/delocating.py b/delocate/delocating.py index e2d2875f..131386cf 100644 --- a/delocate/delocating.py +++ b/delocate/delocating.py @@ -858,25 +858,31 @@ def _calculate_minimum_wheel_name( # Wheel platform tags MUST use the macOS release version, not the literal # version provided by macOS. Since macOS 11 the minor version number is not # part of the macOS release version and MUST be zero for tagging purposes. - def get_release_version(version: Version) -> str: - """Return the macOS release version from the given actual version.""" + def get_macos_platform_tag(version: Version, architecture: str) -> str: + """Return the macOS platform tag for this version and architecture. + + `version` will be converted to a release version expected by pip. + """ if require_target_macos_version is not None: + # Version was specified explicitly with MACOSX_DEPLOYMENT_TARGET. version = max(version, require_target_macos_version) elif version.major >= 11 and version.minor > 0: - # Warn when the platform tag is given a deceptive version number. + # This is the range where an automatic version is deceptive. logger.warning( - "Wheel will be tagged as supporting macOS %i," + "Wheel will be tagged as supporting macOS %i (%s)," " but will not support macOS versions older than %i.%i\n\t" "Configure MACOSX_DEPLOYMENT_TARGET to suppress this warning.", version.major, + architecture, version.major, version.minor, ) - return f"{version.major}_{0 if version.major >= 11 else version.minor}" + minor: Final = 0 if version.major >= 11 else version.minor + return f"macosx_{version.major}_{minor}_{architecture}" platform_tag: Final = ".".join( - f"macosx_{get_release_version(version)}_{arch}" + get_macos_platform_tag(version, arch) for arch, version in _pack_architectures(arch_version).items() ) prefix: Final = wheel_name.rsplit("-", 1)[0] diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 07adbae6..1b20c2f0 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -897,7 +897,7 @@ def test_delocate_wheel_macos_release_version_warning( ["delocate-wheel", plat_wheel.whl, "-vv"], check=True ) - assert "will be tagged as supporting macOS 12" in result.stderr + assert "will be tagged as supporting macOS 12 (x86_64)" in result.stderr assert "will not support macOS versions older than 12.1" in result.stderr # Should create a 12.0 wheel instead of 12.1