Sublime Text 3 から Clang++14 でビルドして PowerShell 上で実行する

別記事で Windows に Clang++ を導入できたので, Sublime Text 3 から使えるといいなと思ったところ, PackageResourceViewer で開いたデフォルトの C++/C++ Single File.sublime-build を参考にすることで, Clang++14 でビルドして PowerShell 上で実行するビルドシステムを Sublime Text 3 に追加できたよ,という話.

ついでに Terminality の設定をした話も.


別記事でなんとか Windows に Clang++ 3.9.1 を導入できたので,これを Sublime Text 3 から使った.

まず Sublime Text 3 には g++ を使ってビルドする設定がデフォルトで入っているので,それを参考にした. PackageResourceViewer を使うことで,パッケージ化されている C++.sublime-package の中身を覗ける. Package Control を利用してインストールしたのち, Ctrl+Shift+P で PackageResourceViewer: Open Resource を実行して C++ の中の C++ Single File.sublime-build を開くと

{
 "shell_cmd": "g++ \"${file}\" -o \"${file_path}/${file_base_name}\"",
 "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
 "working_dir": "${file_path}",
 "selector": "source.c, source.c++",

 "variants":
 [
  {
   "name": "Run",
   "shell_cmd": "g++ \"${file}\" -o \"${file_path}/${file_base_name}\" && \"${file_path}/${file_base_name}\""
  }
 ]
}

を得た.これをコピーした.

Tools > Build System > New Build System... を開いて C++14.sublime-build の名前で保存し,さっきのをペーストした. shell_cmd の値をClang++を実行するように編集し,C++14を有効化する -std=c++14 と,最適化レベル2にする -O2 をつけた.

ちなみに clang-cl (Visual C++互換版)を使う場合にはエラーメッセージの形式が異なるっぽいので, file_regex の値を(clangとclang-clのエラーメッセージを見比べながら)適切な正規表現に変えれば,該当ファイル上にエラー箇所を表示してくれる機能が壊れずに済みそう.キャプチャリング括弧の1つ目がファイルパス,2つ目が行数,3つ目が文字数,4つ目がエラーの概要に対応するものと思われる(未検証).clangならg++と同じ形式なのでそのままでよかった.

これであとは Tools > Build With... で作ったやつを選べばうまくビルドされた.(なおSTを開きっぱでClangをインストールした場合は当然ながら環境変数の読み直しのためにSTの再起動が必要.頭が悪いのではまった.)

ただし,このままだとclangのエラーメッセージ色付けの恩恵を受けられないのと,実行時に標準入力を与えたい場合に困るのとがあったので,PowerShellを開く設定も足した. start powershell -Command "hogehoge" (終了時に閉じない場合は start powershell /noexit -Command "hogehoge" )とすればPowerShellを開いてhogehogeを実行できるから,hogehogeにclangのコマンドを入れればよい.

また, Windows PowerShell Tip: Press Any Key to Continue によれば,cmdにおける pause に相当する挙動は Write-Host "Press any key to continue ..."; $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") とすれば得られるのでこれも使った.あとどの時点でBuildからRunに移って標準入力待ち受け状態に入るか分かりづらかったので,Buildが終わった段階でメッセージを出力するようにした.

できあがった C++14.sublime-build は以下.

{
 "shell_cmd": "clang++ -O2 -std=c++14 \"${file}\" -o \"${file_path}/${file_base_name}\" -I \"C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/ucrt\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/ucrt/x64\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/um/x64\"",
 "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
 "working_dir": "${file_path}",
 "selector": "source.c, source.c++",

 "variants":
 [
  {
   "name": "Build with Terminal",
   "shell_cmd": "start powershell -Command \"clang++ -O2 -std=c++14 \\\"${file}\\\" -o \\\"${file_path}/${file_base_name}\\\" -I \\\"C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/ucrt\\\" -L \\\"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/ucrt/x64\\\" -L \\\"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/um/x64\\\"; Write-Host \\\"Press any key to continue ...\\\"; \\$x = \\$host.UI.RawUI.ReadKey(\\\"NoEcho,IncludeKeyDown\\\")\""
  },
  {
   "name": "Run",
   "shell_cmd": "clang++ -O2 -std=c++14 \"${file}\" -o \"${file_path}/${file_base_name}\" -I \"C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/ucrt\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/ucrt/x64\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/um/x64\" && \"${file_path}/${file_base_name}\"; pause"
  },
  {
   "name": "Run with Terminal",
   "shell_cmd": "clang++ -O2 -std=c++14 \"${file}\" -o \"${file_path}/${file_base_name}\" -I \"C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/ucrt\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/ucrt/x64\" -L \"C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/um/x64\" && start powershell -Command \"Write-Output \\\"Run\\\"; Write-Output \\\"---\\\"; ${file_path}/${file_base_name}; Write-Host \\\"Press any key to continue ...\\\"; \\$x = \\$host.UI.RawUI.ReadKey(\\\"NoEcho,IncludeKeyDown\\\")\""
  }
 ]
}

PowerShellに与えるコマンドの中のクォートは2度のエスケープを耐え抜く必要があるのでエスケープシーケンス盛り盛りと化した.

おまけ:TerminalityでClang++14ビルド&ラン

Terminality を使うと Sublime Text のタブとしてターミナルっぽいものを用意してくれてとても手軽なのでこれもClang++14版をこしらえた.

Terminality もデフォルトでg++を使うものがついているので, PackageResourceViewer で Terminality > Collections > C++.terminality-collections を開いた:

{
 "execution_units": {
  "source.c++": {
   "run": {
    "name": "Run $file_name",
    "description": "Compile and Run $file_name with default C++ compiler",
    "required": ["file"],
    "command": "g++ $file -o $file_without_ext && $file_without_ext $arguments",
    "macros": {
     "file_without_ext": [
      ["$file", ".*(?=\\.)"]
     ]
    }
   }
  }
 }
}

これを C++14.terminality-collections として SublimeText3\Data\Packages\User 以下に別名保存した.

この設定ファイルにはmacrosという概念があるせいかクォーテーションが正常に効かないので,空白を含むインクルードパスは文字列を返すだけのマクロとして外に出したり,末尾に空白が残るなどの細かい点は無視したり,BuildとRunの境目をわかりやすくするための echo を足したり,ビルドしないで単に実行するコマンドも足したりして,以下の C++14.terminality-collections を得た:

{
 "execution_units": {
  "source.c++": {
   "run_c++14": {
    "order": "0",
    "name": "Run $file_name with Clang++14",
    "description": "Compile and Run $file_name with Clang++14 compiler",
    "required": ["file"],
    "command": "clang++ -O2 -std=c++14 $file -o $file_without_ext -I $inc1 -L $inc2 -L $inc3 && echo $file_without_ext && $file_without_ext $arguments",
    "macros": {
     "file_without_ext": [
      ["$file", ".*(?=\\.)"]
     ],
     "inc1": ["C:/Program Files (x86)/Windows Kits/10/Include/10.0.14393.0/ucrt"], // avoid space handling problem
     "inc2": ["C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/ucrt/x64"],
     "inc3": ["C:/Program Files (x86)/Windows Kits/10/Lib/10.0.14393.0/um/x64"]
    }
   },
   "run_without_build": {
    "order": "1",
    "name": "Run $file_name (No build)",
    "description": "Run $file_name without build",
    "required": ["file"],
    "command": "$file_without_ext $arguments",
    "macros": {
     "file_without_ext": [
      ["$file", ".*(?=\\.)"]
     ]
    }
   }
  }
 }
}

これで,C++ファイル上で Ctrl+Alt+R すれば作ったものを選べてTerminal風タブを開けた.なお, order をセットするとコマンド選択肢のソーティングのための読み仮名みたいなものになるっぽかったので雑に使って1番目と2番目になるようにした.快適さを感じる.

ちなみに $file_without_ext はコマンド選択肢表示時にはまだセットされないようだったので name や description 内に使うのを諦めた.