概要
本記事では,fpmとVSCodeを連携させて,コマンド入力を簡略化する方法を説明します.
過去の記事の更新版です.
fpmの基本的な使い方は下記の記事を参考にしてください.
- fpm (Fortran Package Manager)の使い方
- fpm (Fortran Package Manager)の設定ファイルの解説
- fpm (Fortran Package Manager)のプロジェクトの作成例
環境
- Windows 10
- fpm 0.5.0
- gfortran 10.3.0 (TDM-GCC-64)
- VSCode 1.62.3
- Modern Fortran拡張 2.6.2
Windowsで実行し,動作確認をしています.パスの区切りは\
になっています.Unix系OSで動かす場合には,ドライブレターを無視し,パスの区切りを/
に置き換えてください.
題材
fpmでビルドするプロジェクトとして,fpm (Fortran Package Manager)のプロジェクトの作成例で作成した,non_number
プロジェクトを少し改変して利用します.
non_numberプロジェクトの構造
non_number
プロジェクトのうち,src
, test
, example
がfpmプロジェクトのディレクトリです.それ以外に依存プロジェクトであるcheck
プロジェクトがutil
ディレクトリに,外部ライブラリであるstdlibがlib
, include
ディレクトリに置かれています.
non_number
ライブラリにアプリケーションを追加しているところが改変点です.
.
├── README.md
├── app
│ └── main.f90
├── example
│ └── demo_is_nan.f90
├── fpm.toml
├── include
│ └── stdlib_stats.mod
├── lib
│ └── libstdlib.a
├── src
│ └── non_number.f90
├── test
│ ├── test_is_nan_real32.f90
│ └── test_is_nan_real64.f90
└── util
└── check
├── README.md
├── fpm.toml
└── src
└── check.f90
アプリケーション(app\main.f90
の中)では,4要素の配列の中にNaN
を一つ代入し,is_nan
関数でそれを検出しています.
program main
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use :: non_number
implicit none
real(real32) :: f(4) = [1., 2., 3., 4.]
logical :: f_nan(4)
f(3) = ieee_value(f(3), ieee_quiet_nan)
block
integer(int32) :: i
do i = 1, size(f)
f_nan(i) = is_nan(f(i))
end do
end block
if (all(f_nan .eqv. .true.)) then
print *, "f does not have nan"
else
print *, "f has nan"
end if
end program main
プロジェクトのビルドとテスト
fpm build
,fpm run
およびfpm test
コマンドを実行し,プロジェクトが正しく実行できるのかを確認します.
> fpm build --profile debug --flag "-Iinclude" --link-flag "-Llib -lstdlib"
> fpm run --profile debug --flag "-Iinclude" --link-flag "-Llib -lstdlib"
f has nan
> fpm test --profile debug --flag "-Iinclude" --link-flag "-Llib -lstdlib"
is_nan returns true for fp32 number that is nan
True
is_nan returns false for fp32 number that is not nan
True
is_nan returns true for fp64 number that is nan
True
is_nan returns false for fp64 number that is not nan
True
アプリケーションでは,f
の中にあるNaN
を検出できています.テストも全てPassしており,アプリケーション,テスト共に正しく実行できています.
しかし,実行コマンドを見て判るように,fpmではbuild
, run
, test
の際にすべて同じコンパイラオプションを付ける必要があります.これをコマンド入力するのは煩わしいので,VSCodeと連携して簡略化します.
VSCodeからfpmを使う
VSCodeのタスクを登録し,タスクを呼び出すことでfpmのコマンド入力を簡略化します.
いくつかのタスクはショートカットキーで呼び出せますし,タスク呼出し時に未保存のソースファイルを自動で保存してくれるなど,タスクとして登録することの利点は地味ですが無視できないと感じています.
Modern Fortran拡張のエラー表示への対処
VSCodeを使うので,VSCodeのFortran拡張についても少し触れておきます.
non_number
プロジェクトのディレクトリをVSCodeでを開くと,サブディレクトリやファイルにアクセスできるようになります.
ここで,ファイル名が赤く表示されているのは,Modern Fortran拡張がファイル内でエラーを検出している状態です.app\main.f90
内で,non_number
モジュールをuse
するところで,non_number.mod
が見つからないというエラーが生じています.
Modern Fortran拡張を用いると,モジュールとなるソースファイルを開いた時点で,モジュール名.mod
というファイルが自動的に作成されます.src
ディレクトリやutil\check\src
ディレクトリに表示されているモジュールファイルがそれらに相当します.Modern Fortran拡張は,それを利用してモジュールが存在するか,存在していればモジュール内で定義されている手続などを確認しています.fpmのプロジェクトではディレクトリ構造が定められているので,.mod
ファイルが必ずしも同じ場所にあるとは限りません.
そこで,Modern Fortran拡張の設定を利用して,.mod
ファイルが出力されるディレクトリを設定します.
fpmプロジェクトディレクトリ直下に.mod
ディレクトリを作成し,そこに.mod
ファイルが出力されるよう設定します.Modern Fortran拡張をプロジェクトごとに設定するには,fpmのプロジェクトディレクトリ下に,.vscode\settings.json
ファイルを作成し,設定を記述します.既に存在している場合には既存のsettings.json
に追記します.
{
"fortran.linterModOutput": ".modディレクトリへのフルパス"
}
fortran.linterModOutput
に,.mod
ファイルを出力したいディレクトリを指定します.例えば,プロジェクトディレクトリがD:\non_number
の場合,下記の様になります.Windowsでは,パスの区切りが\
であるため,エスケープして\\
と書きます.Unix系OSの場合はパスの区切りを/
に置き換えるだけで問題ありません.
{
"fortran.linterModOutput": "D:\\non_number\\.mod"
}
相対パスは受け付けてくれません.VSCodeを使っているのであれば,ディレクトリを右クリックし,パスのコピーを選択すれば,簡単にフルパスを取得できます.
既に.mod
ファイルがソースファイルと同じ場所に作られてしまっている場合,それらのファイルを.mod
ディレクトリに移動します.あるいは,既に作られた.mod
ファイルを削除した後に,モジュールとなるソースファイルを開くと,.mod
ディレクトリにモジュールファイルが出力されることが確認できます.
参照されるモジュールファイルが全て作られた後,エラーが表示されていたソースファイル(app\main.f90
)を開き,保存するとエラーが消えます.このとき,ファイルを変更する必要はなく,ctrl
+s
を入力する等,保存する動作を行うだけで構いません.
ビルドタスクの設定
VSCodeのビルドタスクを設定し,ショートカットキーでビルドできるようにします.設定の方法は何通りかありますが,.vscode\tasks.json
に設定を記述します.
[ターミナル]→[既定のビルドタスクの構成...]→[テンプレートからtasks.jsonを生成]→[Others]を選択し,tasks.jsonを作成します.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "echo",
"type": "shell",
"command": "echo Hello"
}
]
}
"label": "echo"
から始まる3行を消して書き換えます.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "fpm build",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"group": {
"kind": "build",
"isDefault": true
},
"command": "fpm",
"args": [
"build",
"--profile",
"debug",
"--flag",
"\"-Iinclude\"",
"--link-flag",
"\"-Llib -lstdlib\""
]
}
]
}
ここでは,重要な設定が4個あります.
-
"command": "fpm"
で,ビルドタスクとして実行されるコマンドを指定します.ここではfpm
を指定しています. -
"args": [...]
で,fpm
に渡すサブコマンドおよびオプションを指定します.過去の記事では,"command"
にfpm build
を書いていましたが,VSCodeの仕様かfpmの仕様が変更になったため,サブコマンドはargs
に書くことになりました. -
"options": {"cwd": "${workspaceRoot}"}
で,ビルドタスクが実行されるディレクトリが,必ずfpmプロジェクトのルートディレクトリとなるように指定しています. -
"group": {"kind": "build","isDefault": true}
で,標準のビルドタスクであることを設定しています.この設定を行うことで,キーボードショートカット(ctrl
+shift
+b
)を利用してこのタスクを起動できるようになります.
なお,"options":
にshell:
の項目はWindowsのみで必要で,Unix系OSの場合は必要ありません.この設定がないと,下記のようなメッセージが出力され,ビルドタスクが実行されたまま戻ってこなくなります.
> Executing task: fpm build --profile debug --flag "-Iinclude" --link-flag "-Llib -lstdlib" <
パラメーターの書式が違います - /d
ctrl
+shift
+b
でビルドタスクを実行すると,ターミナルが表示され,fpm build
を実行した時の結果が表示されます.
テストタスクの設定
ビルドタスクと同様にして,テストタスクも構成できます.
ビルドタスクをコピペして,"label"
,args
のサブコマンド,"group"
の"kind"
を編集します.
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "fpm build",
中略
},
{
"label": "fpm test",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"group": {
"kind": "test",
"isDefault": true
},
"command": "fpm",
"args": [
"test",
"--profile",
"debug",
"--flag",
"\"-Iinclude\"",
"--link-flag",
"\"-Llib -lstdlib\""
]
}
]
}
"label"
を"fpm test"
に,"group"
の"kind"
を"test"
に,"args"
のサブコマンドを"test"
に変更しました.
デフォルトのテストタスクを実行するには,ctrl
+shift
+p
を入力してコマンドパレットを呼び出し,タスク: テスト タスクの実行
を選択します.タスク: テスト タスクの実行
は,コマンドパレットに"run test task"と入力すると絞り込まれます.
テストタスクを実行するショートカットキーは定められていないので,各自で設定する必要があります.設定の方法は過去の記事を参考にしてください.
その他のタスクの設定
fpm run
ビルドしたアプリケーションを実行する場合もビルドと同じオプションが必要になるので,設定しておいた方が楽はできます.ビルドタスクをコピペしてテストタスクを作成したように,コピペして設定を若干変更します.
変更点は,下記の通りです.
-
"label"
を"fpm run"
に変更 -
"group"
を: "none"
に変更- 特定のタスクグループには属さないタスクとして登録します.
-
"args"
のサブコマンドを"run"
に変更
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "fpm build",
中略
},
{
"label": "fpm test",
中略
},
{
"label": "fpm run",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"group": "none",
"command": "fpm",
"args": [
"run",
"--profile",
"debug",
"--flag",
"\"-Iinclude\"",
"--link-flag",
"\"-Llib -lstdlib\""
],
"problemMatcher": []
}
]
}
タスクを実行するには,ctrl
+shift
+p
を入力してコマンドパレットを呼び出し,実行するタスクを選択します.コマンドパレットに"run task"と入力すると,タスクの一覧が表示されるので,実行したいタスク(fpm run)を選択します.
タスクを実行する際に,スキャンするタスク出力のエラーと警告の種類を選択
というメニューが表示された場合は,タスクの出力をスキャンせずに続行
を選択します.このメニューを表示しないようにするには,タスクの設定に"problemMatcher": []
を追加します.
fpm install
fpm (Fortran Package Manager)の使い方で解説していますが,fpmはコンパイラやビルドオプションに応じて異なるビルドディレクトリ名を作成し,その中でビルドを実行します.そのため,オプションやコンパイラを切り替えてビルドしていると,デバッグするバイナリがどこにあるのか判らなくなります.デバッグ用の設定ファイル("launch.json")に実行するバイナリを記述したくても,パスが判らないという事態に陥ります.
そこで,.\build\bin
にビルドしたバイナリをコピーし,それをデバッガに渡すことで設定の共通化を図ります..\build\bin
にビルドしたバイナリをコピーするために,fpm install
をタスクとして登録します.
fpm run
をコピペし,若干変更します.変更点は,下記の通りです.
-
"label"
を"fpm install"
に変更 -
"args"
のサブコマンドを"install"
に変更 -
"args"
にインストールディレクトリを指定するオプション"--prefix", "./build"
を追加
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "fpm build",
中略
},
{
"label": "fpm test",
中略
},
{
"label": "fpm run",
中略
},
{
"label": "fpm install",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}",
"shell": {
"executable": "${env:windir}\\system32\\cmd.exe",
"args": [
"/d",
"/c"
]
}
},
"group": "none",
"command": "fpm",
"args": [
"install",
"--profile",
"debug",
"--flag",
"\"-Iinclude\"",
"--link-flag",
"\"-Llib -lstdlib\"",
"--prefix",
"./build"
],
"problemMatcher": []
}
]
}
タスクを実行するには,ctrl
+shift
+p
を入力してコマンドパレットを呼び出し,実行するタスクを選択します.コマンドパレットに"run task"と入力すると,タスクの一覧が表示されるので,実行したいタスク(fpm install)を選択します.
実行すると,build\gfortran_24048832C8A119A4
をビルドディレクトリとしてビルドされた実行ファイルnon_number.exe
が,.\build\bin
にコピーされていることが確認されます.
fpm install
をタスクとして登録しておくことで,異なるオプションでビルドする度に,このディレクトリを探して変更する必要がなくなります.
デバッグの設定
ビルドしたアプリケーションのデバッグも,VSCodeで可能です.VSCodeではいくつかのデバッガと統合されており,VSCode内と統合されたインタフェースを利用してデバッグできます.gdbもVSCodeと統合されているので,gfortranの場合はVSCode内でデバッグが可能です.
なお,ここで述べるデバッグの設定は,インストールタスクを実行して,バイナリを.\build\bin
にコピーすることを前提としています.
デバッグの設定は,.vscode\launch.json
に記述します.
[実行]→[デバッグの開始]→[Fortran (GDB)]を選択し,launch.jsonを作成します.
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 起動",
"type": "cppdbg",
"request": "launch",
"program": "プログラム名を入力してください (例: ${workspaceFolder}/a.exe)",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/path/to/gdb",
"setupCommands": [
{
"description": "gdb の再フォーマットを有効にする",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "逆アセンブリ フレーバーを Intel に設定",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
既定のlaunch.json
の内容を書き換え,デバッグできるように設定します.
-
"program":
を,fpm install
タスクでコピーしたバイナリの名前に変更- 今回は
${workspaceFolder}/build/bin/non_number.exe
に変更する.
- 今回は
-
"stopAtEntry":
をtrue
に変更- この設定がないとただバイナリが実行されて終了するだけになる.
-
"cwd":
を${workspaceFolder}
に変更- fpmプロジェクトのルートで実行する設定.
-
"miDebuggerPath":
をgdb
に変更- gdbにパスを通しているため.パスを通していない場合は,gdbへのフルパスに変更する.
configurations:
の内容は下記のようになりました.
"configurations": [
{
"name": "(gdb) 起動",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/non_number.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "gdb の再フォーマットを有効にする",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "逆アセンブリ フレーバーを Intel に設定",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
この状態で,再び[実行]→[デバッグの開始]を実行すると,デバッグが開始されます.ブレークポイントを設定するには,VSCodeの拡張機能の一つであるFortran Breakpoint Supportをインストールします.
ステップインをクリックしてデバッグを進めていくと,左のデバッグウィンドウに表示された変数の値が変化していくことを確認できます.
表示だけでなく,メモリを直接編集することや,特定の変数や式を常に監視し続けることも可能です.
まとめ
fpmとVSCodeを連携する方法を紹介しました.
VSCodeを利用することで,fpm特有の煩わしさはかなり改善できるようになりました.しかし,タスクごとにコンパイラオプションを設定する必要があり,まとめて変更する事はできません.
将来的にはコンパイラオプションはfpm.toml
に記述できるようになるので幾分簡略化できますが,このあたりの挙動はもう少し改善してほしいと思います.