Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

building: MERGE: fix symlink bookkeeping #8124

Merged
merged 1 commit into from Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 20 additions & 7 deletions PyInstaller/building/api.py
Expand Up @@ -1181,6 +1181,7 @@ def __init__(self, *args):
directory name and executable name (e.g., `myapp/myexecutable`).
"""
self._dependencies = {}
self._symlinks = set()

# Process all given (analysis, identifier, path_to_exe) tuples
for analysis, identifier, path_to_exe in args:
Expand Down Expand Up @@ -1208,19 +1209,31 @@ def _process_toc(self, toc, path_to_exe):
toc_refs = []
for entry in toc:
dest_name, src_name, typecode = entry

# Special handling and bookkeeping for symbolic links. We need to account both for dest_name and src_name,
# because src_name might be the same in different contexts. For example, when collecting Qt .framework
# bundles on macOS, there are multiple relative symbolic links `Current -> A` (one in each .framework).
if typecode == 'SYMLINK':
key = dest_name, src_name
if key not in self._symlinks:
# First occurrence; keep the entry in "for-keep" TOC, same as we would for binaries and datas.
logger.debug("Keeping symbolic link %r entry in original TOC.", entry)
self._symlinks.add(key)
toc_keep.append(entry)
else:
# Subsequent occurrence; keep the SYMLINK entry intact, but add it to the references TOC instead of
# "for-keep" TOC, so it ends up in `a.dependencies`.
logger.debug("Moving symbolic link %r entry to references TOC.", entry)
toc_refs.append(entry)
del key # Block-local variable
continue

if src_name not in self._dependencies:
logger.debug("Adding dependency %s located in %s", src_name, path_to_exe)
self._dependencies[src_name] = path_to_exe
# Add entry to list of kept TOC entries
toc_keep.append(entry)
else:
# Keep SYMLINK entries intact (but add them to the references TOC instead of "for-keep" TOC, so they
# end up in `a.dependencies`).
if typecode == 'SYMLINK':
logger.debug("Referenced dependency %s is symbolic link; keeping it intact!", src_name)
toc_refs.append(entry)
continue

# Construct relative dependency path; i.e., the relative path from this executable (or rather, its
# parent directory) to the executable that contains the dependency.
dep_path = os.path.relpath(self._dependencies[src_name], os.path.dirname(path_to_exe))
Expand Down
4 changes: 4 additions & 0 deletions news/8124.bugfix.rst
@@ -0,0 +1,4 @@
Fix symbolic link tracking in ``MERGE`` processing, so that distinct
symbolic links with same relative target (e.g. ``Current -> A``
symbolic links in Qt .framework bundles collected on macOS) are properly
processed, and kept in the original TOC upon their first occurrence.