
Wine上でZigのrealpathAllocがerror: Unexpectedになる原因と、壊れないパス処理の実践解
LinuxでWindows向けにクロスコンパイルしたZigバイナリをWineで動かしたら、std.fs.cwd().realpathAlloc(...)だけがerror: Unexpectedで落ちる――この現象は「あなたのコードが間違っている」というより、realpath系APIが抱える設計上・実装上の地雷と、Wine環境の“Windowsらしさ”の差が正面衝突して起きがちです。ここでは、なぜそうなるのかを整理し、実運用で壊れにくい代替案に置き換える方法をまとめます。 Ziggit+1
何が起きているか:Linuxでは正常、WineのWindows実行だけが失敗する理由
realpathAllocは「相対パスを絶対パスへ」「./..の解決」「シンボリックリンクの解決」などを“実体に即して”行おうとします。ここがポイントで、ファイルシステムやOS APIの挙動差を強く受けます。
-
Linuxネイティブ実行:POSIX寄りの前提でうまくいく
-
Windowsターゲット + Wine:Windows API(パス正規化、ドライブ、UNC、再解析ポイント等)を介するが、Wine側の実装や環境差で“想定外”が起こりやすい
さらにZig標準ライブラリ側でも、realpath相当は昔から挙動不一致・バグ・仕様の難しさが指摘され、そもそも標準から外したい議論まであります。つまり、Wineが悪い/Zigが悪いという単純な話ではなく、「実体ベースの正規化」はクロス環境で再現性が取りにくいのが本質です。 GitHub+2GitHub+2
error: Unexpectedが出るときに“本当に困る”ポイント
error: Unexpectedは多くの場合、「本来ならもう少し具体的なエラー(FileNotFound等)に落とせるのに、標準ライブラリが予期しない枝に入った」タイプの失敗です。加えて表示の末尾に
-
Unable to dump stack trace: Unexpected
が出ることがあり、これがデバッグをさらに難しくします。標準ライブラリのスタックトレース周りも、環境(作業ディレクトリやパス解決)で崩れる報告があります。 GitHub+1
結論:realpathAllocを“使わない設計”に寄せるのが最短で堅い
最も堅い対策はシンプルで、**「絶対パス文字列を必要としない形に設計を寄せる」**ことです。Zigが推奨しがちな流儀でもあり、標準ライブラリ側の方向性とも一致します。 GitHub+1
以下、実務で効く代替案を優先度順に紹介します。
代替案1:ディレクトリハンドル(std.fs.Dir)を基準に処理する
「cwdからの相対パスを絶対化したい」動機の多くは、結局のところ**“その場所のファイルを開きたい”**だけです。ならば、パス文字列を正規化するより、基準ディレクトリを開いてそこから辿る方が堅いです。
-
const dir = try std.fs.cwd().openDir("path", .{}); -
try dir.openFile("file.txt", .{}); -
try dir.makePath("sub/dir");
この方式だと、Wineのパス正規化差分を踏みにくく、テストもしやすいです。
代替案2:“文字列としての”正規化で済ませる(実体解決しない)
もし欲しいのが「ログ表示のためにそれっぽく整えたパス」や「..を消したい」程度なら、実体(シンボリックリンク等)に触るrealpathは過剰です。
-
std.fs.path.resolveなど、純粋な文字列操作で整形する -
ただし、これは「存在確認」や「リンク解決」をしないので、用途を限定する
“実体に触れない”ので、Wineの再解析ポイントや特殊パスで死ににくいのが利点です。
代替案3:Windows専用のAPIで絶対パスが必要な場合だけ分岐する
どうしても外部ツール連携やGUI、ログ要件で「Windowsの絶対パス(C:\...)が必須」なケースはあります。その場合は、
-
WindowsではWindows API相当(例:
GetFullPathNameW系)で解決 -
LinuxではPOSIX寄りで解決
-
Wineテスト時は「Windows分岐」で動くかを見る
という形で、ターゲットOSに即した実装に寄せるのが安全です。realpath一発で全部片付けようとすると、まさに今回のような差分で苦しみます。
代替案4:テスト環境を“Wine前提”から一段だけ現実に寄せる
Wineは非常に便利ですが、ファイルシステムまわりは「完全にWindowsと同じ」ではありません。再現性を上げるなら、
-
実Windows(VMでも可)で1回は通す
-
CIにWindowsランナーを入れる
-
Wineは“動作の目安”として扱い、パス系は特に過信しない
という運用が結果的に早いです。Wine上でZigやWindows API絡みの問題が起きる報告自体もあります。 GitHub
ありがちな落とし穴チェックリスト
realpathAlloc系でハマりやすい条件を先回りで潰すと、原因切り分けが早くなります。
-
指定した相対パスがWine実行時のcwdから見て本当に存在しているか
-
Windowsパス区切り(
\)とZig内の表現(/)の混在 -
ドライブレター、UNC、特殊ディレクトリ(
C:だけ等)を踏んでいないか -
シンボリックリンク/ジャンクションの解決を要求していないか
-
実行時にcwdが変わる設計(起動スクリプトや親プロセス都合)になっていないか
まとめ:壊れない方針は「絶対パス文字列を最終手段にする」
-
realpathAllocはクロス環境、とくにWineで不安定になりやすい -
目的が「ファイルを扱うこと」なら、Dir基準(ハンドル基準)に設計を寄せるのが最強
-
表示用なら“文字列正規化”で十分なことが多い
-
どうしても必要なときだけWindows専用の方法に分岐する
この方針に切り替えるだけで、今回のerror: Unexpectedだけでなく、将来のパス周りの不具合(環境依存・再現困難)をまとめて減らせます。 GitHub+1