Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build-engine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -472,10 +472,10 @@ jobs:
LIBONNXRUNTIME_PATH=download/onnxruntime/lib/libonnxruntime.so
fi

CORE_MODEL_DIR_PATH="download/core/model" \
LIBCORE_PATH="$LIBCORE_PATH" \
LIBONNXRUNTIME_PATH="$LIBONNXRUNTIME_PATH" \
uv run pyinstaller --noconfirm run.spec
uv run pyinstaller --noconfirm run.spec -- \
--libcore_path="$LIBCORE_PATH" \
--libonnxruntime_path="$LIBONNXRUNTIME_PATH" \
--core_model_dir_path="download/core/model"

# Because PyInstaller does not copy dynamic loaded libraries,
# manually move DLL dependencies into `dist/run/` (cache already saved)
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,10 @@ bash tools/create_venv_and_generate_licenses.bash
uv run pyinstaller --noconfirm run.spec

# 製品版でビルドする場合
CORE_MODEL_DIR_PATH="/path/to/core_model" \
LIBCORE_PATH="/path/to/libcore" \
LIBONNXRUNTIME_PATH="/path/to/libonnxruntime" \
uv run pyinstaller --noconfirm run.spec
uv run pyinstaller --noconfirm run.spec -- \
--libcore_path="/path/to/libcore" \
--libonnxruntime_path="/path/to/libonnxruntime" \
--core_model_dir_path="/path/to/core_model"
```

TODO: Docker 版のビルド手順を GitHub Actions をベースに記述する
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ default-groups = []
pyopenjtalk = { git = "https://github.com/VOICEVOX/pyopenjtalk", rev = "74703b034dd90a1f199f49bb70bf3b66b1728a86" }

[dependency-groups]
build = ["pyinstaller<6"] # NOTE: PyInstaller6ではmacOSのエディタにバンドルすると動作しなくなる (ref: https://github.com/VOICEVOX/voicevox_engine/issues/1022)
build = ["pyinstaller>=6"]
dev = [
"coveralls>=4.0.1",
"httpx>=0.28.1", # NOTE: required by fastapi.testclient.TestClient (fastapi-slim's unmanaged dependency)
Expand Down
6 changes: 3 additions & 3 deletions requirements-build.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ macholib==1.16.3 ; sys_platform == 'darwin'
markupsafe==3.0.2
numpy==2.2.4
packaging==24.2
pefile==2024.8.26 ; sys_platform == 'win32'
pefile==2023.2.7 ; sys_platform == 'win32'
platformdirs==4.3.7
pycparser==2.22
pydantic==2.11.3
pydantic-core==2.33.1
pyinstaller==5.13.2
pyinstaller-hooks-contrib==2025.2
pyinstaller==6.14.2
pyinstaller-hooks-contrib==2025.5
pyopenjtalk @ git+https://github.com/VOICEVOX/pyopenjtalk@74703b034dd90a1f199f49bb70bf3b66b1728a86
python-multipart==0.0.20
pywin32-ctypes==0.2.3 ; sys_platform == 'win32'
Expand Down
86 changes: 46 additions & 40 deletions run.spec
Original file line number Diff line number Diff line change
@@ -1,67 +1,51 @@
# -*- mode: python ; coding: utf-8 -*-
# このファイルはPyInstallerによって自動生成されたもので、それをカスタマイズして使用しています。
from PyInstaller.utils.hooks import collect_data_files
import os

datas = [
('resources', 'resources'),
('engine_manifest.json', '.'),
('licenses.json', '.'),
]
datas += collect_data_files('pyopenjtalk')
from argparse import ArgumentParser
from pathlib import Path
from shutil import copy2, copytree

core_model_dir_path = os.environ.get('CORE_MODEL_DIR_PATH')
if core_model_dir_path:
print('CORE_MODEL_DIR_PATH is found:', core_model_dir_path)
if not os.path.isdir(core_model_dir_path):
raise Exception("CORE_MODEL_DIR_PATH was found, but it is not directory!")
datas += [(core_model_dir_path, "model")]
from PyInstaller.utils.hooks import collect_data_files

# コアとONNX Runtimeはバイナリであるが、`binaries`に加えると
# 依存関係のパスがPyInstallerに書き換えらるので、`datas`に加える
# 参考: https://github.com/VOICEVOX/voicevox_engine/pull/446#issuecomment-1210052318
libcore_path = os.environ.get('LIBCORE_PATH')
if libcore_path:
print('LIBCORE_PATH is found:', libcore_path)
if not os.path.isfile(libcore_path):
raise Exception("LIBCORE_PATH was found, but it is not file!")
datas += [(libcore_path, ".")]
parser = ArgumentParser()
parser.add_argument("--libcore_path", type=Path)
parser.add_argument("--libonnxruntime_path", type=Path)
parser.add_argument("--core_model_dir_path", type=Path)
options = parser.parse_args()

libonnxruntime_path = os.environ.get('LIBONNXRUNTIME_PATH')
if libonnxruntime_path:
print('LIBONNXRUNTIME_PATH is found:', libonnxruntime_path)
if not os.path.isfile(libonnxruntime_path):
raise Exception("LIBCORE_PATH was found, but it is not file!")
datas += [(libonnxruntime_path, ".")]
libcore_path: Path | None = options.libcore_path
if libcore_path is not None and not libcore_path.is_file():
raise Exception(f"libcore_path: {libcore_path} is not file")

libonnxruntime_path: Path | None = options.libonnxruntime_path
if libonnxruntime_path is not None and not libonnxruntime_path.is_file():
raise Exception(f"libonnxruntime_path: {libonnxruntime_path} is not file")

block_cipher = None
core_model_dir_path: Path | None = options.core_model_dir_path
if core_model_dir_path is not None and not core_model_dir_path.is_dir():
raise Exception(f"core_model_dir_path: {core_model_dir_path} is not dir")


a = Analysis(
['run.py'],
["run.py"],
pathex=[],
binaries=[],
datas=datas,
datas=collect_data_files("pyopenjtalk"),
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
pyz = PYZ(a.pure)
Comment thread
tarepan marked this conversation as resolved.

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='run',
name="run",
debug=False,
bootloader_ignore_signals=False,
strip=False,
Expand All @@ -72,15 +56,37 @@ exe = EXE(
target_arch=None,
codesign_identity=None,
entitlements_file=None,
contents_directory="engine_internal", # 実行時に"sys._MEIPASS"が参照するディレクトリ名
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここのメモは「今そうなってる」というwhatはわかるけど、結局なぜこの値なのかのwhyがわからないかもと感じました!

・・・・・・これちなみになんで指定が必要なんでしたっけ・・・。
無指定だと変なディレクトリ名になるから・・・・・・?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#828 (comment)
以前のissueで決まったことだからとしか言えないような…

無指定だと_internalになります。


正直なところこの引数にこれ以上詳細な説明を書くことに疑問を感じています。
引数の意味はPyInstallerのドキュメントを確認するべきですし、ディレクトリ名も外部からアクセスするべきではないと分かる名前ならなんでもいいからです。

engine_internalに深い意味がなく、この文字列を使ってアクセスしようとするべきではないという情報を短く書くなら今のコメントしか思い浮かばないです。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、なるほどです!!ありがとうございます!!

engine_internal、当初の思いにあったかは不明ですが、「エンジンの中身である」ということをわかりやすくしたかったのかもです。
デフォルトの_internalでも良いけど、何のディレクトリ由来だったかわからなくなるという思いでengine_internalもアリかも。

・・・だったけど、今のエディタはエンジンをvv-engineディレクトリに放り込むから、別に_internalでも良いんですよねぇ・・・・・・・。

engine_internalにして、かつコメントは今の形で良さそうに思いました!!
文脈とてもよく理解できました、ありがとうございました!!!!!

)

coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='run',
name="run",
)

# 実行ファイルのディレクトリに配置するファイルのコピー

# 実行ファイルと同じrootディレクトリ
target_dir = Path(DISTPATH) / "run"

# リソースをコピー
manifest_file_path = Path("engine_manifest.json")
copy2(manifest_file_path, target_dir)
copytree("resources", target_dir / "resources")

license_file_path = Path("licenses.json")
if license_file_path.is_file():
copy2("licenses.json", target_dir)

# 動的ライブラリをコピー
if libonnxruntime_path is not None:
copy2(libonnxruntime_path, target_dir)
if core_model_dir_path is not None:
copytree(core_model_dir_path, target_dir / "model")
if libcore_path is not None:
copy2(libcore_path, target_dir)
41 changes: 21 additions & 20 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.