wavebitscientific / datetime-fortran

Date and time manipulation for modern Fortran
MIT License
137 stars 51 forks source link

"undefined reference" when using datetime-fortran #56

Closed RuiApostolo closed 5 years ago

RuiApostolo commented 5 years ago

Hi,

I'm a relatively new fortran user, with little experience using external libraries. I'm on Ubuntu 16.04 with gfortran 5.4.0 I installed datetime-fortran using the instructions:

git clone https://github.com/wavebitscientific/datetime-fortran
cd datetime-fortran
autoreconf -i
./configure
make check
make install prefix=/home/rfapostolo/code/datetime-fortran

However, when I try to run anything using the module, I get "undefined reference to ...", MWE using the first example:

test_dt.f08

program test_dt
use datetime_module,only:datetime

      type(datetime) :: a

      a = datetime(2013,1,1)
      write(*,*)a % isocalendar() ! Prints: 2013  1  2

end program test_dt

trying to compile with:

f95 -o test_dt  -g -I/home/rfapostolo/code/datetime-fortran/include -L/home/rfapostolo/code/proper/datetime-fortran/lib test_dt.f08

Results in:

/tmp/ccMAIMs7.o: In function `MAIN__':
/home/rfapostolo/Dropbox/code/proper/propc/test_dt.f08:6: undefined reference to `__mod_datetime_MOD_datetime_constructor'
/home/rfapostolo/Dropbox/code/proper/propc/test_dt.f08:7: undefined reference to `__mod_datetime_MOD___vtab_mod_datetime_Datetime'
/home/rfapostolo/Dropbox/code/proper/propc/test_dt.f08:7: undefined reference to `__mod_datetime_MOD_isocalendar'
collect2: error: ld returned 1 exit status

I'd really appreciate if you could point me to my mistake.

zbeekman commented 5 years ago

The issue is your compilation line: you pass a bunch of flags, but none to actually link in the datetime library.

Look inside /home/rfapostolo/code/proper/datetime-fortran/lib for a file named something like libdatetime.a (or ending in .so etc.)

If you find that library, say libdatetime.a, then you should add the following flag to your compile line after the -L/home/... flag: -ldatetime (Or just pass the absolute path to the library... that should work too.)

Let me break down the flags that you are currently passing and what they do:

RuiApostolo commented 5 years ago

HI again,

I tried adding the -ldatetime flag but the result was the same. I tried linking the library directly with L/.../lib/libdatetime and L/.../lib/libdatetime.a, but again got the same result.

I really appreciate the help and I'm sorry for wasting your time with this.

milancurcic commented 5 years ago

Hi Rui,

Does the following work? (put the program file before the linking flag)

f95 test_dt.f08 -o test_dt  -g -I/home/rfapostolo/code/datetime-fortran/include -L/home/rfapostolo/code/proper/datetime-fortran/lib -ldatetime

I remember reading the explanation for this somewhere, but can't remember now from the top of my head. When compiling and linking on the same line, put the source file first, linking flags and paths second.

zbeekman commented 5 years ago

Hi Rui,

I'm not sure what's going on on your system. For me, I installed datetime-fortran through mac Homebrew (brew install datetime-fortran), and was able to compile your test program as follows:

$ gfortran -o test_dt -g -I/usr/local/opt/datetime-fortran/include -L/usr/local/opt/datetime-fortran/lib test_dt.f90 -ldatetime
$ ./test_dt
        2013           1           2

It's possible that you need to list the library to the right of your source file due to the way the static linker works on some OSes.

Also, I would recommend that:

  1. You name any modern (free source form, Fortran 90 onwords) Fortran source file with a .f90 file extension
  2. You call GFortran or whatever your Fortran compiler is directly, not through some system stub like f95
  3. You confirm that datetime-fortran was compiled with the same compiler being used to build and link your code: Fortran .mod files are not compatible between compilers, and sometimes even between major releases of the same compiler.

If you have installed datetime-fortran into a default system location, or configure package-config to find the pc file for datetime-fortran you can get the include flags and the link flags needed to build against it using pkg-config:

gfortran -o test_dt -g $(pkg-config --cflags datetime-fortran) test_dt.f90 $(pkg-config --libs datetime-fortran)

zbeekman commented 5 years ago

I remember reading the explanation for this somewhere, but can't remember now from the top of my head. When compiling and linking on the same line, put the source file first, linking flags and paths second.

It depends on the OS. On some older linux distros (and ubuntu undid the GCC patch for this... 🙄) the static linker is a bit finicky. Here's how it works:

  1. Process arguments from left to right
  2. Look at the object code (i.e. output of compiling test_dt.f90 or the current library archive file) of the thing being examined
  3. Keep any used symbols, symbols needed by other entities already processed (to the left) of this entity
  4. Construct a list of unresolved symbols and entities to search for when processing items to the right
  5. Discard any unused symbols and entities

This means that if a library or program needs to call a procedure declared in a different library, then it should appear to the left of that other library on the link line.

RuiApostolo commented 5 years ago

Hi @zbeekman and @milancurcic ,

I had no idea the compiler was reading the symbols left to right, but I can't say it doesn't make sense. That was my problem, and having my code before the library worked perfectly.

Thank you very much for your help and suggestions, and apologies for the stupid mistake.

zbeekman commented 5 years ago

No worries, the left-to-right thing bites even the most experienced programmers from time to time.

The linker does this to increase speed and decrease memory footprint IIRC, and on macOS and some distros it's a complete non-issue. But on certain/most linux distros the linker throws out un-needed symbol info and definitions as it processes, only keeping a table of symbols that have been seen, but not yet defined in memory as it processes the files. When you link dynamically the order doesn't matter, but then you need to find a sane way to deal with loading the libs at runtime.