概要
本記事では簡単なプロジェクトを作成して,fpmの使用方法やマニフェスト(fpm.toml
)の作成方法を説明します.
fpmの使用例やマニフェスト(fpm.toml
)の設定項目について説明しました.fpmも徐々に機能が増えているので,それらをどう使うかが判りにくいという意見もあるかと思います.そういった声に応えられれば幸いです.
環境
- Windows 10
- fpm 0.5.0
- gfortran 10.3.0 (TDM-GCC-64)
Windowsで実行し,動作確認をしています.パスの区切りは\
になっています.Unix系OSで動かす場合には,ドライブレターを無視し,パスの区切りを/
に置き換えてください.
プロジェクトの作成
プロジェクト概要
NaN
やInf
など,浮動小数点には数値ではない値があります.それらを取り扱うライブラリnon_number
を作成します.なお,本記事の目的はfpmの使い方などを説明することですので,その目的からずれないように,ライブラリは作り込みません.単精度および倍精度の浮動小数点数を受け取って,それらの値がNaN
であるか否かを判別する関数is_nan
のみを作成します.同じ機能を持った関数ieee_is_nan
がFortran標準で用意されています1.ieee_
が冗長なので,短い名前で呼べるようにしたということです.
実装は簡単で,関数内でieee_is_nan
を呼んでその結果を返しているだけです.異なる型に対応するために,interface
を利用しています.
interface is_nan
module procedure :: is_nan_real32
module procedure :: is_nan_real64
end interface
contains
logical function is_nan_real32(x)
implicit none
real(real32), intent(in) :: x
is_nan_real32 = ieee_is_nan(x)
end function is_nan_real32
logical function is_nan_real64(x)
implicit none
real(real64), intent(in) :: x
is_nan_real64 = ieee_is_nan(x)
end function is_nan_real64
fpmプロジェクトの作成
fpm new
を実行してプロジェクトを作成します.ライブラリを作成するので--lib
オプションを付けて実行します.
D:\project> fpm new non_number --lib
+ mkdir non_number
+ cd non_number
+ mkdir non_number\src
+ git init non_number
Initialized empty Git repository in D:/project/non_number/.git/
fpm: Leaving directory 'D:'
D:\project> cd non_number
D:\project\non_number>
non_number
ディレクトリ以下は下記のようになっています.--lib
オプション(もしくは--src
)を付けているので,ライブラリとしてビルドされるソースを配置するsrc
ディレクトリのみが作成されています.
.
├── README.md
├── fpm.toml
└── src
└── non_number.f90
プロジェクトのビルド
ソースの編集
non_number.f90
を,先述の通り編集します.
module non_number
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
implicit none
private
public :: is_nan
interface is_nan
module procedure :: is_nan_real32
module procedure :: is_nan_real64
end interface
contains
logical function is_nan_real32(x)
implicit none
real(real32), intent(in) :: x
is_nan_real32 = ieee_is_nan(x)
end function is_nan_real32
logical function is_nan_real64(x)
implicit none
real(real64), intent(in) :: x
is_nan_real64 = ieee_is_nan(x)
end function is_nan_real64
end module non_number
また,プロジェクトルートに作成されたマニフェストファイルfpm.toml
も編集します.license
, author
, copyright
を書き換えました.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
ビルド
fpm build
を実行してビルドします.
D:\project\non_number>fpm build
+ mkdir build\dependencies
+ mkdir build\gfortran_2A42023B310FA28D
+ mkdir build\gfortran_2A42023B310FA28D\non_number\
+ gfortran -c .\.\src\non_number.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\src_non_number.f90.o
+ ar -rs build\gfortran_2A42023B310FA28D\non_number\libnon_number.a @build\gfortran_2A42023B310FA28D\non_number\libnon_number.a.resp
ar: creating build\gfortran_2A42023B310FA28D\non_number\libnon_number.a
出力を見ると,non_number.f90
のビルドに成功してlibnon_number.a
が作成されていることが確認できました.
プロジェクトのテスト
テストの作成
さて,ライブラリが作成できたとして,それをリンクしてアプリケーションを作り,それを実行してみるまで実行内容が正しいか判らないという状況はよろしくありません.プロジェクト内でテストを行い,正しさを保証しておく必要があります.
既存のfpmプロジェクトにテストを追加するには,--backfill
オプション付きでfpm new
を実行します.
D:\project\non_number> cd ..
D:\project> fpm new non_number --test --backfill
backfilling non_number
+ cd non_number
<INFO> non_number\README.md already exists. Not overwriting
+ mkdir non_number\test
<INFO> non_number\fpm.toml already exists. Not overwriting
+ git init non_number
Reinitialized existing Git repository in D:/project/non_number/.git/
fpm: Leaving directory 'D:'
test
ディレクトリが追加されました.
.
├── README.md
├── build\
├── fpm.toml
├── src
│ └── non_number.f90
└── test
└── check.f90
テストを単一のファイルに書くというルールはないので,既定のファイルcheck.f90
を削除して,単精度実数用のis_nan
に対するテストtest_is_nan_real32.f90
と倍精度実数用のis_nan
に対するテストtest_is_nan_real64.f90
を追加します.
.
├── README.md
├── build\
├── fpm.toml
├── src
│ └── non_number.f90
└── test
├── test_is_nan_real32.f90
└── test_is_nan_real64.f90
テストの中でやっていることは簡単です.
test_is_nan_real32.f90
の中では,二つのサブルーチンreturns_true_for_fp32_number_that_is_nan
, returns_false_for_fp32_number_that_is_not_nan
を実行しています.
returns_true_for_fp32_number_that_is_nan
の中では,ieee_value(0., ieee_quiet_nan)
で発生させたNaN
をis_nan
に渡し,その戻り値が.true.
かを判定し,真であれば成功,偽であればerror stop
で実行を停止し手います.
retval = is_nan(ieee_value(0., ieee_quiet_nan))
if (retval .eqv. .true.) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
returns_false_for_fp32_number_that_is_not_nan
の中では,少し複雑な処理をしています.is_nan
はNaN
以外を渡した時に.false.
を返すことを想定していますが,一つの値だけでテストを行って.false.
が返ってきたとしても,その値以外.true.
が返ってくることを否定できません.一方で,全ての値でテストを行うことは現実的ではありません.広範囲の値をテストするために,[0,1]の乱数に単精度実数の最大値をかけて大きな値を出し,その値を-2
で除していくことにより,正負の幅広い範囲の値をテストできるようにしました.
また,戻り値は,一つでも.true.
が返るとテストが失敗するように,前の値の戻り値と.or.
を取るようにしています.本来は,retval
を論理型の配列として,all(retval .eqv. .false.)
で判定する方が好ましいのですが.
integer(int32) :: i
real(real32) :: x
retval = .false.
call random_number(x)
x = x*huge(x)
do i = 1, 200
retval = is_nan(x) .or. retval
x = -x/2.
end do
test_is_nan_real32.f90
program test_is_nan_real32
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use :: non_number
implicit none
character, parameter :: TAB = achar(int(z'09'))
call returns_true_for_fp32_number_that_is_nan()
call returns_false_for_fp32_number_that_is_not_nan()
contains
subroutine returns_true_for_fp32_number_that_is_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns true for fp32 number that is nan"
retval = is_nan(ieee_value(0., ieee_quiet_nan))
if (retval .eqv. .true.) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
end subroutine returns_true_for_fp32_number_that_is_nan
subroutine returns_false_for_fp32_number_that_is_not_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns false for fp32 number that is not nan"
block
integer(int32) :: i
real(real32) :: x
retval = .false.
call random_number(x)
x = x*huge(x)
do i = 1, 200
retval = is_nan(x) .or. retval
x = -x/2.
end do
end block
if (retval .eqv. .false.) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
end subroutine returns_false_for_fp32_number_that_is_not_nan
end program test_is_nan_real32
test_is_nan_real64.f90
program test_is_nan_real64
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use :: non_number
implicit none
character, parameter :: TAB = achar(int(z'09'))
call returns_true_for_fp64_number_that_is_nan
call returns_false_for_fp64_number_that_is_not_nan()
contains
subroutine returns_true_for_fp64_number_that_is_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns true for fp64 number that is nan"
retval = is_nan(ieee_value(0d0, ieee_quiet_nan))
if (retval .eqv. .true.) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
end subroutine returns_true_for_fp64_number_that_is_nan
subroutine returns_false_for_fp64_number_that_is_not_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns false for fp64 number that is not nan"
block
integer(int32) :: i
real(real64) :: x
retval = .false.
call random_number(x)
x = x*huge(x)
do i = 1, 500
retval = is_nan(x) .or. retval
x = -x/20d0
end do
end block
if (retval .eqv. .false.) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
end subroutine returns_false_for_fp64_number_that_is_not_nan
end program test_is_nan_real64
テストの実行
fpm build
を実行してもテストはビルドされません.テストのビルドは,fpm test
でテストを実行する際に行われます.
テストのビルドと実行を分けたい場合には,fpm test --list
を利用します.fpm test --list
は,プロジェクトに用意されたテストの一覧を確認するコマンドですが,テストの一覧を作成するためにビルドされるという挙動を利用して,ビルドと実行を分けています.
fpm test --list
を実行すると,テストがビルドされた後,2個のテストtest_is_nan_real32
, test_is_nan_real64
が表示されます.
D:\project\non_number> fpm test --list
+ gfortran -c test\test_is_nan_real32.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real32.f90.o
+ gfortran -c test\test_is_nan_real64.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o
+ mkdir build\gfortran_2A42023B310FA28D\test\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real32.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real32.exe
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real64.exe
Matched names:
test_is_nan_real32 test_is_nan_real64
テストのビルドが完了したら,fpm test
でテストを実行します.
D:\project\non_number> fpm test
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
テストを選択して実行するには,--target
を利用します.
:\project\non_number> fpm test --target test_is_nan_real32
is_nan returns true for fp32 number that is nan
True
is_nan returns false for fp32 number that is not nan
True
D:\project\non_number> fpm test --target test_is_nan_real64
is_nan returns true for fp64 number that is nan
True
is_nan returns false for fp64 number that is not nan
True
ローカルに作成したfpmプロジェクトの参照
ライブラリnon_number
を作成し,テストを実行して実装の正しさを確認しました.テストの中で戻り値の判定を計4回書いていました.これは良い実装ではないので,戻り値の判定をテストの外に切り出すことにします.
checkプロジェクト
切り出した際にnon_number
と連携しやすくするために,別のfpmプロジェクトとして,non_number
プロジェクトのローカルに構築します.
fpmの新規プロジェクトの作成は挙動がよく判らないところがあります.例えば,プロジェクトの下に新しいプロジェクトを作成しようとすると,必ずプロジェクトルートに作られます.
下記の実行例では,non_number
ディレクトリに下にutil
ディレクトリを作成し,その中にfpmプロジェクトcheck
を作成しようとしました.しかし,check
はnon_number
ディレクトリ直下に作られてしまいました.
D:\project\non_number> mkdir util
D:\project\non_number> cd util
D:\project\non_number\util> fpm new check --lib
fpm: Entering directory 'D:\project\non_number'
+ mkdir check
+ cd check
+ mkdir check\src
+ git init check
Initialized empty Git repository in D:/project/non_number/check/.git/
fpm: Leaving directory 'D:\project\non_number'
既存プロジェクトの下にサブディレクトリを切ってプロジェクトを作成するには,プロジェクト名にサブディレクトリ名を含めて新規作成を行います.このとき,パスの区切りは/
です.
D:\project\non_number>fpm new util/check --lib
+ mkdir util\check
+ cd util/check
+ mkdir util\check\src
+ git init util/check
Initialized empty Git repository in D:/project/non_number/util/check/.git/
.
├── README.md
├── build\
├── fpm.toml
├── src
│ └── non_number.f90
├── test
│ ├── test_is_nan_real32.f90
│ └── test_is_nan_real64.f90
└── util
└── check
├── README.md
├── fpm.toml
└── src
└── check.f90
module util_check
implicit none
private
public :: check
character, public, parameter :: TAB = achar(int(z'09'))
contains
subroutine check(condition)
implicit none
logical, intent(in) :: condition
if (condition) then
print '(A)', TAB//"True"
else
print '(A)', TAB//"False"
error stop
end if
end subroutine check
end module util_check
name = "check"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
テストも修正します.モジュールをuse
し,重複している定数を削除し,結果の判定をしている箇所をサブルーチン呼出しに置き換えます.
test_is_nan_real32.f90
program test_is_nan_real32
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use :: non_number
use :: util_check
implicit none
call returns_true_for_fp32_number_that_is_nan()
call returns_false_for_fp32_number_that_is_not_nan()
contains
subroutine returns_true_for_fp32_number_that_is_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns true for fp32 number that is nan"
retval = is_nan(ieee_value(0., ieee_quiet_nan))
call check(retval .eqv. .true.)
end subroutine returns_true_for_fp32_number_that_is_nan
subroutine returns_false_for_fp32_number_that_is_not_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns false for fp32 number that is not nan"
block
integer(int32) :: i
real(real32) :: x
retval = .false.
call random_number(x)
x = x*huge(x)
do i = 1, 200
retval = is_nan(x) .or. retval
x = -x/2.
end do
end block
call check(retval .eqv. .false.)
end subroutine returns_false_for_fp32_number_that_is_not_nan
end program test_is_nan_real32
test_is_nan_real64.f90
program test_is_nan_real64
use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
use :: non_number
use :: util_check
implicit none
call returns_true_for_fp64_number_that_is_nan
call returns_false_for_fp64_number_that_is_not_nan()
contains
subroutine returns_true_for_fp64_number_that_is_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns true for fp64 number that is nan"
retval = is_nan(ieee_value(0d0, ieee_quiet_nan))
call check(retval .eqv. .true.)
end subroutine returns_true_for_fp64_number_that_is_nan
subroutine returns_false_for_fp64_number_that_is_not_nan()
implicit none
logical :: retval
print '(A)', "is_nan returns false for fp64 number that is not nan"
block
integer(int32) :: i
real(real64) :: x
retval = .false.
call random_number(x)
x = x*huge(x)
do i = 1, 500
retval = is_nan(x) .or. retval
x = -x/20d0
end do
end block
call check(retval .eqv. .false.)
end subroutine returns_false_for_fp64_number_that_is_not_nan
end program test_is_nan_real64
ローカルプロジェクトの参照
ローカルにおかれたfpmプロジェクトを参照するには,プロジェクトのマニフェストfpm.toml
に[dependencies]
の項目を設け,そこにプロジェクト名と場所を記述します.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
external-modules = ["stdlib_stats"]
[install]
library = false
[dependencies]
check = {path = "util/check"}
もう一度ビルドして実行したところ,正しく動作しているようです.本来はcheck
プロジェクトでもテストをしなければなりませんが,都合により省略します.
D:\project\non_number> fpm test --list
+ mkdir build\gfortran_2A42023B310FA28D\test\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real32.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real32.exe
+ gfortran -c test\test_is_nan_real64.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real64.exe
Matched names:
test_is_nan_real32 test_is_nan_real64
D:\project\non_number> fpm test
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
依存関係の正確な記述
上で作成したutil_check
モジュールは,non_number
モジュール内では参照せず,テストでのみ参照していました.
テストのみが必要とするfpmプロジェクトを参照する設定として,[test.dependencies]
が用意されています.
fpm.toml
の[dependencies]
を削除し,[test.dependencies]
の設定を記述してみます.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[test.dependencies]
check = {path = "util/check"}
しかし,この設定でビルドし直しても,util_check
モジュールが参照できないというエラーが出て,ビルドできません.
D:\project\non_number> rmdir /S build
build、よろしいですか (Y/N)? Y
D:\project\non_number> fpm build
+ mkdir build\dependencies
+ mkdir build\gfortran_2A42023B310FA28D
+ mkdir build\gfortran_2A42023B310FA28D\non_number\
+ gfortran -c .\.\src\non_number.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\src_non_number.f90.o
+ ar -rs build\gfortran_2A42023B310FA28D\non_number\libnon_number.a @build\gfortran_2A42023B310FA28D\non_number\libnon_number.a.resp
ar: creating build\gfortran_2A42023B310FA28D\non_number\libnon_number.a
D:\project\non_number> fpm test --list
<ERROR>*cmd_run*:targets error:Unable to find source for module dependency: "util_check" used by "test\test_is_nan_real32.f90"
STOP 1
設定ファイルの書き方の記事でも言及しましたが,テストのみが必要とするプロジェクトを記述するには,少なくとも一つはテスト設定を書く必要があります.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
ここでは,単精度実数用のis_nan
に対するテストのみを指定していますが,この設定があれば倍精度実数用のis_nan
もビルドされるようになります.これはTOMLの書式の都合でしょうが,改善してほしいところです.
D:\project\non_number> fpm test --list
+ gfortran -c .\util/check\src\check.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\util_check_src_check.f90.o
+ ar -rs build\gfortran_2A42023B310FA28D\non_number\libnon_number.a @build\gfortran_2A42023B310FA28D\non_number\libnon_number.a.resp
+ gfortran -c test\test_is_nan_real32.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real32.f90.o
+ gfortran -c test\test_is_nan_real64.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o
+ mkdir build\gfortran_2A42023B310FA28D\test\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real32.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real32.exe
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\test_test_is_nan_real64.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\test\test_is_nan_real64.exe
Matched names:
test_is_nan_real32 test_is_nan_real64
D:\project\non_number> fpm test
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
プロジェクトのデモ
デモの作成
ライブラリの作成方法を示すデモを作成しておくと,ライブラリを利用するユーザの助けになります.
既存のfpmプロジェクトにデモを追加するには,テストを追加したの同様に,--backfill
オプション付きでfpm new
を実行します.
D:\project\non_number> cd ..
D:\project> fpm new non_number --example --backfill
backfilling non_number
+ cd non_number
<INFO> non_number\README.md already exists. Not overwriting
+ mkdir non_number\example
<INFO> non_number\fpm.toml already exists. Not overwriting
+ git init non_number
Reinitialized existing Git repository in D:/project/non_number/.git/
fpm: Leaving directory 'D:'
example
ディレクトリが追加されました.
.
├── README.md
├── build\
├── example
│ └── demo.f90
├── fpm.toml
├── src\
├── test\
└── util
└── check\
デモの作成については,テストと同じです.単一のファイルに書くというルールはありません.今後,複数のデモを追加することを想定して,既定のファイル名demo.f90
ではなくdemo_is_nan.f90
を用いる事にします.
.
├── README.md
├── build\
├── example
│ └── demo_is_nan.f90
├── fpm.toml
├── src\
├── test\
└── util
└── check\
デモとして,配列の平均値mean
に長さ0の配列を渡した際に発生するNaN
を捕まえる処理を実装しました.
demo_is_nan.f90
program demo_is_nan
use, intrinsic :: iso_fortran_env
use :: non_number
use :: util_check
implicit none
character(*), parameter :: fmt = '(A,*(g0:,","))'
real(real32), allocatable :: x(:)
real(real32) :: x_mean
block
integer(int32) :: i
x = [(i, i=1, 10)]
x_mean = mean(x)
print fmt, "vals = ", x
print fmt, "mean = ", x_mean
end block
block
x = [real(real32) ::]
x_mean = mean(x)
if (.not. is_nan(x_mean)) then
print *, TAB//"value error: x_mean is nan"
end if
end block
contains
real(real32) function mean(x)
implicit none
real(real32), intent(in) :: x(:)
mean = sum(x)/size(x)
end function mean
end program demo_is_nan
デモの実行
デモはテストとは異なり,fpm build
でビルドされます.
D:\project\non_number> fpm build
+ gfortran -c example\demo_is_nan.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o
+ mkdir build\gfortran_2A42023B310FA28D\example\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\example\demo_is_nan.exe
実行は,--example
オプション付きでfpm run
を実行します.
D:\project\non_number> fpm run --example
vals = 1.00000000,2.00000000,3.00000000,4.00000000,5.00000000,6.00000000,7.00000000,8.00000000,9.00000000,10.0000000
mean = 5.50000000
value error: x_mean is nan
デモの一覧を確認するには,fpm run --example --list
を実行します.デモを選択して実行するには,--target
を利用します.
D:\project\non_number> fpm run --example --list
Matched names:
demo_is_nan
オンラインのfpmプロジェクトの参照
関数mean
はFortranの標準ライブラリstdlibに実装されているので,わざわざ作る必要はありません.stdlibはgithubにリポジトリが存在するので,それを参照します.参照するには,プロジェクトのマニフェストfpm.toml
に[dependencies]
の項目を設け,そこにプロジェクト名と場所を記述します.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm" }
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
demo_is_nan.f90
にuse :: stdlib_stats
を追記し,ソース内のmean
の定義を削除します.
fpm build
を実行すると,githubからstdlibが取得され,同時にビルド・リンクが行われます.
D:\project\non_number>fpm build
Initialized empty Git repository in D:/project/non_number/build/dependencies/stdlib/.git/
remote: Enumerating objects: 91, done.
remote: Counting objects: 100% (91/91), done.
remote: Compressing objects: 100% (83/83), done.
remote: Total 91 (delta 21), reused 43 (delta 7), pack-reused 0
Unpacking objects: 100% (91/91), 202.57 KiB | 1.07 MiB/s, done.
From https://github.com/fortran-lang/stdlib
* branch stdlib-fpm -> FETCH_HEAD
:(中略)
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\example\demo_is_nan.exe
D:\project\non_number> fpm run --example --target demo_is_nan
vals = 1.00000000,2.00000000,3.00000000,4.00000000,5.00000000,6.00000000,7.00000000,8.00000000,9.00000000,10.0000000
mean = 5.50000000
value error: x_mean is nan
依存関係の正確な記述
stdlibは,non_number
モジュール内でもテストでも参照せず,example
でのみ参照していました.
デモのみが必要とするfpmプロジェクトを参照する設定として,[example.dependencies]
が用意されています.[dependencies]
の代わりに[example.dependencies]
を記述するのですが,これもテストと同じで,デモのみが必要とするプロジェクトを記述するには,少なくとも一つはデモ設定を書く必要があります.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[example.dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm" }
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
D:\project\non_number> rmdir /S build
build、よろしいですか (Y/N)? Y
D:\project\non_number> fpm build
+ mkdir build\dependencies
<ERROR>*cmd_build*:target error:Unable to find source for module dependency: "stdlib_stats" used by "example\demo_is_nan.f90"
STOP 1
デモの設定を追記してビルドすると,ビルドに成功します.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[[example]]
name = "demo_is_nan"
source-dir = "example"
main = "demo_is_nan.f90"
[example.dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm" }
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
D:\project\non_number> fpm build
+ mkdir build\dependencies
Initialized empty Git repository in D:/project/non_number/build/dependencies/stdlib/.git/
remote: Enumerating objects: 91, done.
remote: Counting objects: 100% (91/91), done.
remote: Compressing objects: 100% (83/83), done.
remote: Total 91 (delta 21), reused 43 (delta 7), pack-reused 0
Unpacking objects: 100% (91/91), 202.57 KiB | 1.12 MiB/s, done.
From https://github.com/fortran-lang/stdlib
* branch stdlib-fpm -> FETCH_HEAD
:(中略)
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_2A42023B310FA28D\example\demo_is_nan.exe
D:\project\non_number> fpm run --example --target demo_is_nan
vals = 1.00000000,2.00000000,3.00000000,4.00000000,5.00000000,6.00000000,7.00000000,8.00000000,9.00000000,10.0000000
mean = 5.50000000
value error: x_mean is nan
インストール済みライブラリの参照
先ほどは,githubにあるstdlibを参照しましたが,既にインストールしている場合もあります.
その場合は,[build]
の項目でlink
およびexternal-modules
を設定します.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
external-modules = ["stdlib_stats"]
link = "stdlib"
[install]
library = false
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
ライブラリおよびモジュール(.mod
)の置き場所にパスが通っている場合は,ビルドの際にオプションを追加する必要はありません.
D:\project\non_number> rmdir /S build
build、よろしいですか (Y/N)? Y
D:\project\non_number> fpm build
+ mkdir build\dependencies
+ mkdir build\gfortran_2A42023B310FA28D
+ mkdir build\gfortran_2A42023B310FA28D\non_number\
+ gfortran -c .\.\src\non_number.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\src_non_number.f90.o
+ gfortran -c .\util/check\src\check.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\util_check_src_check.f90.o
+ ar -rs build\gfortran_2A42023B310FA28D\non_number\libnon_number.a @build\gfortran_2A42023B310FA28D\non_number\libnon_number.a.resp
ar: creating build\gfortran_2A42023B310FA28D\non_number\libnon_number.a
+ gfortran -c example\demo_is_nan.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o
+ mkdir build\gfortran_2A42023B310FA28D\example\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -lstdlib -o build\gfortran_2A42023B310FA28D\example\demo_is_nan.exe
D:\project\non_number> fpm run --example --target demo_is_nan
vals = 1.00000000,2.00000000,3.00000000,4.00000000,5.00000000,6.00000000,7.00000000,8.00000000,9.00000000,10.0000000
mean = 5.50000000
value error: x_mean is nan
正確な依存関係の記述
インストールされたstdlibをリンクしましたが,ログを見ると,non_number
の静的ライブラリにstdlibがリンクされています.
build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -lstdlib -o
しかし,non_number
はstdlibを必要としておらず,リンクが必要なのは,デモ(demo_is_nan.exe
)だけでした.リンクの設定link
は,[[example]]
や[[test]]
にも記述できるので,これを利用する事でリンクするターゲットを正確に設定できます.ただし,external-modules
は[build]
にしか記述できません.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
external-modules = ["stdlib_stats"]
[install]
library = false
[[example]]
name = "demo_is_nan"
source-dir = "example"
main = "demo_is_nan.f90"
link = "stdlib"
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
[[example]]
にlink
の設定を移動してビルドすると,fpm build
のログの最後1行で,demo_is_nan.exe
のリンクの際にstdlibがリンクされていることを確認できます.
D:\project\non_number> rmdir /S build
build、よろしいですか (Y/N)? Y
D:\project\non_number> fpm build
+ mkdir build\dependencies
+ mkdir build\gfortran_2A42023B310FA28D
+ mkdir build\gfortran_E231ED6BEC2C0EC6
+ mkdir build\gfortran_2A42023B310FA28D\non_number\
+ gfortran -c .\.\src\non_number.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\src_non_number.f90.o
+ gfortran -c .\util/check\src\check.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\util_check_src_check.f90.o
+ ar -rs build\gfortran_2A42023B310FA28D\non_number\libnon_number.a @build\gfortran_2A42023B310FA28D\non_number\libnon_number.a.resp
ar: creating build\gfortran_2A42023B310FA28D\non_number\libnon_number.a
+ gfortran -c example\demo_is_nan.f90 -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o
+ mkdir build\gfortran_E231ED6BEC2C0EC6\example\
+ gfortran -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single build\gfortran_2A42023B310FA28D\non_number\example_demo_is_nan.f90.o -lstdlib build\gfortran_2A42023B310FA28D\non_number\libnon_number.a -o build\gfortran_E231ED6BEC2C0EC6\example\demo_is_nan.exe
D:\project\non_number> fpm run --example --target demo_is_nan
vals = 1.00000000,2.00000000,3.00000000,4.00000000,5.00000000,6.00000000,7.00000000,8.00000000,9.00000000,10.0000000
mean = 5.50000000
value error: x_mean is nan
パスの通っていないライブラリを参照する
先ほどは,システムにインストールされ,パスが通っているライブラリを参照しました.しかし,プロジェクトによっては,プロジェクトのルートにlib
やinclude
ディレクトリを作成し,そこにライブラリおよびモジュールを置く場合もあります.この場合は,lib
やinclude
ディレクトリにパスを通すことは希で,ディレクトリを指定してビルドを行います.
例えば下記のようにライブラリとモジュールファイルを置いた場合は,fpmでビルドする際に,--flag
および--link-flag
を用いて,コンパイラおよびリンカに渡すオプションを明記します.
.
├── README.md
├── example\
├── fpm.toml
├── include
│ └── stdlib_stats.mod
├── lib
│ └── libstdlib.a
├── src\
├── test\
└── util
└── check\
D:\project\non_number>rmdir /S build
build、よろしいですか (Y/N)? Y
D:\project\non_number>fpm build --flag "-Iinclude" --link-flag "-Llib -lstdlib"
+ mkdir build\dependencies
+ mkdir build\gfortran_D959645BECF235A9
+ mkdir build\gfortran_0C5EEBEEBACDEC93
+ mkdir build\gfortran_D959645BECF235A9\non_number\
+ gfortran -c .\.\src\non_number.f90 -Iinclude -I.\.\include -J build\gfortran_D959645BECF235A9 -Ibuild\gfortran_D959645BECF235A9 -o build\gfortran_D959645BECF235A9\non_number\src_non_number.f90.o
+ gfortran -c .\util/check\src\check.f90 -Iinclude -I.\.\include -J build\gfortran_D959645BECF235A9 -Ibuild\gfortran_D959645BECF235A9 -o build\gfortran_D959645BECF235A9\non_number\util_check_src_check.f90.o
+ ar -rs build\gfortran_D959645BECF235A9\non_number\libnon_number.a @build\gfortran_D959645BECF235A9\non_number\libnon_number.a.resp
ar: creating build\gfortran_D959645BECF235A9\non_number\libnon_number.a
+ gfortran -c example\demo_is_nan.f90 -Iinclude -I.\.\include -J build\gfortran_D959645BECF235A9 -Ibuild\gfortran_D959645BECF235A9 -o build\gfortran_D959645BECF235A9\non_number\example_demo_is_nan.f90.o
+ mkdir build\gfortran_0C5EEBEEBACDEC93\example\
+ gfortran -Iinclude -I.\.\include -Llib -lstdlib build\gfortran_D959645BECF235A9\non_number\example_demo_is_nan.f90.o -lstdlib build\gfortran_D959645BECF235A9\non_number\libnon_number.a -o build\gfortran_0C5EEBEEBACDEC93\example\demo_is_nan.exe
ログからわかるように,fpmは,プロジェクトルートのinclude
を標準でインクルードディレクトリとして扱うオプションを付与している(-Iinclude -I.\.\include
となってインクルードディレクトリの指定が2回現れている)ので,--flag "-Iinclude"
は必要ありません.
インクルードディレクトリの名前を変更するには,ライブラリ設定[library]
のinclude-dir
で名前を指定します.
コンパイラオプション付きでビルドした場合,デモやテストを実行するときにも同じオプションが必要です.
インストールの設定
ビルドしたnon_number
のインストールもfpmで可能です.fpmは,標準ではアプリケーション(実行ファイル)のインストールしかしてくれませんが,[install]
の項目でlibrary
を設定すると,ライブラリおよびモジュールをインストールしてくれるようになります.
name = "non_number"
version = "0.1.0"
license = "MIT"
author = "Implicit None"
copyright = "Copyright 2021, Implicit None"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
external-modules = ["stdlib_stats"]
[install]
library = true
[[example]]
name = "demo_is_nan"
source-dir = "example"
main = "demo_is_nan.f90"
link = "stdlib"
[[test]]
name = "test_is_nan_real32"
source-dir = "test"
main = "test_is_nan_real32.f90"
[test.dependencies]
check = {path = "util/check"}
参照fpmプロジェクトのコンパイラオプションの指定
fpmは,現状ではマニフェストfpm.toml
にコンパイラオプションを記述できません.一方で,ビルド時に--flag
で指定したコンパイラオプションは,参照しているfpmプロジェクトのビルドにも適用されます.そのため,参照しているライブラリのビルドにオプションが必要な場合は,ビルドの際にまとめて指定します.
fpmはまだ発展途上なので,プリプロセスを多用するような複雑なプロジェクトはまだ作らない方がよいでしょう.他のビルドツールを用いて複雑なビルド設定を構築しておき,条件を限定したfpmプロジェクト用のソースを作成するのも,現状ではやむなしです.
マニフェスト作成の現状の方針
fpmを利用する事で,プロジェクトの構築が非常に楽になりました.しかし,まだまだ発展途上であるため,煩わしいところもあります.
煩わしさを低減するために,著者らのマニフェスト(fpm.toml
)作成の方針を紹介します.
ディレクトリ名は標準(app
, src
, test
, example
)から変更しない
- ディレクトリ名を変更すると設定の煩わしさが跳ね上がる.
外部ライブラリを参照する場合は,可能な限りfpmプロジェクトをオンラインから取得する
- fpmプロジェクトを参照すれば,
external-modules
やlink
の設定やを記述する必要がなくなる. - プロジェクトをビルドするための環境構築が必要なくなり,ビルドや配布が簡単になる.
- fpmプロジェクトでないライブラリでも,複雑ではない場合はfpmプロジェクトに変換して利用する.
- githubリポジトリの場合は,forkしてfpmプロジェクトのブランチを作成する.
- 労力に見合った恩恵がある.
- 参照するライブラリが大規模でビルドに時間がかかる場合は,予めビルドしてローカルにインストールする.
- 多用途で他のプロジェクトからも利用できるライブラリの場合は,プロジェクトと異なる場所に置いてパスを通しておく.
- 単一のプロジェクトでしか使わない場合は,
fpm install
を用いてプロジェクトのルートにコピーする.
参照するfpmプロジェクトは[dependencies]
にまとめて書く
- 依存関係を正確に表現するために,
[executable.dependencies]
,[test.dependencies]
,[example.dependencies]
は利用できるが,利用するために他の設定を書く必要があって煩わしい. - 作成するアプリケーションやライブラリが,所属組織外で広く使われることを想定している場合は,依存関係を正確に記述する.
外部ライブラリのリンクの設定は[build]
にまとめて書く
- リンクの設定の記述に,
[[executable]]
,[[test]]
,[[example]]
を利用できるが,利用するために他の設定を書く必要があって煩わしい. -
external-modules
は[build]
にしか書けないので,link
の設定もそれほどき気を遣わなくてもよい. - 作成するアプリケーションやライブラリが,所属組織外で広く使われることを想定している場合は,依存関係を正確に記述する.
-
コンパイラによっては
isNan
が方言として提供されています. ↩