Todo: 集成多平台 解决因SaiNiu线程抢占资源问题 本地提交测试环境打包 和 正式打包脚本与正式环境打包bat 提交Python32环境包 改进多日志文件生成情况修改打包日志细节
This commit is contained in:
@@ -0,0 +1 @@
|
||||
#
|
||||
@@ -0,0 +1,37 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
# The directory names must match TCL_ROOTNAME and TK_ROOTNAME constants defined in `PyInstaller.utils.hooks.tcl_tk`.
|
||||
tcldir = os.path.join(sys._MEIPASS, '_tcl_data')
|
||||
tkdir = os.path.join(sys._MEIPASS, '_tk_data')
|
||||
|
||||
# Notify "tkinter" of data directories. On macOS, we do not collect data directories if system Tcl/Tk framework is
|
||||
# used. On other OSes, we always collect them, so their absence is considered an error.
|
||||
is_darwin = sys.platform == 'darwin'
|
||||
|
||||
if os.path.isdir(tcldir):
|
||||
os.environ["TCL_LIBRARY"] = tcldir
|
||||
elif not is_darwin:
|
||||
raise FileNotFoundError('Tcl data directory "%s" not found.' % tcldir)
|
||||
|
||||
if os.path.isdir(tkdir):
|
||||
os.environ["TK_LIBRARY"] = tkdir
|
||||
elif not is_darwin:
|
||||
raise FileNotFoundError('Tk data directory "%s" not found.' % tkdir)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,34 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# This Django rthook was tested with Django 1.8.3.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import django.utils.autoreload
|
||||
|
||||
_old_restart_with_reloader = django.utils.autoreload.restart_with_reloader
|
||||
|
||||
def _restart_with_reloader(*args):
|
||||
import sys
|
||||
a0 = sys.argv.pop(0)
|
||||
try:
|
||||
return _old_restart_with_reloader(*args)
|
||||
finally:
|
||||
sys.argv.insert(0, a0)
|
||||
|
||||
# Override restart_with_reloader() function, otherwise the app might complain that some commands do not exist;
|
||||
# e.g., runserver.
|
||||
django.utils.autoreload.restart_with_reloader = _restart_with_reloader
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,41 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import atexit
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
pixbuf_file = os.path.join(sys._MEIPASS, 'lib', 'gdk-pixbuf', 'loaders.cache')
|
||||
|
||||
# If we are not on Windows, we need to rewrite the cache -> we rewrite on macOS to support --onefile mode
|
||||
if os.path.exists(pixbuf_file) and sys.platform != 'win32':
|
||||
with open(pixbuf_file, 'rb') as fp:
|
||||
contents = fp.read()
|
||||
|
||||
# Create a temporary file with the cache and cleverly replace the prefix we injected with the actual path.
|
||||
fd, pixbuf_file = tempfile.mkstemp()
|
||||
with os.fdopen(fd, 'wb') as fp:
|
||||
libpath = os.path.join(sys._MEIPASS, 'lib').encode('utf-8')
|
||||
fp.write(contents.replace(b'@executable_path/lib', libpath))
|
||||
|
||||
try:
|
||||
atexit.register(os.unlink, pixbuf_file)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
os.environ['GDK_PIXBUF_MODULE_FILE'] = pixbuf_file
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,21 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ['GI_TYPELIB_PATH'] = os.path.join(sys._MEIPASS, 'gi_typelibs')
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,21 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ['GIO_MODULE_DIR'] = os.path.join(sys._MEIPASS, 'gio_modules')
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,37 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Prepend the frozen application's data dir to XDG_DATA_DIRS. We need to avoid overwriting the existing paths in
|
||||
# order to allow the frozen application to run system-installed applications (for example, launch a web browser via
|
||||
# the webbrowser module on Linux). Should the user desire complete isolation of the frozen application from the
|
||||
# system, they need to clean up XDG_DATA_DIRS at the start of their program (i.e., remove all entries but first).
|
||||
pyi_data_dir = os.path.join(sys._MEIPASS, 'share')
|
||||
|
||||
xdg_data_dirs = os.environ.get('XDG_DATA_DIRS', None)
|
||||
if xdg_data_dirs:
|
||||
if pyi_data_dir not in xdg_data_dirs:
|
||||
xdg_data_dirs = pyi_data_dir + os.pathsep + xdg_data_dirs
|
||||
else:
|
||||
xdg_data_dirs = pyi_data_dir
|
||||
os.environ['XDG_DATA_DIRS'] = xdg_data_dirs
|
||||
|
||||
# Cleanup aux variables
|
||||
del xdg_data_dirs
|
||||
del pyi_data_dir
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,32 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Without this environment variable set to 'no' importing 'gst' causes 100% CPU load. (Tested on macOS.)
|
||||
os.environ['GST_REGISTRY_FORK'] = 'no'
|
||||
|
||||
gst_plugin_paths = [sys._MEIPASS, os.path.join(sys._MEIPASS, 'gst-plugins')]
|
||||
os.environ['GST_PLUGIN_PATH'] = os.pathsep.join(gst_plugin_paths)
|
||||
|
||||
# Prevent permission issues on Windows
|
||||
os.environ['GST_REGISTRY'] = os.path.join(sys._MEIPASS, 'registry.bin')
|
||||
|
||||
# Only use packaged plugins to prevent GStreamer from crashing when it finds plugins from another version which are
|
||||
# installed system wide.
|
||||
os.environ['GST_PLUGIN_SYSTEM_PATH'] = ''
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,27 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ['GTK_DATA_PREFIX'] = sys._MEIPASS
|
||||
os.environ['GTK_EXE_PREFIX'] = sys._MEIPASS
|
||||
os.environ['GTK_PATH'] = sys._MEIPASS
|
||||
|
||||
# Include these here, as GTK will import pango automatically.
|
||||
os.environ['PANGO_LIBDIR'] = sys._MEIPASS
|
||||
os.environ['PANGO_SYSCONFDIR'] = os.path.join(sys._MEIPASS, 'etc') # TODO?
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,99 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
# Use sys._MEIPASS with normalized path component separator. This is necessary on some platforms (i.e., msys2/mingw
|
||||
# python on Windows), because we use string comparisons on the paths.
|
||||
SYS_PREFIX = os.path.normpath(sys._MEIPASS)
|
||||
BASE_LIBRARY = os.path.join(SYS_PREFIX, "base_library.zip")
|
||||
|
||||
# Obtain the list of modules in base_library.zip, so we can use it in our `_pyi_getsourcefile` implementation.
|
||||
def _get_base_library_files(filename):
|
||||
# base_library.zip might not exit
|
||||
if not os.path.isfile(filename):
|
||||
return set()
|
||||
|
||||
with zipfile.ZipFile(filename, 'r') as zf:
|
||||
namelist = zf.namelist()
|
||||
|
||||
return set(os.path.normpath(entry) for entry in namelist)
|
||||
|
||||
base_library_files = _get_base_library_files(BASE_LIBRARY)
|
||||
|
||||
# Provide custom implementation of inspect.getsourcefile() for frozen applications that properly resolves relative
|
||||
# filenames obtained from object (e.g., inspect stack-frames). See #5963.
|
||||
#
|
||||
# Although we are overriding `inspect.getsourcefile` function, we are NOT trying to resolve source file here!
|
||||
# The main purpose of this implementation is to properly resolve relative file names obtained from `co_filename`
|
||||
# attribute of code objects (which are, in turn, obtained from in turn are obtained from `frame` and `traceback`
|
||||
# objects). PyInstaller strips absolute paths from `co_filename` when collecting modules, as the original absolute
|
||||
# paths are not portable/relocatable anyway. The `inspect` module tries to look up the module that corresponds to
|
||||
# the code object by comparing modules' `__file__` attribute to the value of `co_filename`. Therefore, our override
|
||||
# needs to resolve the relative file names (usually having a .py suffix) into absolute module names (which, in the
|
||||
# frozen application, usually have .pyc suffix).
|
||||
#
|
||||
# The `inspect` module retrieves the actual source code using `linecache.getlines()`. If the passed source filename
|
||||
# does not exist, the underlying implementation end up resolving the module, and obtains the source via loader's
|
||||
# `get_source` method. So for modules in the PYZ archive, it ends up calling `get_source` implementation on our
|
||||
# `PyiFrozenLoader`. For modules in `base_library.zip`, it ends up calling `get_source` on python's own
|
||||
# `zipimport.zipimporter`; to properly handle out-of-zip source files, we therefore need to monkey-patch
|
||||
# `get_source` with our own override that translates the in-zip .pyc filename into out-of-zip .py file location
|
||||
# and loads the source (this override is done in `pyimod02_importers` module).
|
||||
#
|
||||
# The above-described fallback takes place if the .pyc file does not exist on filesystem - if this ever becomes
|
||||
# a problem, we could consider monkey-patching `linecache.updatecache` (and possibly `checkcache`) to translate
|
||||
# .pyc paths in `sys._MEIPASS` and `base_library.zip` into .py paths in `sys._MEIPASS` before calling the original
|
||||
# implementation.
|
||||
_orig_inspect_getsourcefile = inspect.getsourcefile
|
||||
|
||||
def _pyi_getsourcefile(object):
|
||||
filename = inspect.getfile(object)
|
||||
filename = os.path.normpath(filename) # Ensure path component separators are normalized.
|
||||
if not os.path.isabs(filename):
|
||||
# Check if given filename matches the basename of __main__'s __file__.
|
||||
main_file = getattr(sys.modules['__main__'], '__file__', None)
|
||||
if main_file and filename == os.path.basename(main_file):
|
||||
return main_file
|
||||
|
||||
# If the relative filename does not correspond to the frozen entry-point script, convert it to the absolute
|
||||
# path in either `sys._MEIPASS/base_library.zip` or `sys._MEIPASS`, whichever applicable.
|
||||
#
|
||||
# The modules in `sys._MEIPASS/base_library.zip` are handled by python's `zipimport.zipimporter`, and have
|
||||
# their __file__ attribute point to the .pyc file in the archive. So we match the behavior, in order to
|
||||
# facilitate matching via __file__ attribute and use of loader's `get_source`, as per the earlier comment
|
||||
# block.
|
||||
#
|
||||
# The modules in PYZ archive are handled by our `PyFrozenLoader`, which now sets the module's __file__
|
||||
# attribute to point to where .py files would be. Therefore, we can directly merge SYS_PREFIX and filename
|
||||
# (and if the source .py file exists, it will be loaded directly from filename, without the intermediate
|
||||
# loader look-up).
|
||||
pyc_filename = filename + 'c'
|
||||
if pyc_filename in base_library_files:
|
||||
return os.path.normpath(os.path.join(BASE_LIBRARY, pyc_filename))
|
||||
return os.path.normpath(os.path.join(SYS_PREFIX, filename))
|
||||
elif filename.startswith(SYS_PREFIX):
|
||||
# If filename is already an absolute file path pointing into application's top-level directory, return it
|
||||
# as-is and prevent any further processing.
|
||||
return filename
|
||||
# Use original implementation as a fallback.
|
||||
return _orig_inspect_getsourcefile(object)
|
||||
|
||||
inspect.getsourcefile = _pyi_getsourcefile
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,24 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
root = os.path.join(sys._MEIPASS, 'kivy_install')
|
||||
|
||||
os.environ['KIVY_DATA_DIR'] = os.path.join(root, 'data')
|
||||
os.environ['KIVY_MODULES_DIR'] = os.path.join(root, 'modules')
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,46 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# matplotlib will create $HOME/.matplotlib folder in user's home directory. In this directory there is fontList.cache
|
||||
# file which lists paths to matplotlib fonts.
|
||||
#
|
||||
# When you run your onefile exe for the first time it's extracted to for example "_MEIxxxxx" temp directory and
|
||||
# fontList.cache file is created with fonts paths pointing to this directory.
|
||||
#
|
||||
# Second time you run your exe new directory is created "_MEIyyyyy" but fontList.cache file still points to previous
|
||||
# directory which was deleted. And then you will get error like:
|
||||
#
|
||||
# RuntimeError: Could not open facefile
|
||||
#
|
||||
# We need to force matplotlib to recreate config directory every time you run your app.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import atexit
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import _pyi_rth_utils.tempfile # PyInstaller's run-time hook utilities module
|
||||
|
||||
# Isolate matplotlib's config dir into temporary directory.
|
||||
# Use our replacement for `tempfile.mkdtemp` function that properly restricts access to directory on all platforms.
|
||||
configdir = _pyi_rth_utils.tempfile.secure_mkdtemp()
|
||||
os.environ['MPLCONFIGDIR'] = configdir
|
||||
|
||||
try:
|
||||
# Remove temp directory at application exit and ignore any errors.
|
||||
atexit.register(shutil.rmtree, configdir, ignore_errors=True)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,55 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2017-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import sys
|
||||
|
||||
import multiprocessing
|
||||
import multiprocessing.spawn
|
||||
|
||||
from subprocess import _args_from_interpreter_flags
|
||||
|
||||
# Prevent `spawn` from trying to read `__main__` in from the main script
|
||||
multiprocessing.process.ORIGINAL_DIR = None
|
||||
|
||||
def _freeze_support():
|
||||
# We want to catch the two processes that are spawned by the multiprocessing code:
|
||||
# - the semaphore tracker, which cleans up named semaphores in the `spawn` multiprocessing mode
|
||||
# - the fork server, which keeps track of worker processes in the `forkserver` mode.
|
||||
# Both of these processes are started by spawning a new copy of the running executable, passing it the flags
|
||||
# from `_args_from_interpreter_flags` and then "-c" and an import statement.
|
||||
# Look for those flags and the import statement, then `exec()` the code ourselves.
|
||||
|
||||
if (
|
||||
len(sys.argv) >= 2 and sys.argv[-2] == '-c' and sys.argv[-1].startswith(
|
||||
('from multiprocessing.resource_tracker import main', 'from multiprocessing.forkserver import main')
|
||||
) and set(sys.argv[1:-2]) == set(_args_from_interpreter_flags())
|
||||
):
|
||||
exec(sys.argv[-1])
|
||||
sys.exit()
|
||||
|
||||
if multiprocessing.spawn.is_forking(sys.argv):
|
||||
kwds = {}
|
||||
for arg in sys.argv[2:]:
|
||||
name, value = arg.split('=')
|
||||
if value == 'None':
|
||||
kwds[name] = None
|
||||
else:
|
||||
kwds[name] = int(value)
|
||||
multiprocessing.spawn.spawn_main(**kwds)
|
||||
sys.exit()
|
||||
|
||||
multiprocessing.freeze_support = multiprocessing.spawn.freeze_support = _freeze_support
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,178 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# To make pkg_resources work with frozen modules, we need to set the 'Provider' class for PyiFrozenLoader.
|
||||
# This class decides where to look for resources and other stuff.
|
||||
#
|
||||
# 'pkg_resources.NullProvider' is dedicated to abitrary PEP302 loaders, such as our PyiFrozenLoader. It uses method
|
||||
# __loader__.get_data() in methods pkg_resources.resource_string() and pkg_resources.resource_stream().
|
||||
#
|
||||
# We provide PyiFrozenProvider, which subclasses the NullProvider and implements _has(), _isdir(), and _listdir()
|
||||
# methods, which are needed for pkg_resources.resource_exists(), resource_isdir(), and resource_listdir() to work. We
|
||||
# cannot use the DefaultProvider, because it provides filesystem-only implementations (and overrides _get() with a
|
||||
# filesystem-only one), whereas our provider needs to also support embedded resources.
|
||||
#
|
||||
# The PyiFrozenProvider allows querying/listing both PYZ-embedded and on-filesystem resources in a frozen package. The
|
||||
# results are typically combined for both types of resources (e.g., when listing a directory or checking whether a
|
||||
# resource exists). When the order of precedence matters, the PYZ-embedded resources take precedence over the
|
||||
# on-filesystem ones, to keep the behavior consistent with the actual file content retrieval via _get() method (which in
|
||||
# turn uses PyiFrozenLoader's get_data() method). For example, when checking whether a resource is a directory via
|
||||
# _isdir(), a PYZ-embedded file will take precedence over a potential on-filesystem directory. Also, in contrast to
|
||||
# unfrozen packages, the frozen ones do not contain source .py files, which are therefore absent from content listings.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
category=UserWarning,
|
||||
message="pkg_resources is deprecated",
|
||||
)
|
||||
import pkg_resources
|
||||
|
||||
import pyimod02_importers # PyInstaller's bootstrap module
|
||||
|
||||
SYS_PREFIX = pathlib.PurePath(sys._MEIPASS)
|
||||
|
||||
class _TocFilesystem:
|
||||
"""
|
||||
A prefix tree implementation for embedded filesystem reconstruction.
|
||||
|
||||
NOTE: as of PyInstaller 6.0, the embedded PYZ archive cannot contain data files anymore. Instead, it contains
|
||||
only .pyc modules - which are by design not returned by `PyiFrozenProvider`. So this implementation has been
|
||||
reduced to supporting only directories implied by collected packages.
|
||||
"""
|
||||
def __init__(self, tree_node):
|
||||
self._tree = tree_node
|
||||
|
||||
def _get_tree_node(self, path):
|
||||
path = pathlib.PurePath(path)
|
||||
current = self._tree
|
||||
for component in path.parts:
|
||||
if component not in current:
|
||||
return None
|
||||
current = current[component]
|
||||
return current
|
||||
|
||||
def path_exists(self, path):
|
||||
node = self._get_tree_node(path)
|
||||
return isinstance(node, dict) # Directory only
|
||||
|
||||
def path_isdir(self, path):
|
||||
node = self._get_tree_node(path)
|
||||
return isinstance(node, dict) # Directory only
|
||||
|
||||
def path_listdir(self, path):
|
||||
node = self._get_tree_node(path)
|
||||
if not isinstance(node, dict):
|
||||
return [] # Non-existent or file
|
||||
# Return only sub-directories
|
||||
return [entry_name for entry_name, entry_data in node.items() if isinstance(entry_data, dict)]
|
||||
|
||||
class PyiFrozenProvider(pkg_resources.NullProvider):
|
||||
"""
|
||||
Custom pkg_resources provider for PyiFrozenLoader.
|
||||
"""
|
||||
def __init__(self, module):
|
||||
super().__init__(module)
|
||||
|
||||
# Get top-level path; if "module" corresponds to a package, we need the path to the package itself.
|
||||
# If "module" is a submodule in a package, we need the path to the parent package.
|
||||
#
|
||||
# This is equivalent to `pkg_resources.NullProvider.module_path`, except we construct a `pathlib.PurePath`
|
||||
# for easier manipulation.
|
||||
#
|
||||
# NOTE: the path is NOT resolved for symbolic links, as neither are paths that are passed by `pkg_resources`
|
||||
# to `_has`, `_isdir`, `_listdir` (they are all anchored to `module_path`, which in turn is just
|
||||
# `os.path.dirname(module.__file__)`. As `__file__` returned by `PyiFrozenLoader` is always anchored to
|
||||
# `sys._MEIPASS`, we do not have to worry about cross-linked directories in macOS .app bundles, where the
|
||||
# resolved `__file__` could be either in the `Contents/Frameworks` directory (the "true" `sys._MEIPASS`), or
|
||||
# in the `Contents/Resources` directory due to cross-linking.
|
||||
self._pkg_path = pathlib.PurePath(module.__file__).parent
|
||||
|
||||
# Construct _TocFilesystem on top of pre-computed prefix tree provided by pyimod02_importers.
|
||||
self.embedded_tree = _TocFilesystem(pyimod02_importers.get_pyz_toc_tree())
|
||||
|
||||
def _normalize_path(self, path):
|
||||
# Avoid using `Path.resolve`, because it resolves symlinks. This is undesirable, because the pure path in
|
||||
# `self._pkg_path` does not have symlinks resolved, so comparison between the two would be faulty. Instead,
|
||||
# use `os.path.normpath` to normalize the path and get rid of any '..' elements (the path itself should
|
||||
# already be absolute).
|
||||
return pathlib.Path(os.path.normpath(path))
|
||||
|
||||
def _is_relative_to_package(self, path):
|
||||
return path == self._pkg_path or self._pkg_path in path.parents
|
||||
|
||||
def _has(self, path):
|
||||
# Prevent access outside the package.
|
||||
path = self._normalize_path(path)
|
||||
if not self._is_relative_to_package(path):
|
||||
return False
|
||||
|
||||
# Check the filesystem first to avoid unnecessarily computing the relative path...
|
||||
if path.exists():
|
||||
return True
|
||||
rel_path = path.relative_to(SYS_PREFIX)
|
||||
return self.embedded_tree.path_exists(rel_path)
|
||||
|
||||
def _isdir(self, path):
|
||||
# Prevent access outside the package.
|
||||
path = self._normalize_path(path)
|
||||
if not self._is_relative_to_package(path):
|
||||
return False
|
||||
|
||||
# Embedded resources have precedence over filesystem...
|
||||
rel_path = path.relative_to(SYS_PREFIX)
|
||||
node = self.embedded_tree._get_tree_node(rel_path)
|
||||
if node is None:
|
||||
return path.is_dir() # No match found; try the filesystem.
|
||||
else:
|
||||
# str = file, dict = directory
|
||||
return not isinstance(node, str)
|
||||
|
||||
def _listdir(self, path):
|
||||
# Prevent access outside the package.
|
||||
path = self._normalize_path(path)
|
||||
if not self._is_relative_to_package(path):
|
||||
return []
|
||||
|
||||
# Relative path for searching embedded resources.
|
||||
rel_path = path.relative_to(SYS_PREFIX)
|
||||
# List content from embedded filesystem...
|
||||
content = self.embedded_tree.path_listdir(rel_path)
|
||||
# ... as well as the actual one.
|
||||
if path.is_dir():
|
||||
# Use os.listdir() to avoid having to convert Path objects to strings... Also make sure to de-duplicate
|
||||
# the results.
|
||||
path = str(path) # not is_py36
|
||||
content = list(set(content + os.listdir(path)))
|
||||
return content
|
||||
|
||||
pkg_resources.register_loader_type(pyimod02_importers.PyiFrozenLoader, PyiFrozenProvider)
|
||||
|
||||
# With our PyiFrozenFinder now being a path entry finder, it effectively replaces python's FileFinder. So we need
|
||||
# to register it with `pkg_resources.find_on_path` to allow metadata to be found on filesystem.
|
||||
pkg_resources.register_finder(pyimod02_importers.PyiFrozenFinder, pkg_resources.find_on_path)
|
||||
|
||||
# For the above change to fully take effect, we need to re-initialize pkg_resources's master working set (since the
|
||||
# original one was built with assumption that sys.path entries are handled by python's FileFinder).
|
||||
# See https://github.com/pypa/setuptools/issues/373
|
||||
if hasattr(pkg_resources, '_initialize_master_working_set'):
|
||||
pkg_resources._initialize_master_working_set()
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,64 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# The run-time hook provides a custom module iteration function for our PyiFrozenFinder, which allows
|
||||
# `pkgutil.iter_modules()` to return entries for modules that are embedded in the PYZ archive. The non-embedded modules
|
||||
# (binary extensions, modules collected as only source .py files, etc.) are enumerated using the `fallback_finder`
|
||||
# provided by `PyiFrozenFinder` (which typically would be the python's `FileFinder`).
|
||||
def _pyi_rthook():
|
||||
import pkgutil
|
||||
|
||||
import pyimod02_importers # PyInstaller's bootstrap module
|
||||
|
||||
# This could, in fact, be implemented as `iter_modules()` method of the `PyiFrozenFinder`. However, we want to
|
||||
# avoid importing `pkgutil` in that bootstrap module (i.e., for the `pkgutil.iter_importer_modules()` call on the
|
||||
# fallback finder).
|
||||
def _iter_pyi_frozen_finder_modules(finder, prefix=''):
|
||||
# Fetch PYZ TOC tree from pyimod02_importers
|
||||
pyz_toc_tree = pyimod02_importers.get_pyz_toc_tree()
|
||||
|
||||
# Finder has already pre-computed the package prefix implied by the search path. Use it to find the starting
|
||||
# node in the prefix tree.
|
||||
if finder._pyz_entry_prefix:
|
||||
pkg_name_parts = finder._pyz_entry_prefix.split('.')
|
||||
else:
|
||||
pkg_name_parts = []
|
||||
|
||||
tree_node = pyz_toc_tree
|
||||
for pkg_name_part in pkg_name_parts:
|
||||
tree_node = tree_node.get(pkg_name_part)
|
||||
if not isinstance(tree_node, dict):
|
||||
# This check handles two cases:
|
||||
# a) path does not exist (`tree_node` is `None`)
|
||||
# b) path corresponds to a module instead of a package (`tree_node` is a leaf node (`str`)).
|
||||
tree_node = {}
|
||||
break
|
||||
|
||||
# Dump the contents of the tree node.
|
||||
for entry_name, entry_data in tree_node.items():
|
||||
is_pkg = isinstance(entry_data, dict)
|
||||
yield prefix + entry_name, is_pkg
|
||||
|
||||
# If our finder has a fall-back finder available, iterate its modules as well. By using the public
|
||||
# `fallback_finder` attribute, we force creation of the fallback finder as necessary.
|
||||
# NOTE: we do not care about potential duplicates here, because `pkgutil.iter_modules()` itself
|
||||
# keeps track of yielded names for purposes of de-duplication.
|
||||
if finder.fallback_finder is not None:
|
||||
yield from pkgutil.iter_importer_modules(finder.fallback_finder, prefix)
|
||||
|
||||
pkgutil.iter_importer_modules.register(
|
||||
pyimod02_importers.PyiFrozenFinder,
|
||||
_iter_pyi_frozen_finder_modules,
|
||||
)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,68 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2014-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# The path to Qt's components may not default to the wheel layout for self-compiled PyQt5 installations. Mandate the
|
||||
# wheel layout. See ``utils/hooks/qt.py`` for more details.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
|
||||
from _pyi_rth_utils import qt as qt_rth_utils
|
||||
|
||||
# Ensure this is the only Qt bindings package in the application.
|
||||
qt_rth_utils.ensure_single_qt_bindings_package("PyQt5")
|
||||
|
||||
# Try PyQt5 5.15.4-style path first...
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt5')
|
||||
if not os.path.isdir(pyqt_path):
|
||||
# ... and fall back to the older version
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt')
|
||||
|
||||
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
|
||||
|
||||
if is_macos_app_bundle:
|
||||
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
|
||||
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
|
||||
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
|
||||
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
|
||||
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
|
||||
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
|
||||
pyqt_path_res = os.path.normpath(
|
||||
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
|
||||
)
|
||||
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
|
||||
os.path.join(pyqt_path_res, 'qml'),
|
||||
os.path.join(pyqt_path, 'qml'),
|
||||
])
|
||||
else:
|
||||
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
|
||||
|
||||
# Back in the day, this was required because PyQt5 5.12.3 explicitly checked that `Qt5Core.dll` was in `PATH`
|
||||
# (see #4293), and contemporary PyInstaller versions collected that DLL to `sys._MEIPASS`.
|
||||
#
|
||||
# Nowadays, we add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might
|
||||
# have been collected there (i.e., when they were not shipped with the package, and were collected from an external
|
||||
# location).
|
||||
if sys.platform.startswith('win'):
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
|
||||
|
||||
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
|
||||
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
|
||||
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
|
||||
# initialization (for example, conda-installed packages), try to perform this step ourselves.
|
||||
qt_rth_utils.create_embedded_qt_conf("PyQt5", pyqt_path)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,70 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# The path to Qt's components may not default to the wheel layout for self-compiled PyQt6 installations. Mandate the
|
||||
# wheel layout. See ``utils/hooks/qt.py`` for more details.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
|
||||
from _pyi_rth_utils import qt as qt_rth_utils
|
||||
|
||||
# Ensure this is the only Qt bindings package in the application.
|
||||
qt_rth_utils.ensure_single_qt_bindings_package("PyQt6")
|
||||
|
||||
# Try PyQt6 6.0.3-style path first...
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt6', 'Qt6')
|
||||
if not os.path.isdir(pyqt_path):
|
||||
# ... and fall back to the older version.
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PyQt6', 'Qt')
|
||||
|
||||
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
|
||||
|
||||
if is_macos_app_bundle:
|
||||
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
|
||||
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
|
||||
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
|
||||
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
|
||||
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
|
||||
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
|
||||
pyqt_path_res = os.path.normpath(
|
||||
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
|
||||
)
|
||||
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
|
||||
os.path.join(pyqt_path_res, 'qml'),
|
||||
os.path.join(pyqt_path, 'qml'),
|
||||
])
|
||||
else:
|
||||
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
|
||||
|
||||
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
|
||||
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
|
||||
if sys.platform.startswith('win'):
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
|
||||
|
||||
# For macOS POSIX builds, we need to add `sys._MEIPASS` to `DYLD_LIBRARY_PATH` so that QtNetwork can discover
|
||||
# OpenSSL dynamic libraries for its `openssl` TLS backend. This also prevents fallback to external locations, such
|
||||
# as Homebrew. For .app bundles, this is unnecessary because `QtNetwork` explicitly searches `Contents/Frameworks`.
|
||||
if sys.platform == 'darwin' and not is_macos_app_bundle:
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'DYLD_LIBRARY_PATH')
|
||||
|
||||
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
|
||||
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
|
||||
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
|
||||
# initialization (for example, conda-installed packages), try to perform this step ourselves.
|
||||
qt_rth_utils.create_embedded_qt_conf("PyQt6", pyqt_path)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,63 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# The path to Qt's components may not default to the wheel layout for self-compiled PySide2 installations. Mandate the
|
||||
# wheel layout. See ``utils/hooks/qt.py`` for more details.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
|
||||
from _pyi_rth_utils import qt as qt_rth_utils
|
||||
|
||||
# Ensure this is the only Qt bindings package in the application.
|
||||
qt_rth_utils.ensure_single_qt_bindings_package("PySide2")
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PySide2')
|
||||
else:
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PySide2', 'Qt')
|
||||
|
||||
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
|
||||
|
||||
if is_macos_app_bundle:
|
||||
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
|
||||
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
|
||||
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
|
||||
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
|
||||
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
|
||||
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
|
||||
pyqt_path_res = os.path.normpath(
|
||||
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
|
||||
)
|
||||
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
|
||||
os.path.join(pyqt_path_res, 'qml'),
|
||||
os.path.join(pyqt_path, 'qml'),
|
||||
])
|
||||
else:
|
||||
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
|
||||
|
||||
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
|
||||
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
|
||||
if sys.platform.startswith('win'):
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
|
||||
|
||||
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
|
||||
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
|
||||
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
|
||||
# initialization (for example, conda-installed packages), try to perform this step ourselves.
|
||||
qt_rth_utils.create_embedded_qt_conf("PySide2", pyqt_path)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,69 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2021-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# The path to Qt's components may not default to the wheel layout for self-compiled PySide6 installations. Mandate the
|
||||
# wheel layout. See ``utils/hooks/qt.py`` for more details.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
import os
|
||||
import sys
|
||||
|
||||
from _pyi_rth_utils import is_macos_app_bundle, prepend_path_to_environment_variable
|
||||
from _pyi_rth_utils import qt as qt_rth_utils
|
||||
|
||||
# Ensure this is the only Qt bindings package in the application.
|
||||
qt_rth_utils.ensure_single_qt_bindings_package("PySide6")
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PySide6')
|
||||
else:
|
||||
pyqt_path = os.path.join(sys._MEIPASS, 'PySide6', 'Qt')
|
||||
|
||||
os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins')
|
||||
|
||||
if is_macos_app_bundle:
|
||||
# Special handling for macOS .app bundles. To satisfy codesign requirements, we are forced to split `qml`
|
||||
# directory into two parts; one that keeps only binaries (rooted in `Contents/Frameworks`) and one that keeps
|
||||
# only data files (rooted in `Contents/Resources), with files from one directory tree being symlinked to the
|
||||
# other to maintain illusion of a single mixed-content directory. As Qt seems to compute the identifier of its
|
||||
# QML components based on location of the `qmldir` file w.r.t. the registered QML import paths, we need to
|
||||
# register both paths, because the `qmldir` file for a component could be reached via either directory tree.
|
||||
pyqt_path_res = os.path.normpath(
|
||||
os.path.join(sys._MEIPASS, '..', 'Resources', os.path.relpath(pyqt_path, sys._MEIPASS))
|
||||
)
|
||||
os.environ['QML2_IMPORT_PATH'] = os.pathsep.join([
|
||||
os.path.join(pyqt_path_res, 'qml'),
|
||||
os.path.join(pyqt_path, 'qml'),
|
||||
])
|
||||
else:
|
||||
os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml')
|
||||
|
||||
# Add `sys._MEIPASS` to `PATH` in order to ensure that `QtNetwork` can discover OpenSSL DLLs that might have been
|
||||
# collected there (i.e., when they were not shipped with the package, and were collected from an external location).
|
||||
if sys.platform.startswith('win'):
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'PATH')
|
||||
|
||||
# For macOS POSIX builds, we need to add `sys._MEIPASS` to `DYLD_LIBRARY_PATH` so that QtNetwork can discover
|
||||
# OpenSSL dynamic libraries for its `openssl` TLS backend. This also prevents fallback to external locations, such
|
||||
# as Homebrew. For .app bundles, this is unnecessary because `QtNetwork` explicitly searches `Contents/Frameworks`.
|
||||
if sys.platform == 'darwin' and not is_macos_app_bundle:
|
||||
prepend_path_to_environment_variable(sys._MEIPASS, 'DYLD_LIBRARY_PATH')
|
||||
|
||||
# Qt bindings package installed via PyPI wheels typically ensures that its bundled Qt is relocatable, by creating
|
||||
# embedded `qt.conf` file during its initialization. This run-time generated qt.conf dynamically sets the Qt prefix
|
||||
# path to the package's Qt directory. For bindings packages that do not create embedded `qt.conf` during their
|
||||
# initialization (for example, conda-installed packages), try to perform this step ourselves.
|
||||
qt_rth_utils.create_embedded_qt_conf("PySide6", pyqt_path)
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
@@ -0,0 +1,37 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2022-2023, 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
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# This runtime hook performs the equivalent of the distutils-precedence.pth from the setuptools package;
|
||||
# it registers a special meta finder that diverts import of distutils to setuptools._distutils, if available.
|
||||
|
||||
|
||||
def _pyi_rthook():
|
||||
def _install_setuptools_distutils_hack():
|
||||
import os
|
||||
import setuptools
|
||||
|
||||
# We need to query setuptools version at runtime, because the default value for SETUPTOOLS_USE_DISTUTILS
|
||||
# has changed at version 60.0 from "stdlib" to "local", and we want to mimic that behavior.
|
||||
setuptools_major = int(setuptools.__version__.split('.')[0])
|
||||
default_value = "stdlib" if setuptools_major < 60 else "local"
|
||||
|
||||
if os.environ.get("SETUPTOOLS_USE_DISTUTILS", default_value) == "local":
|
||||
import _distutils_hack
|
||||
_distutils_hack.add_shim()
|
||||
|
||||
try:
|
||||
_install_setuptools_distutils_hack()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
_pyi_rthook()
|
||||
del _pyi_rthook
|
||||
Reference in New Issue
Block a user