Skip to content

Commit

Permalink
building: collect built-in extensions into lib-dynload sub-directory
Browse files Browse the repository at this point in the history
On macOS and linux, some of the python's built-ins have extension
modules that originally reside in python3.X/lib-dynload directory.
This directory is in sys.path, therefore the collected extensions
have no parent directory and end up directly in the _MEIPASS.

This commit explicitly diverts such extensions into lib-dynload
sub-directory in the _MEIPASS.

In addition to decluttering the _MEIPASS on linux and macOS, this
also prevents ctypes.CDLL() from picking up the extensions'
shared libraries and causing inconsistent behavior between
frozen and unfrozen application, which in some corner cases
leads to issues with shadowing, such as in pyinstaller#5583.
  • Loading branch information
rokm committed Apr 13, 2021
1 parent d762f1d commit eddac48
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 1 deletion.
6 changes: 5 additions & 1 deletion PyInstaller/building/api.py
Expand Up @@ -840,7 +840,11 @@ def _set_dependencies(self, analysis, path):
# relative path needs to be reconstructed from the
# name components.
if tpl[2] == 'EXTENSION':
ext_components = tpl[0].split('.')[:-1]
# Split on os.path.sep first, to handle additional
# path prefix (e.g., lib-dynload)
ext_components = tpl[0].split(os.path.sep)
ext_components = ext_components[:-1] \
+ ext_components[-1].split('.')[:-1]
if ext_components:
rel_path = os.path.join(*ext_components)
else:
Expand Down
12 changes: 12 additions & 0 deletions PyInstaller/building/build_main.py
Expand Up @@ -480,6 +480,18 @@ def assemble(self):
self.binding_redirects[:] = list(set(self.binding_redirects))
logger.info("Found binding redirects: \n%s", self.binding_redirects)

# Filter binaries to adjust path of extensions that come from
# python's lib-dynload directory. Prefix them with lib-dynload
# so that we'll collect them into subdirectory instead of
# directly into _MEIPASS
for idx, tpl in enumerate(self.binaries):
name, path, typecode = tpl
if typecode == 'EXTENSION' \
and not os.path.dirname(os.path.normpath(name)) \
and os.path.basename(os.path.dirname(path)) == 'lib-dynload':
name = os.path.join('lib-dynload', name)
self.binaries[idx] = (name, path, typecode)

# Place Python source in data files for the noarchive case.
if self.noarchive:
# Create a new TOC of ``(dest path for .pyc, source for .py, type)``.
Expand Down
1 change: 1 addition & 0 deletions news/5583.bugfix.rst
@@ -0,0 +1 @@
(OSX) Fix issues with ``pycryptodomex`` on macOS.
5 changes: 5 additions & 0 deletions news/5604.core.rst
@@ -0,0 +1,5 @@
Collect python extension modules that correspond to built-ins into
``lib-dynload`` sub-directory instead of directly into bundle's root
directory. This prevents them from shadowing shared libraries with the
same basename that are located in a package and loaded via ``ctypes`` or
``cffi``, and also declutters the bundle's root directory.

0 comments on commit eddac48

Please sign in to comment.