Skip to content

feat: re-implement delocate for repairing macOS wheels#3114

Draft
messense wants to merge 7 commits intoPyO3:mainfrom
messense:delocate
Draft

feat: re-implement delocate for repairing macOS wheels#3114
messense wants to merge 7 commits intoPyO3:mainfrom
messense:delocate

Conversation

@messense
Copy link
Copy Markdown
Member

@messense messense commented Apr 2, 2026

No description provided.

messense added 4 commits April 2, 2026 19:02
Add arwen = 0.0.5 (Mach-O patching) and arwen-codesign = 0.0.1-alpha.1
(pure Rust ad-hoc codesigning) as optional dependencies behind a new
'auditwheel' Cargo feature. Add 'auditwheel' to the 'full' feature list.

These enable cross-platform wheel repair:
- macOS: Mach-O install name rewriting and ad-hoc codesigning
- Works from any host OS (pure Rust, no macOS tools needed)
Add src/auditwheel/macos.rs with MacOSRepairer that uses arwen for
Mach-O install name/rpath manipulation and arwen-codesign for pure-Rust
ad-hoc code signing. No macOS-only tool dependencies.

Key behavior:
- Filters system libraries (/usr/lib/*, /System/*) and libpython
- Rewrites LC_LOAD_DYLIB to @loader_path-relative names
- Sets LC_ID_DYLIB to /DLC/<libs_dir>/<name> (matching delocate)
- Removes absolute rpaths, keeps @loader_path/@executable_path
- Ad-hoc codesigns all modified binaries (cross-platform)
- Uses .dylibs directory (matching delocate convention)
Update auditwheel/mod.rs to export MacOSRepairer (feature-gated behind
'auditwheel'). Wire it into make_repairer() in build_context/repair.rs
so macOS builds use MacOSRepairer for wheel repair instead of returning
None.
The is_libpython check now recognizes Python.framework paths in addition
to traditional libpython3.*.dylib files. This fixes pyo3-bin bindings
that link against /Library/Frameworks/Python.framework/Versions/X/Python.

Also adds should_bundle_library() to properly error on missing non-system
dependencies instead of silently skipping them.
messense added 2 commits April 2, 2026 20:48
arwen's MachoContainer caches parsed load command offsets, which become
stale after modifications that change install name lengths. Re-parsing
between each operation ensures correct offsets are used.

This fixes corruption when changing install names to longer strings,
which shifts subsequent load commands in the binary.
The is_libpython() check only recognized Python.framework but
free-threaded Python builds (3.13t, 3.14t) use PythonT.framework.
This caused the framework to be bundled and the binary patched to
reference a non-existent hashed library name.
The pure-Rust arwen-codesign library requires an LC_CODE_SIGNATURE load
command which older dylibs (e.g., Homebrew's libintl) don't have. Use
Apple's codesign CLI directly on macOS for reliability, keeping the
pure-Rust implementation for cross-compilation from other platforms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant