Files
kadastr-nalog/build.py
2026-06-01 17:04:41 +05:00

160 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Сборка ФИАС_поиск.exe — standalone, без установки Python.
Запускать на Windows с 32-bit Python 3.8:
python build.py
Перед первым запуском установить зависимости:
pip install -r requirements.txt
"""
from __future__ import annotations
import os
import subprocess
import sys
import shutil
import struct
import platform
from pathlib import Path
ROOT = Path(__file__).parent
def check_env():
bits = struct.calcsize("P") * 8
ver = sys.version_info
print(f"Python {ver.major}.{ver.minor}.{ver.micro} {bits}-bit {platform.system()}")
if ver < (3, 8):
sys.exit("Требуется Python 3.8+")
if bits != 32:
print(f"ВНИМАНИЕ: Python {bits}-bit. Для win32 .exe используйте 32-bit Python.")
try:
import PyInstaller
print(f"PyInstaller {PyInstaller.__version__}")
except ImportError:
print("Установка PyInstaller...")
subprocess.run(
[sys.executable, "-m", "pip", "install", "pyinstaller>=5.13,<7"],
check=True,
)
def clean():
for name in ("build", "dist", "__pycache__"):
p = ROOT / name
if p.exists():
shutil.rmtree(p)
for spec in ROOT.glob("*.spec"):
spec.unlink()
print("Очищено")
def _data_args() -> list[str]:
"""--add-data for resources bundled into the .exe so the program is
self-contained: КриптоПро XPI, and (if present) portable Firefox + geckodriver."""
args: list[str] = []
# Расширение КриптоПро — обязательно, всегда в репозитории.
ext = ROOT / "extensions"
if ext.is_dir():
args += ["--add-data", f"{ext}{os.pathsep}extensions"]
else:
print("ВНИМАНИЕ: папка extensions/ не найдена — расширение КриптоПро не попадёт в сборку!")
# Портативный Firefox + geckodriver — наполняются скриптом fetch_vendor.py.
vendor = ROOT / "vendor"
if vendor.is_dir() and any(vendor.iterdir()):
args += ["--add-data", f"{vendor}{os.pathsep}vendor"]
print(f"vendor/ включён в сборку: {[p.name for p in vendor.iterdir()]}")
else:
print(
"ВНИМАНИЕ: папка vendor/ пуста или отсутствует — Firefox и geckodriver НЕ будут\n"
" встроены. Сначала запустите: python fetch_vendor.py"
)
return args
def build():
cmd = [
sys.executable, "-m", "PyInstaller",
"--onefile",
"--noconsole",
"--name", "ФИАС_поиск",
*_data_args(),
# Собрать целиком вместе с data-файлами
"--collect-all", "selenium",
"--collect-all", "webdriver_manager",
"--collect-all", "pdfplumber",
"--collect-all", "pdfminer",
"--collect-all", "openpyxl",
"--collect-all", "certifi",
# Явные скрытые импорты
"--hidden-import", "selenium.webdriver.firefox.webdriver",
"--hidden-import", "selenium.webdriver.firefox.options",
"--hidden-import", "selenium.webdriver.firefox.service",
"--hidden-import", "selenium.webdriver.support.expected_conditions",
"--hidden-import", "selenium.webdriver.common.by",
"--hidden-import", "webdriver_manager.firefox",
"--hidden-import", "pdfminer.high_level",
"--hidden-import", "pdfminer.converter",
"--hidden-import", "pdfminer.layout",
"--hidden-import", "pdfminer.pdfpage",
"--hidden-import", "pdfminer.pdfinterp",
"--hidden-import", "pdfminer.pdffont",
"--hidden-import", "pdfminer.pdftypes",
"--hidden-import", "pdfminer.pdfparser",
"--hidden-import", "pdfminer.pdfdevice",
"--hidden-import", "openpyxl.styles",
"--hidden-import", "openpyxl.utils",
"--hidden-import", "cryptography",
"--hidden-import", "tkinter",
"--hidden-import", "tkinter.filedialog",
"--hidden-import", "tkinter.scrolledtext",
"--hidden-import", "tkinter.messagebox",
"--hidden-import", "queue",
"--hidden-import", "logging",
"--hidden-import", "configparser",
"--hidden-import", "platform",
str(ROOT / "main.py"),
]
print("\nСборка...")
result = subprocess.run(cmd, cwd=ROOT)
if result.returncode != 0:
sys.exit("PyInstaller завершился с ошибкой")
exe = ROOT / "dist" / "ФИАС_поиск.exe"
if exe.exists():
mb = exe.stat().st_size / 1024 / 1024
print(f"\n{'='*55}")
print(f" Готово: {exe}")
print(f" Размер: {mb:.1f} МБ")
print(f"{'='*55}")
print(" Firefox должен быть установлен на компьютере.")
print(" geckodriver скачается автоматически при первом запуске.")
else:
sys.exit("dist/ФИАС_поиск.exe не найден после сборки")
if __name__ == "__main__":
import argparse
p = argparse.ArgumentParser()
p.add_argument("--clean", action="store_true", help="Только очистить build/dist")
args = p.parse_args()
if args.clean:
clean()
else:
check_env()
clean()
build()