feat: 配布パッケージ自動生成スクリプトを追加

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kenichiro NOGI 2026-08-02 10:18:35 +09:00
parent 379d1d09e4
commit 099a40d476

View File

@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
"""
配布パッケージ生成スクリプト
開発者PC上で1回実行するとdist/ScanOCR/ 配下に
Python embeddable版依存pipパッケージtesseract-ocrpoppler一式を含む
配布用フォルダを生成する
前提:
- ネットワーク接続必須
- 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()