概要
VSCodeのtasks.jsonに複数のコンパイラでコンパイル・ビルドするタスクを登録することで,複数コンパイラの利用を容易にする方法を説明します.また,ショートカットキーの設定についても言及します.
背景
Fortranで開発を行っていると,様々なコンパイラを利用する機会があります.
普段(プライベート)では開発にgfortranを行い,業務ではIntel FortranあるいはNAG Fortran,スーパーコンピュータ上ではFujitsu Fortran,GPUで実行するプログラムの開発はnvfortranを使うという状況があり得ます.
各種Fortranコンパイラは,規格のサポート状況が異なっていたり,独自拡張が行われていたりするので,記述したソースファイルが一つのコンパイラでコンパイル・ビルドができたからといって,他のコンパイラでコンパイルできる(あるいはコンパイルできたとしても正しく動作する)とは限りません.また,Intel Fortranを対象に開発したコードがnvfortranで動かず,nvfortranで動くように修正したコードが,次はIntel Fortranで動かないということもあり得ます.
一つのコンパイラだけで開発を継続すると,他のコンパイラでコンパイル・ビルドする際に,非常に多くの手直しが必要になります.そのため,開発の初期段階から,(入手が可能であれば)複数のコンパイラでコンパイル・ビルドを行い,動作確認した方が後々の手間を削減できます.
VSCodeのタスクの機能を活用して,複数のコンパイラで同時にビルドを行う方法,コンパイラを切り替える方法を説明します.
環境
- Windows 10
- VSCode 1.52.0
- gfortran 8.1.0
- intel fortran 2021.1
- Visual Studio 2017
Windows上で,かつ無料で利用できるコンパイラのみを用いてテストしていますが,異なるのはコンパイルのコマンド,オプションのみなので,異なるコンパイラやOSでも動作します.
サンプルプログラム
異なるコンパイラでコンパイルされてた事が確認できるように,下記のようなプログラムをサンプルとして利用します.
program main
implicit none
#if __INTEL_COMPILER
print *, "Intel Fortran"
#endif
#if __GFORTRAN__
print *, "gfortran"
#endif
end program main
プリプロセッサディレクティブ
サンプルプログラム中の#if
~#endif
はプリプロセッサディレクティブです.プリプロセッサとは,ソースファイルをコンパイルする前に,ソースファイルを加工するプログラムのことです.プリプロセッサディレクティブは,そのプリプロセッサにどのようにソースファイルを加工するかを指示します.
#if
の後ろに置かれた識別子が定義され,かつ0以外の値であれば,#if
~#endif
で挟まれた範囲はプリプロセッサによって何も処理されません.識別子が定義されていないか,定義されていてもその値が0なら,#if
~#endif
で挟まれた範囲はプリプロセッサによって削除されます.
Intel Fortranでコンパイルする際は,識別子__INTEL_COMPILER
が定義され,その値はコンパイラのバージョン(この記事の環境では2021)になります.
gfortranでコンパイルする際は,__GFORTRAN__
が定義され,その値は1になります.
識別子の値は,#if
~#endif
で挟まれた範囲の中であれば,print
文などを使って確認できます.
#if __INTEL_COMPILER
print *, __INTEL_COMPILER
#endif
#if __GFORTRAN__
print *, __GFORTRAN__
#endif
コンパイルオプション
Fortranは,標準でプリプロセスが動作しないので,プリプロセスを行うにはコンパイルオプションが必要です.Intel Fortranの場合は/fpp
,gfortranの場合は-cpp
をコンパイルオプションとして付与します.
ビルドの設定
tasks.jsonの作成
メニューバーのターミナル→ビルドタスクの実行を選択するか,ショートカットキーCtrl+Shift+B
を入力すると,コマンドパレットに実行するビルドタスクはありません.ビルドタスクを構成するというメッセージが現れるので,それを選択→テンプレートからtasks.jsonを生成→Othersと選んでいくと,tasks.json
が作られます.
tasks.json
に,gfortranでビルドするタスクと,Intel Fortranでビルドするタスクを追加します.
gfortranを使ったビルド
gfortranを使ってビルドするためのタスクを,下記のように設定しました.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build using gfortran",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"env": {
"PATH": "C:\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin"
},
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"command": "gfortran.exe",
"args": [
"-cpp",
"-o",
"${workspaceFolder}\\${fileBasenameNoExtension}_gnu.exe",
"${fileBasename}",
],
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"problemMatcher": [],
},
]
}
"label"
はタスクの名前です.
"type"
は"shell"
にして,gfortran.exeがあるフォルダにPATHを通すために"options"
で指定します.
"cwd"
はタスクを行うフォルダです.VSCodeの変数を用いて,VSCodeで開いているフォルダ名を指定しています.
"env"
で環境変数を指定します.ここでは,環境変数PATH
の値として,gfortran.exeのあるフォルダのパスを与えました.
"shell"
は,使用するシェルを指定します.
"command"
がコンパイラの実行ファイル名,"args"
がコンパイルオプションです.コンパイルオプションには,プリプロセスを有効化するオプションを忘れないように与えます."-o"
で出力される実行ファイル名を指定しています.これは,他のコンパイラで作成される実行ファイルに上書きされないようにするためです.
"presentation"
は必須の設定ではありませんが,"focus": true
を設定しておくと,ビルド時に統合ターミナルにフォーカスが移るので,ビルドタスクが終了した後,Enterキーを押せばターミナルを閉じつつエディタに戻れます.
"problemMatcher"
は,タスクのエラーが発生した際に,エラーメッセージをどのような形式で処理するかを指定します.ここでは何も指定しません.何も指定はしないのですが,この設定を書いておかないと,タスクを実行する度に,スキャンするタスク出力のエラーと警告の種類を選択する必要が生じます.
Intel Fortranを使ったビルド
Intel Fortran (+Visual Studio 2017)を使ってビルドするためのタスクを,下記のように設定しました.PATHの設定が非常に長くなるので省略しています.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build using gfortran",
省略...
},
{
"label": "build using intel fortran",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"env": {
"PATH": "省略...",
"LIB": "省略...",
"INCLUDE": "省略..."
},
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"command": "ifort.exe",
"args": [
"/fpp",
"/o",
"${workspaceFolder}\\${fileBasenameNoExtension}_intel.exe",
"${fileBasename}",
],
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"problemMatcher": [],
},
]
}
WindowsでIntel Fortranを使う場合は,プログラムをビルドするのにVisual Studioが必要です.そして,環境変数として,PATH以外にLIBとINCLUDEも必要です.
どのフォルダにPATHが通されるかはすぐには判りませんが,確認する方法があります.
まずはコマンドプロンプトを起動して,echo %PATH%
で登録されているPATHを表示します.
次に,スタートメニューのIntel oneAPIのフォルダにあるコマンドプロンプト(Intel oneAPI command prompt for Intel 64 for Visual Studio 2017)を起動し,同様にecho %PATH%
で登録されているPATHを表示した後,通常のコマンドプロンプトで表示されたPATHとの差を抽出します.
通常のコマンドプロンプトのPATHに登録されておらず,Intel oneAPI command prompt for Intel 64 for Visual Studio 2017のPATHに登録されているフォルダが,Intel Fortranでコンパイルするのに必要なフォルダということです.
そのフォルダのパスを,上のタスクにあるoptions.env.PATH
に登録します.
同様に,Intel oneAPI command prompt for Intel 64 for Visual Studio 2017上でecho %LIB%
,echo %INCLUDE%
を実行して,表示されたフォルダパスを,それぞれoptions.env.LIB
,options.env.INCLUDE
に登録します.
登録したタスクの実行
タスクを二つ登録したので,実行してみます.
ショートカットキーCtrl+Shift+P
を入力してコマンドパレットを呼出し,runtask
と入力すると,タスク:タスクの実行というメニューが表示されるので,それを選択すると,登録した二つのタスクの名前が表示されます.
"build using gfortran"を選択するとgfortranでコンパイル・ビルドされ,"build using intel fortran"を選択するとIntel Fortranでコンパイル・ビルドされます.
作成された実行ファイルを実行すると,プリプロセッサディレクティブによって表示が切り替わっていることが確認できます.
>main_gnu.exe
gfortran
>main_intel.exe
Intel Fortran
複数のタスクの設定
一つのtasks.json
ファイルに,異なるコンパイラでコンパイル・ビルドする設定を書くことができました.しかし,一つずつタスクを呼び出すのも億劫ですし,そのうち一つのタスクしか呼び出されなくなることは想像に難くありません.また,上記のtasks.json
は,標準のビルドタスクが登録されていないので,いちいちコマンドバレットの呼出し→タスクの選択をする必要があります.
メニューのターミナル→ビルドタスクの実行を選択するか,ショートカットキーCtrl+Shift+B
を入力したときに,ここで登録した二つのビルドタスクを同時に呼び出せるようにします.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "build using gfortran",
省略...
},
{
"label": "build using intel fortran",
省略...
},
{
"label": "build all",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}"
},
"command": "echo",
"args": [
"Build using all compilers done."
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"problemMatcher": [],
"dependsOn": [
"build using gfortran",
"build using intel fortran"
]
}
]
}
"build all"
というタスクを登録しました.このタスクでは,実際にはBuild using all compilers done.
を表示するだけです.このタスクで重要なのは,"group"
と"dependsOn"
です.
"group"
のプロパティで"kind": "build"
,"isDefault": true
とすることで,このタスクが標準のビルドタスクという扱いになります.メニューのターミナル→ビルドタスクの実行を選択するか,ショートカットキーCtrl+Shift+B
を入力したときに,このタスクが実行されます.
"dependsOn"
は,このタスクを実行する前に実行されるタスクを指定します.ここでは,"build using gfortran"
と"build using intel fortran"
を指定しているので,この二つのタスクが実行されます.二つのタスクに依存性が無い場合は,二つのタスクが同時に実行されます.
タスクを実行すると,ターミナルが3個に増えます.一つ目が通常のターミナル,二つ目と三つ目が"dependsOn"
で指定したタスクを行っているターミナルです.これらのタスクは,終了後にEnterキーの入力を待つような設定になっているので,Enterキーを入力すればターミナルごと終了します.
"dependsOn"
には2個以上のタスクを書けるので,タスクを増やしても問題ありません.
ショートカットキーの設定
複数のタスクを設定して異なるコンパイラでコンパイル・ビルド出来るようになりました.一方で,一つのコンパイラでのコンパイル・ビルドが非常にやりにくくなりました.
例えば,gfortranでビルドしようとすると,Ctrl+Shift+P
でコマンドパレットを呼び出し,runtaskを入力してタスク:タスクの実行を呼び出してから,build using gfortran
タスクを選択する必要があります.
ショートカットキーを選択しようにも,項目が見当たりません.タスク:タスクの実行は,内部的にはworkbench.action.tasks.runTaskという名前が付けられています.ファイル→ユーザー設定→キーボードショートカットでショートカットキーの一覧を開き,runtaskを検索しても見つかりません.
workbench.action.tasks.runTaskにショートカットキーを設定するには,keybindings.json
を直接編集する必要があります.キーボードショートカットの一覧を開き,右上の方にある書類マークをクリックするとkeybindings.json
が開くので,キー入力と実行するコマンドを入力します.
{
"key": "ctrl+t ctrl+b",
"command": "workbench.action.tasks.runTask",
},
ここでは,Ctrl+t, Ctrl+b
を割り当てました.workbench.action.tasks.runTask
のショートカットキーの設定には,本来はもう一つarg
というプロパティを設定する必要があります1.標準で使うコンパイラを定め,そのコンパイラでビルドするタスクをarg
に設定すれば,ショートカットキー一発でコンパイル・ビルドできるようになります.arg
を設定しないと,タスクを選択するメニューが呼び出されます.どちらのコンパイラを使うかを予め決めたくない,あるいはビルド毎に切り替えたい等の場合は,arg
を設定しない方が都合がよいでしょう.
workbench.action.tasks.runTask
にarg
を設定しない場合は,このショートカットキーに加えて,タスク:最後のタスクを再実行(workbench.action.tasks.reRunTask
)にもショートカットキーを設定しておくと,さらに便利になります.設定は,ショートカットキーの一覧を開き,rerunを検索します.
ある機能を開発していて,しばらくはgfortranでコンパイルをしたいと思ったときは,まずショートカットキーでタスク:タスクの実行(workbench.action.tasks.runTask
)を実行して,build using gfortran
タスクを実行します.それ以降は,タスク:最後のタスクを再実行(workbench.action.tasks.reRunTask
)に割り当てたショートカットキーを入力すれば,常にbuild using gfortran
が実行されます.
まとめ
Fortranを用いた開発において複数のコンパイラを用いる場面がある背景を述べ,VSCodeで複数のコンパイラによるビルドを簡単に行う方法を説明しました.
fpm(Fortran package manager)では,まだコンパイラの切替には対応していないので,fpmの開発を待つ間,VSCodeを上手く使って開発の効率化を図っていきましょう.