ken_nogi/PythonProj/ScanOCR/build/build_package.py
Kenichiro NOGI 099a40d476 feat: 配布パッケージ自動生成スクリプトを追加
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 10:18:35 +09:00

162 lines
4.8 KiB
Python
Raw 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.

# -*- coding: utf-8 -*-
"""
配布パッケージ生成スクリプト。
開発者PC上で1回実行すると、dist/ScanOCR/ 配下に
Python embeddable版・依存pipパッケージ・tesseract-ocr・poppler一式を含む
配布用フォルダを生成する。
前提:
- ネットワーク接続必須
- 7-Zip7zコマンドがPATHに通っていることtesseractインストーラー展開用
使い方:
python build/build_package.py
"""
from __future__ import annotations
import shutil
import subprocess
import tempfile
import urllib.request
import zipfile
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
DIST_DIR = REPO_ROOT / "dist" / "ScanOCR"
PYTHON_VERSION = "3.11.9"
PYTHON_EMBED_URL = (
f"https://www.python.org/ftp/python/{PYTHON_VERSION}/"
f"python-{PYTHON_VERSION}-embed-amd64.zip"
)
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
PIP_PACKAGES = ["opencv-python", "pdf2image", "pytesseract", "pillow", "numpy"]
TESSERACT_VERSION = "5.4.0.20240606"
TESSERACT_INSTALLER_URL = (
"https://github.com/UB-Mannheim/tesseract/releases/download/"
f"v{TESSERACT_VERSION}/tesseract-ocr-w64-setup-{TESSERACT_VERSION}.exe"
)
POPPLER_VERSION = "24.02.0-0"
POPPLER_ZIP_URL = (
"https://github.com/oschwartz10612/poppler-windows/releases/download/"
f"v{POPPLER_VERSION}/Release-{POPPLER_VERSION}.zip"
)
CODE_FILES = (
"extract_and_rename.py",
"box_detector.py",
"config_loader.py",
"file_ops.py",
"lock_manager.py",
"logger.py",
)
RUNTIME_FOLDERS = ("テンプレート", "スキャン", "アウトプット", "成功", "失敗", "ログ")
def download(url: str, dest: Path) -> None:
print(f"ダウンロード: {url}")
urllib.request.urlretrieve(url, dest)
def prepare_dist_dir() -> None:
if DIST_DIR.exists():
shutil.rmtree(DIST_DIR)
DIST_DIR.mkdir(parents=True)
def copy_code_files() -> Path:
script_dst = DIST_DIR / "スクリプト"
script_dst.mkdir(parents=True)
for name in CODE_FILES:
shutil.copy2(REPO_ROOT / "スクリプト" / name, script_dst / name)
shutil.copy2(REPO_ROOT / "OCR仕分け実行.bat", DIST_DIR / "OCR仕分け実行.bat")
shutil.copy2(REPO_ROOT / "config.txt", DIST_DIR / "config.txt")
return script_dst
def setup_python(script_dst: Path, tmp_dir: Path) -> None:
python_dir = script_dst / "python"
python_dir.mkdir(parents=True)
zip_path = tmp_dir / "python-embed.zip"
download(PYTHON_EMBED_URL, zip_path)
with zipfile.ZipFile(zip_path) as zf:
zf.extractall(python_dir)
pth_files = list(python_dir.glob("python*._pth"))
if not pth_files:
raise RuntimeError("python*._pth が見つかりませんembeddable zipの構成が変わった可能性")
pth_path = pth_files[0]
content = pth_path.read_text(encoding="utf-8")
content = content.replace("#import site", "import site")
pth_path.write_text(content, encoding="utf-8")
get_pip_path = tmp_dir / "get-pip.py"
download(GET_PIP_URL, get_pip_path)
python_exe = python_dir / "python.exe"
subprocess.run([str(python_exe), str(get_pip_path)], check=True)
subprocess.run(
[str(python_exe), "-m", "pip", "install", *PIP_PACKAGES],
check=True,
)
def setup_tesseract(script_dst: Path, tmp_dir: Path) -> None:
tools_dst = script_dst / "tools" / "tesseract"
tools_dst.mkdir(parents=True)
installer_path = tmp_dir / "tesseract-setup.exe"
download(TESSERACT_INSTALLER_URL, installer_path)
extract_dir = tmp_dir / "tesseract-extract"
extract_dir.mkdir()
subprocess.run(
["7z", "x", str(installer_path), f"-o{extract_dir}", "-y"],
check=True,
)
for item in extract_dir.iterdir():
shutil.move(str(item), str(tools_dst / item.name))
def setup_poppler(script_dst: Path, tmp_dir: Path) -> None:
tools_parent = script_dst / "tools"
tools_parent.mkdir(parents=True, exist_ok=True)
tools_dst = tools_parent / "poppler"
zip_path = tmp_dir / "poppler.zip"
download(POPPLER_ZIP_URL, zip_path)
extract_dir = tmp_dir / "poppler-extract"
with zipfile.ZipFile(zip_path) as zf:
zf.extractall(extract_dir)
inner_items = list(extract_dir.iterdir())
source_dir = inner_items[0] if len(inner_items) == 1 else extract_dir
shutil.move(str(source_dir), str(tools_dst))
def create_runtime_folders() -> None:
for name in RUNTIME_FOLDERS:
(DIST_DIR / name).mkdir(parents=True, exist_ok=True)
def main() -> None:
prepare_dist_dir()
script_dst = copy_code_files()
with tempfile.TemporaryDirectory() as tmp:
tmp_dir = Path(tmp)
setup_python(script_dst, tmp_dir)
setup_tesseract(script_dst, tmp_dir)
setup_poppler(script_dst, tmp_dir)
create_runtime_folders()
print(f"配布パッケージ生成完了: {DIST_DIR}")
if __name__ == "__main__":
main()