Open9

aqua の Windows サポートでシンボリックリンクにまつわる問題をどうするか検討する

aqua の Windows サポートを進めていますが、そこでシンボリックリンクにまつわる問題が出てきたので、
どうやって解決するか検討しています。

https://github.com/orgs/aquaproj/projects/4
https://github.com/aquaproj/aqua/issues/885

ちなみに自分は普段 Windows を使っていないのであまり Windows に関する知識がありません。

aqua はシンボリックリンクを作成します。

(AQUA_ROOT_DIR)/bin/<command> => (AQUA_ROOT_DIR)/bin/aqua-proxy => (AQUA_ROOT_DIR)/pkgs/.../aqua-proxy

問題は 2 つあります。

  1. シンボリックリンクを作成するのは、 terminal (cmd.exe, powershell, git bash, etc) を管理者として実行するか、開発者モードを有効にする必要がある

https://www.ipentec.com/document/windows-windows-10-set-developer-mode

  1. シンボリックリンクが連鎖していると、 PowerShell や cmd.exe ではコマンドが実行できない(シンボリックリンクをたどってくれない。ただし Git Bash では動く)

https://github.com/PowerShell/PowerShell/issues/16171

aqua を使うために terminal を管理者として実行するか開発者モードを有効にする必要があるとしたら、
それはだいぶ制約が大きいのではないかという気がしています。
ただシンボリックリンクを作るようなツールは aqua 以外にもある気はするので、他のツールはどうしているのか気になります。
もし上記の制約がユーザーにとって特に問題にならないのであれば助かるのですが、どうなんでしょう。

2 に関しては PowerShell では Issue を見つけました。 cmd.exe はわかりませんが、同様の問題かなという気はします。
PowerShell 側の修正が直ぐ入ることは期待できないので、 aqua 側で対応するとしたら

  1. シンボリックを使わない方式にする
  2. シンボリックリンクを連鎖しないようにする

かのどちらかでしょう。どちらにせよ、 aqua 本体に修正が必要です。
どちらにせよ修正が必要なら 1 のほうがシンボリックリンクの問題を解決できるので良い気はしますが、
1 ではシンボリックリンクの代わりにスクリプトを配置する感じになるので、あまりやりたくはありません(だからこそ現状シンボリックリンクと aqua-proxy を使っています)。
やりたくはないですが、現状やるしかないかなという気はしています。
2 は可能ではありますが、結局シンボリックリンクの問題1 を解決できないことには仕方ありません。

Windows の bat と格闘

以下のファイルを gh.bat という名前でシンボリックリンクの代わりに配置すると gh が実行できます。
これは aqua-proxy がやってることとほぼ同等です。
ただし、 %0 の値が gh.bat になってしまうので .bat を除去する必要があります。 aqua 側で .bat を消すという手もなくはないです。

@echo off
aqua exec -- %0 %*

あるいはファイル生成時に %0 を gh に展開するのもありといえばありな気はします。

こうした場合に process tree やシグナルハンドリングがどうなるのかは気になります。
bat のプロセスを kill したとしてちゃんと子プロセスが kill されるのかとか

bin 配下に gh.bat を置いてもなぜか実行できない

$ ls -lh ~/AppData/Local/aquaproj-aqua/bin/
total 1.0K
-rw-r--r-- 1 shunsukesuzuki 1049089 29 Jun 19 09:14 gh.bat

shunsukesuzuki@WSAMZN-8KCTT8DQ MINGW64 ~/workspace/powershell
$ gh version
bash: gh: command not found

PowerShell 環境変数

$Env:Path = "hoge" + $Env:Path

cmd.exe 環境変数

set PATH=hoge;%PATH%

Git Bash と PowerShell, cmd.exe を両方サポートする苦肉の策

上記の通り Windows ではシンボリックリンクをやめてスクリプトを PATH 配下に置くことにします。
シンボリックリンクの代わりに Go のバイナリを置くのはファイルサイズを無駄に喰うのでしません。

Go でバイナリをビルドした場合って、 main が空でも 1MB は消費するんですね。
まぁもっとスリムにする方法があるかもしれませんが、どのみちスクリプトのほうが軽量だと思うので深追いはしません。

スクリプトと言っても Python や Ruby, Node.js といった処理系には依存したくないので、
BAT を採用します。スクリプトと言っても 2 行です。

@echo off
aqua exec -- gh %*

これを AQUA_ROOT_DIR/bin 配下に置こうと思ったのですが、それだと Git Bash で問題になりました。
Windows に BAT として認識させるには拡張子 .bat を付ける必要があります。
拡張子をつけずに gh として実行しようとすると、 Windows がなんのプログラムで実行するか聞いてきて実行できません。
gh であれば gh.bat になります。それでも PowerShell や cmd.exe では拡張子を省略して gh で実行できるのですが、 Git Bash では拡張子を省略できないので gh ではコマンドを実行できませんでした。
Git Bash でも実行できるようにするとなると .exe 形式にする必要がありますが、スクリプトをそのまま .exe に拡張子を変更しても実行できませんし、なんかのスクリプトで書いたものを .exe にビルドするのは割と面倒です。

解決策

そこで Windows では AQUA_ROOT_DIR/bin とは別に BAT ファイルを配置する AQUA_ROOT_DIR/bat ディレクトリを生成します。
PowerShell や cmd.exe では bat ディレクトリを PATH に追加します。
bin 配下には 2 行のシェルスクリプトを配置します。

#!/usr/bin/env bash
exec aqua exec -- $0 $@

これを gh であれば AQUA_ROOT_DIR/bin/gh に配置して実行権限を付与します。
PowerShell や cmd.exe では実行できませんが、 Git Bash ではこれで実行できるので、 Git Bash ではこちらを PATH に追加します。

aqua は symbolic link を生成する代わりに bin, bat ディレクトリにスクリプトを生成します。

symbolic link を作らないので、恐らく terminal を管理者として実行する必要もないし、開発者モードを有効にする必要もありません(自分の環境ではしないで実行できています)。

ログインするとコメントできます