fpm (Fortran Package Manager)をBuilder兼Runnerとして利用する

# 概要
本記事では，fpmのインストールと，簡単な使い方を解説します．

fpm (Fortran Package Manager)は，Fortran-langプロジェクトの一環として開発されているパッケージマネージャです．将来的には，リモートにあるリポジトリを取得して，ビルド，インストールするところまでを目指しています．また，ユーザがビルドシステムの設定で悩まないよう，プロジェクトの構造を標準化することも目指しています．

Fortran-langプロジェクトがFortranに関するプロジェクトの調査をしていますが，パッケージマネージャとして機能するのはしばらく先になりそうです．一方，fpmの機能を確認すると，プロジェクトのビルドや実行を短いコマンドで実現できる，Builder兼Runnerとしては使えることが判りました．

追記：
バージョン0.5.0での使い方を別記事にまとめました．

- [fpm (Fortran Package Manager)の使い方](https://qiita.com/implicit_none/items/6a65ac1b83efefc782db)
- [fpm (Fortran Package Manager)の設定ファイルの解説](https://qiita.com/implicit_none/items/99c5d12f5c18733c66fd)
- [fpm (Fortran Package Manager)のプロジェクトの作成例](https://qiita.com/implicit_none/items/08a2d26f8288dda7b4f5)

# 環境

- Ubuntu 18.04
- fpm prototype version
- gfortran 7.5
- Haskell stack 2.3.1
- git 2.17.1

# fpmのインストール

fpmはHaskellで記述されており，インストールにもHaskell (Haskell Stack)が使われます．
インストール手順は公式の[README](https://github.com/fortran-lang/fpm/blob/master/README.md)に大体書いてあるのですが，必要なライブラリについては漏れがあるので，それも含めたインストール手順を記します．

fpmのインストール時にはgitを使うので，gitは事前にインストールしておいてください．また，fpmはプロジェクトのビルドにgfortranを利用するので，gfortranについても事前にインストールしておいてください．


1. GNU Multiple Precision Arithmetic Library Developers Toolsのインストール
2. Haskell Stackのダウンロードと展開
3. fpmのインストール

fpmの[README](https://github.com/fortran-lang/fpm/blob/master/README.md)には，必要なソフトウェアの要件が書かれていませんが，GNU Multiple Precision Arithmetic Library (GMP)がないとHaskell Stackのインストール時にエラーが出るので，予めインストールしておきます．

```console
Linking /home/hoge/.stack/setup-exe-cache/x86_64-linux/tmp-Cabal-simple_mPHDZzAJ_2.4.0.1_ghc-8.6.5 ...
/usr/bin/ld.gold: エラー: -lgmp が見つかりませ
```

## GNU Multiple Precision Arithmetic Library Developers Toolsのインストール
GMP Developers Toolsのインストールをインストールします．
Ubuntuを利用している場合は，`apt`で簡単にインストールできます．

```console
$ sudo apt install libgmp-dev
```

## Haskell Stackのダウンロードと展開
Haskell Stackの`tar.gz`ファイルをダウンロードし，展開した後，パスを通しておきます．著者は，ホームディレクトリの下に`lib`フォルダを作り，そこにダウンロードして展開しました．

```console
$ cd ~
$ mkdir lib
$ cd lib
$ wget https://get.haskellstack.org/stable/linux-x86_64-static.tar.gz
$ tar xaf linux-x86_64-static.tar.gz
$ cd linux-x86_64-static
$ export PATH=$PATH:`pwd`
```

## fpmのインストール
GitHubからfpmのリポジトリをクローンし，stackを利用してbuildします．このとき，GHC (The Glasgow Haskell Compiler)のダウンロードも行われるので，かなり時間がかかります．気長に待ちましょう．

```console
$ cd ~/lib
$ git clone https://github.com/fortran-lang/fpm
$ cd fpm
$ stack build
```

fpmのビルドが完了すると，実行ファイルは異様に長いパスに置かれます．それを使いやすい場所にコピーするために，次のコマンドを実行します．

```console
$ stack install
```

実行すると，以下のようなメッセージが出力され，ビルドされたfpmの実行ファイルが，`~/.local/bin`にコピーされた事が判ります．

```console
Copied executables to /home/hoge/.local/bin:
- fpm
```

このフォルダにもパスを通しておきます．

```console
$ export PATH=$PATH:~/.local/bin
```

これでインストールは完了です．

# fpmによるビルドと実行

## fpmのパッケージングレイアウト

fpmでは，用途に応じて3通りのパッケージングの方法を提供しています．

- アプリケーション（実行ファイルのみを生成）
- ライブラリ（静的ライブラリのみを生成）
- アプリケーションとライブラリ

これらは，ディレクトリ構造が異なるだけです．
また，パッケージ情報を`fpm.toml`ファイルに記述します．
`fpm.toml`ファイルには，プログラムの名前やバージョン，ライセンス情報などを記述できます．**プログラム名（コンパイルした際の実行ファイルの名前）は必須**ですが，それ以外は必須ではありません．

```:fpm.toml
name = "hello"
```

詳しくは，公式の[Preparing your package for FPM](https://github.com/fortran-lang/fpm/blob/master/PACKAGING.md)を読んでください．

### アプリケーション
アプリケーションは，下記のようなディレクトリ構造で管理されます．

```console
project dir
├── app
│   └── main.f90
└── fpm.toml
```

必要なのは，`main.f90`と`fpm.toml`のみです．プロジェクトのディレクトリ直下に`app`ディレクトリと`fpm.toml`を置き，ソースファイルを`app`ディレクトリ直下に置きます．このとき，ソースファイル名は，必ず`main.f90`です．

公式のサンプルと同様にhello worldを作ってみると，下記のようになります．

```console
hello
├── app
│   └── main.f90
└── fpm.toml
```

```:fpm.toml
name = "hello"
```

```Fortran:main.f90
program main
    implicit none

    print *, "hello, world"
end program main
```

プログラムのビルドは，プロジェクトのディレクトリで次のコマンドを実行します．

```console
$ fpm build
# gfortran (for build/gfortran_debug/app/main.o)
# gfortran (for build/gfortran_debug/app/hello)
```

プロジェクトをビルドすると，プロジェクトディレクトリ直下に`build\gfortran_debug\app`ディレクトリが作られ，その中に実行ファイルが作成されます．実行ファイルの名前は，`fpm.toml`の`name`で指定した名前です．

ビルドしたプログラムは，`fpm run`で実行できます．

```console
$ fpm run
 hello, world
```

### ライブラリ
ライブラリの場合はディレクトリ構造が若干変わります．

```console
project dir
├── fpm.toml
└── src
    └── module source files
```

このとき，`fpm.toml`に書いた名前は，静的ライブラリ(`*.a`ファイル)の名前（頭にlibが追加されます）として使われます．

```console
const
├── fpm.toml
└── src
    └── math_constants.f90
```

```:fpm.toml
name = "const"
```

```Fortran:math_constants.f90
module math_constants
    use, intrinsic :: iso_fortran_env
    implicit none

    real(real64),parameter :: PI = acos(-1d0)

end module math_constants
```

ビルドはアプリケーションの場合と同様に，プロジェクトのディレクトリで`fpm build`を実行します．

```console
$ fpm build
# gfortran (for build/gfortran_debug/const/math_constants.o build/gfortran_debug/const/math_constants.mod)
# ar (for build/gfortran_debug/const/libconst.a)
ar: build/gfortran_debug/const/libconst.a を作成しています
```

プロジェクトディレクトリ直下に`build\gfortran_debug\const`（`fpm.toml`の`name`で指定した名前の）ディレクトリが作られ，`lib+nameで指定した名前.a`の静的ライブラリが作成されました．

### アプリケーションとライブラリ
別ファイルでモジュールを作成し，メインルーチンからそれらを読み込む場合には，このディレクトリ構造を用います．これは非常に単純で，アプリケーションとライブラリのディレクトリ構造を合わせた構造です．

```console
project dir
├── app
│   └── main.f90
├── fpm.toml
└── src
    └── module source files
```

このとき，`fpm.toml`の`name`で指定するのは実行ファイルの名前ですが，静的ライブラリの名前にも使われます．

公式に類似した，以下のようなサンプルを作ってみました．

```console
app_and_lib
├── app
│   └── main.f90
├── fpm.toml
└── src
    └── math_constants.f90
```

```:fpm.toml
name = "app_and_lib"
```

```Fortran:main.f90
program main
    use :: math_constants, only: PI, PI05, PI2
    implicit none

    print *, "application and static library demo"
    print *, "π = ", PI
    print *, "π/2 = ", PI05
    print *, "2π = ", PI2
end program main
```

```Fortran:math_constants.f90
module math_constants
    use, intrinsic :: iso_fortran_env
    implicit none

    real(real64),parameter :: PI = acos(-1d0)
    real(real64),parameter :: PI2  = PI * 2.0_real64
    real(real64),parameter :: PI05 = PI * 0.5_real64

end module math_constants
```

ビルドと実行は，アプリケーションのときと何も変わりません．

```console
$ fpm build
# gfortran (for build/gfortran_debug/app_and_lib/math_constants.o build/gfortran_debug/app_and_lib/math_constants.mod)
# ar (for build/gfortran_debug/app_and_lib/libapp_and_lib.a)
ar: build/gfortran_debug/app_and_lib/libapp_and_lib.a を作成しています
# gfortran (for build/gfortran_debug/app/app_and_lib)

$ fpm run
 application and static library demo
 π =    3.1415926535897931
 π/2 =    1.5707963267948966
 2π =    6.2831853071795862
```

### 階層化されたモジュール
複数のモジュールが関係し，モジュールに階層構造がある場合は，モジュール名に独自のルールが設けられます．

```console
multi_level_module
├── app
│   └── main.f90
├── fpm.toml
└── src
    ├── math_constants
    │   ├── derived.f90
    │   └── fundamental.f90
    └── math_constants.f90
```

```:fpm.toml
name = "multi_level_module"
```

```Fortran:math_constants.f90
module math_constants
    use :: math_constants_fundamental, only: PI
    use :: math_constants_derived, only: PI05, PI2
end module math_constants
```

```Fortran:fundamental.f90
module math_constants_fundamental
    use, intrinsic :: iso_fortran_env
    implicit none

    real(real64),parameter :: PI = acos(-1d0)

end module math_constants_fundamental
```

```Fortran:derived.f90
module math_constants_derived
    use, intrinsic :: iso_fortran_env
    use :: math_constants_fundamental, only: PI
    implicit none

    real(real64),parameter :: PI2  = PI * 2.0_real64
    real(real64),parameter :: PI05 = PI * 0.5_real64

end module math_constants_derived
```

`main.f90`は前の例と同じです．

モジュールの名前付けにはルールがあり，`math_constants.f90`ではモジュール名を`math_constants`とします．
`src/math_constants/derived.f90`と`fundamental.f90`は，それぞれモジュール名を`math_constants_derived`と`math_constants_fundamental`にしなければなりません．

`src`ディレクトリのサブディレクトリに置かれたモジュールの名前は，`src`以下のサブディレクトリ名をアンダースコア`_`で順次つないでいき，最後にファイル名で終わります．
モジュールを`src/a/b/c/d.f90`で定義するとき，モジュール名は`a_b_c_d`になるということです．

公式のサンプルとほとんど同じなのですが，これをビルドしてみるとエラーが出ます．

```console
$ fpm build
# gfortran (for build/gfortran_debug/multi_level_module/math_constants_derived.o build/gfortran_debug/multi_level_module/math_constants_derived.mod)
# gfortran (for build/gfortran_debug/multi_level_module/math_constants.o build/gfortran_debug/multi_level_module/math_constants.mod)
# gfortran (for build/gfortran_debug/multi_level_module/math_constants_fundamental.o build/gfortran_debug/multi_level_module/math_constants_fundamental.mod)
src/math_constants.f90:3:10:

     use :: math_constants_fundamental, only: PI
          1
Fatal Error: Can't open module file ‘math_constants_fundamental.mod’ for reading at (1): そのようなファイルやディレクトリはありません
compilation terminated.
```

恐らく，依存性解析に失敗し，必要なモジュールの`mod`ファイルが作られる前に，別のモジュールのコンパイルを行おうとして，失敗したと考えられます．もう一度実行すると，ビルドに成功します．1回目のビルドでいくつかの`mod`ファイルが作られるので，2回目は既に作られた`mod`ファイルを参照してコンパイルできるからです．

```console
$ fpm build
# gfortran (for build/gfortran_debug/multi_level_module/math_constants_fundamental.o build/gfortran_debug/multi_level_module/math_constants_fundamental.mod)
# gfortran (for build/gfortran_debug/multi_level_module/math_constants_derived.o build/gfortran_debug/multi_level_module/math_constants_derived.mod)
# gfortran (for build/gfortran_debug/multi_level_module/math_constants.o build/gfortran_debug/multi_level_module/math_constants.mod)
# ar (for build/gfortran_debug/multi_level_module/libmulti_level_module.a)
ar: build/gfortran_debug/multi_level_module/libmulti_level_module.a を作成しています
# gfortran (for build/gfortran_debug/app/main.o)
# gfortran (for build/gfortran_debug/app/multi_level_module)

$ fpm run
 multi-level module demo
 π =    3.1415926535897931
 π/2 =    1.5707963267948966
 2π =    6.2831853071795862
```

モジュールの参照エラーであれば，何回か`fpm build`を実行すればそのうち成功するでしょう．

# まとめ
fpmはまだまだ開発途上ですが，簡単なプログラムのビルドおよび実行に使えることは判りました．

ビルドの補助ツールとしてはcmakeもありますが，`CMakeLists.txt`は`fpm.toml`ほど気軽には書けませんし，Makefileを作ったあとにmakeも必要になります．簡単な設定とコマンドで実行できるfpmは，Builder兼Runnerとしては非常に手軽で魅力的です．

今後の進展が楽しみです．fpmの進展を待つだけでなく，プロジェクトの構造についても，この機会に見直したいと思います．