Skip to content

Commit

Permalink
building: use shutil.copyfile when copying files into onedir build
Browse files Browse the repository at this point in the history
Use `shutil.copyfile` instead of `shutil.copy` / `shutil.copy2`
when copying files into onedir build. Preserving original file
permissions (and/or metadata) may lead to issues when those are
too restrictive (e.g., read-only permissions, or immutable flag
on FreeBSD); for example, during application re-build attempt
(as the file cannot be removed or overwritten), or when trying
to move the application bundle.
  • Loading branch information
rokm committed Sep 22, 2023
1 parent 3b4099b commit 3bd5160
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 15 deletions.
8 changes: 6 additions & 2 deletions PyInstaller/building/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1140,8 +1140,12 @@ def assemble(self):
raise ValueError(
f"Attempting to collect a duplicated file into COLLECT: {dest_name} (type: {typecode})"
)
shutil.copy2(src_name, dest_path) # Use copy2 to (attempt to) preserve metadata
if typecode in ('EXTENSION', 'BINARY'):
# Use `shutil.copyfile` to copy file with default permissions. We do not attempt to preserve original
# permissions nor metadata, as they might be too restrictive and cause issues either during subsequent
# re-build attempts or when trying to move the application bundle. For binaries, we manually set the
# executable bits after copying the file.
shutil.copyfile(src_name, dest_path)
if typecode in ('EXTENSION', 'BINARY', 'EXECUTABLE'):
os.chmod(dest_path, 0o755)
logger.info("Building COLLECT %s completed successfully.", self.tocbasename)

Expand Down
8 changes: 6 additions & 2 deletions PyInstaller/building/osx.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ def assemble(self):
self.icon = os.path.abspath(self.icon)

# Copy icns icon to Resources directory.
shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources'))
shutil.copyfile(self.icon, os.path.join(self.name, 'Contents', 'Resources', os.path.basename(self.icon)))

# Key/values for a minimal Info.plist file
info_plist_dict = {
Expand Down Expand Up @@ -644,7 +644,11 @@ def assemble(self):
raise ValueError(
f"Attempting to collect a duplicated file into BUNDLE: {dest_name} (type: {typecode})"
)
shutil.copy2(src_name, dest_path) # Use copy2 to (attempt to) preserve metadata
# Use `shutil.copyfile` to copy file with default permissions. We do not attempt to preserve original
# permissions nor metadata, as they might be too restrictive and cause issues either during subsequent
# re-build attempts or when trying to move the application bundle. For binaries, we manually set the
# executable bits after copying the file.
shutil.copyfile(src_name, dest_path)
if typecode in ('EXTENSION', 'BINARY', 'EXECUTABLE'):
os.chmod(dest_path, 0o755)

Expand Down
15 changes: 4 additions & 11 deletions PyInstaller/building/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,17 +249,10 @@ def process_collected_binary(
# Ensure parent path exists
os.makedirs(os.path.dirname(cached_name), exist_ok=True)

# There are known some issues with 'shutil.copy2' on Mac OS 10.11 with copying st_flags. Issue #1650.
# 'shutil.copy' copies also permission bits and it should be sufficient for PyInstaller's purposes.
shutil.copy(src_name, cached_name)
# TODO: find out if this is still necessary when no longer using shutil.copy2()
if hasattr(os, 'chflags'):
# Some libraries on FreeBSD have immunable flag (libthr.so.3, for example). If this flag is preserved,
# os.chmod() fails with: OSError: [Errno 1] Operation not permitted.
try:
os.chflags(cached_name, 0)
except OSError:
pass
# Use `shutil.copyfile` to copy the file with default permissions bits, then manually set executable
# bits. This way, we avoid copying permission bits and metadata from the original file, which might be too
# restrictive for further processing (read-only permissions, immutable flag on FreeBSD, and so on).
shutil.copyfile(src_name, cached_name)
os.chmod(cached_name, 0o755)

if cmd:
Expand Down
3 changes: 3 additions & 0 deletions news/7938.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
When copying files into ``onedir`` application bundles, use
:func:`shutil.copyfile` instead of :func:`shutil.copy2` to avoid issues
with original permissions/metadata being too restrictive.

0 comments on commit 3bd5160

Please sign in to comment.