[CMake] Fortran-C mixed code (possible solution)

Radu Serban radu at llnl.gov
Fri Jan 19 14:29:47 EST 2007


Arjen Markus wrote:
>> But knowing the Fortran name mangling scheme is still crucial (if
>> interested,  see below why). For now I rely on the user to provide the
>> case (lower or upper)  and number of appended underscores (none, one,
>> or two) which determine it. The  defaults I provide (lower case with
>> one underscore) is probably the most common  one, but I'd be much
>> happier if I could automatically determine it at  configuration time...
>> Any suggestions?
>>

> Now the fun part:
> You can simply provide _both_ of these wrappers in your Fortran-compatible
> library. The Fortran compiler will generate a routine with a name that
> depends on its mangling scheme, but the linker will pick out the right
> version.
> 
> The only problem left is Fortran compilers that produce the same
> mangled name but different methods for passing the length of character
> strings.
> Of course, the compatibility library will be bit larger, but it solves
> your problem in what I think an elegant way.

Hi Arjen,

That's not what I want for several reasons:
1) I really think this is something that the build system should take care of. I 
intend to provide cmake only as an alternative to autotools and determining the 
Fortran name mangling scheme with the later is no problem.
2) A typical Fortran-C interface library (and we provide such libraries for 3 
different solvers) contains around 30 different functions which would then 
require 180 wrappers (for all combinations lower/upper case and none/one/two 
underscores)! A nightmare of (unnecessary) code duplication.
3) We distribute our solvers open source and we'd like to keep the sources as 
clean and readable as possible.

In any case, I think I have a solution. I seems to work, but then I've only 
tried it on my Linux box. It's probably not the cleanest implementation (I've 
only started using cmake very recently). I copied the relevant portion of my 
CMakeLists.txt below. I'd appreciate any comments and suggestions, both on 
whether it can be simplified/cleaned-up and also on whether it's portable.

Thanks,
--Radu

   # Enable Fortran support
   ENABLE_LANGUAGE(Fortran)

   # Determine the Fortran name-mangling scheme. We do this by:
   #  1) create a library from a Fortran source file which defines a function 
"sundials"
   #  2) attempt to link with this library a C source file which calls the 
"sundials"
   #     function using various possible schemes (6 different schemes, corresponding
   #     to all combinations lower/upper case and none/one/two underscores)

   SET(HAVE_SCHEME FALSE)

   # Create a CMakeLists.txt file which will generate the "flib" library
   FILE(WRITE ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CMakeLists.txt
     "PROJECT(FortranTest Fortran)\n"
     "SET(CMAKE_VERBOSE_MAKEFILE ON)\n"
     "ADD_LIBRARY(flib ftest.f)\n"
     )

   # Create a simple Fortran source which defines the subroutine "sundials"
   FILE(WRITE ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/ftest.f
     "        SUBROUTINE sundials\n"
     "        RETURN\n"
     "        END\n"
     )

   # Use TRY_COMPILE to make the target "flib"
   TRY_COMPILE(
     FTEST_OK
     ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
     ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
     flib
     OUTPUT_VARIABLE MY_OUTPUT
     )

   # Continue only if we were successful in creating the "flib" library
   IF(FTEST_OK)

     # Overwrite CMakeLists.txt with one which will generate the "ctest" executable
     FILE(WRITE 
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CMakeLists.txt
       "PROJECT(FortranTest C)\n"
       "#SET(CMAKE_VERBOSE_MAKEFILE ON)\n"
       "ADD_EXECUTABLE(ctest ctest.c)\n"
       "TARGET_LINK_LIBRARIES(ctest \"-L. -lflib\")\n"
       )

     # Define the list "options" of all possible schemes that we want to consider
     # Get its length and initialize the counter "iopt" to zero
     SET(options sundials sundials_ sundials__ SUNDIALS SUNDIALS_ SUNDIALS__)
     LIST(LENGTH options imax)
     SET(iopt 0)

     # We will attempt to sucessfully generate the "ctest" executable as long as
     # there still are entries in the "options" list
     WHILE(${iopt} LESS ${imax})

       # Get the current list entry (current scheme)
       LIST(GET options ${iopt} opt)

       # Generate a simple C source which calls the "sundials" function using the
       # current scheme
       FILE(WRITE ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/ctest.c
         "int main(){${opt}();return(0);}\n"
         )

       # Use TRY_COMPILE to make the "ctest" executable from the current C source
       # and linking to the previously created "flib" library.
       # QUESTION: Is this method of linking the "flib" library portable?
       TRY_COMPILE(
         CTEST_OK
         ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
         ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
         ctest
         CMAKE_FLAGS -DLINK_LIBRARIES:STRING="-L. -lflib"
         OUTPUT_VARIABLE MY_OUTPUT
         )

       # To ensure we do not use stuff from the previous attempts, we must 
remove the
       # CMakeFiles directory.
       # QUESTION: I didn't think I'll have to do this, but it doesn't work 
otherwise
       FILE(REMOVE_RECURSE 
${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CMakeFiles)

       MESSAGE("${iopt}  ${opt}  ... ${CTEST_OK}")

       # Test if we successfully created the "ctest" executable.
       # If yes, flag that we have successfuly determined the name mangling scheme,
       # save the current scheme, and set the counter "iopt" to "imax" so that we
       # exit the while loop.
       # Otherwise, increment the counter "iopt" and go back in the while loop.
       IF(CTEST_OK)
         SET(SCHEME ${opt})
         SET(HAVE_SCHEME TRUE)
         SET(iopt ${imax})
       ELSE(CTEST_OK)
         MATH(EXPR iopt ${iopt}+1)
       ENDIF(CTEST_OK)

     ENDWHILE(${iopt} LESS ${imax})

   ENDIF(FTEST_OK)

   IF(NOT HAVE_SCHEME)
     message("Unable to determine Fortran name mangling scheme")
   ENDIF(NOT HAVE_SCHEME)


More information about the CMake mailing list