はじめに
個別の状態にあまり依存しないMakefileを考えてみた。
構成
sample/
├ bin/
├ src/
├ makefile
├ main.f90
├ mod_a.f90
└ mod_b.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
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
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
# 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
が上書きされる。
ifeq ($(DEBUG), 1)
FFLAGS += $(FFLAGS_FOR_DEBUG)
else
FFLAGS += $(FFLAGS_FOR_RELEASE)
endif
この分岐により入力されるものが変化する。
パターンルール
%.o: %.f90
$(FC) $(FFLAGS) -c $<
f90ファイルからoファイルを作る時のルールとなる。
※ちなみに同様のルールにサフィックスルールがあるが、互換性のために残している古い書き方で可読性も悪いので使用しない。
.f90.o:
$(FC) $(FFLAGS) -c $<
一つのターゲットに対して、複数のルールを設定する
# 個別設定(modファイルを追加)
main.o: mod_a.mod mod_b.mod
%.o: %.f90
$(FC) $(FFLAGS) -c $<
このようにmain.oを作る場合は、この2つを組み合わせて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
をコンパイルする必要がある。
このルールを加えると、次のような制御になり順番が確保される。
-
main.o
はmain.f90 mod_a.mod mod_b.mod
に依存する -
mod_a.mod
はmod_a.o
に依存する -
mod_b.mod
はmod_b.o
に依存する -
mod_a.o
はmod_a.f90
に依存する -
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/に書かれているものを参考にした。
# 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
と記入する。
機械的に書けるのでこちらのほうがよさそう。
参考文献