Remove hardcoded libpython binaries and add debug step
All checks were successful
build / build-linux (push) Successful in 16s

This commit is contained in:
kdusek
2025-12-07 23:15:18 +01:00
parent 308ce7768e
commit 6a1fe63684
1807 changed files with 172293 additions and 1 deletions

View File

@@ -0,0 +1,10 @@
# ------------------------------------------------------------------
# Copyright (c) 2020 PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
# ------------------------------------------------------------------

View File

@@ -0,0 +1,20 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2024, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import os
import sys
# If we collected OpenSSL modules into `ossl-modules` directory, override the OpenSSL search path by setting the
# `OPENSSL_MODULES` environment variable.
_ossl_modules_dir = os.path.join(sys._MEIPASS, 'ossl-modules')
if os.path.isdir(_ossl_modules_dir):
os.environ['OPENSSL_MODULES'] = _ossl_modules_dir
del _ossl_modules_dir

View File

@@ -0,0 +1,22 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import os
import sys
# On Mac OS X tell enchant library where to look for enchant backends (aspell, myspell, ...).
# Enchant is looking for backends in directory 'PREFIX/lib/enchant'
# Note: env. var. ENCHANT_PREFIX_DIR is implemented only in the development version:
# https://github.com/AbiWord/enchant
# https://github.com/AbiWord/enchant/pull/2
# TODO Test this rthook.
if sys.platform.startswith('darwin'):
os.environ['ENCHANT_PREFIX_DIR'] = os.path.join(sys._MEIPASS, 'enchant')

View File

@@ -0,0 +1,19 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2023, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
# Starting with v4.3.5, the `ffpyplayer` package attempts to use `site.USER_BASE` in path manipulation functions.
# As frozen application runs with disabled `site`, the value of this variable is `None`, and causes path manipulation
# functions to raise an error. As a work-around, we set `site.USER_BASE` to an empty string, which is also what the
# fake `site` module available in PyInstaller prior to v5.5 did.
import site
if site.USER_BASE is None:
site.USER_BASE = ''

View File

@@ -0,0 +1,58 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2024, PyInstaller Development Team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
# Override the findlibs.find() function to give precedence to sys._MEIPASS, followed by `ctypes.util.find_library`,
# and only then the hard-coded paths from the original implementation. The main aim here is to avoid loading libraries
# from Homebrew environment on macOS when it happens to be present at run-time and we have a bundled copy collected from
# the build system. This happens because we (try not to) modify `DYLD_LIBRARY_PATH`, and the original `findlibs.find()`
# implementation gives precedence to environment variables and several fixed/hard-coded locations, and uses
# `ctypes.util.find_library` as the final fallback...
def _pyi_rthook():
import sys
import os
import ctypes.util
# findlibs v0.1.0 broke compatibility with python < 3.10; due to incompatible typing annotation, attempting to
# import the package raises `TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'`. Gracefully
# handle this situation by making this run-time hook no-op, in order to avoid crashing the frozen program even
# if it would never end up importing/using `findlibs`.
try:
import findlibs
except TypeError:
return
_orig_find = getattr(findlibs, 'find', None)
def _pyi_find(lib_name, *args, **kwargs):
extension = findlibs.EXTENSIONS.get(sys.platform, ".so")
# First check sys._MEIPASS
fullname = os.path.join(sys._MEIPASS, "lib{}{}".format(lib_name, extension))
if os.path.isfile(fullname):
return fullname
# Fall back to `ctypes.util.find_library` (to give it precedence over hard-coded paths from original
# implementation).
lib = ctypes.util.find_library(lib_name)
if lib is not None:
return lib
# Finally, fall back to original implementation
if _orig_find is not None:
return _orig_find(lib_name, *args, **kwargs)
return None
findlibs.find = _pyi_find
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,17 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import sys
import os
import nltk
#add the path to nltk_data
nltk.data.path.insert(0, os.path.join(sys._MEIPASS, "nltk_data"))

View File

@@ -0,0 +1,32 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import os
import sys
# Installing `osgeo` Conda packages requires to set `GDAL_DATA`
is_win = sys.platform.startswith('win')
if is_win:
gdal_data = os.path.join(sys._MEIPASS, 'data', 'gdal')
if not os.path.exists(gdal_data):
gdal_data = os.path.join(sys._MEIPASS, 'Library', 'share', 'gdal')
# last attempt, check if one of the required file is in the generic folder Library/data
if not os.path.exists(os.path.join(gdal_data, 'gcs.csv')):
gdal_data = os.path.join(sys._MEIPASS, 'Library', 'data')
else:
gdal_data = os.path.join(sys._MEIPASS, 'share', 'gdal')
if os.path.exists(gdal_data):
os.environ['GDAL_DATA'] = gdal_data

View File

@@ -0,0 +1,32 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2021, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import pygraphviz
# Override pygraphviz.AGraph._which method to search for graphviz executables inside sys._MEIPASS
if hasattr(pygraphviz.AGraph, '_which'):
def _pygraphviz_override_which(self, name):
import os
import sys
import platform
program_name = name
if platform.system() == "Windows":
program_name += ".exe"
program_path = os.path.join(sys._MEIPASS, program_name)
if not os.path.isfile(program_path):
raise ValueError(f"Prog {name} not found in the PyInstaller-frozen application bundle!")
return program_path
pygraphviz.AGraph._which = _pygraphviz_override_which

View File

@@ -0,0 +1,26 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2015-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import os
import sys
# Installing `pyproj` Conda packages requires to set `PROJ_LIB`
is_win = sys.platform.startswith('win')
if is_win:
proj_data = os.path.join(sys._MEIPASS, 'Library', 'share', 'proj')
else:
proj_data = os.path.join(sys._MEIPASS, 'share', 'proj')
if os.path.exists(proj_data):
os.environ['PROJ_LIB'] = proj_data

View File

@@ -0,0 +1,52 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2022, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import sys
import os
def _setup_pyqtgraph_multiprocess_hook():
# NOTE: pyqtgraph.multiprocess spawns the sub-process using subprocess.Popen (or equivalent). This means that in
# onefile builds, the executable in subprocess will unpack itself again, into different sys._MEIPASS, because
# the _MEIPASS2 environment variable is not set (bootloader / bootstrap script cleans it up). This will make the
# argv[1] check below fail, due to different sys._MEIPASS value in the subprocess.
#
# To work around this, at the time of writing (PyInstaller 5.5), the user needs to set _MEIPASS2 environment
# variable to sys._MEIPASS before using `pyqtgraph.multiprocess` in onefile builds. And stlib's
# `multiprocessing.freeze_support` needs to be called in the entry-point program, due to `pyqtgraph.multiprocess`
# internally using stdlib's `multiprocessing` primitives.
if len(sys.argv) == 2 and sys.argv[1] == os.path.join(sys._MEIPASS, 'pyqtgraph', 'multiprocess', 'bootstrap.py'):
# Load as module; this requires --hiddenimport pyqtgraph.multiprocess.bootstrap
try:
import importlib.util
spec = importlib.util.find_spec("pyqtgraph.multiprocess.bootstrap")
bootstrap_co = spec.loader.get_code("pyqtgraph.multiprocess.bootstrap")
except Exception:
bootstrap_co = None
if bootstrap_co:
exec(bootstrap_co)
sys.exit(0)
# Load from file; requires pyqtgraph/multiprocess/bootstrap.py collected as data file
# This is obsolete for PyInstaller >= v6.10.0
bootstrap_file = os.path.join(sys._MEIPASS, 'pyqtgraph', 'multiprocess', 'bootstrap.py')
if os.path.isfile(bootstrap_file):
with open(bootstrap_file, 'r') as fp:
bootstrap_code = fp.read()
exec(bootstrap_code)
sys.exit(0)
raise RuntimeError("Could not find pyqtgraph.multiprocess bootstrap code or script!")
_setup_pyqtgraph_multiprocess_hook()
del _setup_pyqtgraph_multiprocess_hook

View File

@@ -0,0 +1,24 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2022, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
# Unfortunately, __import_pywin32_system_module__ from pywintypes module assumes that in a frozen application, the
# pythoncom3X.dll and pywintypes3X.dll that are normally found in site-packages/pywin32_system32, are located
# directly in the sys.path, without bothering to check first if they are actually available in the standard location.
# This obviously runs afoul of our attempts at preserving the directory layout and placing them in the pywin32_system32
# sub-directory instead of the top-level application directory. So as a work-around, add the sub-directory to sys.path
# to keep pywintypes happy...
import sys
import os
pywin32_system32_path = os.path.join(sys._MEIPASS, 'pywin32_system32')
if os.path.isdir(pywin32_system32_path) and pywin32_system32_path not in sys.path:
sys.path.append(pywin32_system32_path)
del pywin32_system32_path

View File

@@ -0,0 +1,24 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2022, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
# Unfortunately, __import_pywin32_system_module__ from pywintypes module assumes that in a frozen application, the
# pythoncom3X.dll and pywintypes3X.dll that are normally found in site-packages/pywin32_system32, are located
# directly in the sys.path, without bothering to check first if they are actually available in the standard location.
# This obviously runs afoul of our attempts at preserving the directory layout and placing them in the pywin32_system32
# sub-directory instead of the top-level application directory. So as a work-around, add the sub-directory to sys.path
# to keep pywintypes happy...
import sys
import os
pywin32_system32_path = os.path.join(sys._MEIPASS, 'pywin32_system32')
if os.path.isdir(pywin32_system32_path) and pywin32_system32_path not in sys.path:
sys.path.append(pywin32_system32_path)
del pywin32_system32_path

View File

@@ -0,0 +1,53 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2023, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
def _pyi_rthook():
import sys
# `tensorflow` versions prior to 2.3.0 attempt to use `site.USER_SITE` in path/string manipulation functions.
# As frozen application runs with disabled `site`, the value of this variable is `None`, and causes path/string
# manipulation functions to raise an error. As a work-around, we set `site.USER_SITE` to an empty string, which is
# also what the fake `site` module available in PyInstaller prior to v5.5 did.
import site
if site.USER_SITE is None:
site.USER_SITE = ''
# The issue described about with site.USER_SITE being None has largely been resolved in contemporary `tensorflow`
# versions, which now check that `site.ENABLE_USER_SITE` is set and that `site.USER_SITE` is not None before
# trying to use it.
#
# However, `tensorflow` will attempt to search and load its plugins only if it believes that it is running from
# "a pip-based installation" - if the package's location is rooted in one of the "site-packages" directories. See
# https://github.com/tensorflow/tensorflow/blob/6887368d6d46223f460358323c4b76d61d1558a8/tensorflow/api_template.__init__.py#L110C76-L156
# Unfortunately, they "cleverly" infer the module's location via `inspect.getfile(inspect.currentframe())`, which
# in the frozen application returns anonymized relative source file name (`tensorflow/__init__.py`) - so we need one
# of the "site directories" to be just "tensorflow" (to fool the `_running_from_pip_package()` check), and we also
# need `sys._MEIPASS` to be among them (to load the plugins from the actual `sys._MEIPASS/tensorflow-plugins`).
# Therefore, we monkey-patch `site.getsitepackages` to add those two entries to the list of "site directories".
_orig_getsitepackages = getattr(site, 'getsitepackages', None)
def _pyi_getsitepackages():
return [
sys._MEIPASS,
"tensorflow",
*(_orig_getsitepackages() if _orig_getsitepackages is not None else []),
]
site.getsitepackages = _pyi_getsitepackages
# NOTE: instead of the above override, we could also set TF_PLUGGABLE_DEVICE_LIBRARY_PATH, but that works only
# for tensorflow >= 2.12.
_pyi_rthook()
del _pyi_rthook

View File

@@ -0,0 +1,25 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
# 'traitlets' uses module 'inspect' from default Python library to inspect
# source code of modules. However, frozen app does not contain source code
# of Python modules.
#
# hook-IPython depends on module 'traitlets'.
import traitlets.traitlets
def _disabled_deprecation_warnings(method, cls, method_name, msg):
pass
traitlets.traitlets._deprecated_method = _disabled_deprecation_warnings

View File

@@ -0,0 +1,85 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2020, PyInstaller Development Team.
#
# This file is distributed under the terms of the Apache License 2.0
#
# The full license is available in LICENSE, distributed with
# this software.
#
# SPDX-License-Identifier: Apache-2.0
#-----------------------------------------------------------------------------
import ctypes
import glob
import os
import sys
# Pyusb changed these libusb module names in commit 2082e7.
try:
import usb.backend.libusb10 as libusb10
except ImportError:
import usb.backend.libusb1 as libusb10
try:
import usb.backend.libusb01 as libusb01
except ImportError:
import usb.backend.libusb0 as libusb01
import usb.backend.openusb as openusb
def get_load_func(type, candidates):
def _load_library(find_library=None):
exec_path = sys._MEIPASS
library = None
for candidate in candidates:
# Find a list of library files that match 'candidate'.
if find_library:
# Caller provides a function that lookup lib path by candidate name.
lib_path = find_library(candidate)
libs = [lib_path] if lib_path else []
else:
# No find_library callback function, we look at the default location.
if os.name == 'posix' and sys.platform == 'darwin':
libs = glob.glob("%s/%s*.dylib*" % (exec_path, candidate))
elif sys.platform == 'win32' or sys.platform == 'cygwin':
libs = glob.glob("%s\\%s*.dll" % (exec_path, candidate))
else:
libs = glob.glob("%s/%s*.so*" % (exec_path, candidate))
# Do linker's path lookup work to force load bundled copy.
for libname in libs:
try:
# NOTE: libusb01 is using CDLL under win32.
# (see usb.backends.libusb01)
if sys.platform == 'win32' and type != 'libusb01':
library = ctypes.WinDLL(libname)
else:
library = ctypes.CDLL(libname)
if library is not None:
break
except OSError:
library = None
if library is not None:
break
else:
raise OSError('USB library could not be found')
if type == 'libusb10':
if not hasattr(library, 'libusb_init'):
raise OSError('USB library could not be found')
return library
return _load_library
# NOTE: Need to keep in sync with future PyUSB updates.
if sys.platform == 'cygwin':
libusb10._load_library = get_load_func('libusb10', ('cygusb-1.0', ))
libusb01._load_library = get_load_func('libusb01', ('cygusb0', ))
openusb._load_library = get_load_func('openusb', ('openusb', ))
else:
libusb10._load_library = get_load_func('libusb10', ('usb-1.0', 'libusb-1.0', 'usb'))
libusb01._load_library = get_load_func('libusb01', ('usb-0.1', 'usb', 'libusb0', 'libusb'))
openusb._load_library = get_load_func('openusb', ('openusb', ))