ken_nogi/PythonProj/ScanOCR/build/build_package.py
Kenichiro NOGI 913b4f5431 fix: 7-ZipがPATH未登録でも標準インストール場所から見つけるように対応
実機実行時、7-ZipはインストールされているがPATHに7zコマンドが無くFileNotFoundErrorで失敗したため、
C:\Program Files\7-Zip\7z.exe 等へのフォールバック探索を追加。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 11:19:47 +09:00

181 lines
5.4 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,
)
SEVEN_ZIP_FALLBACK_PATHS = (
r"C:\Program Files\7-Zip\7z.exe",
r"C:\Program Files (x86)\7-Zip\7z.exe",
)
def find_7zip_exe() -> str:
found = shutil.which("7z")
if found:
return found
for candidate in SEVEN_ZIP_FALLBACK_PATHS:
if Path(candidate).exists():
return candidate
raise RuntimeError(
"7-Zip7z.exeが見つかりません。PATHに通すか、標準的な場所にインストールしてください。"
)
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)
seven_zip_exe = find_7zip_exe()
extract_dir = tmp_dir / "tesseract-extract"
extract_dir.mkdir()
subprocess.run(
[seven_zip_exe, "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()