Meson & GFortran for Old Code

Hi everyone,

First of all thank you for taking the time to read my post. I am currently working on compiling an old code with meson and the gnu compilers. While I have been able to build the first two static libraries, I have sadly run into trouble on the main source and am hoping that some of you might have a solution / helpful pointers.

I am working off from an old Makefile, which is non-functional by now as it mixed explicit and implicit syntax. Drawing from the Makefile:

PP        = /lib/cpp
PFLAGS    = -P
F90       = mpif90 
CC        = mpicc  

FFLAGS    = -c
OPTIONS   = -D GFORTRAN                                                    \
               -O3 -fdefault-integer-8 -fdefault-real-8 -fno-range-check      \
               -ftree-vectorize -funroll-all-loops -cpp
LOPTIONS  = -fopenmp -g -fbacktrace
$(TARGET):   $(LIBS) $(OBJS)
	    @$(PF90) $(FFLAGS) $(OPTIONS) -o $(OBJDIR)/<main-file-name>.o <main-file-name>.f
	    @echo "COMPILE END   := `date "+%T"`        " >> $(INFOFILE)
	    @$(PF90) -o $(TARGET) $(LOPTIONS) $(OBJS) $(LIBRARY) 

I have now gone and transported this into meson with the usual mpi dependencies, as well as adding the compiler arguments for the Fortran compiler.

# Get default compiler configurations
if fc.get_id() == 'gcc'
    add_project_arguments('-fdefault-real-8', language: 'fortran')
    add_project_arguments('-fdefault-double-8', language: 'fortran')
    add_project_arguments('-fdefault-integer-8', language: 'fortran')
    add_project_arguments('-ffree-line-length-none', language: 'fortran')
    add_project_arguments('-fbacktrace', language: 'fortran')
    add_project_arguments('-DLINUX', language: 'cpp')
    add_project_arguments('-DLINUX64', language: 'cpp')
    # Specify a fortran standard to the compiler
    add_project_arguments('-std=legacy', language: 'fortran')
    # Suppress warnings to find the actual errors
    add_project_arguments('-w', language: 'fortran')
endif

And the main build argument for the executable as

executable(
    'NAME',
    sources: srcs,
    link_with: [lib_1, lib_2],
    fortran_args: ['-fno-range-check', '-ftree-vectorize', '-funroll-all-loops', '-cpp'],
    dependencies: mpif,
)

Today I was butting heads with the compiler and eventually ended up at a syntax error argument of gfortran, i.e.

Error: Syntax error in argument list at (1)
../source/filename.f:836:72:
  836 |                call FN-NAME        ( __FILE__, __LINE__

I understand that __FILE__, especially with the double-underscore is reserved for gfortranā€™s internal use, and I need to use a preprocessor to make this amenable to gfortran, but am unsure how to actually implement that.

Would be really glad for any pointers and help :)))

Gfortran follows the convention that a file suffix of .F or .F90 indicates that the source file should be preprocessed before being handed to the compiler. Your source file, as shown in the error message, has .f as the file suffix. Change the case of the file suffix to upper case.

1 Like

If you run

gfortran -E gfortran.F90

on the following file:

"__FILE__" __FILE__
"__DATE__" __DATE__ "__LINE__" __LINE__ "__TIMESTAMP__" __TIMESTAMP__

"__GFORTRAN__" __GFORTRAN__ "_LANGUAGE_FORTRAN" _LANGUAGE_FORTRAN
"__VERSION__" __VERSION__ "__GNUC__" __GNUC__ "__GNUC_MINOR__" __GNUC_MINOR__ "__GNUC_PATCHLEVEL__" __GNUC_PATCHLEVEL__

"__BYTE_ORDER__" __BYTE_ORDER__ "__ORDER_BIG_ENDIAN__"
__ORDER_BIG_ENDIAN__ "__ORDER_LITTLE_ENDIAN__" __ORDER_LITTLE_ENDIAN__ "__ORDER_PDP_ENDIAN__" __ORDER_PDP_ENDIAN__

"__ATOMIC_ACQ_REL" __ATOMIC_ACQ_REL "__ATOMIC_ACQUIRE" __ATOMIC_ACQUIRE "__ATOMIC_CONSUME" __ATOMIC_CONSUME
"__ATOMIC_RELAXED" __ATOMIC_RELAXED "__ATOMIC_RELEASE" __ATOMIC_RELEASE "__ATOMIC_SEQ_CST" __ATOMIC_SEQ_CST
"__BIGGEST_ALIGNMENT__" __BIGGEST_ALIGNMENT__ "__CHAR_BIT__" __CHAR_BIT__ "__FINITE_MATH_ONLY__" __FINITE_MATH_ONLY__
"__FLOAT_WORD_ORDER__" __FLOAT_WORD_ORDER__ "__GFC_INT_1__" __GFC_INT_1__ "__GFC_INT_16__" __GFC_INT_16__
"__GFC_INT_2__" __GFC_INT_2__ "__GFC_INT_8__" __GFC_INT_8__ "__GFC_REAL_10__" __GFC_REAL_10__
"__GFC_REAL_16__" __GFC_REAL_16__ "__LP64__" __LP64__ "_LP64" _LP64
"__NO_MATH_ERRNO__" __NO_MATH_ERRNO__ "__pic__" __pic__ "__PIC__" __PIC__
"__pie__" __pie__ "__PIE__" __PIE__ "__SIZEOF_DOUBLE__" __SIZEOF_DOUBLE__
"__SIZEOF_FLOAT__" __SIZEOF_FLOAT__ "__SIZEOF_INT__" __SIZEOF_INT__ "__SIZEOF_LONG__" __SIZEOF_LONG__
"__SIZEOF_LONG_DOUBLE__" __SIZEOF_LONG_DOUBLE__ "__SIZEOF_LONG_LONG__" __SIZEOF_LONG_LONG__ "__SIZEOF_POINTER__" __SIZEOF_POINTER__
"__SIZEOF_SHORT__" __SIZEOF_SHORT__ "__SIZEOF_SIZE_T__" __SIZEOF_SIZE_T__ "__STDC_HOSTED__" __STDC_HOSTED__

you might also find the commands

touch empty.F90 empty.c
gfortran  -E -dM  empty.F90
gcc -cpp -E -dM empty.c

useful; which indicate they will display all the predefined macros, but they do not show names
such as LINE, TIMESTAMP, and others collected in the example ā€œgfortran.F90ā€ file last time I checked (hence the collection into the file ā€œgfortran.F90ā€.).

Implemented this change, but have not seen any noteworthy difference so far tbhā€¦

Here an example compiler command for a file:

gfortran -I<name>.p -I. -I.. -Ilib1.a.p -Ilib2.a.p -I/usr/local/include -I/usr/local/lib -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall -O3 -fdefault-real-8 -fdefault-double-8 -fdefault-integer-8 -ffree-line-length-none -fbacktrace -std=legacy -w -pthread -fno-range-check -ftree-vectorize -funroll-all-loops -cpp -J<name>.p -o <name>.p/source_<name>_ordnung.F.o -c ../source/<name>_ordnung.F

Which is throwing this error

150 |      &                               , action = 'WRITE'
      |                                                        1
Error: Syntax error in OPEN statement at (1)

originating from the following statement in the code, the buffer specs are commented out as they also threw errorsā€¦

open( unit = io , file = ioname, status = 'replace'
     &                               , action = 'WRITE'
!    &                               , buffered = 'YES'
!    &                               , buffercount = 2
!    &                               , blocksize = 100000
     &                               , iostat = io_stat )

With a second example for a different file

gfortran -I<name>.p -I. -I.. -Ilib1.a.p -Ilib2.a.p -I/usr/local/include -I/usr/local/lib -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall -O3 -fdefault-real-8 -fdefault-double-8 -fdefault-integer-8 -ffree-line-length-none -fbacktrace -std=legacy -w -pthread -fno-range-check -ftree-vectorize -funroll-all-loops -cpp -J<name>.p -o <name>.p/source_<name>_rom.F.o -c ../source/<name>_rom.F

throwing the aforementioned error on the call to the function

../source/<name>_rom.F:228:72:

  228 |      & call FN-NAME(__FILE__,__LINE__,'Check no. of eigenvalues')
      |                                                                        1
Error: Missing ā€˜)ā€™ in statement at or before (1)

with the corresponding code

if( noeig <= 0 )
     & call FN-NAME(__FILE__,__LINE__,'Check no. of eigenvalues')

Iā€™m butting my head and looking through all the available resources out there, but remain really puzzled by some of the errorsā€¦

I donā€™t know if this is the problem, but if you are using free source form, & should appear at the end of continuation lines.

1 Like

I just took out the free source form statement, it seems to have had no bearing as the error still appear in the same way. But it made the compiler statement shorter and eliminated possible errors resulting from it.

Thank you!

It makes it harder for someone to respond when you give incomplete fragments of code or just the compiler error messages that may be related to those fragments. In particular, the declarations of the variables involved need to be provided. When fixed source form is being used, the fragments of code should be shown with the columns preserved.

I have reconstructed a minimal source file from the information that you have provided. First, note that I have commented out some of the clauses in the OPEN statement that are not standard and not supported as extensions by Gfortran. Second, hyphens are not allowed within variable and subprogram names, so FN-NAME is not allowed; I replaced the ā€˜-ā€™ by ā€˜_ā€™ .

      program xlpae
      implicit none
      integer :: io = 11, io_stat,noeig
      character(8) :: ioname = 'data.dat'
      open( unit = io , file = ioname, status = 'replace'
     &                               , action = 'WRITE'
!     &                               , buffered = 'YES'  ! non-standard
!     &                               , buffercount = 2  ! non-standard
!     &                               , blocksize = 100000  ! non-standard
     &                               , iostat = io_stat )
      print *,'IO_STAT = ',io_stat
      if( noeig <= 0 )
     & call FN_NAME(__FILE__,__LINE__,'Check no. of eigenvalues')
      end program

Gfortran compiles this file without error messages, and that is not surprising.

Another observation: you mentioned ā€˜Mesonā€™ in the title, and stated that you were ā€œworking on compiling an old code with meson and the gnu compilersā€. I do not have Meson, but I know that it is a build tool and not a compiler. My first inclination upon reading the title was to skip the thread, but reading with a little skepticism indicated that Meson was not probably involved at all as far as the build errors were concerned.

1 Like

It looks like your makefile is doing something else than what you want meson to do. To build a source in your makefile you are apparently invoking cpp and than mpif90 for a fixed format source file ending in .f (Iā€™m guessing a bit here, because I donā€™t have the complete picture).

While you tell make how you are building your project, you have to tell meson what it should do for you. Your meson build file only contains the information that you want to compile a couple of .f files to an executable, but there is no information on the preprocessor. Preprocessing manually in meson like you might do in make is not recommended.

Instead, adding the -cpp flag in case you are working with GFortran would help to build your project correctly

# Enable preprocessing for all source files with GFortran
if fc.get_id() == 'gcc'
  add_project_argument(
    '-cpp',
    language: 'fortran',
  )
endif

Alternatively, renaming your source files from .f to .F, which implies for most compiler that they should preprocess the file first, would be another option.

1 Like

@mecej4 @awvwgk after working through all the compilation problems over the past few days, and getting all the codeā€™s components to compile (but not link yet) I fully agree that the question was mostly ill-posed. To preserve some of the learned lessons for someone else facing a similar problem in the future Iā€™ll try to go through what happened here:

Removing all the buffer-information from all the open statements was the first statement. The syntax originates from the Intel-compiler, and one way to preserve the statement for compilation with Intel would be

open( unit = io , file = ioname, status = 'replace'
     &                               , action = 'WRITE'
#ifdef INTEL
     &                               , buffered = 'YES'  ! non-standard
     &                               , buffercount = 2  ! non-standard
     &                               , blocksize = 100000  ! non-standard
#endif
     &                               , iostat = io_stat )

As @mecej4 rightfully pointed out, the GNU compiler toolchain does not recognise the buffer-related arguments to open.

So why did the compiler complain about a syntax error in Write? After deeper digging in the code I found __FILE__ being defined as the absolute path to the files, which depending on the file system can become a quite lengthy string. So long in fact, that it blows up FORTRANā€™s constraints on the length of a line. It is not optimal, but a way to hack around this behaviour is to force __FILE__ to be an arbitrary, short string i.e.

-D__FILE__="abc"

that way the statement is once again within FORTRANā€™s line limits and compilation progresses smoothly.

In terms of the used compilation flags, I basically had to add

executable(
    ...
    fortran_args:['-D__FILE__="abc"', ...]
)

Thank you everyone for all your help!

There is still some issue with the cross-language linking, i.e. the executable (FORTRAN) is dependent on a C++ library & a FORTRAN library, where the C++ library is not found at linking time, but that is actually a meson problem and as such I will break out it out into a separate, way more specific, topic.

1 Like

Another option would be to pass the flag to turn off the line length limit (which I believe the next standard will also remove the line length limit), -ffree-line-length-none. That wonā€™t have the downside of ruining your (presumably) debug information.

Judging by the commands @lpaehler provided in the original post, -ffree-line-length-none was set in both cases.

I think it remains unclear whether the source files are fixed or free form. Gfortran also has the flag -ffixed-line-length-none, which might be the one needed.

Edit: You should be careful to respect differences between preprocessors as a current (& future) source of non-portabilityā€¦ E.g. the Intel Fortran fpp preprocessor will try to fold lines, if they exceed the limit:

If, during expansion of a macro, the column width of a line exceeds column 72 (for fixed format) or column 132 (for free format), fpp inserts appropriate Fortran continuation lines.

Flang developers have made a summary of the Fortran preprocessing behavior across several compilers: Fortran Preprocessing ā€” The Flang Compiler

2 Likes

On this matter, note the next standard revision is only slated to increase the maximum length of a line to 10,000 characters, not remove the limit.

The standard will likely state, ā€œA line shall contain at most ten thousand characters.ā€

Ahh yes, the age old ā€œnobody will ever exceed this limitā€

2 Likes