Compilation

Fortran source code is compiled by executing the desired compiler with given input and output files. The following basic example can be built with all modern compilers:

! hello.f90
program main
    print '(a)', 'Hello, World!'
end program main

Save the source code to hello.f90, and execute GNU Fortran:

$ gfortran10 -o hello hello.f90

In most cases, the name of the compiler binary is just gfortran, without the explicit version number.

The binaries compiled with GNU Fortran may depend on the shared runtime library /usr/local/lib/gcc10/libgfortran.so (path may differ), unless the flag -static-libgfortran is used. Make sure to link libgfortran.a statically in order to run the executable on systems without the respective GNU Fortran compiler version installed.

The example program will be accepted by other compilers as well, like Flang:

$ flang -o hello hello.f90

Or, Intel:

$ ifort -o hello hello.f90

All compilers output a binary hello, we can execute:

$ ./hello
Hello, World!

Compiler Flags

The behaviour of a compiler can be controlled by command-line flags. The table describes some of the flags of GNU Fortran.

Parameter Description
-Wall Enable all warnings.
-Werror Turn all warnings into errors.
-c Compile source code without linking.
-g Generate debug information to be used by a debugger (GDB).
-o file Set file name of output to file.
-I path Add path to the include search path.
-J path Specifies where to put Fortran .mod files for compiled modules.
-L path Add path to the library search path.
-On Level of optimisation to use, where n = 0, 1, 2, 3.
-Os Optimise for binary size.
-Wl,-rpath=path Set runtime library search path to path, for example, -Wl,-rpath=/usr/local/bin/gcc10/. May be required for older versions of GNU Fortran on FreeBSD.
-std=std Set Fortran language standard to std, which may be one of f95, f2003, f2008, f2018, gnu, or legacy. The default value is gnu.
-fbackslash Change the interpretation of backslashes in string literals from a single backslash character to C-style escape characters.
-ffree-form Force free-format. Required for Fortran ≥ 90 source code files with ending .f.
-ffree-line-length-n Set column after which characters are ignored in typical free-form lines in the source file. The default value is 132. n may be none, meaning that the entire line is meaningful. -ffree-line-length-0 is the same as -ffree-line-length-none. Use if lines have more than 132 characters.
-fimplicit-none Specify that no implicit typing is allowed, unless overridden by explicit implicit statements. This is the equivalent of adding implicit none to the start of every program or module.
-fmax-errors=n Limit the maximum number of error messages to n. If n is set to 1, compilation aborts after the occurrence of the first error.
-static-libgfortran Compile libgfortran statically into the executable. Otherwise, the dynamically linked library must be present in the library search path in order to run the program.

On FreeBSD, it is often necessary to set the include and library search paths manually, if a program is linked against a system-wide installed library, for instance:

$ gfortran10 -I/usr/local/include/ -L/usr/local/lib/ -o example example.f90 -lX11

Preprocessor & Conditional Compilation

A preprocessor can be invoked before the actual compilation process to include predefined macros and to allow conditional compilation upon these macros. GNU Fortran passes the source code to the GNU C Preprocessor (CPP) in the traditional mode, if the argument -cpp is given. The Intel Fortran compiler includes a similar preprocessor, called FPP, that is enabled with command-line parameter -fpp. Other generic and Fortran-specific preprocessors are available. See the Fortran Wiki for an overview.

Macros

The GNU C Preprocessor lets us generate a list of all predefined macros in the used GNU Fortran version:

$ touch empty.f90
$ gfortran10 -cpp -E -dM empty.f90

GNU Fortran 10 on FreeBSD (amd64) outputs:

#define __ATOMIC_ACQUIRE 2
#define __CHAR_BIT__ 8
#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __ORDER_PDP_ENDIAN__ 3412
#define __GFC_REAL_10__ 1
#define __FINITE_MATH_ONLY__ 0
#define __GNUC_PATCHLEVEL__ 0
#define __GFC_INT_2__ 1
#define __SIZEOF_INT__ 4
#define __SIZEOF_POINTER__ 8
#define __GFORTRAN__ 1
#define __GFC_REAL_16__ 1
#define __STDC_HOSTED__ 0
#define __NO_MATH_ERRNO__ 1
#define __SIZEOF_FLOAT__ 4
#define _LANGUAGE_FORTRAN 1
#define __SIZEOF_LONG__ 8
#define __GFC_INT_8__ 1
#define __SIZEOF_SHORT__ 2
#define __GNUC__ 10
#define __SIZEOF_LONG_DOUBLE__ 16
#define __BIGGEST_ALIGNMENT__ 16
#define __ATOMIC_RELAXED 0
#define _LP64 1
#define __GFC_INT_1__ 1
#define __ORDER_BIG_ENDIAN__ 4321
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __SIZEOF_SIZE_T__ 8
#define __SIZEOF_DOUBLE__ 8
#define __ATOMIC_CONSUME 1
#define __GNUC_MINOR__ 2
#define __GFC_INT_16__ 1
#define __LP64__ 1
#define __ATOMIC_SEQ_CST 5
#define __SIZEOF_LONG_LONG__ 8
#define __ATOMIC_ACQ_REL 4
#define __ATOMIC_RELEASE 3
#define __VERSION__ "10.2.0"

The Intel Fortran Compiler 19 on Linux includes macros similar to the following (depending on version and platform):

$ touch empty.f90
$ ifort -dryrun -E -fpp empty.f90 &>&1 | grep -e '-D' | sed 's/^\s\+-D//g' | sed 's/\\//g'
__INTEL_COMPILER=1910
__INTEL_COMPILER_UPDATE=0
__unix__
__unix
__linux__
__linux
__gnu_linux__
unix
linux
__ELF__
__x86_64
__x86_64__
__amd64
__amd64__
__INTEL_COMPILER_BUILD_DATE=20200306
__INTEL_OFFLOAD
__MMX__
__SSE__
__SSE_MATH__
__SSE2__
__SSE2_MATH__
__pentium4
__pentium4__
__tune_pentium4__

Example

The example implements preprocessor conditionals in module os to set the type of operating system (either Windows, macOS, Linux, or FreeBSD) at compile time. The function os_type() then returns an integer indicating the current platform, using #if defined conditionals.

! os.f90
module os
    implicit none
    private

    integer, parameter, public :: OS_UNKNOWN = 0
    integer, parameter, public :: OS_WINDOWS = 1
    integer, parameter, public :: OS_MACOS   = 2
    integer, parameter, public :: OS_LINUX   = 3
    integer, parameter, public :: OS_FREEBSD = 4

    public :: os_type
contains
    pure function os_type()
        integer :: os_type

#if defined (WIN32) || defined (_WIN32) || defined (__WIN32__) || defined (__NT__)
        os_type = OS_WINDOWS
#elif defined (__APPLE__)
        os_type = OS_MACOS
#elif defined (__linux__)
        os_type = OS_LINUX
#elif defined (__FreeBSD__)
        os_type = OS_FREEBSD
#else
        os_type = OS_UNKNOWN
#endif
    end function os_type
end module os

The example program example.f90 has to import the module os and call the function os_type() to get the operating system the source code was compiled on:

! example.f90
program main
    use :: os
    implicit none
    integer :: current_os

    print '(a)', 'Current Operating System'
    print '(a)', repeat('-', 24)

    current_os = os_type()

    select case (current_os)
        case (OS_WINDOWS)
            print '("Name: ", a)', 'Microsoft Windows (Cygwin, MSYS2)'

        case (OS_MACOS)
            print '("Name: ", a)', 'macOS'

        case (OS_LINUX)
            print '("Name: ", a)', 'GNU/Linux'

        case (OS_FREEBSD)
            print '("Name: ", a)', 'FreeBSD'

        case default
            print '("Name: ", a)', 'Unknown OS'
    end select
end program main

As CPP, the preprocessor of GNU Fortran, is not indicating the current operating system, we have to pass the macro through argument -D, in this particular case, -D__FreeBSD__:

$ gfortran10 -cpp -D__FreeBSD__ -c os.f90
$ gfortran10 -o example example.f90 os.o
$ ./example
Current Operating System
------------------------
Name: FreeBSD

Using the Intel Fortran compiler on Linux, the argument can be omitted, as the macro __linux__ is predefined:

$ ifort -fpp -c os.f90
$ ifort -o example example.f90 os.o
$ ./example
Current Operating System
------------------------
Name: GNU/Linux

References