diff --git a/build.bat b/build.bat index 177fbcf..90422c3 100644 --- a/build.bat +++ b/build.bat @@ -43,12 +43,16 @@ if exist dist rmdir /s /q dist echo. echo 4/5. Building executable... +echo NOTE: Using custom hooks to exclude heavy ML libraries that cause Python 3.13 issues +echo These will be installed dynamically at runtime if needed python -m PyInstaller ^ --noconfirm ^ --name "CutThenThink" ^ --windowed ^ --onefile ^ --add-data "src:src" ^ + --runtime-hook=pyi_hooks/pyi_rth_ignore_torch.py ^ + --additional-hooks-dir=pyi_hooks ^ --hidden-import=PyQt6.QtCore ^ --hidden-import=PyQt6.QtGui ^ --hidden-import=PyQt6.QtWidgets ^ @@ -67,6 +71,18 @@ python -m PyInstaller ^ --hidden-import=yaml ^ --hidden-import=requests ^ --collect-all pyqt6 ^ + --exclude-module=torch ^ + --exclude-module=transformers ^ + --exclude-module=tensorflow ^ + --exclude-module=onnx ^ + --exclude-module=onnxruntime ^ + --exclude-module=sentencepiece ^ + --exclude-module=tokenizers ^ + --exclude-module=diffusers ^ + --exclude-module=accelerate ^ + --exclude-module=datasets ^ + --exclude-module=huggingface_hub ^ + --exclude-module=safetensors ^ src/main.py if errorlevel 1 ( diff --git a/build.sh b/build.sh index 909c5eb..0869606 100644 --- a/build.sh +++ b/build.sh @@ -44,15 +44,44 @@ fi echo "" echo "4/4. 开始打包..." +echo "NOTE: 使用自定义 hooks 排除与 Python 3.13 不兼容的 ML 库" $PYTHON -m PyInstaller \ --name "CutThenThink" \ --windowed \ --onefile \ --add-data "src:src" \ + --runtime-hook=pyi_hooks/pyi_rth_ignore_torch.py \ + --additional-hooks-dir=pyi_hooks \ --hidden-import=PyQt6.QtCore \ --hidden-import=PyQt6.QtGui \ --hidden-import=PyQt6.QtWidgets \ --hidden-import=sqlalchemy \ + --hidden-import=sqlalchemy.orm \ + --hidden-import=PIL \ + --hidden-import=PIL.Image \ + --hidden-import=PIL.ImageEnhance \ + --hidden-import=PIL.ImageFilter \ + --hidden-import=numpy \ + --hidden-import=pyperclip \ + --hidden-import=tkinter \ + --hidden-import=tkinter.ttk \ + --hidden-import=tkinter.scrolledtext \ + --hidden-import=tkinter.messagebox \ + --hidden-import=yaml \ + --hidden-import=requests \ + --collect-all pyqt6 \ + --exclude-module=torch \ + --exclude-module=transformers \ + --exclude-module=tensorflow \ + --exclude-module=onnx \ + --exclude-module=onnxruntime \ + --exclude-module=sentencepiece \ + --exclude-module=tokenizers \ + --exclude-module=diffusers \ + --exclude-module=accelerate \ + --exclude-module=datasets \ + --exclude-module=huggingface_hub \ + --exclude-module=safetensors \ src/main.py echo "" diff --git a/pyi_hooks/__init__.py b/pyi_hooks/__init__.py new file mode 100644 index 0000000..941913a --- /dev/null +++ b/pyi_hooks/__init__.py @@ -0,0 +1,7 @@ +""" +PyInstaller hooks for CutThenThink + +This directory contains custom PyInstaller hooks to handle: +- Excluding heavy ML libraries (torch, transformers, etc.) +- Runtime-only dependencies like PaddleOCR +""" diff --git a/pyi_hooks/hook-exclude-ml.py b/pyi_hooks/hook-exclude-ml.py new file mode 100644 index 0000000..6f1ba61 --- /dev/null +++ b/pyi_hooks/hook-exclude-ml.py @@ -0,0 +1,36 @@ +""" +PyInstaller hook to exclude ML libraries during build + +This prevents PyInstaller from attempting to analyze and bundle +heavy ML libraries that are incompatible with Python 3.13 or +cause build crashes. + +These libraries will be installed at runtime if needed. +""" + +# Collect any hidden imports (empty for this hook) +hiddenimports = [] + +# Exclude all heavy ML dependencies +excludedimports = [ + 'torch', + 'torch.nn', + 'torch.utils', + 'torchvision', + 'transformers', + 'tensorflow', + 'tensorflow.keras', + 'onnx', + 'onnxruntime', + 'sentencepiece', + 'tokenizers', + 'diffusers', + 'accelerate', + 'datasets', + 'huggingface_hub', + 'safetensors', + 'optimum', + 'coloredlogs', + 'docutils', + 'tqdm', +] diff --git a/pyi_hooks/hook-paddleocr.py b/pyi_hooks/hook-paddleocr.py new file mode 100644 index 0000000..a3ea1ae --- /dev/null +++ b/pyi_hooks/hook-paddleocr.py @@ -0,0 +1,29 @@ +""" +PyInstaller hook for PaddleOCR + +Prevents PyInstaller from trying to bundle PaddleOCR and its dependencies. +These will be installed dynamically at runtime. +""" + +from PyInstaller.utils.hooks import is_module_satisfies + +# Tell PyInstaller to NOT collect these modules +# They will be installed at runtime via ensure_paddleocr() + +hiddenimports = [] + +# Explicitly exclude heavy ML dependencies +excludedimports = [ + 'torch', + 'transformers', + 'tensorflow', + 'onnx', + 'onnxruntime', + 'sentencepiece', + 'tokenizers', + 'diffusers', + 'accelerate', + 'datasets', + 'huggingface_hub', + 'safetensors', +] diff --git a/pyi_hooks/pyi_rth_ignore_torch.py b/pyi_hooks/pyi_rth_ignore_torch.py new file mode 100644 index 0000000..2ba5adf --- /dev/null +++ b/pyi_hooks/pyi_rth_ignore_torch.py @@ -0,0 +1,47 @@ +""" +PyInstaller runtime hook to prevent torch import during analysis + +This hook runs before PyInstaller's module analysis phase. +It blocks problematic modules from being imported during the build process. +""" + +import sys + +# List of modules to block during PyInstaller analysis +BLOCKED_MODULES = [ + 'torch', + 'transformers', + 'tensorflow', + 'onnx', + 'onnxruntime', + 'sentencepiece', + 'tokenizers', + 'diffusers', + 'accelerate', + 'datasets', + 'huggingface_hub', + 'safetensors', +] + +# Override the import mechanism to block these modules +class BlockModuleImport: + """Meta path importer to block specific modules during PyInstaller build""" + + def find_module(self, fullname, path=None): + if fullname in BLOCKED_MODULES or any( + fullname.startswith(m + '.') for m in BLOCKED_MODULES + ): + # Return self to handle the import (will raise ImportError) + return self + return None + + def load_module(self, fullname): + raise ImportError( + f"Module '{fullname}' is excluded from PyInstaller build. " + f"It will be installed at runtime if needed." + ) + +# Install the blocker at the start of PyInstaller analysis +if '_MEIPASS' not in sys.__dict__: + # Only during build time (not when running the frozen app) + sys.meta_path.insert(0, BlockModuleImport())