Preprocessor

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.

Usually, a Fortran source file with file ending in upper-case (.F90 instead of .f90) indicates preprocessor macros to the compiler. The command-line flag can be omitted for such files. 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
$ gfortran13 -cpp -E -dM empty.f90

GNU Fortran 12 on FreeBSD outputs, for example:

#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__ 12
#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__ 1
#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__ "12.1.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

    write (*, '("Current Operating System", /, a)') repeat('-', 24)
    write (*, '("Name: ")', advance='no')

    current_os = os_type()

    select case (current_os)
        case (OS_WINDOWS)
            write (*, '("Microsoft Windows (Cygwin, MSYS2)")')
        case (OS_MACOS)
            write (*, '("macOS")')
        case (OS_LINUX)
            write (*, '("GNU/Linux")')
        case (OS_FREEBSD)
            write (*, '("FreeBSD")')
        case default
            write (*, '("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__:

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

Using the Intel Fortran compiler on Linux, the argument is optional, 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

The compiler flag is not required for source files with endings in upper-case:

$ mv os.f90 os.F90
$ ifort -c os.F90
$ ifort -o example example.f90 os.o

References