In our project we first create a static library of all the lower level Fortran subroutines and create a library "EMsoftLib". Then since we have a bunch of executables each are linked to the "EMsoftLib" static Fortran library. This sort of works with shared libraries but today I was trying to chase down something else and decided to try and create a static library instead. Now when the compiler process (driven through CMake) gets to the linker pahse I get a number of LNK2019: unresolved external symbol. Each of the unresolved symbols are located in the EMsoftLib.
I come from the C++ world where I understand what to do about this error. I have checked over the command line that is generated by CMake and I see an argument in there for Bin/EMsoftLib.lib to link against.
Is there anything special we need to do to the Fortran source to export the symbols? We have added the !DEC$ ATTRIBUTES DLLEXPORT ::XXXXXX comments for the shared library versions. Is there some intermediate step that needs to be done?
There is no special step to take, but you wouldn't need DLLEXPORT directives for a static library.
From a command prompt you can do:
dumpbin -symbols libraryname.lib
to get a list of all the symbols it defines (and uses).
I didn't think there should be any thing special to do. I'm still working through some issues from the cmake side of things and I the current cmake codes may be forcibly clobbering some compiler flags. I just needed that sanity check to ensure that the fortran world is the same as the C/C++ world when it comes to compiling and linking.
That's a rather vague statement - there are some differences (especially if you use Fortran modules) - but the basics of compiling and linking are the same.
What I meant by the statement is that Fortran/C/C++ all go through some sort of pre-process step, compile step, link step. General link errors such as "unresolved symbols" mean the same between Fortran and C. It means that what I am trying to build can not resolve some symbols that were built in another compile unit. This usually means something like a missing argument to the link line versus a compile error where the compiler can not find a declaration to a function or subroutine which means a missing include argument.
For DLLs on Windows one needs to put in the special comment with the !DEC ATTRIBUTES. In C/C++ on MSVC there are similar "compile export macros" that are used. Similar ideas.
We went back to building shared libraries as the static library builds just are not really needed.
As always, appreciate the help and sanity checks.
... General link errors such as "unresolved symbols" mean the same between Fortran and C. It means that what I am trying to build can not resolve some symbols that were built in another compile unit. This usually means something like a missing argument to the link line versus a compile error where the compiler can not find a declaration to a function or subroutine which means a missing include argument.
That is true only if no name decoration is being performed for external symbols. You may have the name mysub in C and Fortran sources, but the C compiler may decorate that name to _mysub and the Fortran compiler to MYSUB. Such inconsistencies are best handled with directives/pragmas in the source files, or compiler options, rather than by using rename scripts for the linker.
Furthermore, getting the names to match at link time is no guarantee that the calling conventions are compatible. The earlier that errors can be caught in the compile-link-run sequence, the better.
Absolutely correct. I was just speaking in general terms. It is one of the largest areas of frustration that the designers of the languages could not agree on calling conventions which are then exacerbated by compiler vendors adding their own "pixie dust". I used to stay away from Fortran like the plague but once my collaborator (who writes Fortran) and I discovered the Fortran 2003 standard and the ISO_C_* stuff that changed overnight. We updated the code base to F2003 standard utilized the ISO_C_* standard everywhere we could. After that a simple C Header file that declares the Fortran functions is easy to maintain so that bridging from our Qt5 based UI into the Fortran code is simple and straight forward. I don't dive much into the Fortran side of things unless I need to so I am still very new to how some of the Fortran compilation works on each platform. Note that we use IFort on Windows, GFortran & IFort on macOS and GFortran on Linux. Mostly there are not many problems between the platforms and compilers but every once in a while something odd pops up. We keep continuous builds going on macOS and Windows and push out nightly builds of the software. All in an attempt to ensure that changes are cross platform and cross compiler correct.
Now.. if I could actually debug *into* the Fortran sections that would be spectacular (on Windows...)
This is what Fortran's C Interoperability features are intended to address. When you declare a procedure with the BIND(C) attribute, the standard specifies that the external name is downcased and then any naming conventions used by the "companion C processor" are applied. If you want mixed-case, there's a NAME= specifier on BIND. This also regulates how arguments are passed to be compatible with C, with restrictions such that anything C can't directly handle is prohibited. (The F2018 "C descriptor" features expand what you can do here.)
Ideally you would not use the non-standard directives, and that's why the Intel documentation relegates them to a "legacy" section.