feat: 配布パッケージ自動生成スクリプトを追加
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
379d1d09e4
commit
099a40d476
161
PythonProj/ScanOCR/build/build_package.py
Normal file
161
PythonProj/ScanOCR/build/build_package.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
配布パッケージ生成スクリプト。
|
||||||
|
|
||||||
|
開発者PC上で1回実行すると、dist/ScanOCR/ 配下に
|
||||||
|
Python embeddable版・依存pipパッケージ・tesseract-ocr・poppler一式を含む
|
||||||
|
配布用フォルダを生成する。
|
||||||
|
|
||||||
|
前提:
|
||||||
|
- ネットワーク接続必須
|
||||||
|
- 7-Zip(7zコマンド)が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()
|
||||||
Loading…
Reference in New Issue
Block a user