Fortran用のMakefileについて

## はじめに

個別の状態にあまり依存しないMakefileを考えてみた。

## 構成

```
sample/
 ├ bin/
 ├ src/
   ├ makefile
   ├ main.f90
   ├ mod_a.f90   
　 └ mod_b.f90
```

```fortran:main.f90
program main
    use, intrinsic :: iso_fortran_env, only: real64, int32
    use mod_a, only: sub_a
    use mod_b, only: sub_b
    implicit none
    real(real64) :: a
    integer(int32) :: i

    i = 10
    a = 0.1_real64

    print *, i, a
    call sub_a()
    call sub_b()
    
end program main
```

```fortran:mod_a.f90
module mod_a
    use, intrinsic :: iso_fortran_env, only: real64
    implicit none
    private
    public :: sub_a
    real(real64), parameter :: pi = 4.0_real64*atan(1.0_real64)
    
contains
    subroutine sub_a()
        print *, 'pi=', pi
    end subroutine sub_a
end module mod_a
```

```fortran:mod_b.f90
module mod_b
    use, intrinsic :: iso_fortran_env, only: real64
    implicit none
    private
    public :: sub_b
    real(real64), parameter :: pi = 4.0_real64*atan(1.0_real64)
    
contains
    subroutine sub_b()
        print *, 'pi=', pi
    end subroutine sub_b
end module mod_b
```

※モジュールが入っているファイルは

- モジュールを一つとする
- module名とファイル名を同じにする
 
という条件にする。

## makefile

```makefile:makefile
# Usage:

# for release
# make
# for debug
# make DEBUG=1

# install and clean
# make install
# make clean-all

FC := gfortran
FFLAGS := -Wall -Wno-maybe-uninitialized
FFLAGS_FOR_RELEASE := -O3
FFLAGS_FOR_DEBUG := -g -O0
LDFLAGS :=

ifeq ($(DEBUG), 1)
    FFLAGS += $(FFLAGS_FOR_DEBUG)
else
    FFLAGS += $(FFLAGS_FOR_RELEASE)
endif

SOURCES := $(wildcard *.f90)
OBJS := $(SOURCES:.f90=.o)
PROGRAMS := main

.PHONY: all install clean clean-all
all: $(PROGRAMS)
	@:

$(PROGRAMS): $(OBJS)
	$(FC) $(FFLAGS) -o $@ $^ $(LDFLAGS)

%.o: %.f90
	$(FC) $(FFLAGS) -c $<

%.mod: %.o
	@:

# 個別設定(modファイルを追加)
main.o: mod_a.mod mod_b.mod

install: all
	install $(PROGRAMS) ../bin

clean:
	rm -f *.o *.mod

clean-all: clean
	rm -f $(PROGRAMS)
```

## 解説

### 変数の代入について

`=`を使用するとRecursively expanded variable（再帰展開変数）となり使用する時入れ子になっている変数を展開する。
`=:`を使用するとSimply expanded variables（単純展開変数）となり代入の際にすべて展開されてから入力される。  
基本的に上から順に入力していくように作られている場合は単純展開変数を使用する。  

### release or debug
`make`または`make DEBUG=1`により変数`FFLAGS`に入る内容が変わる。  
`make DEBUG=1`とすると、makefileの変数`DEBUG`が上書きされる。  

```makefile
ifeq ($(DEBUG), 1)
    FFLAGS += $(FFLAGS_FOR_DEBUG)
else
    FFLAGS += $(FFLAGS_FOR_RELEASE)
endif
```

この分岐により入力されるものが変化する。  

### パターンルール

```makefile
%.o: %.f90
	$(FC) $(FFLAGS) -c $<
```

f90ファイルからoファイルを作る時のルールとなる。

※ちなみに同様のルールにサフィックスルールがあるが、互換性のために残している古い書き方で可読性も悪いので使用しない。  

```makefile
.f90.o:
    $(FC) $(FFLAGS) -c $<
```

### 一つのターゲットに対して、複数のルールを設定する

```makefile
# 個別設定(modファイルを追加)
main.o: mod_a.mod mod_b.mod

%.o: %.f90
	$(FC) $(FFLAGS) -c $<
```

このようにmain.oを作る場合は、この２つを組み合わせて`main.f90,mod_a.mod,mod_b.mod`を材料に`$(FC) $(FFLAGS) -c $<`を実行することができる。  
基本はパターンルールで個別で必要なものがあればこのように追加する。  

### `%.mod: %.o` パターンルールについて

コンパイルする順番を制御するために置いている。  
`main.o`を作るには`mod_a.mod mod_b.mod`ファイルが必要でそれは`mod_a.o mod_b.o`ファイルの作成と同時に作れられる。  
`main.f90`をコンパイルする前に`mod_a.f90 mod_b.f90`をコンパイルする必要がある。

このルールを加えると、次のような制御になり順番が確保される。  

1. `main.o`は`main.f90 mod_a.mod mod_b.mod`に依存する
1. `mod_a.mod`は`mod_a.o`に依存する
1. `mod_b.mod`は`mod_b.o`に依存する
1. `mod_a.o`は`mod_a.f90`に依存する
1. `mod_b.o`は`mod_b.f90`に依存する

`main.o`->`main.f90 mod_a.mod mod_b.mod`->`mod_a.o mod_b.o`->`mod_a.f90 mod_b.f90`の順になる。

## もう一つの書き方(makefile)

先ほどは、
モジュールが入っているファイルは

- モジュールを一つとする
- module名とファイル名を同じにする
 
という条件のもとに作成されていたが、そうではないパターンもあるかと思う。それでも耐えうる書き方に変更する。
<https://fortran-lang.org/en/learn/building_programs/project_make/>に書かれているものを参考にした。  

```makefile:makefile
# Usage:

# for release
# make
# for debug
# make DEBUG=1

# install and clean
# make install
# make clean-all

FC := gfortran
FFLAGS := -Wall -Wno-maybe-uninitialized
FFLAGS_FOR_RELEASE := -O3
FFLAGS_FOR_DEBUG := -g -O0
LDFLAGS :=

ifeq ($(DEBUG), 1)
    FFLAGS += $(FFLAGS_FOR_DEBUG)
else
    FFLAGS += $(FFLAGS_FOR_RELEASE)
endif

SOURCES := $(wildcard *.f90)
OBJS := $(SOURCES:.f90=.o)
PROGRAMS := main

.PHONY: all install clean clean-all
all: $(PROGRAMS)
	@:

$(PROGRAMS): $(OBJS)
	$(FC) $(FFLAGS) -o $@ $^ $(LDFLAGS)

%.o: %.f90
	$(FC) $(FFLAGS) -c $<

# 依存関係をすべて書き出す
mod_a.mod: mod_a.o
mod_b.mod: mod_b.o
main.o: mod_a.mod
main.o: mod_b.mod

install: all
	install $(PROGRAMS) ../bin

clean:
	rm -f *.o *.mod

clean-all: clean
	rm -f $(PROGRAMS)
```

モジュールを生み出す側は`モジュール名.mod: ファイル名(.f90->.o)`として記入し
利用する側は`ファイル名(.f90->.o): モジュール名.mod`と記入する。  
機械的に書けるのでこちらのほうがよさそう。  

## 参考文献

https://www.gnu.org/software/make/manual/make.html

https://runebook.dev/ja/docs/gnu_make/

https://fortran-lang.org/en/learn/building_programs/project_make/

https://linuxcommand.net/make/

https://maku77.github.io/memo/tool/make.html

https://qiita.com/chirimen/items/af8479d8c0c7cf545b1e

https://qiita.com/jco_z/items/639d3929871b02d22fa2