forked from VOICEVOX/voicevox_engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
macOS向け自動ビルドの際に.dylibファイルの問題を検出・修正するスクリプトを追加 (VOICEVOX#191)
* run build.yml on every push (for test-build) * macOSビルドの.dylibファイル関連の修正を行うスクリプトを追加 * macOS向け自動ビルド時に.dylibファイル関連の修正スクリプトを実行 * fix typo * run build.yml on master push * strオブジェクトへの不要な変換を削除 * add docstring
- Loading branch information
1 parent
c5af440
commit b5db92f
Showing
5 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
""" | ||
macOSにおいて共有ライブラリを操作するためのツールをまとめたモジュール | ||
""" | ||
|
||
import subprocess | ||
from pathlib import Path | ||
from typing import List | ||
|
||
|
||
def get_dylib_paths(base_path: Path) -> List[Path]: | ||
"""base_path以下の全てのサブディレクトリにあるdylibファイルのリストを返す""" | ||
return list(base_path.glob("**/*.dylib")) | ||
|
||
|
||
def get_rpaths(shared_lib_path: Path) -> List[Path]: | ||
"""引数で指定された共有ライブラリのrpathのリストを返す""" | ||
proc = subprocess.run(["otool", "-L", str(shared_lib_path)], stdout=subprocess.PIPE) | ||
output = proc.stdout.decode("utf-8") | ||
paths = [ | ||
Path(line.lstrip().split(" ", maxsplit=1)[0]) | ||
for line in output.splitlines()[1:] | ||
] | ||
# 得られたパスのリストのうち、共有ライブラリ自体とライブラリ名が同じものは | ||
# rpath ではなく install ID というものなので除外 | ||
return [ | ||
path | ||
for path in paths | ||
if path.name.split(".")[0] != shared_lib_path.name.split(".")[0] | ||
] | ||
|
||
|
||
def is_distributable_rpath(rpath: Path) -> bool: | ||
"""開発環境にインストールされたパッケージに依存しないrpathかどうか""" | ||
# 以下のプレフィックスで始まるrpathは配布に際して問題がない | ||
# - プレースホルダ。実行時に自動で解決される | ||
# - @executable_path/ | ||
# - @loader_path/ | ||
# - @rpath/ | ||
# - システム標準のライブラリがあるディレクトリ | ||
# - /usr/lib/ | ||
# - /System/Library/Frameworks/ | ||
# - /System/Library/PrivateFrameworks/ | ||
DISTRIBUTABLE_PREFIXES = [ | ||
"@executable_path/", | ||
"@loader_path/", | ||
"@rpath/", | ||
"/usr/lib/", | ||
"/System/Library/Frameworks/", | ||
"/System/Library/PrivateFrameworks/", | ||
] | ||
result = False | ||
|
||
for prefix in DISTRIBUTABLE_PREFIXES: | ||
if str(rpath).startswith(prefix): | ||
result = True | ||
break | ||
else: | ||
continue | ||
|
||
return result | ||
|
||
|
||
def change_rpath(old_rpath: Path, new_rpath: Path, dylib_path: Path, base_path: Path): | ||
"""dylib_pathで指定されたdylibのrpathを、old_rpathから、new_rpath(base_pathからの相対パスに変換したもの)に変更する""" | ||
relative_new_rpath = new_rpath.relative_to(base_path) | ||
subprocess.run( | ||
[ | ||
"install_name_tool", | ||
"-change", | ||
old_rpath, | ||
"@rpath/" + str(relative_new_rpath), | ||
dylib_path, | ||
] | ||
) | ||
|
||
|
||
class SharedLib: | ||
"""共有ライブラリの情報""" | ||
|
||
__path: Path | ||
__rpaths: List[Path] | ||
|
||
def __init__(self, shared_lib_path: Path): | ||
self.__path = shared_lib_path | ||
self.__rpaths = get_rpaths(shared_lib_path) | ||
|
||
@property | ||
def path(self) -> Path: | ||
return self.__path | ||
|
||
def get_non_distributable_rpaths(self) -> List[Path]: | ||
"""rpathのうち、開発環境に依存しているもののリスト""" | ||
return [rpath for rpath in self.__rpaths if not is_distributable_rpath(rpath)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
""" | ||
配布物内の.dylibファイルの不足を解消するためのスクリプト | ||
引数で指定したbase_directory以下にある.dylibファイルのrpathをチェックし、 | ||
rpathの指す.dylibファイルがbase_directory以下に存在しなかった場合、 | ||
rpathの指している場所からその.dylibファイルをbase_directory直下へとコピーする。 | ||
""" | ||
|
||
import argparse | ||
import shutil | ||
import sys | ||
from pathlib import Path | ||
from typing import List, Set | ||
|
||
from build_util_macos.shlib_tools import SharedLib, get_dylib_paths | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"base_directory", help="copy the missing dylibs under base_directory", type=str | ||
) | ||
args = parser.parse_args() | ||
base_dir_path = Path(args.base_directory) | ||
|
||
if not (base_dir_path.exists() and base_dir_path.is_dir()): | ||
print("could not find the directory:", str(base_dir_path), file=sys.stderr) | ||
exit(1) | ||
|
||
# base_dir_path以下の全てのサブディレクトリを探索して得たdylibのリスト | ||
dylib_paths: List[Path] = get_dylib_paths(base_dir_path) | ||
# 全てのdylibのファイル名のリスト | ||
dylib_names: List[str] = [path.name for path in dylib_paths] | ||
|
||
# 開発環境に依存したrpathを持つdylibのリスト | ||
non_distributable_dylibs: List[SharedLib] = [] | ||
for dylib_path in dylib_paths: | ||
lib = SharedLib(dylib_path) | ||
if lib.get_non_distributable_rpaths(): | ||
non_distributable_dylibs.append(lib) | ||
|
||
# 開発環境に依存したrpathの集合 | ||
non_distributable_rpaths: Set[Path] = set() | ||
for dylib in non_distributable_dylibs: | ||
rpaths: Set[Path] = set([rpath for rpath in dylib.get_non_distributable_rpaths()]) | ||
non_distributable_rpaths = non_distributable_rpaths.union(rpaths) | ||
|
||
# rpathが指しているdylibのうち、base_dir_path以下に存在しないもののリスト | ||
external_dylib_paths: List[Path] = [] | ||
for rpath in non_distributable_rpaths: | ||
if not (rpath.name in dylib_names): | ||
external_dylib_paths.append(rpath) | ||
|
||
# 不足しているdylibをbase_dir_path直下にコピー | ||
for dylib_path in external_dylib_paths: | ||
shutil.copy(dylib_path, base_dir_path, follow_symlinks=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
""" | ||
配布物内の.dylibファイルのrpathをどのようなユーザー環境においても有効になるように修正するスクリプト | ||
引数で指定したbase_directory以下にある.dylibファイルのrpathをチェックし、 | ||
開発環境に依存した(配布先の環境に存在することが保証されていない)rpathであった場合、 | ||
base_directory以下の.dylibファイルを相対パスで指すように変更する。 | ||
(base_directory以下の.dylibファイルに不足がないことを前提とする。) | ||
""" | ||
|
||
import argparse | ||
import sys | ||
from pathlib import Path | ||
from typing import List, Set | ||
|
||
from build_util_macos.shlib_tools import SharedLib, change_rpath, get_dylib_paths | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument( | ||
"base_directory", help="fix the rpaths of the dylibs under base_directory", type=str | ||
) | ||
args = parser.parse_args() | ||
base_dir_path = Path(args.base_directory) | ||
|
||
if not (base_dir_path.exists() and base_dir_path.is_dir()): | ||
print("could not find the directory:", str(base_dir_path), file=sys.stderr) | ||
exit(1) | ||
|
||
# base_dir_path以下の全てのサブディレクトリを探索して得たdylibのリスト | ||
internal_dylib_paths: List[Path] = get_dylib_paths(base_dir_path) | ||
# 全てのdylibのファイル名のリスト | ||
internal_dylib_names: List[str] = [path.name for path in internal_dylib_paths] | ||
|
||
# 開発環境に依存したrpathを持つdylibのリスト | ||
non_distributable_dylibs: List[SharedLib] = [] | ||
for internal_dylib_path in internal_dylib_paths: | ||
lib = SharedLib(internal_dylib_path) | ||
if lib.get_non_distributable_rpaths(): | ||
non_distributable_dylibs.append(lib) | ||
|
||
# 開発環境に依存したrpathの集合 | ||
non_distributable_rpaths: Set[Path] = set() | ||
for dylib in non_distributable_dylibs: | ||
rpaths: Set[Path] = set([rpath for rpath in dylib.get_non_distributable_rpaths()]) | ||
non_distributable_rpaths = non_distributable_rpaths.union(rpaths) | ||
|
||
# rpathが指しているdylibのうち、base_dir_path以下に存在しないもののリスト | ||
external_dylib_paths: List[Path] = [] | ||
for rpath in non_distributable_rpaths: | ||
if not (rpath.name in internal_dylib_names): | ||
external_dylib_paths.append(rpath) | ||
|
||
# base_dir_path以下でdylibが不足している場合は、不足しているdylibを表示して終了 | ||
if external_dylib_paths: | ||
print( | ||
f"following dylibs not found under base_dir_path ({str(base_dir_path)}):", | ||
file=sys.stderr, | ||
) | ||
for path in external_dylib_paths: | ||
print(f"\t{path.name}", file=sys.stderr) | ||
exit(1) | ||
|
||
# 開発環境に依存したrpathを、base_dir_path以下のdylibを指すように変更 | ||
for dylib in non_distributable_dylibs: | ||
for rpath in dylib.get_non_distributable_rpaths(): | ||
for internal_dylib_path in internal_dylib_paths: | ||
if internal_dylib_path.name == rpath.name: | ||
change_rpath(rpath, internal_dylib_path, dylib.path, base_dir_path) |