[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