Chapter 05

WSL2時代のアプリケーション実装(Windowsアプリ編)

WSL2から起動されたWindowsネイティブな開発ツール、という想定で、Linuxアプリとして行儀良く振舞う方法について紹介します。

環境の判定

例えば、SSHするようなコマンドの場合、Windowsアプリであっても親がwsl.exeなら、Linuxのホームの.sshを取ってきて鍵を取得してくると親切に思えます。またログファイルを書き出す場合も、Linuxのパス名に変換して書き出すとか。

フォルダによる判定

Windowsのプロセスが現在の環境がWSL2上かどうかはカレントフォルダが\\wsl$かどうかで判定できるかと思います。現在、Windowsのコマンドプロンプトなどはこのフォルダに入ることはできないため、親プロセスがWindowsのコマンドプロンプトで、たまたま作業フォルダWSL2上にしているだけのWindowsネイティブ環境、というのは状況的にありえませんので、これで今のところ十分かと思います。しかし、今後の機能拡張で入れるようになったら別途対策が必要です。

親プロセスによる判定

  • bashからWiindowsアプリを起動すると親プロセスのコマンドがwsl.exe -d Ubuntu-20.04。VSCodeのターミナル内部でもC:\WINDOWS\System32\wsl.exe -d Ubuntu-20.04 sh -c '"$VSCODE_WSL_EXT_LOCATION/scripts/wslServer.sh" 3dd905126b34dcd4de81fa624eb3a8cbe7485f13 stable .vscode-server 0みたいな感じで、どちらにしてもwsl.exe経由。

ただし、ユーザーがどちらの環境を欲しているかはこれとは限らないのでデフォルト動作としてはこの判定にしておいて、コマンドの引数で強制WSLか強制Windowsが切り替えられるのが良いのではないかと思います。

子プロセスの起動

WindowsからLinuxコマンドを起動するには、wslコマンド経由で行ます。プログラムからもwslを実行コマンドとして、引数にコマンドと引数を渡すと実行できます。特に難しいことはないでしょう。

// cmd string
// args []string
exec.CommandContext(ctx, "wsl", append([]string{cmd}, args...)...)

パスの変換

WSL2環境上でWindows用のアプリが起動されたが、設定ファイルに書き出したファイル名をLinuxアプリで活用したいので、Linuxのパス名を出力しておきたいみたいなケースがあるかもしれません。

wslpathというユーティリティコマンドがあり、Linux上ではこれを使ってパスの変換が行えますが、Windows上からは直接このコマンドを実行できません。wslコマンド経由で使います。

  • wsl wslpath -w (Linuxのパス): Windowsのパスに変換
  • wsl wslpath (Windowsのパス): Linuxのパスに変換

環境変数やホームフォルダの取得

WindowsからLinuxの環境変数を見るには次の方法で行えます。

  • wsl env

ホームフォルダなどの取得はこの環境変数を元に行えばOKです。Linux同様、Goのos.UserCacheDiros.UserConfigDiros.UserHomeDirあたりのコードをみて、同じように実装すれば良いでしょう。

  • ホームは$HOME
  • キャッシュのフォルダは$XDG_CACHE_HOMEがあれば利用し、なければ$HOME/.cache
  • 設定フォルダは$XDG_CONFIG_HOME$HOME/.config

Linuxの登録アプリケーションを使ってファイルを開く

Windows側の同様の機能と比較すると利用する機会はだいぶ限られると思いますが、xdg-openを使って、Linux側で登録されたプログラムを起動できます。PostScriptの.psファイルをghostscriptで閲覧するぐらいでしょうか?

cmd := exec.Command("wsl", "xdg-open", 変換済みパス)
cmd.Run()

まとめ

さまざまな環境をクロスする方法を紹介してきました。ちょっとしたコードであれば、Linux環境の.sshの鍵を参照スルトかぐらいで済むとは思いますが、より大規模なファイルの入出力を行う場合、VSCodeのように(Dockerも?)、環境を確認して親がWSLだった場合は、作業フォルダ情報や設定ファイルのパスなどを変換した上で、Linuxバイナリとして実装されたワーカーに実処理を投げることになるでしょう。