Make and CMake

Fortran code can be easily build by executing the compiler from the command-line. But this process can be automated by various tools. A common way on Unix are Makefiles, which specify all steps for compilation, linking, and installation of your project.

Unfortunately, Makefiles are not platform-independent. This is why it is often necessary to write Makefiles for each targeted system. Furthermore, there are two major implementations which slightly differ: BSD make and GNU make. In most cases, a Makefile written for BSD make won’t be accepted by GNU make, and vice versa.

The CMake build utility bypasses these limitations and allows the generation of Makefiles with respect to the currently used environment and the compilers available.

The compilation of the source code can be optimised by using specific compiler options (CFLAGS) at build stage.

Make

Makefiles are an easy way to maintain the build process of a Fortran project. Once written, the file can executed by the make utility it is written for (either BSD make or GNU make). The very simplistic Makefile for BSD make below is used to compile an example Fortran program example.f90 with Flang. Please note the hard tabs in the source.

FC  = flang
SRC = example.f90
OBJ = example

all: $(OBJ)

$(OBJ):
	$(FC) -o $(OBJ) $(SRC)

.PHONY: clean

clean:
	rm $(OBJ)

Save the file as Makefile in the directory that locates your source files. Execute make or make all inside the directory to compile the example:

$ make
flang -o example example.f90

To change the defined compiler to GNU Fortran, run:

$ make FC=gfortran7

Running make clean will delete the built executable.

Linking

The example Makefile below also links the program against required dependencies. At first, the object files ncurses.o and macros.o will be compiled. Then, the main program is build and linked against the objects (see the ncurses section for a complete example).

CC = clang
FC = flang

CFLAGS  = -Wall -O2
LDFLAGS = -lncurses
OBJ     = macros.o ncurses.o
SRC     = main.f90
TARGET  = example

all: $(TARGET)

$(TARGET): $(OBJ)
	$(FC) -o $@ $(SRC) $(OBJ) $(LDFLAGS)

macros.o: $*.c
	$(CC) $(CFLAGS) -c $?

ncurses.o: $*.f90
	$(FC) $(CFLAGS) -c $?

.PHONY: clean

clean:
	rm $(TARGET) *.o *.mod

CMake

Using CMake to generate the Makefile automatically allows to regard the used computer platform and to set compiler-specific options. In the following example, the source file main.f90 will be compiled and statically linked against a library in f90getopt.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 CMakeLists.txt in your source code directory:

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} -ffree-form -std=f2008 -fimplicit-none -fall-intrinsics -fbackslash")
    set(CMAKE_Fortran_FLAGS_DEBUG   "-Wall -O0 -g3 -fbounds-check")
    set(CMAKE_Fortran_FLAGS_RELEASE "-O2 -march=native")
endif()

set(TARGET example)
set(SOURCE_FILES main.f90)

add_library(f90getopt STATIC f90getopt.f90)

add_executable(${TARGET} ${SOURCE_FILES})
target_link_libraries(${TARGET} f90getopt)
set_target_properties(${TARGET} PROPERTIES LINKER_LANGUAGE Fortran)

Generate the Makefile and build the project with:

$ mkdir build
$ cd build/
$ cmake ..
$ make

You can force a specific compiler by using the -DCMAKE_Fortran_COMPILER flag:

$ cmake -DCMAKE_Fortran_COMPILER=gfortran7 -DCMAKE_INSTALL_RPATH=/usr/local/lib/gcc7 ..