Build Automation
The build process of a Fortran program or library can be automated by using build utilities that specify all steps necessary for compilation, linking, and installation of a project.
On Unix, Makefiles are a common way to compile from source. Except for simple projects, Makefiles are not portable and not platform-independent, as they have to target a specific environment (compilers, libraries, paths, …). GNU make and BSD make are two variants that provide extensions to the POSIX standard, albeit not compatible to each other. It is therefore preferable to rely on POSIX rules only.
Other build systems bypass these limitations by generating Makefiles with respect to the currently used environment, or by using an independent build backend. Fortran is usually supported by the most common build automation tools, such as:
The Fortran Package Manager (fpm) is specifically written for Fortran projects, while being developed in Fortran itself.
Make
Makefiles are an easy way to maintain the build process of a Fortran project.
Once written, the file is run by the make
utility (either BSD make
or GNU make) that will execute the declared steps, such as compiling the source
code and linking the object files.
The following basic Makefile
below is used to compile an example
Fortran program example.f90
with GNU Fortran. Please note the
mandatory hard tabs in the source.
.POSIX:
# Parameters:
#
# FC - Fortran compiler.
# FFLAGS - Fortran compiler flags.
# TARGET - Build target name (executable).
FC = gfortran13
FFLAGS = -Wall
TARGET = example
.PHONY: all clean run
all: $(TARGET)
$(TARGET): example.f90
$(FC) -o $(TARGET) example.f90
clean:
rm $(TARGET)
run:
./$(TARGET)
Save the file as Makefile
in the directory that contains the
source code file example.f90
. Execute make
or
make all
inside the directory to compile the example:
$ make
gfortran13 -o example example.f90
To change the compiler to Flang, run:
$ make FC=flang
Or, using Intel Fortran:
$ make FC=ifort
Execute the binary with make run
, or delete the target with
make clean
.
Linking
The Makefile below also links the Fortran program source
example.f90
against required dependencies (see ncurses for more information):
.POSIX:
# Parameters:
#
# FC - Fortran compiler.
# PREFIX - Include and library search path prefix.
# FFLAGS - Fortran compiler flags.
# LDFLAGS - Linker flags.
# LDLIBS - Linker libraries.
# TARGET - Build target name.
FC = flang
FFLAGS = -Wall
PREFIX = /usr/local
LDFLAGS = -I$(PREFIX)/include -L$(PREFIX)/lib
LDLIBS = libm_ncurses.a -lncurses
TARGET = example
.PHONY: all clean
all: $(TARGET)
$(TARGET): example.f90
$(FC) $(FFLAGS) $(LDFLAGS) -o $(TARGET) example.f90 $(LDLIBS)
clean:
rm $(TARGET) *.o *.mod
The variables can be overwritten through command-line arguments. For instance, on Linux, we would have to run:
$ make PREFIX=/usr
CMake
Using CMake to generate a clean Makefile makes it possible to take the used
computer platform into account or to set custom build options. In the following
example, the source file src/main.f90
will be compiled and
statically linked against a library in src/util.f90
.
Please be aware that the ending of all Fortran source files must either be
set to .f90
or .f95
to be recognised by CMake. The
CMake configuration has to be saved to file CMakeLists.txt
in your
workspace directory:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(Example)
set(VERSION 1.0)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RELEASE)
endif()
if(CMAKE_Fortran_COMPILER MATCHES "gfortran*")
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -std=f2003 -fimplicit-none")
set(CMAKE_Fortran_FLAGS_DEBUG "-Wall -O0 -g3 -fbounds-check")
set(CMAKE_Fortran_FLAGS_RELEASE "-O2")
endif()
set(TARGET example)
set(SOURCE_FILES src/main.f90)
add_library(util STATIC src/util.f90)
add_executable(${TARGET} ${SOURCE_FILES})
target_link_libraries(${TARGET} util)
set_target_properties(${TARGET} PROPERTIES LINKER_LANGUAGE Fortran)
We first create our build directory build/
, change into it, and
generate a Makefile with CMake. Finally, we build the project with
make
:
$ mkdir build
$ cd build/
$ cmake ..
$ make
We can force a specific compiler by using the
-DCMAKE_Fortran_COMPILER
flag:
$ cmake -DCMAKE_Fortran_COMPILER=gfortran13 -DCMAKE_INSTALL_RPATH=/usr/local/lib/gcc13/ ..
xmake
The platform-independend xmake build utility is based on Lua and supports multiple programming languages, including Fortran. It is not related to the older XMake. In comparision to other build systems, xmake is more lightweight, consisting of only a single executable with no further dependencies.
On FreeBSD, simply compile the program from source using GNU make:
$ git clone --depth 1 --recursive https://github.com/xmake-io/xmake
$ cd xmake/
$ gmake build
$ doas gmake install PREFIX=/usr/local
On Linux, run make
and sudo
instead of
gmake
and doas
. The PREFIX
parameter may
be omitted. The compiled xmake
executable will be copied to
/usr/local/bin/
.
Inside your Fortran workspace directory, create a new build script named
xmake.lua
, containing:
-- xmake.lua
target("example")
set_kind("binary")
set_languages("fortran")
add_files("src/util.f90")
add_files("src/main.f90")
The Fortran program is split into the files src/main.f90
and
src/util.f90
. Just run xmake
inside the
workspace directory to build an executable:
$ xmake
[ 46%]: compiling.release src/util.f90
[ 60%]: compiling.release src/main.f90
[ 62%]: linking.release example
[100%]: build ok!
The default output directory is build/
. Fortran modules are
stored in build/.objs/<name>/<platform>/<architecture>/
.
We can change the module files directory to, for example, build/
by setting the value fortran.moduledir
to the according path:
set_values("fortran.moduledir", "$(buildir)")
We may also build
static and shared libraries
with xmake. The following xmake script will output the static library
libexample.a
and the associated Fortran module files to
build/
:
-- xmake.lua
target("example")
set_kind("static")
set_languages("fortran")
add_files("src/library.f90")
set_values("fortran.moduledir", "$(buildir)")
Fortran Package Manager
The Fortran Package Manager (FPM) is a build utility aimed to become the default package manager for Fortran. The protoype version was initially written in Haskell and is now replaced by a Fortran implementation. The project is in early development, but can already be used to compile and package applications or libraries in Fortran.
Installation
To build FPM, clone the GitHub repository and run the installation script:
$ git clone --depth 1 https://github.com/fortran-lang/fpm
$ cd fpm/
$ sh install.sh
A Fortran 2008 compiler is required for this step. The binary is installed to
$HOME/.local/bin/
by default. Make sure the path is in your
$PATH
environment variable, for instance:
$ export PATH=${PATH}:${HOME}/.local/bin/
$ echo $PATH
Add the directory to the $PATH
variable in your profile to make
the changes permanent.
-
Environment Variable Defines Overridden by FPM_FC
Fortran compiler path --compiler
FPM_CC
C compiler path --c-compiler
FPM_FFLAGS
Fortran compiler flags --flag
FPM_CFLAGS
C compiler flags --c-flag
FPM_AR
Archiver path --archiver
FPM_LDFLAGS
Linker flags --link-flag
- Tab. 1: Environment variables and command-line arguments
Packages
Fortran projects using FPM have to follow a recommended file system structure
if not configured otherwise. The source code of Fortran applications is normally
saved to app/
, while Fortran module source files must be located in
src/
. The manifest file fpm.toml
in the project
workspace directory contains the package description and some meta information.
Non-default directory paths can be set in the file.
The workspace of an example Fortran project may include the following files and directories:
<project>/
app/
main.f90
src/
library.f90
util.f90
.gitignore
README.md
fpm.toml
The build manifest fpm.toml
contains a basic package
description:
# fpm.toml
name = "example"
version = "0.1.0"
license = "ISC"
author = "Jane Doe"
maintainer = "mail@example.com"
copyright = "2020 Jane Doe"
We just have to run fpm build
to compile the target:
$ fpm build --profile debug
Choose between the build profile debug
and release
.
Afterwards, start the executable with fpm run
:
$ fpm run
Compiler and linker flags can be set through environment variables and/or command-line arguments (table 1).
Further Reading
- A. O. de la Roza: Writing Makefiles for Modern Fortran
- C. Wellons: A Tutorial on Portable Makefiles
References
- CMake: Official website
- xmake: Official website
- Fortran Package Manager: Official website
< Preprocessor | [Index] | Source Code Documentation > |