"""LLVM JIT 自動検出と診断ログモジュール.
Mitsuba 3 の llvm_* variant を使う際に必要な DRJIT_LIBLLVM_PATH 環境変数を
自動設定する機能と,variant 設定後の状態を診断ログに出力する機能を提供する.
mitsuba は import しない(テストの軽量化・副作用なし設計).
os.environ への書き込みのみが副作用.
"""
from __future__ import annotations
import glob
import logging
import os
from pathlib import Path
_logger = logging.getLogger(__name__)
def _iter_default_candidates() -> list[str]:
"""LLVM-C.dll / libLLVM-C.so の標準インストール先パス候補を返す.
OS に応じて Windows / POSIX を分岐する.
テスト容易化のため独立関数として定義する.
Returns:
パス候補文字列のリスト(優先度順).
"""
candidates: list[str] = []
# 環境変数 LLVM_DIR が設定されていれば bin/ 配下を優先候補に追加する
llvm_dir = os.environ.get("LLVM_DIR")
if llvm_dir:
if os.name == "nt":
candidates.append(str(Path(llvm_dir) / "bin" / "LLVM-C.dll"))
else:
candidates.append(str(Path(llvm_dir) / "lib" / "libLLVM-C.so"))
candidates.append(str(Path(llvm_dir) / "lib" / "libLLVM-C.dylib"))
if os.name == "nt":
# Windows 標準インストール先
candidates += [
r"C:\Program Files\LLVM\bin\LLVM-C.dll",
r"C:\Program Files (x86)\LLVM\bin\LLVM-C.dll",
r"C:\ProgramData\chocolatey\lib\llvm\tools\LLVM\bin\LLVM-C.dll",
r"C:\msys64\mingw64\bin\LLVM-C.dll",
r"C:\msys64\clang64\bin\LLVM-C.dll",
]
else:
# POSIX 系(Linux / macOS)
# glob で llvm-* の複数バージョンを一括検索
candidates += glob.glob("/usr/lib/llvm-*/lib/libLLVM-C.so")
candidates += [
"/usr/local/lib/libLLVM-C.so",
"/opt/homebrew/opt/llvm/lib/libLLVM-C.dylib",
"/usr/local/opt/llvm/lib/libLLVM-C.dylib",
]
return candidates
def ensure_drjit_libllvm_path(extra_candidates: list[str] | None = None) -> str | None:
"""DRJIT_LIBLLVM_PATH が未設定なら標準パスを探索して os.environ に設定する.
優先順位:
1. 既に os.environ['DRJIT_LIBLLVM_PATH'] が設定されている場合はそれを尊重して
何もしない(戻り値はその既存値).
2. 環境変数 LLVM_DIR が設定されていれば `$LLVM_DIR/bin/LLVM-C.dll`(Windows)
または `$LLVM_DIR/lib/libLLVM-C.so` 等(POSIX)を試す.
3. Windows の標準インストール先:
- C:\\Program Files\\LLVM\\bin\\LLVM-C.dll
- C:\\Program Files (x86)\\LLVM\\bin\\LLVM-C.dll
- C:\\ProgramData\\chocolatey\\lib\\llvm\\tools\\LLVM\\bin\\LLVM-C.dll
- C:\\msys64\\mingw64\\bin\\LLVM-C.dll
- C:\\msys64\\clang64\\bin\\LLVM-C.dll
4. POSIX 系:
- /usr/lib/llvm-*/lib/libLLVM-C.so(glob)
- /usr/local/lib/libLLVM-C.so
- /opt/homebrew/opt/llvm/lib/libLLVM-C.dylib
- /usr/local/opt/llvm/lib/libLLVM-C.dylib
5. extra_candidates が指定されていればそれを最後に試す.
Args:
extra_candidates: 追加で試すパス候補のリスト(任意).
標準候補で見つからない場合に最後に試す.
Returns:
発見・設定したパス文字列.既設定だった場合はその値.
何も見つからなければ None.
Side effect:
os.environ['DRJIT_LIBLLVM_PATH'] を設定する(未設定かつ発見時のみ).
"""
# 1. 既に設定されている場合はそのまま返す
existing = os.environ.get("DRJIT_LIBLLVM_PATH")
if existing:
_logger.debug("DRJIT_LIBLLVM_PATH は既に設定されています: %s", existing)
return existing
# 標準候補 + 追加候補を順番に探索する
all_candidates = _iter_default_candidates()
if extra_candidates:
all_candidates += extra_candidates
for candidate in all_candidates:
if Path(candidate).is_file():
os.environ["DRJIT_LIBLLVM_PATH"] = candidate
_logger.info(
"DRJIT_LIBLLVM_PATH を自動設定しました: %s",
candidate,
)
return candidate
_logger.warning(
"LLVM-C.dll / libLLVM-C.so が標準パスに見つかりませんでした."
"LLVM をインストールするか,DRJIT_LIBLLVM_PATH 環境変数を手動で設定してください."
"詳細は docs/02_ENV/ENV_02_環境構築手順.md を参照してください."
)
return None
def log_variant_status(logger: logging.Logger, requested_variant: str, actual_variant: str) -> None:
"""variant 設定後の状態を INFO / WARNING でログ出力する.
actual_variant が requested_variant と異なる(scalar fallback 等)場合は
WARNING で明示する.
Args:
logger: 出力先の Logger オブジェクト.
requested_variant: 要求した variant 文字列(YAML の variant フィールド等).
actual_variant: `mi.variant()` で確認した実際の variant 文字列.
"""
if actual_variant.startswith("llvm_"):
llvm_path = os.environ.get("DRJIT_LIBLLVM_PATH", "(未設定)")
logger.info(
"Mitsuba variant=%s, LLVM JIT 経路で実行(DRJIT_LIBLLVM_PATH=%s)",
actual_variant,
llvm_path,
)
elif actual_variant.startswith("cuda_"):
logger.info("Mitsuba variant=%s, CUDA GPU 経路で実行", actual_variant)
elif actual_variant.startswith("scalar_"):
logger.info("Mitsuba variant=%s, scalar 経路で実行(単スレッド)", actual_variant)
else:
logger.info("Mitsuba variant=%s", actual_variant)
# 要求が llvm_* なのに scalar_* に落ちている場合は WARNING を追加する
if requested_variant.startswith("llvm_") and actual_variant.startswith("scalar_"):
logger.warning(
"要求 variant=%s に対し実 variant=%s"
"(LLVM 経路に乗れていません."
"LLVM-C.dll の場所と DRJIT_LIBLLVM_PATH を確認してください)",
requested_variant,
actual_variant,
)