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

Avoid collecting Wine built-in DLLs #6149

Merged
merged 3 commits into from Aug 23, 2021
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
35 changes: 35 additions & 0 deletions PyInstaller/compat.py
Expand Up @@ -37,6 +37,7 @@

is_win = sys.platform.startswith('win')
is_win_10 = is_win and (platform.win32_ver()[0] == '10')
is_win_wine = False # Running under Wine; determined later on.
is_cygwin = sys.platform == 'cygwin'
is_darwin = sys.platform == 'darwin' # Mac OS X

Expand Down Expand Up @@ -214,6 +215,40 @@
# Machine suffix for bootloader.
machine = _pyi_machine(platform.machine(), platform.system())


# Wine detection and support
def is_wine_dll(filename):
"""
Check if the given PE file is a Wine DLL (PE-converted built-in, or fake/placeholder one).

Returns True if the given file is a Wine DLL, False if not (or if file cannot be analyzed or does not exist).
"""
_WINE_SIGNATURES = (
b'Wine builtin DLL', # PE-converted Wine DLL
b'Wine placeholder DLL', # Fake/placeholder Wine DLL
)
_MAX_LEN = max([len(sig) for sig in _WINE_SIGNATURES])

# Wine places their DLL signature in the padding area between the IMAGE_DOS_HEADER and IMAGE_NT_HEADERS. So we need
# to compare the bytes that come right after IMAGE_DOS_HEADER, i.e., after initial 64 bytes. We can read the file
# directly and avoid using the pefile library to avoid performance penalty associated with full header parsing.
try:
with open(filename, 'rb') as fp:
fp.seek(64)
signature = fp.read(_MAX_LEN)
return signature.startswith(_WINE_SIGNATURES)
except Exception:
pass
return False


if is_win:
try:
import ctypes.util # noqa: E402
is_win_wine = is_wine_dll(ctypes.util.find_library('kernel32'))
except Exception:
pass

# Set and get environment variables does not handle unicode strings correctly on Windows.

# Acting on os.environ instead of using getenv()/setenv()/unsetenv(), as suggested in
Expand Down
30 changes: 21 additions & 9 deletions PyInstaller/depend/dylib.py
Expand Up @@ -225,23 +225,35 @@ def search(self, libname):

exclude_list = WinExcludeList(exclude_list)

_seen_wine_dlls = set() # Used for warning tracking in include_library()


def include_library(libname):
"""
Check if the dynamic library should be included with application or not.
"""
# For configuration phase we need to have exclude / include lists None so these checking is skipped and library gets
# included.
if exclude_list:
if exclude_list.search(libname) and not include_list.search(libname):
# Library is excluded and is not overriden by include list. It should be then excluded.
# Library is excluded and is not overriden by include list. It should be excluded.
return False
else:
# Include library.
return True
else:
# By default include library.
return True

# If we are running under Wine and the library is a Wine built-in DLL, ensure that it is always excluded. Typically,
# excluding a DLL leads to an incomplete bundle and run-time errors when the said DLL is not installed on the target
# system. However, having Wine built-in DLLs collected is even more detrimental, as they usually provide Wine's
# implementation of low-level functionality, and therefore cannot be used on actual Windows (i.e., system libraries
# from the C:\Windows\system32 directory that might end up collected due to ``_win_includes`` list; a prominent
# example are VC runtime DLLs, for which Wine provides their own implementation, unless user explicitly installs
# Microsoft's VC redistributable package in their Wine environment). Therefore, excluding the Wine built-in DLLs
# actually improves the chances of the bundle running on Windows, or at least makes the issue easier to debug by
# turning it into the "standard" missing DLL problem. Exclusion should not affect the bundle's ability to run under
# Wine itself, as the excluded DLLs are available there.
if compat.is_win_wine and compat.is_wine_dll(libname):
if libname not in _seen_wine_dlls:
logger.warning("Excluding Wine built-in DLL: %s", libname) # displayed only if DLL would have been included
rokm marked this conversation as resolved.
Show resolved Hide resolved
_seen_wine_dlls.add(libname) # display only once for each DLL
return False

return True


# Patterns for suppressing warnings about missing dynamically linked libraries
Expand Down
3 changes: 3 additions & 0 deletions news/6149.feature.rst
@@ -0,0 +1,3 @@
(Wine) Prevent collection of Wine built-in DLLs (in either PE-converted or
fake/placeholder form) when building a Windows frozen application under
Wine. Display a warning for each excluded Wine built-in DLL.